diff --git a/CHANGELOG.md b/CHANGELOG.md index d701d0e..773c5b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **Test credentials are generated at run time.** Secrets and tokens in the tests come from + `TestCredentials` instead of literals, which secret scanning reported as leaks. A guard test + fails on any literal left behind. The `laranail/package-tools` floor is now `^0.1.10`, which + adds a second check: every purpose passed to `TestCredentials` must be a single plain lowercase + word, because secret scanning reads a compound purpose as the password itself. - `laravel/framework ^13.0` is now declared in `require`. `src/` uses `FormRequest` from `Illuminate\Foundation`, which no `illuminate/*` component ships, so the dependency only arrived through the host application. - **Route names are vendor-scoped.** `env-kit.keys.{index,show,store,update,destroy}` and `env-kit.panel` are now registered as `laranail-env-kit-webui.keys.*` and diff --git a/composer.json b/composer.json index a42be30..435ccc4 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "require": { "php": "^8.4.1 || ^8.5", "laranail/env-kit": "^0.1", - "laranail/package-tools": "^0.1.3", + "laranail/package-tools": "^0.1.10", "illuminate/contracts": "^13.0", "illuminate/http": "^13.0", "illuminate/routing": "^13.0", diff --git a/tests/Feature/AccessControlTest.php b/tests/Feature/AccessControlTest.php index cef12f4..3c3a037 100644 --- a/tests/Feature/AccessControlTest.php +++ b/tests/Feature/AccessControlTest.php @@ -8,6 +8,7 @@ use Illuminate\Contracts\Auth\Authenticatable; use Simtabi\Laranail\EnvKit\WebUI\Tests\TestCase; use Simtabi\Laranail\EnvKit\WebUI\Events\AccessDenied; +use Simtabi\Laranail\Package\Tools\Testing\TestCredentials; uses(TestCase::class); @@ -33,11 +34,14 @@ it('requires a valid secret token when configured', function () { $this->bindEnv("A=1\n"); - config(['laranail.env-kit-webui.access.token' => 'sekret']); + $token = TestCredentials::secret('access'); + $wrong = TestCredentials::secret('wrong'); + expect($wrong)->not->toBe($token); + config(['laranail.env-kit-webui.access.token' => $token]); $this->getJson('api/v1/env-kit/keys')->assertForbidden(); - $this->getJson('api/v1/env-kit/keys', ['X-EnvKit-Token' => 'wrong'])->assertForbidden(); - $this->getJson('api/v1/env-kit/keys', ['X-EnvKit-Token' => 'sekret'])->assertOk(); + $this->getJson('api/v1/env-kit/keys', ['X-EnvKit-Token' => $wrong])->assertForbidden(); + $this->getJson('api/v1/env-kit/keys', ['X-EnvKit-Token' => $token])->assertOk(); }); it('blocks outside the configured daily time-window', function () { diff --git a/tests/Feature/ConfigContractTest.php b/tests/Feature/ConfigContractTest.php index 4464685..e72b5eb 100644 --- a/tests/Feature/ConfigContractTest.php +++ b/tests/Feature/ConfigContractTest.php @@ -4,6 +4,7 @@ use Simtabi\Laranail\EnvKit\WebUI\Tests\TestCase; use Simtabi\Laranail\EnvKit\WebUI\Support\PanelAccess; +use Simtabi\Laranail\Package\Tools\Testing\TestCredentials; use Simtabi\Laranail\EnvKit\WebUI\Http\Middleware\RequireEnvKitWebUIToken; uses(TestCase::class); @@ -94,7 +95,7 @@ function shippedWebUiConfig(): array it('honours a configured shared-secret token', function (): void { // RequireEnvKitWebUIToken no-ops when it reads no token, so an inert config key did not merely // ignore the setting -- it turned the gate off while reporting nothing. - config()->set('laranail.env-kit-webui.access.token', 'a-configured-secret'); + config()->set('laranail.env-kit-webui.access.token', TestCredentials::secret('configured')); $middleware = new RequireEnvKitWebUIToken; $passed = false; diff --git a/tests/Unit/Architecture/NoLiteralCredentialsTest.php b/tests/Unit/Architecture/NoLiteralCredentialsTest.php new file mode 100644 index 0000000..4442b2a --- /dev/null +++ b/tests/Unit/Architecture/NoLiteralCredentialsTest.php @@ -0,0 +1,19 @@ +assertNoLiteralCredentialsIn([dirname(__DIR__, 2)], minFiles: 15); + + expect(true)->toBeTrue(); +}); + +it('names every generated credential with a single word', function (): void { + $this->assertCredentialPurposesAreWordsIn([dirname(__DIR__, 2)], minFiles: 15); + + expect(true)->toBeTrue(); +});