Secret-safe about capture
Captures one artisan about section and pins that it renders what it must while leaking none of the secrets it must not:
expect($section)->toLeakNoSecrets(array $secrets, array $mustRender);expect('passkeys')->toLeakNoSecrets(
secrets: ['auth.acme-internal.example', '/srv/acme/secrets', 'ea9b8d66'],
mustRender: ['Sign-count policy', 'AAGUID allow-list'],
);| Parameter | Type | Meaning |
|---|---|---|
$section | string | The section passed to artisan about --only=<section>. |
$secrets | list<string> | Values that must not render — keys, paths, hosts, fingerprints. Every entry must be a non-blank string; an empty list is a deliberate render-only check. |
$mustRender | list<string> | Strings that must render — positive proof the capture worked. Required, non-empty, and every entry a non-blank string. |
Order of operations
- First, the output must be non-empty.
- Then every $mustRender string must be present.
- Only then is each $secrets entry asserted absent.
Capture goes through Artisan::call('about', ['--only' => $section]) and Artisan::output(). It never reads app(Kernel::class)->output(), which returns an empty string — that is how a whole section’s “does not leak” checks once passed vacuously, and the leak was caught only because one positive assertion happened to exist. An empty $mustRender throws InvalidArgumentException at call time, because a negative-only check can pass against empty output.
Every entry must be set
Every entry of both lists must be a non-blank string, or the call throws InvalidArgumentException. A null secret — config('a.key.that.is.not.set') — or an empty one from an unset env var can never be found, so the leak check would pass while checking nothing; an empty $mustRender needle is found in every output. Set the secret to a real value in the test first.
In an application
it('does not leak credentials through artisan about', function (): void {
// Only a secret that is set can be looked for — a null or empty entry throws.
config(['services.stripe.secret' => 'sk_test_do_not_render_me']);
expect('environment')->toLeakNoSecrets(
secrets: [config('app.key'), config('services.stripe.secret')],
mustRender: ['Application Name'],
);
});Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.