contains_unattestable_construct classifies heredocs as file-target redirects, so the /dev/null exemption misfires on << /dev/null
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 81.1k
- Forks
- 5.1k
- Avg merge
- 4d 21h
- Merged PRs (30d)
- 35
Description
Summary
contains_unattestable_construct does not distinguish a heredoc (<<, <<-) from a file-target redirect (<, >, >>). A heredoc is classified as unattestable only because its delimiter word happens to sit where a filename would, and redirect_has_file_target's /dev/null exemption — which exists for discarded output — then misfires on a heredoc whose delimiter is literally /dev/null.
No observable consequence today: registry::rewrite_command carries its own explicit has_heredoc guard (registry.rs:601,616) and refuses what slips through. This is about the predicate being right for the right reason, since it is also the permission gate's "never auto-allow" test.
Evidence
Calling the predicate directly:
git status <<EOF -> unattestable=true
git status <<'EOF' -> unattestable=true
git status <<-EOF -> unattestable=true
git status << /dev/null -> unattestable=false <- heredoc, not a file
git status <</dev/null -> unattestable=false <- heredoc, not a file
git status > out.txt -> unattestable=true
git status > /dev/null -> unattestable=false <- correct, this is the exemption's purpose
git status < in.txt -> unattestable=true
The first three are true by accident: EOF reads as a file target. The two /dev/null rows are false for a reason that does not apply — nothing is being written to /dev/null; /dev/null is the delimiter marking where the inline body ends.
Why it matters
contains_unattestable_construct has three callers, and the reasoning differs for each:
hooks::permissions::check_command_with_rules— forcesAskso an undecomposable command is never auto-allowed. A heredoc body is inline data rather than executed text, so no bypass is known here; but the gate is currently reaching the right answer for heredocs via a filename coincidence.hooks::decision::decide_with_params— refuses to rewrite. Backstopped byrewrite_command'shas_heredoc.discover::estimate_hook_coverage_with_verdict— mirrors the hook's decision for analytics.
A change to the /dev/null exemption, or to how a << token's operand is tokenised, moves heredoc classification as a side effect, in a predicate whose other job is a security gate.
Suggested shape
Classify the redirect operator before asking about its operand: << and <<- are heredocs (no file operand at all), everything else takes a file target and keeps the /dev/null exemption. Then heredocs are refused because they are heredocs, and the exemption applies only where a file is genuinely being written.
Worth pinning << /dev/null and <<-EOF in the lexer's own tests at the same time; today's coverage exercises the common <<EOF form only.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at contains_unattestable_construct and compare its redirect handling with the has_heredoc guard in registry.rs:601,616. Run the lexer’s own tests, then cover << /dev/null and <<-EOF alongside existing heredoc cases; done means heredocs are classified independently while the /dev/null exemption remains limited to file-target redirects.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli, security, testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100