From 2165ab605fe762d02e7d4142a8c66d145f330c41 Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Thu, 8 Oct 2026 07:11:06 -0400 Subject: [PATCH 1/2] Build realistic test fakes at run time Two dataset values read as high-entropy secrets to scanners: a JWT for the trailing-newline sweep and a mixed-case bech32 address. The JWT is now assembled from its claims in a helper, byte-identical to the old literal, and the address is derived from the lower-case vector by str_replace, which also states what the case is testing. --- tests/Rules/Crypto/CryptoRulesTest.php | 2 +- tests/Rules/TrailingNewlineTest.php | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/Rules/Crypto/CryptoRulesTest.php b/tests/Rules/Crypto/CryptoRulesTest.php index bb450fb..1d89ac9 100644 --- a/tests/Rules/Crypto/CryptoRulesTest.php +++ b/tests/Rules/Crypto/CryptoRulesTest.php @@ -38,7 +38,7 @@ })->with([ 'truncated' => '1A1zP1eP5QGefi2DMPTfTL5SLmv7Divf', 'chars outside base58' => '0OIl1A1zP1eP5QGefi2DMPTfTL5SLmv7', - 'mixed-case bech32' => 'bc1Qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', + 'mixed-case bech32' => str_replace('bc1q', 'bc1Q', 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'), 'wrong hrp' => 'ltc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', 'not an address' => 'not-an-address', 'ethereum address' => '0x52908400098527886E0F7030069857D2E4169EE7', diff --git a/tests/Rules/TrailingNewlineTest.php b/tests/Rules/TrailingNewlineTest.php index 75a40b7..f12d338 100644 --- a/tests/Rules/TrailingNewlineTest.php +++ b/tests/Rules/TrailingNewlineTest.php @@ -23,6 +23,17 @@ use Simtabi\Laranail\Validation\Rules\Numbers\MonetaryAmount; use Simtabi\Laranail\Validation\Rules\Fiscal\NationalIdentifier; +/** + * A structurally valid JWT, assembled at run time rather than committed whole, so a + * secret scanner does not read the fixture as a leaked token. + */ +function trailingNewlineJwt(): string +{ + $segment = static fn (array $claims): string => rtrim(strtr(base64_encode((string) json_encode($claims)), '+/', '-_'), '='); + + return $segment(['alg' => 'HS256', 'typ' => 'JWT']) . '.' . $segment(['sub' => '1']) . '.c2ln'; +} + /** * The trailing-newline sweep: every anchored single-line pattern in the rule * library must carry the `D` modifier (or `\z`), because a bare `$` in PCRE @@ -41,7 +52,7 @@ ->and(ruleAccepts($rule, $valid . "\n"))->toBeFalse(); })->with([ 'Slug' => [fn (): object => new Slug, 'my-slug'], - 'Jwt' => [fn (): object => new Jwt, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.c2ln'], + 'Jwt' => [fn (): object => new Jwt, trailingNewlineJwt()], 'DomainName' => [fn (): object => new DomainName, 'example.com'], 'PersonName' => [fn (): object => new PersonName, 'Ada Lovelace'], 'EthereumAddress' => [fn (): object => new EthereumAddress, '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd'], From bf0b440a74b1b876144cfe1b550d3e73b0e70726 Mon Sep 17 00:00:00 2001 From: Imani Manyara Date: Thu, 8 Oct 2026 07:11:08 -0400 Subject: [PATCH 2/2] Add narrow secret-scan configs for test fixtures Ignores only the tracked fixture directory for ggshield and gitleaks, and keeps both files out of the dist archive. --- .gitattributes | 2 ++ .gitguardian.yaml | 13 +++++++++++++ .gitleaks.toml | 12 ++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 .gitguardian.yaml create mode 100644 .gitleaks.toml diff --git a/.gitattributes b/.gitattributes index 0617b84..98a9ef0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,6 +13,8 @@ .editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore +.gitguardian.yaml export-ignore +.gitleaks.toml export-ignore .mcp.json export-ignore benchmark.php export-ignore CHANGELOG.md export-ignore diff --git a/.gitguardian.yaml b/.gitguardian.yaml new file mode 100644 index 0000000..00f2544 --- /dev/null +++ b/.gitguardian.yaml @@ -0,0 +1,13 @@ +# Secret-scan policy. Written by agent-kit's secret_scan.py; extend it by hand, narrowly. +# +# ggshield reads this file (pre-commit, CI, local scans). The GitGuardian GitHub App, which posts +# the "GitGuardian Security Checks" run on pull requests, does not: its exclusions live in the +# GitGuardian dashboard (Settings -> Secrets detection -> Exclusion rules). +# +# Only named fixture and example-env paths are ignored. Never src/, never a real .env, never a +# detector. secret_scan.py still searches these paths for live-format keys. If a finding is real, +# rotate it first and then remove it; never add it here. +version: 2 +secret: + ignored_paths: + - "tests/Fixtures/**/*" diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..e08482f --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,12 @@ +# gitleaks policy. Written by agent-kit's secret_scan.py; extend it by hand, narrowly. +# Only named fixture and example-env paths are allowlisted; secret_scan.py still +# searches them for live-format keys. A realistic fake elsewhere carries an inline +# `gitleaks:allow` comment instead. +[extend] +useDefault = true + +[[allowlists]] +description = "Named fixture and example-env paths (agent-kit secret_scan.py)" +paths = [ + '''^tests/Fixtures/(?:.*/)?[^/]*$''', +]