From 365fa8d3a6f6cdc63d37e005bd5e20e345a4707c Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Sun, 17 Nov 2024 15:19:31 -0500 Subject: [PATCH 01/13] Add test --- .github/workflows/lint-test.yml | 30 +++++++++++++++++++++++++++ .github/workflows/lint.yml | 36 --------------------------------- ci/docker-compose.yml | 9 +++++++++ 3 files changed, 39 insertions(+), 36 deletions(-) create mode 100644 .github/workflows/lint-test.yml delete mode 100644 .github/workflows/lint.yml create mode 100644 ci/docker-compose.yml diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml new file mode 100644 index 0000000..b263361 --- /dev/null +++ b/.github/workflows/lint-test.yml @@ -0,0 +1,30 @@ +name: lint-test + +on: + push: + branches: + - '**' + +jobs: + lint-test: + name: lint+test + runs-on: ubuntu-latest + strategy: + matrix: + php-version: ["8.1", "8.2", "8.3"] + drupal-version: ["10.2", "10.3", "10.4", "11.0"] + exclude: + - drupal-version: "11.0" + php-version: "8.1" + - drupal-version: "11.0" + php-version: "8.2" + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: lint+test + working-directory: ci + run: docker compose up -d --quiet-pull + env: + PHP_VERSION: ${{ matrix.php-version }} + DRUPAL_VERSION: ${{ matrix.drupal-version }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 74bf9eb..0000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: lint - -on: - push: - branches: - -jobs: - phpcs: - name: Run PHPCS with Drupal Standards - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up PHP and Composer - uses: shivammathur/setup-php@v2 - with: - php-version: '8.3' - tools: phpcs - coverage: none - - - name: Install Drupal Coder Standards - run: | - composer global config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true - composer global require --no-interaction drupal/coder slevomat/coding-standard - COMPOSER_HOME=$(composer config --global home) - phpcs --config-set installed_paths \ - "$COMPOSER_HOME/vendor/drupal/coder/coder_sniffer,$COMPOSER_HOME/vendor/slevomat/coding-standard" - phpcs -i - - - name: Run PHPCS - run: phpcs --standard=Drupal --extensions=php,module,inc,install,test,profile,theme --ignore=vendor/ ./ - - - name: Run DrupalPractice - run: phpcs --standard=DrupalPractice --extensions=php,module,inc,install,test,profile,theme --ignore=vendor/ ./ diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml new file mode 100644 index 0000000..5bab7d9 --- /dev/null +++ b/ci/docker-compose.yml @@ -0,0 +1,9 @@ +networks: + default: +services: + drupal: + image: lehighlts/drupal-ci:${DRUPAL_VERSION}-php${PHP_VERSION} + volumes: + - ../:/var/www/drupal/web/modules/contrib/turnstile_protect + environment: + ENABLE_MODULES: turnstile_protect From 61cfa0003909527a3db2c246f8c3dca69fce2ff9 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Sun, 17 Nov 2024 15:29:44 -0500 Subject: [PATCH 02/13] Add a basic test --- .github/workflows/lint-test.yml | 10 +++--- ci/docker-compose.yml | 9 ----- composer.json | 20 +++++++++++ tests/src/Kernel/TurnstileProtectTest.php | 43 +++++++++++++++++++++++ 4 files changed, 68 insertions(+), 14 deletions(-) delete mode 100644 ci/docker-compose.yml create mode 100644 composer.json create mode 100644 tests/src/Kernel/TurnstileProtectTest.php diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index b263361..50d9a04 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -23,8 +23,8 @@ jobs: uses: actions/checkout@v4 - name: lint+test - working-directory: ci - run: docker compose up -d --quiet-pull - env: - PHP_VERSION: ${{ matrix.php-version }} - DRUPAL_VERSION: ${{ matrix.drupal-version }} + run: | + docker run \ + --env ENABLE_MODULES=turnstile_protect \ + -v $(pwd):/var/www/drupal/web/modules/contrib/turnstile_protect \ + lehighlts/drupal-ci:${{ matrix.drupal-version }}-php${{ matrix.php-version }} diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml deleted file mode 100644 index 5bab7d9..0000000 --- a/ci/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -networks: - default: -services: - drupal: - image: lehighlts/drupal-ci:${DRUPAL_VERSION}-php${PHP_VERSION} - volumes: - - ../:/var/www/drupal/web/modules/contrib/turnstile_protect - environment: - ENABLE_MODULES: turnstile_protect diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..374c1c4 --- /dev/null +++ b/composer.json @@ -0,0 +1,20 @@ +{ + "name": "drupal/turnstile_protect", + "description": "Put your site routes behind a Cloudflare Turnstile", + "type": "drupal-module", + "license": "GPL-2.0+", + "homepage": "https://www.drupal.org/project/turnstile_protect", + "support": { + "issues": "https://www.drupal.org/project/issues/turnstile_protect" + }, + "authors": [ + { + "name": "Joe Corall", + "email": "jjc223@lehigh.edu", + "role": "Owner" + } + ], + "require" : { + "drupal/turnstile": "^1" + } +} \ No newline at end of file diff --git a/tests/src/Kernel/TurnstileProtectTest.php b/tests/src/Kernel/TurnstileProtectTest.php new file mode 100644 index 0000000..50797d5 --- /dev/null +++ b/tests/src/Kernel/TurnstileProtectTest.php @@ -0,0 +1,43 @@ +assertTrue( + $this->container->get('module_handler')->moduleExists('turnstile_protect'), + 'The turnstile_protect module is enabled.' + ); + } + + /** + * Tests basic configuration or service. + */ + public function testBasicFunctionality() { + $config = $this->config('turnstile_protect.settings'); + $this->assertNotEmpty( + $config, + 'turnstile_protect settings configuration is available.' + ); + } + +} From e9d565772cf73b1cb13dbbdd0e578cf36e45cf65 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Mon, 18 Nov 2024 06:46:35 -0500 Subject: [PATCH 03/13] make version env for easier copy/paste locally --- .github/workflows/lint-test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index 50d9a04..b8cf374 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -27,4 +27,7 @@ jobs: docker run \ --env ENABLE_MODULES=turnstile_protect \ -v $(pwd):/var/www/drupal/web/modules/contrib/turnstile_protect \ - lehighlts/drupal-ci:${{ matrix.drupal-version }}-php${{ matrix.php-version }} + lehighlts/drupal-ci:${DRUPAL_VERSION}-php${PHP_VERSION} + env: + DRUPAL_VERSION: ${{ matrix.drupal-version }} + PHP_VERSION: ${{ matrix.php-version }} From a0c051bb8be48c265cec838fc7fbd50807aa518e Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Mon, 18 Nov 2024 08:03:39 -0500 Subject: [PATCH 04/13] allow tests to bypass bot detection --- src/EventSubscriber/Challenge.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/EventSubscriber/Challenge.php b/src/EventSubscriber/Challenge.php index 0dea443..befdcd6 100644 --- a/src/EventSubscriber/Challenge.php +++ b/src/EventSubscriber/Challenge.php @@ -100,17 +100,20 @@ protected function applies(RequestEvent $event, ImmutableConfig $config): bool { if (captcha_whitelist_ip_whitelisted($clientIp)) { return FALSE; } - // See if the client IP resolves to a good bot. $hostname = gethostbyaddr($clientIp); // Being sure to lookup the domain to avoid spoofing. $resolved_ip = gethostbyname($hostname); if ($clientIp !== $resolved_ip) { - return TRUE; + if ($clientIp !== '127.0.0.1') { + return TRUE; + } } $parts = explode(".", $hostname); if (count($parts) < 2) { - return TRUE; + if ($clientIp !== '127.0.0.1') { + return TRUE; + } } $tld = array_pop($parts); $hostname = array_pop($parts) . '.' . $tld; From 1e86d570e180f9e0976ff243a2d20bce206c7f9e Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Mon, 18 Nov 2024 08:03:51 -0500 Subject: [PATCH 05/13] fix config --- config/schema/turnstile_protect.schema.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/schema/turnstile_protect.schema.yml b/config/schema/turnstile_protect.schema.yml index 0cc02b4..8032638 100644 --- a/config/schema/turnstile_protect.schema.yml +++ b/config/schema/turnstile_protect.schema.yml @@ -5,10 +5,12 @@ turnstile_protect.settings: label: 'Turnstile Protect settings' mapping: routes: + type: sequence sequence: type: string label: 'Routes' bots: + type: sequence sequence: type: string label: 'List of good bot domains' From 6bb3247963fc0cd88ad6f2de1b8980f6c738009e Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Mon, 18 Nov 2024 08:04:06 -0500 Subject: [PATCH 06/13] Add redirect test --- tests/src/Functional/RedirectTest.php | 74 +++++++++++++++++++++++ tests/src/Kernel/TurnstileProtectTest.php | 2 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/src/Functional/RedirectTest.php diff --git a/tests/src/Functional/RedirectTest.php b/tests/src/Functional/RedirectTest.php new file mode 100644 index 0000000..75fc3b4 --- /dev/null +++ b/tests/src/Functional/RedirectTest.php @@ -0,0 +1,74 @@ +config('turnstile.settings') + ->set('site_key', '1x00000000000000000000AA') + ->set('secret_key', '1x0000000000000000000000000000000AA') + ->save(); + + $config = $this->config('turnstile_protect.settings') + ->set('routes', ["entity.node.canonical"]) + ->set('bots', []) + ->set('rate_limit', TRUE) + ->set('window', 86400) + ->set('threshold', 1) + ->set('history_enabled', FALSE) + ->save(); + + $this->assertEquals("entity.node.canonical", $config->get('routes')[0], 'Routes configuration is set to node.entity.canonical.'); + \Drupal::service('cache.config')->deleteAll(); + + } + + /** + * Tests redirection from node to /challenge. + */ + public function testNodeRedirect() { + $node = $this->drupalCreateNode(['type' => 'page']); + $nodeUrl = $node->toUrl()->toString(); + $this->drupalGet($nodeUrl); + + $url = $this->getSession()->getCurrentUrl(); + $components = parse_url($url); + $this->assertEquals($nodeUrl, $components['path'], 'User is not redirected to /challenge.'); + + $this->drupalGet($nodeUrl); + $url = $this->getSession()->getCurrentUrl(); + $components = parse_url($url); + $this->assertEquals('/challenge', $components['path'], 'User is redirected to /challenge.'); + } + +} diff --git a/tests/src/Kernel/TurnstileProtectTest.php b/tests/src/Kernel/TurnstileProtectTest.php index 50797d5..3803206 100644 --- a/tests/src/Kernel/TurnstileProtectTest.php +++ b/tests/src/Kernel/TurnstileProtectTest.php @@ -5,7 +5,7 @@ use Drupal\KernelTests\KernelTestBase; /** - * Tests basic functionality of the YourModule module. + * Tests basic functionality of the turnstile_protect module. * * @group turnstile_protect */ From 495cb75daac4f1912f4b44634e1c0c7deff7f88f Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Mon, 18 Nov 2024 08:08:09 -0500 Subject: [PATCH 07/13] Do not test 10.3 and 10.4 due to their deprecation notice --- .github/workflows/lint-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index b8cf374..6d667fa 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: php-version: ["8.1", "8.2", "8.3"] - drupal-version: ["10.2", "10.3", "10.4", "11.0"] + drupal-version: ["10.2", "11.0"] exclude: - drupal-version: "11.0" php-version: "8.1" From c17f99757aea710cb6a07f870dc3c9bde4c325d1 Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Mon, 18 Nov 2024 10:57:35 -0500 Subject: [PATCH 08/13] Test with headless chrome --- .github/workflows/lint-test.yml | 8 ++++---- tests/docker-compose.yml | 22 ++++++++++++++++++++++ tests/src/Functional/RedirectTest.php | 25 ++++++++++++++++++------- 3 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 tests/docker-compose.yml diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index 6d667fa..20eb766 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -23,11 +23,11 @@ jobs: uses: actions/checkout@v4 - name: lint+test + working-directory: tests run: | - docker run \ - --env ENABLE_MODULES=turnstile_protect \ - -v $(pwd):/var/www/drupal/web/modules/contrib/turnstile_protect \ - lehighlts/drupal-ci:${DRUPAL_VERSION}-php${PHP_VERSION} + export MODULE_DIRECTORY=$(pwd | xargs dirname) + docker compose up --quiet-pull --abort-on-container-exit env: DRUPAL_VERSION: ${{ matrix.drupal-version }} PHP_VERSION: ${{ matrix.php-version }} + ENABLE_MODULES: turnstile_protect diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml new file mode 100644 index 0000000..3adcfd8 --- /dev/null +++ b/tests/docker-compose.yml @@ -0,0 +1,22 @@ +networks: + default: +services: + chromedriver: + image: drupalci/webdriver-chromedriver:production + entrypoint: + - chromedriver + - "--log-path=/dev/null" + - "--verbose" + - "--allowed-ips=" + - "--allowed-origins=*" + drupal: + image: lehighlts/drupal-ci:${DRUPAL_VERSION}-php${PHP_VERSION} + volumes: + - ${MODULE_DIRECTORY}:/var/www/drupal/web/modules/contrib/${ENABLE_MODULES} + environment: + SIMPLETEST_BASE_URL: http://drupal:8282 + ENABLE_MODULES: ${ENABLE_MODULES} + MINK_DRIVER_ARGS_WEBDRIVER: '["chrome", {"browserName":"chrome","goog:chromeOptions":{"args":["--disable-gpu","--headless", "--no-sandbox", "--disable-dev-shm-usage"]}}, "http://chromedriver:9515"]' + SYMFONY_DEPRECATIONS_HELPER: weak + links: + - chromedriver diff --git a/tests/src/Functional/RedirectTest.php b/tests/src/Functional/RedirectTest.php index 75fc3b4..8fd0137 100644 --- a/tests/src/Functional/RedirectTest.php +++ b/tests/src/Functional/RedirectTest.php @@ -2,14 +2,14 @@ namespace Drupal\Tests\turnstile_protect\Functional; -use Drupal\Tests\BrowserTestBase; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; /** * Tests redirection from /node/1 to /challenge. * * @group turnstile_protect */ -class RedirectTest extends BrowserTestBase { +class RedirectTest extends WebDriverTestBase { /** * {@inheritdoc} @@ -24,7 +24,7 @@ class RedirectTest extends BrowserTestBase { /** * {@inheritdoc} */ - protected $defaultTheme = 'claro'; + protected $defaultTheme = 'stark'; /** * Sets up the test environment. @@ -49,26 +49,37 @@ protected function setUp(): void { ->save(); $this->assertEquals("entity.node.canonical", $config->get('routes')[0], 'Routes configuration is set to node.entity.canonical.'); - \Drupal::service('cache.config')->deleteAll(); - } /** * Tests redirection from node to /challenge. */ public function testNodeRedirect() { + // Create a node we'll try accessing. $node = $this->drupalCreateNode(['type' => 'page']); $nodeUrl = $node->toUrl()->toString(); - $this->drupalGet($nodeUrl); + // Since turnstile_protect.settings["threshold"] = 1 + // we should be able to view the node once. + $this->drupalGet($nodeUrl); $url = $this->getSession()->getCurrentUrl(); $components = parse_url($url); $this->assertEquals($nodeUrl, $components['path'], 'User is not redirected to /challenge.'); + // We should be challenged now on the second look. $this->drupalGet($nodeUrl); $url = $this->getSession()->getCurrentUrl(); $components = parse_url($url); - $this->assertEquals('/challenge', $components['path'], 'User is redirected to /challenge.'); + $this->assertEquals('/challenge', $components['path'], 'User is not redirected to /challenge.'); + + sleep(15); + + // We should be redirected to the node after the turnstile passed + // which it always will in this test with the turnstile site/secret keys + // set to their always pass test. + $url = $this->getSession()->getCurrentUrl(); + $components = parse_url($url); + $this->assertEquals($nodeUrl, $components['path'], 'User is redirected back to node.'); } } From a1885ee3bb8a723026ade24d34dc45b055bf6f5c Mon Sep 17 00:00:00 2001 From: Joe Corall Date: Mon, 18 Nov 2024 10:58:15 -0500 Subject: [PATCH 09/13] Put 10.3 and 10.4 back --- .github/workflows/lint-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index 20eb766..522c31c 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: php-version: ["8.1", "8.2", "8.3"] - drupal-version: ["10.2", "11.0"] + drupal-version: ["10.2", "10.3", "10.4", "11.0"] exclude: - drupal-version: "11.0" php-version: "8.1" From 708a439fd29baf454195d72502efae6417873607 Mon Sep 17 00:00:00 2001 From: Maccabee Levine Date: Mon, 18 Nov 2024 11:35:30 -0500 Subject: [PATCH 10/13] Add referrer policy to cloudflare JS --- turnstile_protect.module | 3 +++ 1 file changed, 3 insertions(+) diff --git a/turnstile_protect.module b/turnstile_protect.module index d9a3d66..c2ea581 100644 --- a/turnstile_protect.module +++ b/turnstile_protect.module @@ -17,6 +17,9 @@ function turnstile_protect_captcha_alter(&$captcha, $info) { return; } + // Block referrer to cloudflare. + $captcha['form']['turnstile_widget']['#attached']['html_head'][0][0]['#attributes']['referrerpolicy'] = 'no-referrer'; + // Add a javascript callback after the turnstile succeeds. $captcha['form']['turnstile_widget']['#markup'] = str_replace(' Date: Mon, 18 Nov 2024 11:35:51 -0500 Subject: [PATCH 11/13] Add a test to ensure referrerpolicy is set --- tests/src/Functional/NoReferrerTest.php | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/src/Functional/NoReferrerTest.php diff --git a/tests/src/Functional/NoReferrerTest.php b/tests/src/Functional/NoReferrerTest.php new file mode 100644 index 0000000..2c1d068 --- /dev/null +++ b/tests/src/Functional/NoReferrerTest.php @@ -0,0 +1,56 @@ +config('turnstile.settings') + ->set('site_key', '1x00000000000000000000AA') + ->set('secret_key', '1x0000000000000000000000000000000AA') + ->save(); + } + + /** + * Tests referrerpolicy is set on cloudflare