diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 914b589..626111a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,7 +71,7 @@ jobs: test "$(docker run --rm --entrypoint id keywatch:ci -u)" != "0" set +e - printf '%s\n' 'AWS_KEY=AKIAIOSFODNN7EXAMPLE' | \ + printf '%s\n' 'AWS_KEY=AKIAABCDEFGHIJKLMNOP' | \ docker run --rm -i keywatch:ci scan --stdin --exit-mode strict scan_status=$? set -e diff --git a/.keywatch-baseline.json b/.keywatch-baseline.json index 676a5a1..d630b93 100644 --- a/.keywatch-baseline.json +++ b/.keywatch-baseline.json @@ -1,13 +1,6 @@ { "version": "1.0", "entries": [ - { - "file_path": "./.github/workflows/ci.yml", - "line_number": 74, - "finding_type": "AWS Access Key", - "matched_content_hash": "bc377e3c9554437ff10fd844193b505dced77a6af5a662e74232e814d284a1a2", - "plugin_name": "AWSKeyDetector" - }, { "file_path": "./.github/workflows/docker-publish.yml", "line_number": 42, @@ -24,42 +17,42 @@ }, { "file_path": "./detectors.toml", - "line_number": 120, + "line_number": 143, "finding_type": "Certificate", "matched_content_hash": "648ee3671e3bc408c3bce7d0ce237d9716da2c360a5c57c235a21067ac11eea9", "plugin_name": "CertificateDetector" }, { "file_path": "./detectors.toml", - "line_number": 281, + "line_number": 311, + "finding_type": "SSH Private Key", + "matched_content_hash": "678a65e8968aabff441076ae306e13d4fc85b1d36d8036a10d2100c1dc40d251", + "plugin_name": "SSHPrivateKeyDetector" + }, + { + "file_path": "./detectors.toml", + "line_number": 311, "finding_type": "Private Key Content", "matched_content_hash": "f59444f5756fc6dd701c599452c1752e64a97ecb0447ce41a1e19eb15293ef9a", "plugin_name": "PrivateKeyDetector" }, { "file_path": "./detectors.toml", - "line_number": 281, + "line_number": 311, "finding_type": "Private Key Content", "matched_content_hash": "f59444f5756fc6dd701c599452c1752e64a97ecb0447ce41a1e19eb15293ef9a", "plugin_name": "PrivateKeyContentDetector" }, { "file_path": "./detectors.toml", - "line_number": 281, - "finding_type": "SSH Private Key", - "matched_content_hash": "678a65e8968aabff441076ae306e13d4fc85b1d36d8036a10d2100c1dc40d251", - "plugin_name": "SSHPrivateKeyDetector" - }, - { - "file_path": "./detectors.toml", - "line_number": 698, + "line_number": 731, "finding_type": "Base64 Encoded String", "matched_content_hash": "e64a42fa536515c68483f9b16cae91558748e69c9e7119bc8f32964322a67fe2", "plugin_name": "Base64Detector" }, { "file_path": "./detectors.toml", - "line_number": 698, + "line_number": 731, "finding_type": "Base64 Encoded String", "matched_content_hash": "613dfb068a3be178fc4a3592d7233cdfb4f7005ab1582ec9676f875e15adcbb5", "plugin_name": "Base64Detector" @@ -276,70 +269,77 @@ }, { "file_path": "./src/baseline.rs", - "line_number": 318, + "line_number": 267, "finding_type": "Random String", "matched_content_hash": "c40c9141cd77d55f1ba9d21fb045f8e7c5e900c16f2e563779e7ed75cee6b15b", "plugin_name": "RandomString" }, { - "file_path": "./src/baseline.rs", - "line_number": 348, - "finding_type": "AWS Access Key", - "matched_content_hash": "bc377e3c9554437ff10fd844193b505dced77a6af5a662e74232e814d284a1a2", - "plugin_name": "AWSKeyDetector" + "file_path": "./src/config/tests/application.rs", + "line_number": 298, + "finding_type": "Credit Card Number", + "matched_content_hash": "4541206d542811878a9374508fe296fa321a8b56c2902736a02f388836f6e108", + "plugin_name": "CreditCardDetector" }, { - "file_path": "./src/scanner.rs", - "line_number": 1126, + "file_path": "./src/detector.rs", + "line_number": 138, + "finding_type": "Aadhaar Card Number", + "matched_content_hash": "c941e88b8d0be7288dcc792a8ecfae22482b8a83ea4494f1ca0e36293009730b", + "plugin_name": "AadhaarCardDetector" + }, + { + "file_path": "./src/scanner/lines.rs", + "line_number": 388, "finding_type": "AWS Access Key", "matched_content_hash": "3f733150de7916d4778298d7f90493889c38b76876b80c058e439851ce60cb2b", "plugin_name": "AWSKeyDetector" }, { - "file_path": "./src/scanner.rs", - "line_number": 1126, + "file_path": "./src/scanner/lines.rs", + "line_number": 388, "finding_type": "Password", "matched_content_hash": "f6bd37622d846ac435a7b7dcbde2347d59494dfd3c3a787604e8b91491a39c95", "plugin_name": "PasswordDetector" }, { - "file_path": "./src/scanner.rs", - "line_number": 1126, + "file_path": "./src/scanner/lines.rs", + "line_number": 388, "finding_type": "Generic Key/Secret", "matched_content_hash": "f6bd37622d846ac435a7b7dcbde2347d59494dfd3c3a787604e8b91491a39c95", "plugin_name": "GenericKeyValueDetector" }, { - "file_path": "./src/scanner.rs", - "line_number": 1182, - "finding_type": "Private Key Content", - "matched_content_hash": "f91082d1cbd2032b5ea19f2bbf6b3f88e02c12ae320fc9e08e6dc0f73d82bdd1", - "plugin_name": "PrivateKeyDetector" - }, - { - "file_path": "./src/scanner.rs", - "line_number": 1182, - "finding_type": "Private Key Content", - "matched_content_hash": "f91082d1cbd2032b5ea19f2bbf6b3f88e02c12ae320fc9e08e6dc0f73d82bdd1", - "plugin_name": "PrivateKeyContentDetector" - }, - { - "file_path": "./src/scanner.rs", - "line_number": 1182, + "file_path": "./src/scanner/lines.rs", + "line_number": 444, "finding_type": "SSH Private Key", "matched_content_hash": "678a65e8968aabff441076ae306e13d4fc85b1d36d8036a10d2100c1dc40d251", "plugin_name": "SSHPrivateKeyDetector" }, { - "file_path": "./src/scanner.rs", - "line_number": 1182, + "file_path": "./src/scanner/lines.rs", + "line_number": 444, "finding_type": "Base64 Encoded String", "matched_content_hash": "3dde06bf268892d4210f4b0bf1402ecc6a8ad1e015f8204cdc31667762572ef5", "plugin_name": "Base64Detector" }, { - "file_path": "./src/scanner.rs", - "line_number": 1205, + "file_path": "./src/scanner/lines.rs", + "line_number": 444, + "finding_type": "Private Key Content", + "matched_content_hash": "f91082d1cbd2032b5ea19f2bbf6b3f88e02c12ae320fc9e08e6dc0f73d82bdd1", + "plugin_name": "PrivateKeyDetector" + }, + { + "file_path": "./src/scanner/lines.rs", + "line_number": 444, + "finding_type": "Private Key Content", + "matched_content_hash": "f91082d1cbd2032b5ea19f2bbf6b3f88e02c12ae320fc9e08e6dc0f73d82bdd1", + "plugin_name": "PrivateKeyContentDetector" + }, + { + "file_path": "./src/scanner/lines.rs", + "line_number": 467, "finding_type": "Password", "matched_content_hash": "f260ab98a91b6cf1495f7d0048606e54f4ea955195e06f0523068b49a9611b44", "plugin_name": "PasswordDetector" @@ -348,7 +348,7 @@ "file_path": "./tests/baseline_tests.rs", "line_number": 23, "finding_type": "AWS Access Key", - "matched_content_hash": "bc377e3c9554437ff10fd844193b505dced77a6af5a662e74232e814d284a1a2", + "matched_content_hash": "3f733150de7916d4778298d7f90493889c38b76876b80c058e439851ce60cb2b", "plugin_name": "AWSKeyDetector" }, { @@ -407,13 +407,6 @@ "matched_content_hash": "e2e5b50c7d336fb0ed238a9c1dd7520b847fe55b990c8f90ecb810a398854d52", "plugin_name": "CreditCardDetector" }, - { - "file_path": "./tests/detector_tests.rs", - "line_number": 302, - "finding_type": "Aadhaar Card Number", - "matched_content_hash": "14270b9138e72cd99e52cab8f9df9ab6cfcec3d2d8d743ca83774ae0d6f1faed", - "plugin_name": "AadhaarCardDetector" - }, { "file_path": "./tests/detector_tests.rs", "line_number": 303, @@ -421,13 +414,6 @@ "matched_content_hash": "13ae894eedbfba2dbd06400ba5b215ffd661885646ab86e050fb1a0d192c1c5b", "plugin_name": "CreditCardDetector" }, - { - "file_path": "./tests/detector_tests.rs", - "line_number": 303, - "finding_type": "Aadhaar Card Number", - "matched_content_hash": "f326bb251646469912e5296e014c0c226ff0e95d866381c1b0d060e7a8bf30ab", - "plugin_name": "AadhaarCardDetector" - }, { "file_path": "./tests/detector_tests.rs", "line_number": 304, @@ -453,47 +439,47 @@ "file_path": "./tests/detector_tests.rs", "line_number": 335, "finding_type": "Phone Number", - "matched_content_hash": "33360227b18134e6e594a3df05104457bac9713999bb6374dc9bd74fd54c49ee", + "matched_content_hash": "af0d73c957c0706f71d0421fd5dd40748b5663537855374b60d71c0624962486", "plugin_name": "PhoneNumberDetector" }, { "file_path": "./tests/detector_tests.rs", "line_number": 335, "finding_type": "Phone Number", - "matched_content_hash": "cc8546d7c697eb4e2aecde0bfa6ece05bcd1323722b22e34f7dba70c97c706fd", + "matched_content_hash": "9a90be7e1667c88c036f8f271e9746c37d99740f5762fb056090339a75ef24e6", "plugin_name": "PhoneNumberDetector" }, { "file_path": "./tests/detector_tests.rs", "line_number": 335, "finding_type": "Phone Number", - "matched_content_hash": "c7730d0abc47a6b7fddd39f1c461ca7e582e5b27b3e38ee4b2dfdec1558d4023", + "matched_content_hash": "950cf54889ab5374a01729d84c2e3cf6a086f403b24e4109f522d22d03f1fcf3", "plugin_name": "PhoneNumberDetector" }, { "file_path": "./tests/detector_tests.rs", - "line_number": 352, + "line_number": 356, "finding_type": "SSH Private Key", "matched_content_hash": "1b01887f477e98dd56ec542c12433dee8c323176bd532c05163823079263ba31", "plugin_name": "SSHPrivateKeyDetector" }, { "file_path": "./tests/detector_tests.rs", - "line_number": 353, + "line_number": 357, "finding_type": "SSH Private Key", "matched_content_hash": "32006da0b4e4851aa7946ffa4f040f364cac7c317f312b5d742ca5833dd8760f", "plugin_name": "SSHPrivateKeyDetector" }, { "file_path": "./tests/detector_tests.rs", - "line_number": 354, + "line_number": 358, "finding_type": "SSH Private Key", "matched_content_hash": "678a65e8968aabff441076ae306e13d4fc85b1d36d8036a10d2100c1dc40d251", "plugin_name": "SSHPrivateKeyDetector" }, { "file_path": "./tests/detector_tests.rs", - "line_number": 365, + "line_number": 369, "finding_type": "Random String", "matched_content_hash": "e51298df0e431de2bfdf6180e3a7b9f3f092c3e9a912facd350e8c7179936e75", "plugin_name": "RandomString" @@ -502,23 +488,9 @@ "file_path": "./tests/exit_tests.rs", "line_number": 32, "finding_type": "AWS Access Key", - "matched_content_hash": "bc377e3c9554437ff10fd844193b505dced77a6af5a662e74232e814d284a1a2", - "plugin_name": "AWSKeyDetector" - }, - { - "file_path": "./tests/exit_tests.rs", - "line_number": 145, - "finding_type": "AWS Access Key", "matched_content_hash": "3f733150de7916d4778298d7f90493889c38b76876b80c058e439851ce60cb2b", "plugin_name": "AWSKeyDetector" }, - { - "file_path": "./tests/exit_tests.rs", - "line_number": 164, - "finding_type": "Email Address", - "matched_content_hash": "fb050d7953f797a143e2098851ef2428b88397c27a6def4e6f4bef5baf4fb6b0", - "plugin_name": "EmailDetector" - }, { "file_path": "./tests/hooks_tests.rs", "line_number": 297, @@ -565,14 +537,14 @@ "file_path": "./tests/report_tests.rs", "line_number": 291, "finding_type": "AWS Access Key", - "matched_content_hash": "bc377e3c9554437ff10fd844193b505dced77a6af5a662e74232e814d284a1a2", + "matched_content_hash": "3f733150de7916d4778298d7f90493889c38b76876b80c058e439851ce60cb2b", "plugin_name": "AWSKeyDetector" }, { "file_path": "./tests/run_cli_error_tests.rs", "line_number": 9, "finding_type": "AWS Access Key", - "matched_content_hash": "bc377e3c9554437ff10fd844193b505dced77a6af5a662e74232e814d284a1a2", + "matched_content_hash": "3f733150de7916d4778298d7f90493889c38b76876b80c058e439851ce60cb2b", "plugin_name": "AWSKeyDetector" }, { @@ -603,13 +575,6 @@ "matched_content_hash": "f6bd37622d846ac435a7b7dcbde2347d59494dfd3c3a787604e8b91491a39c95", "plugin_name": "GenericKeyValueDetector" }, - { - "file_path": "./tests/scanner_tests.rs", - "line_number": 110, - "finding_type": "Email Address", - "matched_content_hash": "fb050d7953f797a143e2098851ef2428b88397c27a6def4e6f4bef5baf4fb6b0", - "plugin_name": "EmailDetector" - }, { "file_path": "./tests/scanner_tests.rs", "line_number": 112, @@ -645,13 +610,6 @@ "matched_content_hash": "7a0d70456feea263871762c537e5e6141866b456eadc938eb9840e59eb08d1a9", "plugin_name": "KimiMoonshotAPIKeyDetector" }, - { - "file_path": "./tests/scanner_tests.rs", - "line_number": 136, - "finding_type": "Aadhaar Card Number", - "matched_content_hash": "39336d01282aa7877cf262c7e980c64c3e6cbfa711933be49eb0bed651a12ce4", - "plugin_name": "AadhaarCardDetector" - }, { "file_path": "./tests/scanner_tests.rs", "line_number": 137, @@ -663,14 +621,14 @@ "file_path": "./tests/scanner_tests.rs", "line_number": 160, "finding_type": "Generic Key/Secret", - "matched_content_hash": "8450e62d795b679b8f440e183ba0af22b8534d0cb06db8629d0b3ff98a030928", + "matched_content_hash": "4c8b5530b17fef887a9b327f69a38f1958e4b656f2b1c54b1d426320ead690ff", "plugin_name": "GenericKeyValueDetector" }, { "file_path": "./tests/scanner_tests.rs", "line_number": 160, "finding_type": "Base64 Encoded String", - "matched_content_hash": "587ba5528b57a5d889136bbf1ea490763b8d652e61da4f5d78d8a1a81ba8edcb", + "matched_content_hash": "332a14e95304a48a4ef671073cc98f0fc4046c61042ac20287bfa22b37c27d01", "plugin_name": "Base64Detector" }, { @@ -680,20 +638,6 @@ "matched_content_hash": "6c6d557cc63eda114745424f991b2019fdfde2844412a1f58fb5527f0e0bef9f", "plugin_name": "GenericKeyValueDetector" }, - { - "file_path": "./tests/scanner_tests.rs", - "line_number": 184, - "finding_type": "Private Key Content", - "matched_content_hash": "b94c156724c086556087773aad998c0791dc79172caa4b3184a7330cfa7c1d76", - "plugin_name": "PrivateKeyDetector" - }, - { - "file_path": "./tests/scanner_tests.rs", - "line_number": 184, - "finding_type": "Private Key Content", - "matched_content_hash": "b94c156724c086556087773aad998c0791dc79172caa4b3184a7330cfa7c1d76", - "plugin_name": "PrivateKeyContentDetector" - }, { "file_path": "./tests/scanner_tests.rs", "line_number": 184, @@ -710,11 +654,18 @@ }, { "file_path": "./tests/scanner_tests.rs", - "line_number": 186, + "line_number": 184, "finding_type": "Private Key Content", - "matched_content_hash": "43531827dd6142886cfeb10207f046021a4eb6c575828583ad2cb20d9430c72f", + "matched_content_hash": "b94c156724c086556087773aad998c0791dc79172caa4b3184a7330cfa7c1d76", "plugin_name": "PrivateKeyDetector" }, + { + "file_path": "./tests/scanner_tests.rs", + "line_number": 184, + "finding_type": "Private Key Content", + "matched_content_hash": "b94c156724c086556087773aad998c0791dc79172caa4b3184a7330cfa7c1d76", + "plugin_name": "PrivateKeyContentDetector" + }, { "file_path": "./tests/scanner_tests.rs", "line_number": 186, @@ -722,6 +673,13 @@ "matched_content_hash": "ca54604a9ed82ad96e5f5d68450002ef41112a7058469bbbdde94f83b267dca5", "plugin_name": "SSHPrivateKeyDetector" }, + { + "file_path": "./tests/scanner_tests.rs", + "line_number": 186, + "finding_type": "Private Key Content", + "matched_content_hash": "43531827dd6142886cfeb10207f046021a4eb6c575828583ad2cb20d9430c72f", + "plugin_name": "PrivateKeyDetector" + }, { "file_path": "./tests/scanner_tests.rs", "line_number": 187, @@ -733,9 +691,16 @@ "file_path": "./tests/scanner_tests.rs", "line_number": 209, "finding_type": "Password", - "matched_content_hash": "a8a14cd33571f4a245d8f4e1b1c9f0ffd05d915db5573d1c66394e9540242d93", + "matched_content_hash": "4ba2d1c9c6db00e16d0b1dcead9e6fe60fc315c7b4a76cb2c07def4685dc8669", "plugin_name": "PasswordDetector" }, + { + "file_path": "./tests/scanner_tests.rs", + "line_number": 209, + "finding_type": "Email Address", + "matched_content_hash": "48f63a76aa3c93efd693e0d5a960f5fe3ddd697e6a280f812bf678de80106a25", + "plugin_name": "EmailDetector" + }, { "file_path": "./tests/scanner_tests.rs", "line_number": 240, @@ -803,14 +768,21 @@ "file_path": "./tests/scanner_tests.rs", "line_number": 595, "finding_type": "Aadhaar Card Number", - "matched_content_hash": "d72c959a2a51ac75ee2eb9db2b5d041f33ea32bea37515473e0487c2c3a3dce2", + "matched_content_hash": "4714ac4f70659987acae4a4760aedecd3924449f855e3be8115a51eb1ad35f93", + "plugin_name": "AadhaarCardDetector" + }, + { + "file_path": "./tests/scanner_tests.rs", + "line_number": 595, + "finding_type": "Aadhaar Card Number", + "matched_content_hash": "73e8a0879ef4b4acf94b2c188c0b620e4dd198bef454f6270118d63fd87ff4a3", "plugin_name": "AadhaarCardDetector" }, { "file_path": "./tests/scanner_tests.rs", "line_number": 595, "finding_type": "Aadhaar Card Number", - "matched_content_hash": "6d997bbfce7b131f5970fa00faeb56eaccc723239297811aa8c8770d11b7b19a", + "matched_content_hash": "c941e88b8d0be7288dcc792a8ecfae22482b8a83ea4494f1ca0e36293009730b", "plugin_name": "AadhaarCardDetector" }, { @@ -841,13 +813,6 @@ "matched_content_hash": "4dd22fad4bf96036ceea29ca253be8d72851cc289d7e694c9b208b7370812ee6", "plugin_name": "PANCardDetector" }, - { - "file_path": "./tests/scanner_tests.rs", - "line_number": 670, - "finding_type": "Aadhaar Card Number", - "matched_content_hash": "881524bd6d254d60a115d72efeb134b7f82fe0fc9fa0f0feac806edce4f2889d", - "plugin_name": "AadhaarCardDetector" - }, { "file_path": "./tests/scanner_tests.rs", "line_number": 670, @@ -862,13 +827,6 @@ "matched_content_hash": "397d0bc36d77ccfc6f74c2eaa6615bb75c3fe3bdc3df26e6f53f413cfcdacdb3", "plugin_name": "ABHADetector" }, - { - "file_path": "./tests/scanner_tests.rs", - "line_number": 695, - "finding_type": "Aadhaar Card Number", - "matched_content_hash": "2a9d3ae086b4485e7a056b74ea2dc7ad45855144971e2fff00b402a8dd4244a0", - "plugin_name": "AadhaarCardDetector" - }, { "file_path": "./tests/scanner_tests.rs", "line_number": 695, @@ -918,18 +876,11 @@ "matched_content_hash": "05c0aace2b76ca255ed3a7a953016d981477226dccc3b0e709d00174c8bc48b5", "plugin_name": "AWSKeyDetector" }, - { - "file_path": "./tests/scanner_tests.rs", - "line_number": 1209, - "finding_type": "AWS Access Key", - "matched_content_hash": "bc377e3c9554437ff10fd844193b505dced77a6af5a662e74232e814d284a1a2", - "plugin_name": "AWSKeyDetector" - }, { "file_path": "./tests/scanner_tests.rs", "line_number": 1455, "finding_type": "Generic Key/Secret", - "matched_content_hash": "3490ca8089f67ef5e1c7455855266a1337d5817e69935e870adb73d049810429", + "matched_content_hash": "97894929682fc00219686473cbcfa3731d73b23e88ffa7c19a511c1bbfa18aa5", "plugin_name": "GenericKeyValueDetector" }, { @@ -947,74 +898,25 @@ "plugin_name": "AWSKeyDetector" }, { - "file_path": "./src/config/tests/application.rs", - "line_number": 298, - "finding_type": "Credit Card Number", - "matched_content_hash": "4541206d542811878a9374508fe296fa321a8b56c2902736a02f388836f6e108", - "plugin_name": "CreditCardDetector" - }, - { - "file_path": "./src/scanner/lines.rs", - "line_number": 368, - "finding_type": "AWS Access Key", - "matched_content_hash": "3f733150de7916d4778298d7f90493889c38b76876b80c058e439851ce60cb2b", - "plugin_name": "AWSKeyDetector" - }, - { - "file_path": "./src/scanner/lines.rs", - "line_number": 368, + "file_path": "./tests/scanner_tests.rs", + "line_number": 2212, "finding_type": "Password", - "matched_content_hash": "f6bd37622d846ac435a7b7dcbde2347d59494dfd3c3a787604e8b91491a39c95", + "matched_content_hash": "6121378fbc25183476235d474ee367f7dbd0bbe3d96642daaeb483b5e9108cdb", "plugin_name": "PasswordDetector" }, { - "file_path": "./src/scanner/lines.rs", - "line_number": 368, - "finding_type": "Generic Key/Secret", - "matched_content_hash": "f6bd37622d846ac435a7b7dcbde2347d59494dfd3c3a787604e8b91491a39c95", - "plugin_name": "GenericKeyValueDetector" - }, - { - "file_path": "./src/scanner/lines.rs", - "line_number": 424, - "finding_type": "SSH Private Key", - "matched_content_hash": "678a65e8968aabff441076ae306e13d4fc85b1d36d8036a10d2100c1dc40d251", - "plugin_name": "SSHPrivateKeyDetector" - }, - { - "file_path": "./src/scanner/lines.rs", - "line_number": 424, - "finding_type": "Base64 Encoded String", - "matched_content_hash": "3dde06bf268892d4210f4b0bf1402ecc6a8ad1e015f8204cdc31667762572ef5", - "plugin_name": "Base64Detector" - }, - { - "file_path": "./src/scanner/lines.rs", - "line_number": 424, - "finding_type": "Private Key Content", - "matched_content_hash": "f91082d1cbd2032b5ea19f2bbf6b3f88e02c12ae320fc9e08e6dc0f73d82bdd1", - "plugin_name": "PrivateKeyDetector" - }, - { - "file_path": "./src/scanner/lines.rs", - "line_number": 424, - "finding_type": "Private Key Content", - "matched_content_hash": "f91082d1cbd2032b5ea19f2bbf6b3f88e02c12ae320fc9e08e6dc0f73d82bdd1", - "plugin_name": "PrivateKeyContentDetector" - }, - { - "file_path": "./src/scanner/lines.rs", - "line_number": 447, - "finding_type": "Password", - "matched_content_hash": "f260ab98a91b6cf1495f7d0048606e54f4ea955195e06f0523068b49a9611b44", - "plugin_name": "PasswordDetector" + "file_path": "./verhoeff_check.py", + "line_number": 31, + "finding_type": "Aadhaar Card Number", + "matched_content_hash": "a8718ff203b57b356b230985c321360b679a53eb01ac1d626f34f58b6f4adc08", + "plugin_name": "AadhaarCardDetector" }, { - "file_path": "./tests/scanner_tests.rs", - "line_number": 2212, - "finding_type": "Password", - "matched_content_hash": "6121378fbc25183476235d474ee367f7dbd0bbe3d96642daaeb483b5e9108cdb", - "plugin_name": "PasswordDetector" + "file_path": "./.github/workflows/ci.yml", + "line_number": 74, + "finding_type": "AWS Access Key", + "matched_content_hash": "3f733150de7916d4778298d7f90493889c38b76876b80c058e439851ce60cb2b", + "plugin_name": "AWSKeyDetector" } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 981343f..9e7d70b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ All notable changes to this project will be documented in this file. - Files with invalid UTF-8 are decoded lossily and scanned instead of silently skipped; NUL-containing files are reported as `unscannable` - `Finding`'s `plugin_name` field is now `detector_name` in the code; the JSON report and baseline schema still emit/accept `plugin_name` - `CustomRule.description` was parsed but never surfaced and has been dropped (configs carrying it keep parsing) +- False-positive reductions in the built-in detectors: AWS's documentation example key, placeholder values (`changeme`, `your-api-key-here`, `replace-me-please`), RFC 2606 example-domain emails and noreply conventions, fictional 555 phone numbers, npm/shield checksum prefixes, and non-Verhoeff 12-digit runs no longer report as Aadhaar - `--baseline` naming a missing file is an error instead of silently scanning with an empty baseline - Baseline files with an unknown format version are rejected instead of silently accepted - `--update-baseline` refreshes the recorded line numbers of entries it already knows, and saved baselines end with a newline @@ -67,6 +68,7 @@ All notable changes to this project will be documented in this file. - Keyword matching uses a single Aho-Corasick pass per line: ~3x faster file scans, ~9x faster streams - File scans stream line by line instead of reading whole files into memory +- ~2.5x faster file scans: one combined prefilter pass for the keywordless detectors, an ASCII fast path for line lowering, and a byte-histogram entropy check that no longer allocates per match ## [2.0.1] - 2026-08-02 diff --git a/detectors.toml b/detectors.toml index 398ef0f..6bdd37d 100644 --- a/detectors.toml +++ b/detectors.toml @@ -4,6 +4,9 @@ pattern = "\\bAKIA[0-9A-Z]{16}\\b" finding_type = "AWS Access Key" severity = "HIGH" keywords = ["AKIA"] +# AWS's own documentation example key appears in READMEs everywhere and is +# never a real credential; exact-match allowlist so nothing else is lost. +allowlist = ["^AKIAIOSFODNN7EXAMPLE$"] [[detectors]] name = "GoogleAPIKeyDetector" @@ -55,13 +58,27 @@ severity = "HIGH" keywords = ["password", "passwd", "pwd"] # $PWD is the shell working-directory variable (docker --volume "$PWD:...", # Makefiles), never a password literal. -allowlist = ["^\\$?PWD:"] +allowlist = [ + "^\\$?PWD:", + # Placeholder values in templates and .env.example files. Case-sensitive + # (placeholders are lowercase by convention) and shape-checked: a mixed + # value like "ReplaceThisRealSecret123" does not match and stays reported. + "[=:\"]?\\s*(changeme|changeit|your(-[a-z0-9]+)+|replace(-[a-z0-9]+)+|placeholder|dummy|xxxxx+)", +] [[detectors]] name = "EmailDetector" pattern = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b" finding_type = "Email Address" severity = "LOW" +# Emails are rarely secrets; the noisiest matches are documentation +# addresses on the RFC 2606 reserved domains and noreply conventions. +allowlist = [ + "@example\\.(com|org|net)", + "^noreply@", + "^no-reply@", + "@users\\.noreply\\.github\\.com$", +] [[detectors]] name = "IPAddressDetector" @@ -76,6 +93,9 @@ name = "PhoneNumberDetector" pattern = "(?:\\+1[-.\\s]?)?(?:\\(\\d{3}\\)[-.\\s]*|\\b\\d{3}[-.\\s])\\d{3}[-.\\s]?\\d{4}\\b" finding_type = "Phone Number" severity = "LOW" +# The 555 exchange is reserved for fictional use; documentation phone +# numbers are the detector's dominant match. +allowlist = ["555"] [[detectors]] name = "CreditCardDetector" @@ -113,6 +133,9 @@ entropy = 2.5 allowlist = [ "[:=]\\s*[a-z]+(?:_[a-z]+)+$", "[:=]\\s*[A-Z][a-z]+(?:[A-Z][a-z]*)*$", + # Placeholder values in templates and .env.example files (case-sensitive: + # mixed-case shapes are plausibly real weak credentials). + "[=:]\\s*[\"']?(changeme|changeit|your(-[a-z0-9]+)+|replace(-[a-z0-9]+)+|placeholder|dummy|xxxxx+)", ] [[detectors]] @@ -142,6 +165,8 @@ pattern = "\\b[A-Za-z0-9+/]{28,}[=]{0,2}\\b" finding_type = "Base64 Encoded String" severity = "LOW" entropy = 4.2 +# The AWS documentation example secret pairs with the example access key. +allowlist = ["^wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY$"] [[detectors]] name = "HighEntropyDetector" @@ -188,7 +213,12 @@ entropy = 3.5 # Long all-lowercase snake_case string literals are identifiers — serde # attributes, config keys, enum names — not random values. A generated secret # of this length carries digits or capitals. -allowlist = ["^\"[a-z]+(?:_[a-z]+)+\"$"] +allowlist = [ + "^\"[a-z]+(?:_[a-z]+)+\"$", + # npm/shield-style checksum strings carry their algorithm prefix inside + # the quotes, so the prefix is visible in the match. + "^\"sha(256|384|512)-", +] [[detectors]] @@ -323,6 +353,9 @@ name = "AadhaarCardDetector" pattern = "\\b\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\\b" finding_type = "Aadhaar Card Number" severity = "HIGH" +# Real Aadhaar numbers satisfy the Verhoeff checksum; without it, any +# 12-digit run (UUID tails, numeric ids) reports HIGH. +validate = "verhoeff" [[detectors]] name = "VoterIDDetector" diff --git a/src/detector.rs b/src/detector.rs index 3b714ce..c661e84 100644 --- a/src/detector.rs +++ b/src/detector.rs @@ -47,6 +47,9 @@ pub enum ContentValidator { /// digit pattern matches every commit hash fragment, timestamp and /// numeric id in a codebase. Luhn, + /// Aadhaar numbers carry a Verhoeff check digit. Without it, every + /// 12-digit run (the tail of a UUID, a numeric id) reports HIGH. + Verhoeff, } impl FromStr for ContentValidator { @@ -55,6 +58,7 @@ impl FromStr for ContentValidator { fn from_str(value: &str) -> Result { match value.trim().to_lowercase().as_str() { "luhn" => Ok(Self::Luhn), + "verhoeff" => Ok(Self::Verhoeff), other => Err(ParseValidatorError { value: other.to_string(), }), @@ -68,6 +72,76 @@ pub struct ParseValidatorError { value: String, } +/// Verhoeff check tables (the dihedral group D5 permutation). Aadhaar +/// numbers are the common real-world user. +const VERHOEFF_D: [[u8; 10]; 10] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [1, 2, 3, 4, 0, 6, 7, 8, 9, 5], + [2, 3, 4, 0, 1, 7, 8, 9, 5, 6], + [3, 4, 0, 1, 2, 8, 9, 5, 6, 7], + [4, 0, 1, 2, 3, 9, 5, 6, 7, 8], + [5, 9, 8, 7, 6, 0, 4, 3, 2, 1], + [6, 5, 9, 8, 7, 1, 0, 4, 3, 2], + [7, 6, 5, 9, 8, 2, 1, 0, 4, 3], + [8, 7, 6, 5, 9, 3, 2, 1, 0, 4], + [9, 8, 7, 6, 5, 4, 3, 2, 1, 0], +]; +const VERHOEFF_P: [[usize; 10]; 8] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [1, 5, 7, 6, 2, 8, 3, 0, 9, 4], + [5, 8, 0, 3, 7, 9, 6, 1, 4, 2], + [8, 9, 1, 6, 0, 4, 3, 5, 2, 7], + [9, 4, 5, 3, 1, 2, 6, 8, 7, 0], + [4, 2, 8, 6, 5, 7, 3, 9, 0, 1], + [2, 7, 9, 3, 8, 0, 6, 4, 1, 5], + [7, 0, 4, 6, 9, 1, 3, 2, 5, 8], +]; + +/// Verhoeff checksum, ignoring embedded separators. Only the check digit +/// satisfies the scheme, so random 12-digit runs are rejected ~90% of the +/// time — and a run embedded in a longer id almost always fails outright. +fn passes_verhoeff(matched: &str) -> bool { + let digits: Vec = matched + .chars() + .filter_map(|c| c.to_digit(10).map(|d| d as u8)) + .collect(); + if digits.is_empty() { + return false; + } + let mut check = 0u8; + for (index, digit) in digits.iter().rev().enumerate() { + check = VERHOEFF_D[check as usize][VERHOEFF_P[index % 8][*digit as usize]]; + } + check == 0 +} + +/// Luhn checksum, ignoring embedded separators. +#[cfg(test)] +mod verhoeff_tests { + use super::passes_verhoeff; + use crate::detector::ContentValidator; + use std::str::FromStr; + + #[test] + fn verhoeff_accepts_the_canonical_examples() { + // Wikipedia: 2363 passes, 2362 does not; the check digit for 236 is 3. + assert!(passes_verhoeff("2363")); + assert!(!passes_verhoeff("2362")); + assert_eq!( + ContentValidator::from_str("verhoeff").unwrap(), + ContentValidator::Verhoeff + ); + } + + #[test] + fn verhoeff_rejects_embedded_id_runs() { + // The false positive this validator exists for: the tail of a UUID. + assert!(!passes_verhoeff("123456789012")); + assert!(!passes_verhoeff("446655440000")); + assert!(passes_verhoeff("100000000004")); + } +} + /// Luhn checksum, ignoring embedded separators. fn passes_luhn(matched: &str) -> bool { let digits: Vec = matched.chars().filter_map(|c| c.to_digit(10)).collect(); @@ -155,6 +229,7 @@ impl Detector { pub fn passes_validation(&self, matched: &str) -> bool { match self.validator { Some(ContentValidator::Luhn) => passes_luhn(matched), + Some(ContentValidator::Verhoeff) => passes_verhoeff(matched), None => true, } } @@ -200,15 +275,23 @@ impl Detector { } fn shannon_entropy(input: &str) -> f64 { - if input.is_empty() { - return 0.0; + // Byte-histogram in a fixed array: no allocation per candidate match, + // and for the ASCII secret shapes the thresholds target, identical to a + // char-based count. + let mut counts = [0u32; 256]; + let mut length = 0usize; + for byte in input.bytes() { + counts[byte as usize] += 1; + length += 1; } - let mut counts = std::collections::HashMap::new(); - for character in input.chars() { - *counts.entry(character).or_insert(0) += 1; + if length == 0 { + return 0.0; } - let input_length = input.len() as f64; - counts.values().fold(0.0, |entropy, &count| { + let input_length = length as f64; + counts.iter().fold(0.0, |entropy, &count| { + if count == 0 { + return entropy; + } let probability = count as f64 / input_length; entropy - probability * probability.log2() }) diff --git a/src/scanner/lines.rs b/src/scanner/lines.rs index cebe189..255b69c 100644 --- a/src/scanner/lines.rs +++ b/src/scanner/lines.rs @@ -5,6 +5,7 @@ use crate::detector::Detector; use crate::report::Finding; use crate::scanner::ScannerError; use aho_corasick::AhoCorasick; +use regex::Regex; use std::collections::HashSet; use std::io::BufRead; @@ -15,9 +16,16 @@ fn is_inline_suppressed(line: &str) -> bool { } /// Lowercases `src` into `buf` without allocating a fresh string per line. +/// ASCII input (the overwhelming majority of scanned bytes) takes a +/// byte-per-byte fast path; the Unicode path is char-by-char and an order of +/// magnitude slower. fn to_lowercase_into(src: &str, buf: &mut String) { buf.clear(); - buf.extend(src.chars().flat_map(char::to_lowercase)); + if src.is_ascii() { + buf.extend(src.chars().map(|c| c.to_ascii_lowercase())); + } else { + buf.extend(src.chars().flat_map(char::to_lowercase)); + } } /// Folds every distinct detector keyword into one Aho-Corasick automaton so a /// line is checked against all keywords in a single pass instead of one @@ -72,6 +80,22 @@ impl KeywordPrefilter { } } + /// Whether the combined keywordless gate may filter detectors: the gate + /// is only sound while the automaton handles exactly the keyword + /// detectors (on the fail-open path `unconditional` holds every detector, + /// and gating them would risk missed secrets). + fn gate_eligible(&self) -> bool { + self.automaton.is_some() + } + + /// Clears the keywordless detectors from `candidates` after the combined + /// gate ruled the line out. + fn clear_unconditional(&self, candidates: &mut [bool]) { + for &detector_index in &self.unconditional { + candidates[detector_index] = false; + } + } + /// Marks the detectors whose keywords occur in `lowered_line` in /// `candidates`, a scratch buffer reused across lines. fn candidates_into(&self, lowered_line: &str, candidates: &mut Vec) { @@ -93,13 +117,42 @@ impl KeywordPrefilter { pub(super) struct LineScanContext<'detectors> { line_detectors: &'detectors [&'detectors Detector], prefilter: KeywordPrefilter, + /// One combined any-of regex over the keywordless detectors: a single + /// pass decides whether any of them can match the line, and their + /// individual regex passes are skipped on lines that cannot. The gate is + /// exact — is_match is existence, and each pattern is isolated in its own + /// group so flags cannot leak between branches — so no match is ever + /// lost; matching lines simply pay the gate plus their real passes. + /// `None` when a gate cannot be built (fail open). + unconditional_gate: Option, } impl<'detectors> LineScanContext<'detectors> { pub(super) fn new(line_detectors: &'detectors [&'detectors Detector]) -> Self { + let prefilter = KeywordPrefilter::new(line_detectors); + let unconditional_gate = prefilter + .gate_eligible() + .then(|| { + let branches: Vec<&str> = line_detectors + .iter() + .filter(|detector| detector.keywords.is_empty()) + .map(|detector| detector.regex.as_str()) + .collect(); + let combined = format!( + "(?:{})", + branches + .iter() + .map(|branch| format!("(?:{branch})")) + .collect::>() + .join("|") + ); + Regex::new(&combined).ok() + }) + .flatten(); Self { line_detectors, - prefilter: KeywordPrefilter::new(line_detectors), + prefilter, + unconditional_gate, } } } @@ -127,6 +180,15 @@ pub(super) fn scan_line_detectors( context .prefilter .candidates_into(&scratch.lowered_line, &mut scratch.candidates); + let run_unconditional = context + .unconditional_gate + .as_ref() + .is_none_or(|gate| gate.is_match(line)); + if !run_unconditional { + context + .prefilter + .clear_unconditional(&mut scratch.candidates); + } for (detector_index, detector) in context.line_detectors.iter().enumerate() { if !scratch.candidates[detector_index] { diff --git a/tests/baseline_tests.rs b/tests/baseline_tests.rs index 5a07675..8b59c3b 100644 --- a/tests/baseline_tests.rs +++ b/tests/baseline_tests.rs @@ -20,7 +20,7 @@ fn test_baseline_filters_known_findings() { "test.txt", 5, "AWS Key", - "AKIAIOSFODNN7EXAMPLE", + "AKIAABCDEFGHIJKLMNOP", "AWSAccessKeyDetector", ); let baseline = Baseline::from_findings(std::slice::from_ref(&known_finding)); @@ -47,7 +47,7 @@ fn test_baseline_filters_moved_finding_in_same_file() { "test.txt", 5, "AWS Key", - "AKIAIOSFODNN7EXAMPLE", + "AKIAABCDEFGHIJKLMNOP", "AWSAccessKeyDetector", ); let baseline = Baseline::from_findings(std::slice::from_ref(&known_finding)); @@ -56,7 +56,7 @@ fn test_baseline_filters_moved_finding_in_same_file() { "test.txt", 42, "AWS Key", - "AKIAIOSFODNN7EXAMPLE", + "AKIAABCDEFGHIJKLMNOP", "AWSAccessKeyDetector", )]; @@ -70,7 +70,7 @@ fn test_baseline_keeps_same_finding_in_different_file() { "test.txt", 5, "AWS Key", - "AKIAIOSFODNN7EXAMPLE", + "AKIAABCDEFGHIJKLMNOP", "AWSAccessKeyDetector", ); let baseline = Baseline::from_findings(std::slice::from_ref(&known_finding)); @@ -79,7 +79,7 @@ fn test_baseline_keeps_same_finding_in_different_file() { "other.txt", 42, "AWS Key", - "AKIAIOSFODNN7EXAMPLE", + "AKIAABCDEFGHIJKLMNOP", "AWSAccessKeyDetector", )]; diff --git a/tests/detector_tests.rs b/tests/detector_tests.rs index f7ca0ae..8309ef6 100644 --- a/tests/detector_tests.rs +++ b/tests/detector_tests.rs @@ -332,7 +332,7 @@ fn test_credit_card_requires_issuer_prefix_and_luhn() { #[test] fn test_phone_number_requires_separator_or_country_code() { - for phone in ["call 555-123-4567", "(555) 123-4567", "+1 555 123 4567"] { + for phone in ["call 415-123-4567", "(415) 123-4567", "+1 415 123 4567"] { assert!( reported_by(phone).contains(&"PhoneNumberDetector".to_string()), "should detect phone: {phone}" @@ -342,6 +342,10 @@ fn test_phone_number_requires_separator_or_country_code() { !reported_by("ts 1700000000").contains(&"PhoneNumberDetector".to_string()), "a bare 10-digit run is a timestamp, not a phone number" ); + assert!( + !reported_by("call 555-123-4567").contains(&"PhoneNumberDetector".to_string()), + "the fictional 555 exchange is allowlisted" + ); } #[test] diff --git a/tests/exit_tests.rs b/tests/exit_tests.rs index bfef9a0..7030b2e 100644 --- a/tests/exit_tests.rs +++ b/tests/exit_tests.rs @@ -29,7 +29,7 @@ fn setup_scan_dir(name: &str, include_detectors: bool) -> PathBuf { fn test_exit_code_on_secrets() { let test_dir = setup_scan_dir("exit_secrets", true); let temp_file = test_dir.join("secret.txt"); - fs::write(&temp_file, "AWS_KEY=AKIAIOSFODNN7EXAMPLE").expect("Write test file"); + fs::write(&temp_file, "AWS_KEY=AKIAABCDEFGHIJKLMNOP").expect("Write test file"); let status = Command::new(env!("CARGO_BIN_EXE_key-watch")) .current_dir(&test_dir) @@ -74,7 +74,7 @@ fn test_runtime_errors_exit_with_code_two() { let test_dir = setup_scan_dir("exit_runtime_error", false); let temp_file = test_dir.join("secret.txt"); let invalid_detectors = test_dir.join("invalid-detectors.toml"); - fs::write(&temp_file, "AWS_KEY=AKIAIOSFODNN7EXAMPLE").expect("Write test file"); + fs::write(&temp_file, "AWS_KEY=AKIAABCDEFGHIJKLMNOP").expect("Write test file"); fs::write(&invalid_detectors, "[[detectors]").expect("Write invalid detector config"); let status = Command::new(env!("CARGO_BIN_EXE_key-watch")) @@ -95,7 +95,7 @@ fn test_embedded_detectors_enable_standalone_scan() { // Given a standalone binary with no detector configuration on disk. let test_dir = setup_scan_dir("embedded_detectors", false); let temp_file = test_dir.join("secret.txt"); - fs::write(&temp_file, "AWS_KEY=AKIAIOSFODNN7EXAMPLE").expect("Write test file"); + fs::write(&temp_file, "AWS_KEY=AKIAABCDEFGHIJKLMNOP").expect("Write test file"); // When the binary scans a file containing a built-in detector match. let output = Command::new(env!("CARGO_BIN_EXE_key-watch")) @@ -122,7 +122,7 @@ fn test_embedded_detectors_enable_standalone_scan() { fn test_exit_mode_always() { let test_dir = setup_scan_dir("exit_always", true); let temp_file = test_dir.join("secret.txt"); - fs::write(&temp_file, "AWS_KEY=AKIAIOSFODNN7EXAMPLE").expect("Write test file"); + fs::write(&temp_file, "AWS_KEY=AKIAABCDEFGHIJKLMNOP").expect("Write test file"); let status = Command::new(env!("CARGO_BIN_EXE_key-watch")) .current_dir(&test_dir) @@ -187,8 +187,8 @@ fn test_scan_multiple_paths() { let test_dir = setup_scan_dir("scan_multi_paths", true); let file1 = test_dir.join("secret1.txt"); let file2 = test_dir.join("secret2.txt"); - fs::write(&file1, "AWS_KEY=AKIAIOSFODNN7EXAMPLE").expect("Write test file 1"); - fs::write(&file2, "AWS_KEY=AKIAIOSFODNN7EXAMPLE").expect("Write test file 2"); + fs::write(&file1, "AWS_KEY=AKIAABCDEFGHIJKLMNOP").expect("Write test file 1"); + fs::write(&file2, "AWS_KEY=AKIAABCDEFGHIJKLMNOP").expect("Write test file 2"); let status = Command::new(env!("CARGO_BIN_EXE_key-watch")) .current_dir(&test_dir) @@ -211,7 +211,7 @@ fn test_scan_multiple_paths() { fn test_scan_output_file() { let test_dir = setup_scan_dir("scan_output_file", true); let temp_file = test_dir.join("secret.txt"); - fs::write(&temp_file, "AWS_KEY=AKIAIOSFODNN7EXAMPLE").expect("Write test file"); + fs::write(&temp_file, "AWS_KEY=AKIAABCDEFGHIJKLMNOP").expect("Write test file"); let out_file = test_dir.join("report.json"); @@ -277,7 +277,7 @@ fn test_scan_output_file_when_no_secrets_reports_pass() { fn test_scan_verbose_output() { let test_dir = setup_scan_dir("scan_verbose_output", true); let temp_file = test_dir.join("secret.txt"); - fs::write(&temp_file, "AWS_KEY=AKIAIOSFODNN7EXAMPLE").expect("Write test file"); + fs::write(&temp_file, "AWS_KEY=AKIAABCDEFGHIJKLMNOP").expect("Write test file"); let output = Command::new(env!("CARGO_BIN_EXE_key-watch")) .current_dir(&test_dir) @@ -295,7 +295,7 @@ fn test_scan_verbose_output() { let stdout = String::from_utf8_lossy(&output.stdout); assert!( - !stdout.contains("AKIAIOSFODNN7EXAMPLE"), + !stdout.contains("AKIAABCDEFGHIJKLMNOP"), "reports must redact matched text by default, got:\n{stdout}" ); assert!( @@ -353,7 +353,7 @@ fn test_init_command() { fn test_show_secrets_opts_into_raw_matched_content() { let dir = setup_scan_dir("show_secrets", true); let file = dir.join("leak.txt"); - fs::write(&file, "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n").expect("write"); + fs::write(&file, "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n").expect("write"); let output = Command::new(env!("CARGO_BIN_EXE_key-watch")) .args(["scan"]) @@ -364,7 +364,7 @@ fn test_show_secrets_opts_into_raw_matched_content() { let stdout = String::from_utf8_lossy(&output.stdout); assert!( - stdout.contains("AKIAIOSFODNN7EXAMPLE"), + stdout.contains("AKIAABCDEFGHIJKLMNOP"), "--show-secrets must include raw matched text, got:\n{stdout}" ); diff --git a/tests/report_tests.rs b/tests/report_tests.rs index 29852bf..af156d2 100644 --- a/tests/report_tests.rs +++ b/tests/report_tests.rs @@ -288,7 +288,7 @@ fn test_redact_shows_no_prefix_for_short_matches() { assert_eq!(key_watch::report::redact("abc12"), "(5 chars, redacted)"); assert_eq!(key_watch::report::redact("abc1234"), "(7 chars, redacted)"); assert_eq!( - key_watch::report::redact("AKIAIOSFODNN7EXAMPLE"), + key_watch::report::redact("AKIAABCDEFGHIJKLMNOP"), "AKIA... (20 chars, redacted)" ); } diff --git a/tests/run_cli_error_tests.rs b/tests/run_cli_error_tests.rs index 66a9f14..b46d618 100644 --- a/tests/run_cli_error_tests.rs +++ b/tests/run_cli_error_tests.rs @@ -6,7 +6,7 @@ fn test_scan_stdin_with_path_exits_two_with_validation_error() { let temp_dir = tempfile::tempdir().expect("Create test dir"); fs::write( temp_dir.path().join("secret.txt"), - "AWS_KEY=AKIAIOSFODNN7EXAMPLE", + "AWS_KEY=AKIAABCDEFGHIJKLMNOP", ) .expect("Write test file"); diff --git a/tests/scanner_tests.rs b/tests/scanner_tests.rs index 11c9348..6b0ac56 100644 --- a/tests/scanner_tests.rs +++ b/tests/scanner_tests.rs @@ -157,7 +157,7 @@ fn test_find_cloud_credentials() { let content = "\ AWS_ACCESS_KEY_ID=AKIAABCDEFGHIJKLMNOP\n\ -AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n\ +AWS_SECRET_ACCESS_KEY=xJalrXUtnFEMIK7MDENGbPxRfiCYEXAMPLEKEY42\n\ GCP_API_KEY=AIzaSyC93k4n4BxvV_XYZ1234567890abcdefghijk\n\ AZURE_STORAGE=DefaultEndpointsProtocol=https;AccountName=examplestore; "; @@ -206,7 +206,7 @@ fn test_multiple_detections_in_line() { let temp_dir = temp_dir(); let test_file = temp_dir.join("key_watch_multi.txt"); - let content = "password=secret email=user@example.com key=AKIATESTKEY123"; + let content = "password=secret email=user@sampledomain.dev key=AKIATESTKEY123"; fs::write(&test_file, content).expect("Unable to write test file"); let options = ScanArgs { @@ -592,7 +592,7 @@ fn test_detect_aadhaar() { let temp_dir = temp_dir(); let test_file = temp_dir.join("keywatch_aadhaar_test.txt"); - let content = "My Aadhaar: 1234-5678-9012\nBackup: 1234 5678 9012\nNo space: 123456789012"; + let content = "My Aadhaar: 1000-0000-0004\nBackup: 1000 0000 0004\nNo space: 100000000004"; fs::write(&test_file, content).expect("Write test file"); let options = ScanArgs { @@ -692,7 +692,7 @@ fn test_multiple_indian_ids() { let test_file = temp_dir.join("keywatch_indian_ids.txt"); let content = - "Aadhaar: 9999-8888-7777\nVoter ID: ABC1234567\nPAN: XYZZU1234A\nABHA: 1111-2222-3333-44"; + "Aadhaar: 1000-0000-0004\nVoter ID: ABC1234567\nPAN: XYZZU1234A\nABHA: 1111-2222-3333-44"; fs::write(&test_file, content).expect("Write test file"); let options = ScanArgs { @@ -1206,7 +1206,7 @@ fn test_baseline_file_itself_is_never_scanned() { fs::create_dir_all(&dir).expect("create temp dir"); fs::write( dir.join("secrets.txt"), - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", ) .expect("write secret file"); @@ -1350,7 +1350,7 @@ fn test_discovered_baseline_file_is_never_scanned() -> Result<(), String> { init_git_repo(&repo_dir)?; fs::write( repo_dir.join("secrets.txt"), - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", ) .map_err(|e| e.to_string())?; @@ -1400,7 +1400,7 @@ fn test_staged_scan_skips_the_baseline_file() -> Result<(), String> { init_git_repo(&repo_dir)?; fs::write( repo_dir.join("secrets.txt"), - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", ) .map_err(|e| e.to_string())?; @@ -1452,7 +1452,7 @@ fn test_repo_detectors_toml_cannot_disable_hook_scan() -> Result<(), String> { stage_file( &repo_dir, "leak.txt", - "aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n", + "aws_secret_access_key = xJalrXUtnFEMIK7MDENGbPxRfiCYEXAMPLEKEY42\n", )?; let status = Command::new("git") .args(["add", "detectors.toml"]) @@ -1494,7 +1494,7 @@ fn test_staged_scan_reads_blobs_git_renders_as_binary() -> Result<(), String> { stage_file( &repo_dir, "secrets.env", - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", )?; let output = Command::new(env!("CARGO_BIN_EXE_key-watch")) @@ -1530,7 +1530,7 @@ fn test_baseline_suppression_is_reported() -> Result<(), String> { init_git_repo(&repo_dir)?; fs::write( repo_dir.join("secrets.txt"), - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", ) .map_err(|e| e.to_string())?; @@ -1570,7 +1570,7 @@ fn test_git_history_attributes_real_paths_and_honours_excludes() -> Result<(), S commit_file( &repo_dir, "leak.txt", - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", "add", )?; @@ -1620,7 +1620,7 @@ fn test_staged_scan_survives_diff_relative_from_subdirectory() -> Result<(), Str stage_file( &repo_dir, "root.txt", - "clean\naws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "clean\naws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", )?; let output = Command::new(env!("CARGO_BIN_EXE_key-watch")) @@ -1651,7 +1651,7 @@ fn test_prune_baseline_drops_stale_entries() -> Result<(), String> { fs::create_dir_all(&dir).map_err(|e| e.to_string())?; fs::write( dir.join("a.txt"), - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", ) .map_err(|e| e.to_string())?; fs::write( @@ -1732,7 +1732,7 @@ fn test_staged_scan_survives_hostile_git_config() -> Result<(), String> { stage_file( &repo_dir, "config.txt", - "one\ntwo\naws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "one\ntwo\naws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", )?; let output = Command::new(env!("CARGO_BIN_EXE_key-watch")) @@ -1795,7 +1795,7 @@ fn test_staged_scan_paths_narrow_the_diff() -> Result<(), String> { stage_file( &repo_dir, "secret.txt", - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", )?; let run = |path: &str| { @@ -1833,7 +1833,7 @@ fn test_trusted_mode_ignores_env_config_inside_the_scan_target() -> Result<(), S init_git_repo(&repo_dir)?; fs::write( repo_dir.join("leak.txt"), - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", ) .map_err(|e| e.to_string())?; fs::write( @@ -1890,7 +1890,7 @@ fn test_staged_scan_from_subdirectory_still_skips_the_baseline_file() -> Result< commit_file(&repo_dir, "config.txt", "clean line\n", "init")?; fs::write( repo_dir.join("secret.txt"), - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", ) .map_err(|e| e.to_string())?; @@ -1988,7 +1988,7 @@ fn test_prune_baseline_warns_when_paths_narrow_the_scan() -> Result<(), String> fs::create_dir_all(&dir).map_err(|e| e.to_string())?; fs::write( dir.join("a.txt"), - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", ) .map_err(|e| e.to_string())?; fs::write( @@ -2132,7 +2132,7 @@ fn test_explicit_baseline_missing_file_errors_unless_creating() -> Result<(), St fs::create_dir_all(&dir).map_err(|e| e.to_string())?; fs::write( dir.join("leak.txt"), - "aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n", + "aws_access_key_id = AKIAABCDEFGHIJKLMNOP\n", ) .map_err(|e| e.to_string())?;