Resist 'pw' as well

This commit is contained in:
Miguel Jacq 2026-06-30 12:11:17 +10:00
parent 3b9cfea404
commit 43fca6b3da
Signed by: mig5
GPG key ID: 03906B4110AAD3B8
3 changed files with 45 additions and 2 deletions

View file

@ -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. 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. **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.

View file

@ -108,7 +108,7 @@ HIGH_CONFIDENCE_SECRET_PATTERNS = [
( (
(?:[A-Za-z0-9]+[_.-])* (?:[A-Za-z0-9]+[_.-])*
( (
password|passwd|passphrase|pwd| password|passwd|passphrase|pwd|pw|
token|auth[_.-]?token|access[_.-]?token|refresh[_.-]?token| token|auth[_.-]?token|access[_.-]?token|refresh[_.-]?token|
secret|client[_.-]?secret|secret[_.-]?key| secret|client[_.-]?secret|secret[_.-]?key|
api[_.-]?key|access[_.-]?key|private[_.-]?key| api[_.-]?key|access[_.-]?key|private[_.-]?key|

View file

@ -93,6 +93,49 @@ def test_password_keyword_no_false_positives():
assert pol._content_deny_reason("/etc/app.conf", data) is None, data 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(): def test_block_comment_open_cannot_mask_private_key():
"""Security regression: a line beginning with ``/*`` must not put the content """Security regression: a line beginning with ``/*`` must not put the content
scanner into a runaway block-comment state that hides later real secrets. scanner into a runaway block-comment state that hides later real secrets.