Skip to content

chore(linter): traverse "runArgs" as the "docker run" argv it is - #92

Merged
nozaq merged 3 commits into
mainfrom
claude/runargs-flag-traversal-swbgu7
Aug 1, 2026
Merged

chore(linter): traverse "runArgs" as the "docker run" argv it is#92
nozaq merged 3 commits into
mainfrom
claude/runargs-flag-traversal-swbgu7

Conversation

@nozaq

@nozaq nozaq commented Aug 1, 2026

Copy link
Copy Markdown
Member

Seven rules read a devcontainer.json's "runArgs", and each opened with the
same preamble: cast the node to an array, check the file type, pull out one
flag's values and loop. Any one of them could get that wrong on its own.

The engine now walks a devcontainer.json's "runArgs" as the argv it becomes.
A rule names the flag it wants in its path ("/runArgs/--cap-add") and is
handed each occurrence, with the value in Node.Arg and the node pointing at
the element the value is written in — the value's element, not the flag's, so
a finding lands where the rule read. Flags are identified by their normalized
long name, so "/runArgs/--volume" also matches "-v", and the traversal runs
only for devcontainer.json, which is the only file type the property belongs
to, so rules no longer check it themselves.

The ordinary walk stops descending at the document's own "runArgs" while
still visiting the array itself. Descending both ways would give each element
an index path and a flag path, and "/runArgs/*" would hand the same element
over twice.

The two rules that report a flag's absence cannot be driven by
per-occurrence dispatch, since a flag that is not there is never matched;
they keep reading the document root, now through a single helper.

Reporting is unchanged except that a flag written more than once is now
reported once per occurrence rather than only at the first, matching what
no-docker-socket-mount already did; suppressing one line no longer hides the
others.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01Sq1U3yDE8B3YYw8M5rqvi9

claude added 3 commits August 1, 2026 10:01
Seven rules read a devcontainer.json's "runArgs", and each opened with the
same preamble: cast the node to an array, check the file type, pull out one
flag's values and loop. Any one of them could get that wrong on its own.

The engine now walks a devcontainer.json's "runArgs" as the argv it becomes.
A rule names the flag it wants in its path ("/runArgs/--cap-add") and is
handed each occurrence, with the value in Node.Arg and the node pointing at
the element the value is written in — the value's element, not the flag's, so
a finding lands where the rule read. Flags are identified by their normalized
long name, so "/runArgs/--volume" also matches "-v", and the traversal runs
only for devcontainer.json, which is the only file type the property belongs
to, so rules no longer check it themselves.

The ordinary walk stops descending at the document's own "runArgs" while
still visiting the array itself. Descending both ways would give each element
an index path and a flag path, and "/runArgs/*" would hand the same element
over twice.

The two rules that report a flag's *absence* cannot be driven by
per-occurrence dispatch, since a flag that is not there is never matched;
they keep reading the document root, now through a single helper.

Reporting is unchanged except that a flag written more than once is now
reported once per occurrence rather than only at the first, matching what
no-docker-socket-mount already did; suppressing one line no longer hides the
others.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sq1U3yDE8B3YYw8M5rqvi9
cmd/dockerflagsgen is a module of its own, and it consumes dockerargs for the
flag table and for the differential test against pflag. Putting the array to
argv step in dockerargs pulled hujson into that module: its go.sum has no
entry for it, so "make dockerflags-test" failed to build, and satisfying it
would have made a generator that never touches a syntax tree depend on a JSON
parser.

The step moves to the traversal that needs it, exported as linter.RunArgs for
the two rules that report a flag's absence and so cannot be driven by the
per-occurrence dispatch. dockerargs goes back to reading strings only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sq1U3yDE8B3YYw8M5rqvi9
…visited as

"A rule is visited at most once per value" was written for the ordinary
traversal, where a value is reached by one path however many patterns match
it. It does not hold for a "runArgs" element: a run of shorthands names
several flags, and the element is visited once for each, so a rule
subscribing to "/runArgs/*" or to more than one of those flags is called
twice on it.

That is the behavior the addressing wants — collapsing the occurrences would
drop a flag silently, which is what naming flags by their long form exists to
prevent — so the three statements of the contract are what change, along with
a test comment that repeated the wrong one. The wildcard case is pinned by a
test: deduplicating per element now fails it rather than quietly narrowing
what rules see.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sq1U3yDE8B3YYw8M5rqvi9
@nozaq
nozaq merged commit e48f3ca into main Aug 1, 2026
12 checks passed
@nozaq
nozaq deleted the claude/runargs-flag-traversal-swbgu7 branch August 1, 2026 11:06
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.

2 participants