use std::path::Path;
use super::bash_tokens::{TokenizeError, tokenize};
use super::{
evaluate_bash_command, evaluate_destructive_delete_command, evaluate_secret_file_copy_command,
unclassifiable_command,
};
use crate::commands::pm_guard_secret_read::evaluate_secret_file_read_command;
fn cwd() -> &'static Path {
Path::new("/tmp/pm-guard-tokenizer-probe")
}
fn refusal(command: &str) -> Option<String> {
unclassifiable_command(command)
.map(str::to_string)
.or_else(|| evaluate_secret_file_read_command(command))
.or_else(|| evaluate_secret_file_copy_command(command, cwd()))
.or_else(|| {
evaluate_destructive_delete_command(command, cwd()).map(|c| c.reason().to_string())
})
}
#[test]
fn guard_7839_sed_expression_wildcard() {
for command in [
"sed -i '' 's/^.*$/replaced/' /tmp/scratch/patch.sh",
"sed -i 's/^.*$/replaced/' /tmp/scratch/patch.sh",
"sed -e 's|^.*foo|bar|' -e 's/.*//' /tmp/scratch/patch.sh",
"sed -n 's/.*version = \"\\(.*\\)\"/\\1/p' Cargo.toml",
] {
assert_eq!(
evaluate_secret_file_read_command(command),
None,
"a sed expression's wildcard names no file: {command}"
);
}
assert!(
evaluate_secret_file_read_command("sed -n '38,46p' .env").is_some(),
"a sed reading a dotenv operand must still refuse"
);
assert!(
evaluate_secret_file_read_command("sed -f script.sed .env").is_some(),
"`-f` loads the program from a file, so every positional stays a path"
);
}
#[test]
fn guard_7833_heredoc_body_word_token() {
for command in [
"cat > /tmp/scratch/probe.py <<'PY'\nprint(f\"skillCount: {n}\")\nPY",
"cat > /tmp/scratch/probe.py <<'PY'\nprint(f\"skillCount: {skills}\")\nprint('agentCount:', a)\nPY",
] {
assert_eq!(
evaluate_secret_file_read_command(command),
None,
"a heredoc body's word token names no file: {command}"
);
}
assert!(
evaluate_secret_file_read_command("cat <<'PY'\nopen('.env').read()\nPY").is_some(),
"a printed heredoc body naming a real dotenv file must still refuse"
);
}
#[test]
fn guard_7833_a_quoted_body_cat_writes_to_a_file_is_data() {
for command in [
"cat > /tmp/s.py <<'EOF'\nprint(r.key)\nEOF",
"cat >> tests/test_sanitized_dataset.py <<'EOF'\nrows.append({\"id\": 1, \"name\": \"a\"})\nEOF",
"cat <<'EOF' > /tmp/s/p.py\nfor r in rows: print(r.key, r['id'])\nEOF",
"cat > /tmp/s/p.py <<\"PY\"\nprint(open('.env').read())\nPY",
"cat <<'EOF' > note.md\n**A pipe confirms nothing at all.**\nEOF",
"cat > run.sh <<'EOF'\nfirst=${line%% *}\nEOF",
] {
assert_eq!(refusal(command), None, "{command}");
}
for command in [
"cat > /tmp/s.py <<EOF\n$(cat .env)\nEOF",
"cat <<'EOF'\nprint(open('.env').read())\nEOF",
"cat > /tmp/s.py <<'EOF'\nopen('.env')\nEOF\npython3 /tmp/s.py",
"cat > /tmp/s.py <<'EOF' && python3 /tmp/s.py\nopen('.env')\nEOF",
"cat > /tmp/s.py <<'EOF' | sh\nopen('.env')\nEOF",
"tee /tmp/s.py <<'EOF'\nopen('.env')\nEOF",
"python3 <<'EOF'\nprint(open('.env').read())\nEOF",
"cat <<'EOF' > .env\nAPI_KEY=1\nEOF",
"cat > $OUT <<'EOF'\nopen('.env')\nEOF",
"cat - .env > /tmp/s.py <<'EOF'\nx\nEOF",
"cat > /dev/stdout <<'EOF'\nprint(open('.env').read())\nEOF",
"cat >/dev/stderr <<'EOF'\nprint(open('.env').read())\nEOF",
"cat <<'EOF' > /dev/fd/1\nprint(open('.env').read())\nEOF",
"cat >> /dev/tty <<'EOF'\nprint(open('.env').read())\nEOF",
"cat > /proc/self/fd/1 <<'EOF'\nprint(open('.env').read())\nEOF",
"cat > //dev/./stdout <<'EOF'\nprint(open('.env').read())\nEOF",
"cat > /tmp/../dev/stdout <<'EOF'\nprint(open('.env').read())\nEOF",
"cat > ../../../../../../../dev/stdout <<'EOF'\nprint(open('.env').read())\nEOF",
] {
assert!(refusal(command).is_some(), "expected DENY: {command}");
}
}
#[test]
fn guard_7479_dotenv_placeholder_suffix() {
for command in [
"cat .env.example",
"cat config/.env.sample",
"cat .env.template",
] {
assert_eq!(
evaluate_secret_file_read_command(command),
None,
"a dotenv placeholder is safe to read: {command}"
);
}
for command in ["cat .env", "cat .env.local", "cat .env.production"] {
assert!(
evaluate_secret_file_read_command(command).is_some(),
"a real dotenv file must still refuse: {command}"
);
}
}
#[test]
fn guard_7744_awk_pattern_match_rule() {
for command in [
"awk '/.*[Oo]pen/ {print}' docs/specs/domain-service-shell.md",
"awk '/^.*$/{n++} END{print n}' docs/specs/domain-service-shell.md",
"awk -F';' '/.*[Cc]losed/{print $2}' docs/specs/domain-service-shell.md",
] {
assert_eq!(
evaluate_secret_file_read_command(command),
None,
"an awk pattern-match rule names no file: {command}"
);
}
assert!(
evaluate_secret_file_read_command("awk '{print}' .env").is_some(),
"an awk naming a dotenv operand must still refuse"
);
}
#[test]
fn guard_7743_ls_with_a_stderr_redirect() {
for command in [
"ls -d .env.local 2>&1",
"ls -d src docs .env.local 2>&1",
"stat .env.local 2>&1",
"ls -d .env.local 2>&- ",
] {
assert_eq!(
refusal(command),
None,
"a descriptor duplication launders nothing: {command}"
);
}
for command in ["cat .env > notes.md", "cat .env &> notes.md"] {
assert!(
evaluate_secret_file_copy_command(command, cwd()).is_some(),
"a real redirect of a secret into an ordinary name must still refuse: {command}"
);
}
}
#[test]
fn guard_7738_python_inline_regex_literal() {
for command in [
r#"python3 -c "import re; m = re.search(r'PASSAGE = \"\"\"(.*?)\"\"\"', s)""#,
r#"python3 -c 'import re; print(re.findall(r".*?", text))'"#,
r#"perl -e 'print $1 if /.*?(\d+)/'"#,
] {
assert_eq!(
evaluate_secret_file_read_command(command),
None,
"a regex literal in program text names no file: {command}"
);
}
assert!(
evaluate_secret_file_read_command(r#"python3 -c "print(open('.env').read())""#).is_some(),
"a secret named literally inside an inline program must still refuse"
);
}
#[test]
fn guard_7728_heredoc_append_inside_a_worktree() {
let command = "cat >> /Users/x/packages/app-session/src/grant.test.ts <<'TESTS'\n\
it('grants once', () => {});\n\
TESTS";
assert_eq!(
unclassifiable_command(command),
None,
"the guard must be able to establish what this runs"
);
assert_eq!(evaluate_secret_file_read_command(command), None);
assert_eq!(evaluate_secret_file_copy_command(command, cwd()), None);
assert_eq!(evaluate_destructive_delete_command(command, cwd()), None);
}
#[test]
fn guard_7863_three_dot_git_diff_range() {
for command in [
"git diff --stat main...HEAD",
"git diff --stat main HEAD",
"git diff origin/main...HEAD -- crates/trusty-mpm",
] {
assert_eq!(
unclassifiable_command(command),
None,
"the guard must be able to establish what this runs: {command}"
);
assert_eq!(
refusal(command),
None,
"a read-only diff must allow: {command}"
);
assert_eq!(evaluate_bash_command(command), None, "{command}");
}
}
#[test]
fn guard_tokenizer_parse_failure_refuses_naming_the_parse_problem() {
assert_eq!(
tokenize("awk '{print} terraform.tfvars"),
Err(TokenizeError::UnbalancedQuoting)
);
let reason = evaluate_secret_file_read_command("awk '{print} terraform.tfvars")
.expect("an unparseable segment naming a secret must refuse");
assert!(
reason.contains("unbalanced"),
"the refusal must name the parse problem, got: {reason}"
);
assert!(
evaluate_destructive_delete_command("rm -rf 'unterminated", cwd()).is_some(),
"an unparseable segment naming a delete verb must refuse"
);
assert!(
unclassifiable_command("sh -c \"git worktree remove 'unterminated\"").is_some(),
"an unlexable wrapper must refuse ahead of every rule"
);
}
#[test]
fn guard_tokenizer_still_refuses_a_real_secret_read_copy_and_delete() {
assert!(
evaluate_secret_file_read_command("cat /Users/x/proj/.env").is_some(),
"reading a dotenv file must still refuse"
);
assert!(
evaluate_secret_file_copy_command("cat .env > notes.md", cwd()).is_some(),
"laundering a dotenv file into an ordinary name must still refuse"
);
assert!(
evaluate_destructive_delete_command("rm -rf /", cwd()).is_some(),
"deleting the filesystem root must still refuse"
);
}
#[test]
fn guard_tokenizer_bounds_the_program_text_relaxations() {
assert!(
evaluate_secret_file_read_command("python3 -c '-e' .env").is_some(),
"only the first inline-program value is program text; the operand is a path"
);
assert!(
evaluate_secret_file_read_command("awk '' .env").is_some(),
"an empty awk program is the program; the operand behind it is a path"
);
assert!(
evaluate_secret_file_read_command(r#"python3 -c "print(open('.[e]nv').read())""#).is_some(),
"a bracket-class dotenv glob is a glob, not a regex quantifier"
);
}
#[test]
fn guard_a_heredoc_operator_inside_a_string_hides_no_deletion() {
for command in [
"echo \"note: use <<'PY' syntax\"\nrm -rf /\nPY",
"echo 'note: use <<\"PY\" syntax'\nrm -rf /\nPY",
"python3 - <<'PY'\n# don't\nPY\necho \"use <<'ZZ' here\"\nrm -rf /\nZZ",
] {
assert!(
evaluate_destructive_delete_command(command, cwd()).is_some(),
"a `<<'WORD'` inside a string opens no body: {command}"
);
}
}
#[test]
fn guard_a_captured_heredoc_body_deletion_still_refuses() {
let rows = [
"eval $(cat <<'PY'\nrm -rf /\nPY\n)",
"eval $(cat <<'PY'\nrm -rf /\nPY)",
"eval $(\ncat <<'PY'\nrm -rf /\nPY\n)",
"eval `cat <<'PY'\nrm -rf /\nPY\n`",
"eval $(cat <<'PY'\n# the agent's step\nrm -rf /\nPY\n)",
"source <(cat <<'PY'\nrm -rf /\nPY\n)",
"source <(cat <<'PY'\nrm -rf /\nPY)",
"source <(cat <<\"PY\"\n# don't\nrm -rf /\nPY\n)",
". <(cat <<'PY'\nrm -rf /\nPY\n)",
". <(cat <<'PY'\nrm -rf /\nPY)",
". /dev/stdin < <(cat <<'PY'\nrm -rf /\nPY\n)",
"x=$(cat <<'PY'\nrm -rf /\nPY\n)\n$x",
"`cat <<'PY'\nrm -rf /\nPY\n`",
"cat <<'PY' > >(\"ba\"sh)\nrm -rf /\nPY",
];
let allowed: Vec<&str> = rows
.iter()
.copied()
.filter(|command| evaluate_destructive_delete_command(command, cwd()).is_none())
.collect();
assert!(
allowed.is_empty(),
"a captured here-document body is shell text again, so its deletion must refuse; \
allowed: {allowed:?}"
);
}