Skip to content

Commit 6011f2b

Browse files
jongioCopilot
andcommitted
fix: tighten root persistent flag exemption to SDK-created roots only
The root persistent flag skip logic previously exempted ALL flags on root.PersistentFlags() via pointer equality. This meant an extension using a plain root (not NewExtensionRootCommand) could add a root persistent flag colliding with reserved globals without detection. Now only roots created by NewExtensionRootCommand (marked with an azd-sdk-root annotation) get the exemption. Extensions using plain roots have all their root persistent flags validated against the reserved list. Adds test proving plain-root collisions are caught. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e7fe8f0 commit 6011f2b

3 files changed

Lines changed: 25 additions & 5 deletions

File tree

cli/azd/pkg/azdext/extension_command.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ func NewExtensionRootCommand(opts ExtensionCommandOptions) (*cobra.Command, *Ext
7373
Short: opts.Short,
7474
Long: opts.Long,
7575
Version: opts.Version,
76+
Annotations: map[string]string{
77+
"azd-sdk-root": "true",
78+
},
7679
}
7780

7881
// Register persistent flags

cli/azd/pkg/azdext/reserved_flags.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,13 @@ func collectConflicts(root, cmd *cobra.Command) []FlagConflict {
130130
}
131131
checked[f.Name] = struct{}{}
132132

133-
// Skip flags that are the SDK-provided root persistent flags (same object).
134-
// Use pointer equality so that a subcommand defining its own flag with the
135-
// same name as a root persistent flag is still validated.
136-
if rootFlag := root.PersistentFlags().Lookup(f.Name); rootFlag != nil && rootFlag == f {
137-
return
133+
// Skip only SDK-provided root persistent flags. The annotation check ensures that
134+
// extensions using a plain root (not NewExtensionRootCommand) that manually add
135+
// root persistent flags colliding with reserved globals are still caught.
136+
if root.Annotations["azd-sdk-root"] == "true" {
137+
if rootFlag := root.PersistentFlags().Lookup(f.Name); rootFlag != nil && rootFlag == f {
138+
return
139+
}
138140
}
139141

140142
conflicts = append(conflicts, checkFlag(cmd, f)...)

cli/azd/pkg/azdext/reserved_flags_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,18 @@ func TestReservedFlagsInSyncWithInternal(t *testing.T) {
169169
long, sdkShort, short)
170170
}
171171
}
172+
173+
func TestValidateNoReservedFlagConflicts_NonSDKRootPersistentFlag(t *testing.T) {
174+
// An extension that manually adds a root persistent flag colliding with a
175+
// reserved global (not via the SDK) should still be caught. This verifies
176+
// the tightened exemption: only known SDK globals are exempt, not arbitrary
177+
// root persistent flags.
178+
root := newPlainRoot("test")
179+
root.PersistentFlags().StringP("environment", "e", "", "custom env flag")
180+
sub := &cobra.Command{Use: "run"}
181+
root.AddCommand(sub)
182+
183+
err := ValidateNoReservedFlagConflicts(root)
184+
require.Error(t, err)
185+
require.Contains(t, err.Error(), "long flag --environment is reserved")
186+
}

0 commit comments

Comments
 (0)