From 43fca6b3da1bf725d315d7c3da5bb39754d76e51 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Tue, 30 Jun 2026 12:11:17 +1000 Subject: [PATCH] Resist 'pw' as well --- README.md | 2 +- enroll/ignore.py | 2 +- tests/test_secret_detection.py | 43 ++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1fc1e94..aa4d431 100644 --- a/README.md +++ b/README.md @@ -266,7 +266,7 @@ enroll validate ./harvest --fail-on-warnings By default, `enroll` does **not** assume how you handle secrets in Ansible. It will attempt to avoid harvesting likely sensitive data (private keys, passwords, tokens, etc.). This can mean it skips some config files you may ultimately want to manage. -Safe-mode content scanning is intentionally conservative. It treats common assignment-style credential keys as sensitive, including names such as `password`, `client_secret`, `secret_key`, `auth_token`, `api_key`, `aws_access_key_id`, `aws_secret_access_key`, `azure_client_secret`, `GOOGLE_APPLICATION_CREDENTIALS`, and service-account key names. +Safe-mode content scanning is intentionally conservative. It treats common assignment-style credential keys as sensitive, including names such as `password` (and abbreviations like `passwd`, `pwd`, and `pw`, e.g. `db_pw`), `client_secret`, `secret_key`, `auth_token`, `api_key`, `aws_access_key_id`, `aws_secret_access_key`, `azure_client_secret`, `GOOGLE_APPLICATION_CREDENTIALS`, and service-account key names. **IMPORTANT**: Enroll ignores comments in files! If you have commented out *real secrets*, there's still a risk that Enroll could capture that data even without `--dangerous`. If you are in doubt, play it safe: use `--sops` and/or encrypt the output at rest in a way that makes sense to you. diff --git a/enroll/ignore.py b/enroll/ignore.py index 2bc75ea..d7c4198 100644 --- a/enroll/ignore.py +++ b/enroll/ignore.py @@ -108,7 +108,7 @@ HIGH_CONFIDENCE_SECRET_PATTERNS = [ ( (?:[A-Za-z0-9]+[_.-])* ( - password|passwd|passphrase|pwd| + password|passwd|passphrase|pwd|pw| token|auth[_.-]?token|access[_.-]?token|refresh[_.-]?token| secret|client[_.-]?secret|secret[_.-]?key| api[_.-]?key|access[_.-]?key|private[_.-]?key| diff --git a/tests/test_secret_detection.py b/tests/test_secret_detection.py index 4b37896..607d059 100644 --- a/tests/test_secret_detection.py +++ b/tests/test_secret_detection.py @@ -93,6 +93,49 @@ def test_password_keyword_no_false_positives(): assert pol._content_deny_reason("/etc/app.conf", data) is None, data +def test_pw_abbreviation_credential_key_denied(): + """The ``pw`` abbreviation is an extremely common password key form + (``db_pw``, ``DB_PW``, ``root_pw``). It must be treated like ``pwd``/``pass`` + so a real assignment-style credential using ``pw`` is not silently harvested + in default safe mode.""" + pol = IgnorePolicy(dangerous=False) + for data in [ + b"pw = hunter2\n", + b"db_pw = SuperSecret123\n", + b"DB_PW=hunter2\n", + b"root_pw: aaaa\n", + b"master_pw = topsecret\n", + b"ftp_pw=x\n", + b"mysql_root_pw: x\n", + b"pw_hash = abc\n", + b'"pw": "x"\n', + ]: + assert ( + pol._content_deny_reason("/etc/app.conf", data) == "sensitive_content" + ), data + + +def test_pw_abbreviation_no_false_positives(): + """Adding the short ``pw`` token must not flag ordinary keys that merely + contain the letters ``pw`` without it being a delimited credential token. + The surrounding token-boundary structure (a keyword must stand alone between + ``[_.-]`` delimiters) is what keeps these benign.""" + pol = IgnorePolicy(dangerous=False) + for data in [ + b"pwned = 1\n", + b"software = x\n", + b"hardware = y\n", + b"firmware: z\n", + b"spwd = z\n", + b"powerline = on\n", + b"wallpaper = img\n", + b"pwm_freq = 1000\n", + b"cpwd_check=1\n", + b"newpwx = 1\n", + ]: + assert pol._content_deny_reason("/etc/app.conf", data) is None, data + + def test_block_comment_open_cannot_mask_private_key(): """Security regression: a line beginning with ``/*`` must not put the content scanner into a runaway block-comment state that hides later real secrets.