sync upstream - #21
Conversation
To avoid cropping up new findings for commonly ignored issues for now.
In pflag, we recently made a breaking change in naming to be more inclusive. This change, while doing nothing to address the same uninclusive naming in cobra itself, adjusts to those changes to ensure automatic bumps (if you do them) can continue uninterrupted.
Signed-off-by: John McBride <jpmmcbride@gmail.com>
We need to keep the old "+build" syntax because Cobra still allows to use a Go version as old as 1.15. Signed-off-by: Marc Khouzam <marc.khouzam@gmail.com>
Resolves spf13#2298 - Added comprehensive "Repeated Flags" section to user guide - Documented CountVarP for SSH-style verbose flags (-v, -vv, -vvv) - Documented StringArrayVarP for multiple value collection - Provided complete code examples and usage patterns 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Vergis_Ron <Vergis_Ron@bah.com> Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: John McBride <jpmmcbride@gmail.com>
The original gopkg.in/yaml.v3 was marked unmaintained (April 2025). go.yaml.in/yaml/v3 is now maintained by the official YAML organization and is a drop-in replacement with an identical API. Signed-off-by: Davanum Srinivas <davanum@gmail.com>
…cesses. (spf13#2333) Today, the loop is printing the completions one value per line and then reading one line to a variable. We can just skip the whole IO rigamarole by looping over the completions directly.
* fix: quote args in fish shell completion The arguments used for dynamic completions in fish were missing quotes. This resulted in an error when one of the arguments had a `*` in it whose expansion matched no file. Once the completion got triggered, fish errored with the message `No matches for wildcard [...]`. Don't quote the first argument as that is the name of the binary. * Protect against arguments that start with -- Signed-off-by: Marc Khouzam <marc.khouzam@gmail.com> --------- Co-authored-by: Marc Khouzam <marc.khouzam@gmail.com>
…spf13#2356) Fixes spf13#2257 When getCompletions() checks for interspersed flags, it calls append(finalArgs, "--") to temporarily add a "--" sentinel. However, finalArgs is a sub-slice of the original args (ultimately derived from os.Args[1:] or SetArgs), and if the sub-slice has spare capacity in its backing array, append writes "--" into the shared array, corrupting the caller's data. This is particularly visible when TraverseChildren is enabled: the Traverse method returns sub-slices that share the original backing array. A user's ValidArgsFunction inspecting os.Args would then see "--" where a real argument should be. Copy trimmedArgs at the top of getCompletions to prevent any future append from accidentally mutating the caller's backing array. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: xushiwei <396972+xushiwei@users.noreply.github.com>
There was a problem hiding this comment.
Code Review
This pull request introduces several improvements and bug fixes to the Cobra library. Key additions include a new NoDuplicateArgs positional argument validator and the ability to configure a DefaultShellCompDirective within CompletionOptions, which supports inheritance across command hierarchies. A critical bug where shell completion could inadvertently mutate os.Args was resolved by ensuring argument slices are copied. Additionally, the PR updates the linting configuration, optimizes documentation generation by using fmt.Fprintf, and expands the user guide. Review feedback suggests reverting to the canonical gopkg.in/yaml.v3 import path and using more idiomatic, shorter receiver names in Go methods.
| github.com/cpuguy83/go-md2man/v2 v2.0.6 | ||
| github.com/goplus/cobra v0.0.0-00010101000000-000000000000 | ||
| gopkg.in/yaml.v3 v3.0.1 | ||
| go.yaml.in/yaml/v3 v3.0.4 |
There was a problem hiding this comment.
The import path for yaml.v3 has been changed to go.yaml.in/yaml/v3. The canonical and standard import path for this library is gopkg.in/yaml.v3. Using a non-canonical mirror path can lead to duplicate packages in the dependency graph if other dependencies use the canonical path, and it deviates from standard Go ecosystem practices.
| go.yaml.in/yaml/v3 v3.0.4 | |
| gopkg.in/yaml.v3 v3.0.4 |
| "github.com/goplus/cobra" | ||
| "github.com/goplus/cobra/pflag" | ||
| "gopkg.in/yaml.v3" | ||
| "go.yaml.in/yaml/v3" |
There was a problem hiding this comment.
| func (receiver *CompletionOptions) SetDefaultShellCompDirective(directive ShellCompDirective) { | ||
| receiver.DefaultShellCompDirective = &directive | ||
| } |
There was a problem hiding this comment.
The receiver name receiver is not idiomatic in Go. Receiver names should be short (usually one or two letters) and reflect the type name (e.g., o or opt for CompletionOptions).
| func (receiver *CompletionOptions) SetDefaultShellCompDirective(directive ShellCompDirective) { | |
| receiver.DefaultShellCompDirective = &directive | |
| } | |
| func (o *CompletionOptions) SetDefaultShellCompDirective(directive ShellCompDirective) { | |
| o.DefaultShellCompDirective = &directive | |
| } |
There was a problem hiding this comment.
Code Review Summary
Overall this is a clean upstream sync. The correctness fixes (trimmedArgs mutation, fish escaping, boolean refactors) are sound. A few items below warrant attention before merging.
Findings: 1 medium security, 1 medium docs accuracy, 2 docs clarity, 2 code quality, 1 low security.
|
|
||
| # Disable ActiveHelp which is not supported for fish shell | ||
| set -l requestComp "%[10]s=0 $args[1] %[3]s $args[2..-1] $lastArg" | ||
| set -l requestComp "%[10]s=0 $args[1] %[3]s $(string join ' ' -- (string escape -- $args[2..-1])) $lastArg" |
There was a problem hiding this comment.
[Security - Medium] Incomplete escaping before eval
The fix correctly escapes $args[2..-1] via string escape, but $args[1] (the binary name) in the same string remains unescaped before being passed to eval on line 59. If an attacker can influence the program name (e.g., a crafted $PATH entry where the binary name contains shell metacharacters), it flows into eval without sanitization.
$lastArg is pre-escaped at assignment time (line 50), so that is safe. But $args[1] should also be wrapped in string escape:
set -l requestComp "%[10]s=0 $(string escape -- $args[1]) %[3]s $(string join ' ' -- (string escape -- $args[2..-1])) $lastArg"| DefaultShellCompDirective *ShellCompDirective | ||
| } | ||
|
|
||
| func (receiver *CompletionOptions) SetDefaultShellCompDirective(directive ShellCompDirective) { |
There was a problem hiding this comment.
[Code Quality] Non-idiomatic receiver name
receiver is not a conventional Go receiver name. The rest of the codebase uses short names (c *Command, etc.). Per Go conventions (and the enabled gocritic linter), this should be o or co:
func (o *CompletionOptions) SetDefaultShellCompDirective(directive ShellCompDirective) {| HiddenDefaultCmd bool | ||
| // DefaultShellCompDirective sets the ShellCompDirective that is returned | ||
| // if no special directive can be determined | ||
| DefaultShellCompDirective *ShellCompDirective |
There was a problem hiding this comment.
[Code Quality] Doc comment doesn't explain pointer semantics or scope
The pointer is needed to distinguish nil ("not set, inherit from parent") from 0 (ShellCompDirectiveDefault). This non-obvious design should be documented on the field:
// DefaultShellCompDirective overrides the directive used when no completion
// function is registered. It is a pointer to distinguish "not set" (nil, inherits
// from the nearest ancestor) from "explicitly set to ShellCompDirectiveDefault (0)".
// Applies only to argument/noun completion; flag-name and flag-value completion
// paths use their own fixed directives.
DefaultShellCompDirective *ShellCompDirective| // Such logs are only printed when the user has set the environment | ||
| // variable BASH_COMP_DEBUG_FILE to the path of some file to be used. | ||
| if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" { | ||
| //nolint:gosec // G703:BASH_COMP_DEBUG_FILE intentionally user-controlled for completion debug logging. |
There was a problem hiding this comment.
[Security - Low] Wrong gosec rule ID in nolint annotation
The comment cites G703, which is not a valid gosec rule. The actual rule that fires here is G304 ("Potential file inclusion via variable"), since path is derived from an environment variable. The suppression intent is reasonable, but the incorrect rule ID undermines auditability and may not suppress the actual finding in all gosec versions:
//nolint:gosec // G304: BASH_COMP_DEBUG_FILE is intentionally user-controlled for completion debug logging.| ```go | ||
| cmd.RegisterFlagCompletionFunc("flag-name", cobra.NoFileCompletions) | ||
| ``` | ||
|
|
There was a problem hiding this comment.
[Docs - Medium] "Recursively change" is misleading
The text says "you can recursively change the default ShellCompDirective for a command and its subcommands". The actual implementation doesn't push anything down — it walks up the parent chain at completion time and stops at the first ancestor with a non-nil DefaultShellCompDirective. Subcommands inherit by lookup, not by mutation. Suggested rephrasing:
you can set a
DefaultShellCompDirectiveon a command so that it and any subcommands that don't override it will useShellCompDirectiveNoFileComp:
| If you find that there are more situations where file completion should be turned off than | ||
| when it is applicable, you can recursively change the default `ShellCompDirective` for a command | ||
| and its subcommands to `ShellCompDirectiveNoFileComp`: | ||
|
|
There was a problem hiding this comment.
[Docs - Medium] Missing cobra. package qualifier in code examples
Both code examples in this new section use unqualified identifiers that require the cobra. prefix for users consuming this library from outside the package:
- Line 324:
ShellCompDirectiveNoFileComp→cobra.ShellCompDirectiveNoFileComp - Line 331:
ShellCompDirectiveDefault→cobra.ShellCompDirectiveDefault
Compare with the correctly-qualified examples earlier in the same file (e.g., cobra.NoFileCompletions on line 316).
| - `myapp -i file1.txt -i file2.txt` | ||
| - With StringSlice: `myapp --input file1.txt,file2.txt,file3.txt` | ||
|
|
||
| **Note**: Both `CountVar` and array flags leverage the underlying [pflag](https://github.com/spf13/pflag) library's support for repeated flags. |
There was a problem hiding this comment.
[Docs - Low] Note refers to CountVar but section documents CountVarP
The note says "Both CountVar and array flags..." but the code example above uses CountVarP (the variant that accepts a shorthand). Either reference both variants explicitly (CountVar / CountVarP) or use the same name as the example above to avoid confusion.
No description provided.