diff --git a/.circleci/_manifest_exclude b/.circleci/_manifest_exclude new file mode 100644 index 0000000..e69de29 diff --git a/.circleci/circleci.csp.yml b/.circleci/circleci.csp.yml new file mode 100644 index 0000000..c2c0b96 --- /dev/null +++ b/.circleci/circleci.csp.yml @@ -0,0 +1,7 @@ +--- +name: circlecitestyml +after: + - CSPHeaders +--- +Firesphere\CSPHeaders\View\CSPBackend: + useNonce: false diff --git a/.circleci/config.yml b/.circleci/config.yml index 0fd9fa6..0ae2192 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,43 @@ version: 2 jobs: - build: + cms4: + docker: + - image: cimg/php:8.1 + environment: + - SS_DATABASE_CLASS=MySQLDatabase + - SS_DATABASE_SERVER=127.0.0.1 + - SS_DATABASE_USERNAME=root + - SS_DATABASE_PASSWORD=ubuntu + - SS_DATABASE_NAME=circle_test + - SS_ENVIRONMENT_TYPE=test + - SS_DEFAULT_ADMIN_USERNAME=admin + - SS_DEFAULT_ADMIN_PASSWORD=password + - image: cimg/mariadb:10.6.4 + environment: + - MYSQL_USER=user + - MYSQL_ROOT_PASSWORD=ubuntu + - MYSQL_DATABASE=circle_test + - MYSQL_HOST=127.0.0.1 + + steps: + # Install additional requirements + - checkout + - run: composer config --no-plugins --no-interaction allow-plugins.composer/installers true + - run: composer config --no-plugins --no-interaction allow-plugins.silverstripe/vendor-plugin true + - run: composer config --no-plugins --no-interaction allow-plugins.silverstripe/recipe-plugin true + - run: composer require silverstripe/recipe-cms:^4 -n + - run: composer install -n + - run: cp .circleci/circleci.env .env + - run: cp .circleci/jstest.js.circleci jstest.js + - run: sudo pecl install pcov + - run: sudo docker-php-ext-enable pcov + + # run tests! + - run: until nc -z -v -w30 127.0.0.1 3306; do sleep 1; done + - run: vendor/bin/sake dev/build flush=all skipannotation=true + - run: vendor/bin/phpunit -d memory_limit=512M tests/unit + + cms5: environment: CC_TEST_REPORTER_ID: 8fc82e19673fd65485d0f66a05d94bad2d3da1df8a848349fcdabab0767f452b docker: @@ -27,10 +64,11 @@ jobs: - run: composer config --no-plugins --no-interaction allow-plugins.composer/installers true - run: composer config --no-plugins --no-interaction allow-plugins.silverstripe/vendor-plugin true - run: composer config --no-plugins --no-interaction allow-plugins.silverstripe/recipe-plugin true - - run: composer require silverstripe/recipe-cms:^4 -n + - run: composer require silverstripe/recipe-cms:^5 -n - run: composer install -n - run: cp .circleci/circleci.env .env - run: cp .circleci/jstest.js.circleci jstest.js +# - run: cp .circleci/circleci.csp.yml _config/circlecsp.yml - run: sudo pecl install pcov - run: sudo docker-php-ext-enable pcov @@ -46,4 +84,47 @@ jobs: echo "---" bash <(curl -s https://codecov.io/bash) -f clover.xml - run: vendor/bin/phpcs --standard=phpcs.xml.dist src tests/unit + framework: + docker: + - image: cimg/php:8.1 + environment: + - SS_DATABASE_CLASS=MySQLDatabase + - SS_DATABASE_SERVER=127.0.0.1 + - SS_DATABASE_USERNAME=root + - SS_DATABASE_PASSWORD=ubuntu + - SS_DATABASE_NAME=circle_test + - SS_ENVIRONMENT_TYPE=test + - SS_DEFAULT_ADMIN_USERNAME=admin + - SS_DEFAULT_ADMIN_PASSWORD=password + - image: cimg/mariadb:10.6.4 + environment: + - MYSQL_USER=user + - MYSQL_ROOT_PASSWORD=ubuntu + - MYSQL_DATABASE=circle_test + - MYSQL_HOST=127.0.0.1 + + steps: + # Install additional requirements + - checkout + - run: composer config --no-plugins --no-interaction allow-plugins.composer/installers true + - run: composer config --no-plugins --no-interaction allow-plugins.silverstripe/vendor-plugin true + - run: composer config --no-plugins --no-interaction allow-plugins.silverstripe/recipe-plugin true + - run: composer install -n + - run: cp .circleci/circleci.env .env + - run: cp .circleci/jstest.js.circleci jstest.js +# - run: cp .circleci/circleci.csp.yml _config/circlecsp.yml + - run: sudo pecl install pcov + - run: sudo docker-php-ext-enable pcov + + # run tests! + - run: until nc -z -v -w30 127.0.0.1 3306; do sleep 1; done + - run: vendor/bin/sake dev/build flush=all skipannotation=true + - run: vendor/bin/phpunit -d memory_limit=512M tests/unit +workflows: + version: 2 + dobuild: + jobs: + - cms4 + - cms5 + - framework diff --git a/composer.json b/composer.json index 4e2cb2c..9e5e594 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "require": { "php": "^8.1", "ext-simplexml": "*", - "silverstripe/cms": "^4|^5", + "silverstripe/framework": "^4|^5", + "silverstripe/admin": "^1|^2", "paragonie/csp-builder": "^2.9", "guzzlehttp/guzzle": ">=6", "symfony/yaml": ">=4" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c995630..3f32c77 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,4 +1,4 @@ - diff --git a/src/Traits/CSPBackendTrait.php b/src/Traits/CSPBackendTrait.php index 01801c8..2d3619c 100644 --- a/src/Traits/CSPBackendTrait.php +++ b/src/Traits/CSPBackendTrait.php @@ -52,7 +52,11 @@ trait CSPBackendTrait */ public static function isJsSRI(): bool { - return CSPBackend::config()->get('jsSRI') || self::$jsSRI; + if (self::$jsSRI === null) { + self::$jsSRI = CSPBackend::config()->get('jsSRI'); + } + + return self::$jsSRI; } /** @@ -68,7 +72,11 @@ public static function setJsSRI(bool $jsSRI): void */ public static function isCssSRI(): bool { - return CSPBackend::config()->get('cssSRI') || self::$cssSRI; + if (self::$cssSRI === null) { + self::$cssSRI = CSPBackend::config()->get('cssSRI'); + } + + return self::$cssSRI; } /** diff --git a/tests/unit/CSPBackendTest.php b/tests/unit/CSPBackendTest.php index cfdb4fe..f931046 100644 --- a/tests/unit/CSPBackendTest.php +++ b/tests/unit/CSPBackendTest.php @@ -14,7 +14,7 @@ class CSPBackendTest extends SapphireTest public function setUp(): void { parent::setUp(); - CSPBackend::config()->merge('useNonce', false); + CSPBackend::config()->set('useNonce', false); } public function testSet() { @@ -219,7 +219,7 @@ public function testSRISettings() CSPBackend::setJsSRI(true); $this->assertEquals(true, CSPBackend::isJsSRI()); CSPBackend::setJsSRI(false); - $this->assertEquals($isSRI, CSPBackend::isJsSRI()); + $this->assertEquals(false, CSPBackend::isJsSRI()); $isSRI = CSPBackend::config()->get('cssSRI'); $this->assertEquals($isSRI, CSPBackend::isCssSRI()); @@ -227,6 +227,6 @@ public function testSRISettings() CSPBackend::setCssSri(true); $this->assertEquals(true, CSPBackend::isCssSRI()); CSPBackend::setCssSri(false); - $this->assertEquals($isSRI, CSPBackend::isCssSRI()); + $this->assertEquals(false, CSPBackend::isCssSRI()); } } diff --git a/tests/unit/CSSBuilderTest.php b/tests/unit/CSSBuilderTest.php index 6ac2479..5d37767 100644 --- a/tests/unit/CSSBuilderTest.php +++ b/tests/unit/CSSBuilderTest.php @@ -28,6 +28,10 @@ private function BuildController() CSPBackend::setUsesNonce(false); $backend = Injector::inst()->get(CSPBackend::class); Requirements::set_backend($backend); + if (!class_exists('\Page')) { + Controller::add_extension(Controller::class, ControllerCSPExtension::class); + return new Controller(); + } $page = new Page(); $request = new HTTPRequest('GET', '/'); @@ -119,12 +123,14 @@ public function testDisableBuildTags() $owner = Requirements::backend(); $builder = $owner->getCSSBuilder(); // Should add integrity - CSPBackend::config()->merge('cssSRI', true); + CSPBackend::config()->set('cssSRI', true); + CSPBackend::setCssSRI(true); $this->assertTrue(CSPBackend::isCssSRI()); $requirements = $builder->buildTags('composer.json', [], [], '/'); $this->assertStringContainsString('integrity=', $requirements[0]); // Shouldn't add integrity if not enabled - CSPBackend::config()->merge('cssSRI', false); + CSPBackend::config()->set('cssSRI', false); + CSPBackend::setCssSRI(false); $this->assertFalse(CSPBackend::isCssSRI()); $controller->onBeforeInit(); $requirements = $builder->buildTags('composer.json', [], [], '/'); diff --git a/tests/unit/ControllerExtensionTest.php b/tests/unit/ControllerExtensionTest.php index b62a75f..9586b6f 100644 --- a/tests/unit/ControllerExtensionTest.php +++ b/tests/unit/ControllerExtensionTest.php @@ -9,6 +9,7 @@ use Page; use PageController; use SilverStripe\Admin\LeftAndMain; +use SilverStripe\Control\Controller; use SilverStripe\Control\Cookie; use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\NullHTTPRequest; @@ -20,21 +21,24 @@ class ControllerExtensionTest extends SapphireTest public function setUp(): void { parent::setUp(); - CSPBackend::config()->merge('useNonce', false); + CSPBackend::config()->set('useNonce', false); ControllerCSPExtension::$isTesting = true; } public function testInit() { $this->assertFalse(ControllerCSPExtension::checkCookie(new NullHTTPRequest())); - $page = new Page(); - $controller = new PageController($page); - + if (class_exists('\Page')) { + $page = new Page(); + $controller = new PageController($page); + } else { + $controller = new Controller(); + } // Shouldn't add CSP if not enabled $extension = new ControllerCSPExtension(); $extension->setOwner($controller); $config = CSPBackend::config()->get('csp_config'); $config['enabled'] = false; - CSPBackend::config()->merge('csp_config', $config); + CSPBackend::config()->set('csp_config', $config); $this->assertFalse($extension->isAddPolicyHeaders()); $extension->onBeforeInit(); @@ -42,7 +46,7 @@ public function testInit() // Should add CSP if enabled (and build headers not requested) $config['enabled'] = true; - CSPBackend::config()->merge('csp_config', $config); + CSPBackend::config()->set('csp_config', $config); $request = new HTTPRequest('GET', '/'); $controller->setRequest($request); $extension = new ControllerCSPExtension(); @@ -58,8 +62,12 @@ public function testNonceOnExcludedControllers() //when CSPBackend.useNonce is true, it should only apply to controllers //with the extension applied. By default, this is root controller CSPBackend::setUsesNonce(true); - $page = new Page(); - $controller = new PageController($page); + if (class_exists('\Page')) { + $page = new Page(); + $controller = new PageController($page); + } else { + $controller = new Controller(); + } $extension = new ControllerCSPExtension(); $extension->setOwner($controller); diff --git a/tests/unit/JSBuilderTest.php b/tests/unit/JSBuilderTest.php index 1013e18..dcfdffe 100644 --- a/tests/unit/JSBuilderTest.php +++ b/tests/unit/JSBuilderTest.php @@ -28,15 +28,19 @@ private function BuildController() CSPBackend::setUsesNonce(false); $backend = Injector::inst()->get(CSPBackend::class); Requirements::set_backend($backend); - - $page = new Page(); - $request = new HTTPRequest('GET', '/'); - $request->setSession(new Session('test')); - - PageController::add_extension(Controller::class, ControllerCSPExtension::class); - $controller = new PageController($page); - $controller->setRequest($request); - $controller->pushCurrent(); + Controller::add_extension(Controller::class, ControllerCSPExtension::class); + + if (class_exists('\Page')) { + $page = new Page(); + $request = new HTTPRequest('GET', '/'); + $request->setSession(new Session('test')); + + $controller = new PageController($page); + $controller->setRequest($request); + $controller->pushCurrent(); + } else { + $controller = new Controller(); + } $controller->onBeforeInit(); return $controller; } @@ -120,13 +124,16 @@ public function testDisableBuildTags() $controller = $this->buildController(); $owner = Requirements::backend(); $builder = $owner->getJsBuilder(); + CSPBackend::config()->set('jsSRI', true); + CSPBackend::setJsSRI(true); // Should add integrity $this->assertTrue(CSPBackend::isJsSRI()); $requirements = $builder->buildTags('composer.json', [], [], '/'); $this->assertStringContainsString('integrity=', $requirements[0]); // Shouldn't add integrity if not enabled - CSPBackend::config()->merge('jsSRI', false); + CSPBackend::config()->set('jsSRI', false); + CSPBackend::setJsSRI(false); $this->assertFalse(CSPBackend::isJsSRI()); $controller->onBeforeInit(); $requirements = $builder->buildTags('composer.json', [], [], '/'); @@ -139,7 +146,7 @@ public function testDisableBuildTags() $controller->onBeforeInit(); $requirements = $builder->buildTags('composer.json', [], [], '/'); $this->assertStringContainsString('integrity=', $requirements[0]); - CSPBackend::config()->merge('jsSRI', true); + CSPBackend::config()->set('jsSRI', true); $request->offsetUnset('build-headers'); Cookie::force_expiry('buildHeaders'); } diff --git a/tests/unit/PageExtensionTest.php b/tests/unit/PageExtensionTest.php index 25cf9b8..b6523b0 100644 --- a/tests/unit/PageExtensionTest.php +++ b/tests/unit/PageExtensionTest.php @@ -12,13 +12,17 @@ class PageExtensionTest extends SapphireTest { public function testUpdateSettingsFields() { - $page = new Page(); - $extension = new PageExtension(); - $extension->setOwner($page); + if (class_exists('\Page')) { + $page = new Page(); + $extension = new PageExtension(); + $extension->setOwner($page); - $fields = $page->getSettingsFields(); - $extension->updateSettingsFields($fields); + $fields = $page->getSettingsFields(); + $extension->updateSettingsFields($fields); - $this->assertInstanceOf(GridField::class, $fields->dataFieldByName('CSPDomains')); + $this->assertInstanceOf(GridField::class, $fields->dataFieldByName('CSPDomains')); + } else { + $this->assertTrue(true); // noop + } } }