Reject --flag=value on no-argument boolean flags#540
Draft
julesmcrt wants to merge 2 commits into
Draft
Conversation
pflag's BoolP silently accepts the explicit-value form (e.g. rm --force=false target), where GNU coreutils reject it before touching any operand. df already worked around this privately; promote that helper to builtins.NoArgBool and migrate all 24 builtins to it so the whole shell matches GNU. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The no-argument migration converted Bool/BoolP flags to builtins.NoArgBool
but left the custom pflag.Value bool flags untouched, so they still
silently accepted the explicit-value form GNU getopt rejects:
- grep -H/-h/-l/-L (orderedBoolFlag) accepted --with-filename=true via
strconv.ParseBool
- strings -o (octalFlagVal) ignored -o=x
- xargs -0/--null (trackedBool) accepted --null=true via strconv.ParseBool
- head -q/--quiet/-v/--verbose (boolSeqFlag) used a literal "true"
NoOptDefVal sentinel, which is forgeable via =true, so --quiet=true
was silently accepted (only --quiet=false was rejected)
All four now use the unforgeable builtins.NoArgSentinel (NUL byte) and
return "flag does not allow an argument", which the flagparser rewrites to
GNU's "option '--foo' doesn't allow an argument". head additionally gains
the precise per-flag GNU wording it previously lacked.
pwd/cd already use a NUL sentinel and reject correctly; du's seqBool still
intentionally accepts --flag=false.
Adds reject scenarios for grep/strings/xargs and tightens head's existing
scenario to cover the previously-accepted =true case.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
pflag's
BoolPsilently accepts explicit values on no-argument boolean flags —rm --force=false targetdeletes the file,head --verbose=falseruns — where GNU coreutils reject--flag=valuebefore doing anything.dfalready worked around this with a private helper.This promotes that helper to
builtins.NoArgBooland migrates all 24 builtins to it, so the whole shell rejects--flag=valuewith the GNU wording (option '--force' doesn't allow an argument). Bare flags are unchanged.It also fixes the custom
pflag.Valueboolean flags that the plainBool/BoolPmigration didn't cover and which silently accepted the explicit-value form:grep-H/-h/-l/-L(orderedBoolFlag) — accepted--with-filename=trueviastrconv.ParseBoolstrings-o(octalFlagVal) — ignored-o=xxargs-0/--null(trackedBool) — accepted--null=trueviastrconv.ParseBoolhead-q/--quiet/-v/--verbose(boolSeqFlag) — used a literal"true"NoOptDefValsentinel, which is forgeable via=true, so--quiet=truewas silently accepted (only--quiet=falsewas rejected)All four now use the unforgeable
builtins.NoArgSentinel(NUL byte) and route through the sameflagparserrewrite, so they produce GNU'soption '--foo' doesn't allow an argument.headadditionally gains the precise per-flag wording it previously lacked.Closes #538.
Out of scope (follow-ups):
du'sseqBoolstill intentionally accepts--flag=false. (pwd/cdalready use a NUL sentinel and reject--flag=valuecorrectly, with a genericoption doesn't allow an argumentmessage.)🤖 Generated with Claude Code