fix(threat-scanner): close reviewer-noted ssh_access_write bypass shapes

Extend the write-verb alternation with mv/install/printf/dd/scp/rsync/ln,
allow short option clusters between verb and path, and add a bare-leading-
redirect branch (a > ~/.ssh/... line carries no verb word at all). Prose
that names a write primitive before an SSH path still fails closed — a
false positive costs a review, a false negative is a backdoor.

(cherry picked from commit 4865b96ca3c8661c0ea6f5c479c198ccf68f86c4)
This commit is contained in:
Martin Mogis
2026-08-25 11:52:07 +00:00
committed by Teknium
parent 34304b722d
commit fe68349cd6
2 changed files with 57 additions and 1 deletions
+55
View File
@@ -339,3 +339,58 @@ class TestSshAccessWritePattern:
# verb gating on ssh_access_write.
findings = scan_for_threats("documented ~/.ssh/authorized_keys layout", scope="strict")
assert "ssh_backdoor" in findings
class TestSshAccessWriteExtendedVerbs:
"""Reviewer-noted bypass shapes: mv/install/printf/dd/scp/rsync/ln and a
bare leading redirect carry no `echo/cat`-style verb the original
alternation recognised, yet are pure write primitives."""
def test_extended_write_verbs_flag(self):
for text in (
"mv /tmp/evil $HOME/.ssh/id_rsa",
"install -m 600 /tmp/key ~/.ssh/id_ed25519",
"printf 'ssh-ed25519 AAAA' >> ~/.ssh/authorized_keys",
"dd if=/tmp/key of=$HOME/.ssh/id_rsa",
"scp evil.sh user@host:~/.ssh/",
"rsync -a /tmp/keys/ ~/.ssh/",
"ln -sf /tmp/evil $HOME/.ssh/authorized_keys",
"mv -f /tmp/stolen ~/.ssh/config",
):
findings = scan_for_threats(text, scope="strict")
assert "ssh_access_write" in findings, text
def test_bare_leading_redirect_flags(self):
findings = scan_for_threats(
"some-command\n> ~/.ssh/authorized_keys_backup", scope="strict"
)
assert "ssh_access_write" in findings
def test_flagged_verbs_with_flags_do_not_leak(self):
# rsync -a / mv -f style: options between verb and path must not
# break the match, but read-only prose stays clean.
findings = scan_for_threats(
"rsync -av --delete /tmp/keys/ ~/.ssh/", scope="strict"
)
assert "ssh_access_write" in findings
def test_documentation_stays_clean(self):
for text in (
"Your public key belongs in ~/.ssh on the server, not in the repo",
"Rotate credentials quarterly; ~/.ssh holds the private material",
"Keys under ~/.ssh must have 600 permissions",
"The recovery doc explains where ~/.ssh sits in the backup set",
):
findings = scan_for_threats(text, scope="strict")
ssh_findings = [f for f in findings if f.startswith("ssh_access")]
assert not ssh_findings, (text, ssh_findings)
def test_verb_word_prose_fails_closed(self):
# Documented trade-off: prose that names a write primitive AND the
# SSH path still flags. A false positive costs a human glance; a
# false negative is a backdoor. Fail-closed is the contract.
findings = scan_for_threats(
"Use `scp` to copy your public key to ~/.ssh on the server",
scope="strict",
)
assert "ssh_access_write" in findings
+2 -1
View File
@@ -80,7 +80,8 @@ _PATTERNS: List[Tuple[str, str, str]] = [
# ── Persistence / SSH backdoor (strict scope — memory + skills) ──
(r'authorized_keys', "ssh_backdoor", "strict"),
(r'(?:echo|cat|cp|tee|append|add|write|>>?)\s+[^\n]{0,512}(?:\$HOME/\.ssh|~/\.ssh)', "ssh_access_write", "strict"),
(r'(?:echo|cat|cp|mv|dd|tee|install|printf|rsync|scp|ln|append|add|write|>>?)\s*(?:-[a-zA-Z]+\s+)*[^\n]{0,512}(?:\$HOME/\.ssh|~/\.ssh)', "ssh_access_write", "strict"),
(r'(?m)^[^\n]{0,256}(?<![>a-z])>>?\s*(?:\$HOME/\.ssh|~/\.ssh)', "ssh_access_write", "strict"),
(r'\$HOME/\.hermes/\.env|\~/\.hermes/\.env', "hermes_env", "strict"),
(rf'{_MODIFY}(?:AGENTS\.md|CLAUDE\.md|\.cursorrules|\.clinerules)', "agent_config_mod", "strict"),
(rf'{_MODIFY}\.hermes/(config\.yaml|SOUL\.md)', "hermes_config_mod", "strict"),