Repository navigation
Conversation
`bashrs lint` 7.4.2 reported BRS0009 "Multiple stdout redirections
specified" on
printf '%s\n' "a <N>m b N>0" >&2
which has one redirection. The rule counted `>` characters on the raw
line: three regexes (sc2096.rs:25-37 on the base), a `split('>')`
counter that skipped a part only when the part before it ended in `2`
or `&` (:126-137), and a command splitter that did not know quotes. It
was not in `QUOTE_SENSITIVE_RULES`, so it read the raw source, where
the `>` of a string literal looks exactly like an operator. Its stderr
and append checks were regexes over the same raw text and had the same
defect. To keep heredoc bodies out it skipped every line containing
`<<`, which also hid the redirections on the heredoc's own line.
The fix:
- SC2096 joins `QUOTE_SENSITIVE_RULES`. The dispatcher masks string
literals, heredoc bodies and comments before the rule sees a line:
one quoting scanner for the whole linter, not a second one here.
- The rule reads operators from tokens: `>`, `>|`, `>>`, `>&`, `<&`,
`&>`, `&>>`, `<`, `<<`, `<<-`, `<<<` and `<>`. The descriptor is the
all-digit word written directly before the operator, so in
`echo file2>out` the word `file2` is an argument and the `>` is a
stdout redirection.
- Commands split at `|`, `;`, `&` and `&&`. `$( )`, `( )`, backticks
and `>( )`/`<( )` are commands of their own. `$(( ))`, `(( ))`,
`[[ ]]`, `${...}`, `\>` and comments are stepped over, because a `>`
there is not a redirection in code either.
- A stdout finding is two truncating redirections of fd 1 in one
command. A stderr finding is two `2>`/`2>>`. An append finding is two
appends to the SAME descriptor, so `cmd >> a 2>> b` (two streams) is
no longer reported.
- A descriptor copy (`2>&1`, `>&2`) ends a run, because it can read
the earlier target: `cmd > a 2>&1 > b` still sends stderr to `a`.
This also means `cmd 2> a 2>&1 2> b` is no longer reported.
Double redirections the character scan missed now fire:
`cat <<EOF > a > b`, `cmd >| a > b`, `echo file2>out > out2` and
`cmd > a >> b > c` (any `>>` in a command used to switch the stdout
check off).
Tests, in sc2096_tests_prop_sc2096.rs, each run through `lint_shell`
(counting BRS0009) and through the dispatcher's masked input:
- One fixture per quoting form, each 0: single-quoted, double-quoted,
`$'...'`, a heredoc body, and the issue's exact line. A negative
control shows the same forms still fire when the rule reads the raw
source, so the fixtures can fail.
- `>` in code that is not an operator, each 0: `$(( ))`, `(( ))`,
`[[ ]]`, `\>`, `${x//>/y}`, `exec 3>a 4>b`, a process substitution,
a background `&`, `>> a 2>> b` and `<>`.
- True positives, each exactly 1: `echo a >x >y`, `x=$(cmd >a >b)`,
`cmd 2>e1 2>e2`, `echo a >>f1 >>f2` and `cmd > a 2> e > b`.
quoting_literal_payload_guard.rs gains the BRS0009 payload: the issue's
line must not fire, and `echo a >x >y` must.
Shown to discriminate. With sc2096.rs and quoting.rs put back to the
base and the new tests kept, four of the new lib tests fail (the issue
line gives `left: 1, right: 0`) and the payload guard fails with
"BRS0009 (bashrs#431): fired on literal text". With the fix restored,
195 sc2096/quoting lib tests and the 4 guard tests pass. `bashrs lint`
on 21 scripts: 15 of the 17 false-positive scripts fire on the base and
none with the fix; the 4 true-positive scripts fire once in both.
SC2094 has the same `split('>')` shape over the raw source
(sc2094.rs:83) and is unchanged here.
Refs: PMAT-431 #431
Pmat-Ticket: PMAT-431
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Non-author review plus quorum sign-off, bound to head
Not armed by this reviewer. 🤖 Generated with Claude Code |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #431
The defect
bashrs lint7.4.2 reports BRS0009 "Multiple stdout redirections specified" onThat line has one redirection. SC2096 (BRS0009) counted
>characters on the raw line:rash/src/linter/rules/sc2096.rs:25-37on the base);split('>')counter that skipped a part only when the part before it ended in2or&(:126-137);It was not in
QUOTE_SENSITIVE_RULES, so it read the raw source. There, the>of a string literal looks exactly like an operator. The stderr and append checks were regexes over the same text and had the same defect. To keep heredoc bodies out, the rule skipped every line containing<<, which also hid the redirections on the heredoc's own line.The fix
QUOTE_SENSITIVE_RULES. The dispatcher masks string literals, heredoc bodies and comments before the rule reads a line. That keeps one quoting scanner for the whole linter, not a second one in this rule.>,>|,>>,>&,<&,&>,&>>,<,<<,<<-,<<<and<>.echo file2>out,file2is an argument.|,;,&and&&.$( ),( ), backticks and process substitutions are commands of their own.$(( )),(( )),[[ ]],${...},\>and comments are stepped over.2>or2>>.What the lint gate now accepts and reports differently
This changes what the lint gate accepts, so it needs a sign-off review round from someone other than the author before merge.
No longer reported (false positives removed):
>inside single quotes, double quotes,$'...', a heredoc body or a comment.>in$(( )),(( )),[[ ]],${x//>/y}or\>.exec 3>a 4>b, and redirections across a process substitution or a background&.cmd >> a 2>> b, which appends to two different streams.No longer reported (a deliberate narrowing):
cmd 2> a 2>&1 2> b. A descriptor copy (2>&1,>&2) now ends a run, because a copy can read the earlier target:cmd > a 2>&1 > breally does send stderr toa.Newly reported (double redirections the character scan missed):
cat <<EOF > a > b: the heredoc line used to be skipped.cmd >| a > b.echo file2>out > out2.cmd > a >> b > c: any>>in the command used to switch the stdout check off.Unchanged: SC2094 has the same
split('>')shape over the raw source (sc2094.rs:83). This PR does not change it.Evidence
Fixtures.
sc2096_tests_prop_sc2096.rshas one fixture per quoting form: single-quoted, double-quoted,$'...', a heredoc body, and the issue's exact line. Each must give 0 BRS0009, both throughlint_shelland through the dispatcher's masked input.Negative control. The same forms still fire when the rule is given the raw source, so the fixtures are able to fail.
True positives. Each of these still gives exactly one finding:
echo a >x >y,x=$(cmd >a >b),cmd 2>e1 2>e2,echo a >>f1 >>f2andcmd > a 2> e > b.Guard.
quoting_literal_payload_guard.rsgains the BRS0009 payload.Discrimination. With the rule and
quoting.rsreverted to the base and the new tests kept:left: 1, right: 0;With the fix restored, 195 sc2096/quoting lib tests and the 4 guard tests pass.
CLI check.
bashrs linton 21 small scripts:Formatting and clippy.
cargo fmt --all -- --checkandcargo clippy -p bashrs --all-targets -- -D warningspass.Full package run, and what it does not cover
cargo test -p bashrs --testswas run on this tree.shellcheck_validation_tests, which spawnshellcheck. It was not installed on the machine that ran them (Failed to run shellcheck - is it installed?).test_property_safe_fixes_preserve_syntax, which also spawnsshellcheck.cli_ssc_testswere left out:test_PMAT142_corpus_*,test_PMAT143_corpus_*andtest_PMAT147_corpus_*. Each one builds a full corpus report and takes more than ten minutes in a debug build.Review
Three review models passed this commit (3/3); none of them is the author's model. One of them noted an overlap that this PR does not change:
cmd 2>> e1 2>> e2gets both the stderr and the append message. Both of the old regexes matched that line too.🤖 Generated with Claude Code