Skip to content

fix(audit): stop counting a flag's value placeholder as an environment variable - #184

Open
brettdavies wants to merge 3 commits into
fix/help-flags-typer-required-rowsfrom
fix/help-flags-env-hint-placeholders
Open

brettdavies wants to merge 3 commits into
fix/help-flags-typer-required-rowsfrom
fix/help-flags-env-hint-placeholders

Conversation

@brettdavies

@brettdavies brettdavies commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Summary

From the code review of the stack. p1-must-env-var passes when the help names an environment variable beside its flags. The scan takes any uppercase token with an underscore within four lines of a definition, and that included the definition's own value placeholder.

rsync is the case. Its --help names no environment variable, and its one underscored token is a placeholder:

--iconv=CONVERT_SPEC     request charset conversion of filenames

Before #169 the row was a skip, because none of rsync's column-0 options was read. Once they were read, CONVERT_SPEC counted as a variable and the row passed. #169's description flagged that pass as a false credit to fix separately. This is that fix.

The scan now blanks the names and placeholders of a definition line before it looks for tokens. Two things in that span still count:

  • A variable written with its sigil. --token=$GITHUB_TOKEN API token and --token ${GITHUB_TOKEN} keep their hint: a $NAME is a variable wherever it sits.
  • Nothing else. A placeholder in a column of its own (--config-file -c CONFIG_FILE Path to the config) is blanked with the names.

A variable named in the description after them still counts: --token GITHUB_TOKEN Token; also read from GITHUB_TOKEN keeps its hint, from the description.

A definition line is scanned as a terminal shows it, so the header is blanked on a TAB-indented line and on a box-table row too: \t--iconv=CONVERT_SPEC and typer's │ --config-file -c CONFIG_FILE Path to the config │ yield no hint.

definition_lines returns each line as shown, with its header length, to make that possible.

Changelog

Fixed

  • Fix p1-must-env-var passing when the only uppercase name near a flag is that flag's own value placeholder, as in --iconv=CONVERT_SPEC. A placeholder is not an environment variable; a $NAME beside the flag and a variable named in the flag's description still count.

Type of Change

  • fix: Bug fix (non-breaking change which fixes an issue)

Related Issues/Stories

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing completed
  • All tests passing

Test Summary:

  • Unit tests: seven in env_hints_bash.rs. rsync's --iconv=CONVERT_SPEC line, a bare -o OUTPUT_FILE placeholder, a placeholder in a column of its own, the same placeholder on a TAB-indented line and in a box-table row, and the whole rsync 3.5.1 capture yield no hint. A variable in a description still yields one, including when the placeholder carries the same name and when the line is TAB-indented or a box-table row; so does a $NAME or ${NAME} beside the names.
  • 1333 passing across the suite. Self-audit: cargo test --test dogfood passes.
  • Snapshots: none change. They record definitions, not hints.

