Skip to content

fix(skills): lint-skill.sh no longer masks cross-fence leaks fed via read here-strings - #2574

Merged
carlos-alm merged 5 commits into
mainfrom
fix/issue-2491
Aug 18, 2026
Merged

fix(skills): lint-skill.sh no longer masks cross-fence leaks fed via read here-strings#2574
carlos-alm merged 5 commits into
mainfrom
fix/issue-2491

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

  • lint-skill.sh's Pattern-1 cross-fence variable check exempted any line containing the substring read from being flagged, without checking whether the variable in question was actually the destination read bound — so a stale variable fed as the input to a read here-string (read -r BAR <<< "$FOO") slipped through undetected.
  • A second, independent blanket exemption (< substring match, meant to detect genuine file redirection) made this worse: <<<'s own text contains a trailing < , so even narrowing the read exemption alone wouldn't have caught the exact repro.
  • Adds extract_read_dest_vars/is_read_dest_of_line (checks whether $var is genuinely the destination a read line binds) and has_file_redirect_in (a regex distinguishing a real single-< file redirect from <</<<<), replacing both blanket substring checks.

Test plan

  • New regression test tests/unit/lint-skill-here-string-2491.test.ts: exact issue repro + here-string-no-read case fail before the fix and pass after (revert-verified against the pre-fix script).
  • Existing read-binding exemption (follow-up: lint-skill.sh flags a cross-fence $COUNT violation in fixer/SKILL.md (possible false positive) #2344) and genuine file-redirection exemption still pass — no regressions.
  • Ran the updated linter against all 21 real SKILL.md files in the repo — output identical to before the fix (zero new false positives).
  • npm test (full suite, 92 files / 1929 tests) and npm run lint both pass.

Closes #2491

…read here-strings

Two blanket substring exemptions in the Pattern-1 cross-fence check ('read '
and '< ') let a stale variable slip through undetected when it appeared as
the INPUT to a `read` here-string (e.g. `read -r BAR <<< "$FOO"`) rather than
as its destination — the 'read ' exemption didn't check which variable was
actually being bound, and the '< ' exemption's blanket match also matched
`<<<`'s own trailing '< ' substring.

Replaces both with is_read_dest_of_line (checks the line's actual read
destination via a new shared extract_read_dest_vars helper) and
has_file_redirect_in (a regex that excludes << and <<< from genuine
single-< file redirection).

Closes #2491
@github-actions

Copy link
Copy Markdown
Contributor

Heads up: this PR references #2344 without a closing keyword (Closes #N / Fixes #N). If this PR fully resolves #2344, update the description so the issue auto-closes on merge — otherwise disregard this comment.

@greptile-apps

greptile-apps Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR refines Pattern-1 parsing of read commands so stale variables passed through here-strings are not mistaken for freshly bound destinations.

  • Separates read destinations from all redirection targets, including multiple and quoted redirects.
  • Handles same-name destination/input references and distinguishes file redirects from here-strings.
  • Adds regression coverage for the six previously reported parsing and exemption cases.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
.claude/skills/create-skill/scripts/lint-skill.sh The read-command parser and Pattern-1 exemptions now address the previously reported destination, comment, quoting, redirect-order, and self-reference failures without an established blocking residual defect.
tests/unit/lint-skill-here-string-2491.test.ts Adds focused regression cases covering the original here-string leak and all six prior review findings.

Reviews (5): Last reviewed commit: "fix(skills): lint-skill.sh comment strip..." | Re-trigger Greptile

Comment thread .claude/skills/create-skill/scripts/lint-skill.sh Outdated
… read

Greptile round 1 on PR #2574: is_read_dest_of_line only checked whether
$var was *a* destination somewhere on the line, so `read -r FOO <<< "$FOO"`
(destination and stale here-string input sharing the same name) still
slipped through — both via the deeper per-reference exemption and via the
block-wide REASSIGNED short-circuit, which correctly protects later lines
referencing the freshly self-bound value but doesn't distinguish those from
this exact line's own pre-rebind reference.

Adds is_read_input_of_line/is_self_referential_read and forces the deeper
check even when REASSIGNED is set, for this one self-referential line only.
@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread .claude/skills/create-skill/scripts/lint-skill.sh
…its redirection

Greptile round 2 on PR #2574: extract_read_dest_vars truncated read_args at
the FIRST '<', discarding any destination that came after the redirection.
`read -r <<< "hello" FOO` is valid bash (FOO really is bound), so a later,
legitimate reference to the freshly-read value was falsely reported as a
stale cross-fence leak, since REASSIGNED never got set for that block.

Replaces the truncation with a shared READ_REDIRECT_RE that removes the
redirection clause from anywhere in the args (not just a trailing cut),
used by both extract_read_dest_vars (destination extraction) and
extract_read_input (redirect-target capture, previously "everything after
the first '<'", now precisely just the clause's own target).
@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread .claude/skills/create-skill/scripts/lint-skill.sh Outdated
Comment thread .claude/skills/create-skill/scripts/lint-skill.sh Outdated
Comment thread .claude/skills/create-skill/scripts/lint-skill.sh Outdated
… redirects on read

Greptile round 3 on PR #2574 found three further gaps in the generalized
redirect handling, each confirmed against real bash before fixing:

- A trailing unquoted `# comment` after a redirect's bare-word target was
  left dangling for the destination grep, so an uppercase comment word
  (`read -r NUM < f # MAX_LIMIT`) got mistaken for a real destination.
- A multiword single-quoted redirect target (`<<< 'FOO BAZ'`) only had its
  first word consumed by the bare-word match, leaving the rest to leak into
  destination extraction.
- Only the first of multiple redirects on one line participated in
  self-reference detection, so a genuine file redirect could paper over an
  unrelated `<<<` leak later on the same line
  (`read -r FOO < "$CONFIG" <<< "$FOO"`).

Adds strip_trailing_comment (quote-aware, so a `#` inside a still-open
double-quoted string isn't mistaken for a comment) and rewrites
split_read_redirects as a proper tokenizer that consumes every redirect
clause on the line (quoted targets in full, regardless of embedded
whitespace) instead of a single regex substitution, tracking here-string
targets separately from file targets so a genuine file redirect no longer
blanket-exempts a different, unrelated here-string leak on the same line.
@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread .claude/skills/create-skill/scripts/lint-skill.sh
…es too

Greptile round 4 on PR #2574: strip_trailing_comment only alternated on
double-quoted spans, so a literal '#' inside a single-quoted redirect
target (read -r <<< '#tag value' FOO EXTRA is valid bash - EXTRA really is
a destination) was mistaken for a genuine comment marker, truncating away
any destination that followed it and causing a false cross-fence error on
a later, legitimate reference.

Generalizes the walk to alternate on whichever quote character opens
first, matching how split_read_redirects already handles both quote kinds
for target extraction.
@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm
carlos-alm merged commit a396b07 into main Aug 18, 2026
37 of 41 checks passed
@carlos-alm
carlos-alm deleted the fix/issue-2491 branch August 18, 2026 18:39
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-skill.sh: blanket 'read ' substring exemption masks genuine cross-fence leaks on read lines

1 participant