Negatives observed failing. The placeholder test against the base (#183):

---- runner::help_probe::env_hints_bash::tests::a_flag_s_own_placeholder_is_not_an_environment_variable stdout ----
assertion `left == right` failed: Options
--iconv=CONVERT_SPEC     request charset conversion of filenames
--checksum-seed=NUM      set block/file checksum seed (advanced)
  left: ["CONVERT_SPEC"]
 right: []
test result: FAILED. 10 passed; 1 failed; 0 ignored; 0 measured; 1048 filtered out; finished in 0.00s

The sigil test against the branch's first commit, which blanked the whole span:

---- runner::help_probe::env_hints_bash::tests::a_variable_beside_the_names_still_counts stdout ----
assertion `left == right` failed
  left: []
 right: ["GITHUB_TOKEN"]

The TAB and box-table tests against the branch's second commit, which blanked the header only where the line's bytes matched the classified text:

---- runner::help_probe::env_hints_bash::tests::a_placeholder_in_a_box_table_row_is_not_a_variable stdout ----
assertion failed: names("│ --config-file  -c  CONFIG_FILE  Path to the config │\n").is_empty()
---- runner::help_probe::env_hints_bash::tests::a_placeholder_on_a_tab_indented_line_is_not_a_variable stdout ----
assertion failed: names("Options:\n\t--iconv=CONVERT_SPEC     request charset conversion\n").is_empty()
test result: FAILED. 14 passed; 2 failed; 0 ignored; 0 measured; 1049 filtered out; finished in 0.00s

The other two new tests pin behavior every commit has, so each was run against a variant that breaks it. Ending the blanked span at the first gap after the names reads the column placeholder as a variable:

---- runner::help_probe::env_hints_bash::tests::a_placeholder_in_a_column_of_its_own_is_not_a_variable stdout ----
assertion failed: names("Options:\n  --config-file  -c  CONFIG_FILE  Path to the config\n").is_empty()

Applying the header length to the raw bytes of a TAB-indented line, not to the line as shown, cuts into the description:

---- runner::help_probe::env_hints_bash::tests::a_tab_indented_line_keeps_the_variable_its_description_names stdout ----
assertion `left == right` failed
  left: ["PI_TOKEN"]
 right: ["API_TOKEN"]

Expected moves. Written before the corpus run, from the base and head builds replayed over the full registry capture set. One row:

tool id audit_id before after why
rsync p1-must-env-var p1-env-hints pass warn its only hint was the placeholder in --iconv=CONVERT_SPEC; the warn reads 154 flag(s) found in --help but no [env: NAME] bindings advertised

Derived, computed from the base scorecard: rsync badge.score_pct 73 to 71, summary.pass 15 to 14, summary.warn 11 to 12. Its band (70-74), badge.eligible (true) and audience hold: p1-env-hints is not one of the four signal rows.

No other tool's env-var row rested on a placeholder: the replay moves no other row. Three other captures lose a placeholder hint and keep their row, because each names real variables too: fzf's --listen=SOCKET_PATH, mise bootstrap's --adopt <GIT_URL|OWNER/REPO> and ripgrep's --colors=COLOR_SPEC. The second and third commits move nothing by themselves: the 71 fixtures, the 910 registry captures and 122 other help texts read the same environment variables with and without them.

Corpus before/after. Run after the table above was written. Base bfebab609d17, head 0c0dd301071d. Image sha256:05db16cf226cd14a93a2682aea41b72d3e6b4d240cadba006f2881d3ffa996b3, network bridge. 98 tools selected, 95 scored under both builds, 2 rerun three times (mods, rsync).

Moved rows (1)

tool id audit_id status confidence evidence note
rsync p1-must-env-var p1-env-hints pass → warn medium base: null
head: 154 flag(s) found in --help but no [env: NAME] bindings advertised

Derived fields moved (3)

tool field base head
rsync badge.score_pct 73 71
rsync summary.pass 15 14
rsync summary.warn 11 12

The moved row and the three derived moves equal the expected moves, and the row returned the same result in all four runs of each build. mods p3-should-about-long-about, the one row on the A/A noise list, is reported as not moved: both builds returned the same result for it in at least one run. No row is unstable, no harness bug is flagged, and no tool failed to score under one build only. cursor, nvidia-smi and xai-grok-build are absent from the image and ran under neither build.

Files Modified

Modified:

  • src/runner/help_probe/env_hints_bash.rs: blank a definition's header before the token scan, keeping any $NAME in it.
  • src/runner/help_probe/flags/mod.rs: definition_lines returns DefinitionLine { index, header }, the line as shown with its header length.

Created:

  • None.

Renamed:

  • None.

Deleted:

  • None.

Breaking Changes

  • No breaking changes

One registry tool loses an env-var pass that rested on a placeholder. The scorecard schema and JSON shape are unchanged.

Deployment Notes

  • No special deployment steps required

Checklist

  • Code follows project conventions and style guidelines
  • Commit messages follow Conventional Commits
  • Self-review of code completed
  • Tests added/updated and passing
  • No new warnings or errors introduced
  • Changes are backward compatible (or breaking changes documented)

@brettdavies
brettdavies added this pull request to stack #166 October 9, 2026 15:40
…t variable

`p1-must-env-var` passes when the help names an environment variable beside its flags. The scan takes any uppercase token with an underscore within four lines of a definition, and that includes the definition's own placeholder: rsync's `--iconv=CONVERT_SPEC` made `CONVERT_SPEC` a variable, and rsync's `--help` names no other. The row passed on a value name.

The scan now blanks the names and placeholders of a definition line before it looks for tokens. A variable named in the description after them still counts, so `--token GITHUB_TOKEN    Token; also read from GITHUB_TOKEN` keeps its hint. A definition line that the terminal cleanup rewrote (escape sequences, TABs, box edges) is scanned whole, as before.

`definition_lines` returns each line's header length beside its index to make that possible.
…riable

Blanking a definition's names and placeholders before the environment-variable scan also hid a variable written with its sigil among them: `--token=$GITHUB_TOKEN   API token` read as naming no variable. The scan keeps every `$NAME` and `${NAME}` in that span and blanks the rest, so `--iconv=CONVERT_SPEC` still names a value and not a variable.

A placeholder in a column of its own (`--config-file  -c  CONFIG_FILE  Path to the config`) stays blanked with the names. Ending the blanked span at the first gap instead would read `CONFIG_FILE` as a variable, which a new test pins.

A third test covers a TAB-indented definition, whose byte offsets the terminal cleanup shifts. The line is scanned whole, so a variable its description names is read intact.

The 910 registry help captures read the same environment variables before and after this change.
…lines too

The environment-variable scan blanked a definition's names and placeholders only when the line's bytes matched the text the classifier read. A line the terminal cleanup rewrites (a TAB, an escape sequence, the edge of a box table) was scanned whole, so `\t--iconv=CONVERT_SPEC` and a typer row `│ --config-file  -c  CONFIG_FILE  Path to the config │` still counted their placeholder as a variable.

A definition line is now scanned as a terminal shows it, with its header blanked. `definition_lines` returns that text with the header length, and the byte-identity check is gone. A variable in the description of such a line still counts.

The environment variables read from the 71 fixtures, the 910 registry captures and 122 other help texts are unchanged.
@brettdavies
brettdavies force-pushed the fix/help-flags-env-hint-placeholders branch from df4f3e5 to 0c0dd30 Compare October 9, 2026 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant