mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
fix: Only check first non-flag arg against NonDownloadCommands
Scanning all args caused false proxy bypasses when package names or script arguments matched a NonDownloadCommands entry. For example: - npm exec test → "test" matched, proxy incorrectly skipped - npm update config → "config" matched, proxy skipped - npm publish --tag version → "version" matched, proxy skipped Fix by checking only the first non-flag argument (the subcommand). If it is not in NonDownloadCommands we break immediately, so trailing args never influence the classification. Applied to all four parsers: npm, pip/pip3, uv, and poetry. Regression tests added for the false positive cases.
This commit is contained in:
@@ -134,12 +134,18 @@ func (npm *npmPackageManager) ParseCommand(args []string) (*ParsedCommand, error
|
||||
}
|
||||
|
||||
if installCmdIndex == -1 {
|
||||
// Check if this is a known non-download command (e.g., npm ls, npm outdated).
|
||||
// Unknown commands are treated as potential downloads — fail safe.
|
||||
// Check only the first non-flag arg (the subcommand) against NonDownloadCommands.
|
||||
// Scanning all args would cause false positives when package names or script
|
||||
// arguments happen to match a known non-download command (e.g. `npm exec test`,
|
||||
// `npm update config`, `npm publish --tag version`).
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
continue
|
||||
}
|
||||
if slices.Contains(npm.Config.NonDownloadCommands, arg) {
|
||||
return &ParsedCommand{Command: command, IsKnownNonDownloadCommand: true}, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return &ParsedCommand{Command: command}, nil
|
||||
|
||||
@@ -561,6 +561,28 @@ func TestNpmProxyBehavior(t *testing.T) {
|
||||
isKnownNonDownloadCmd: true,
|
||||
isInstallationCommand: false,
|
||||
},
|
||||
// False positive regression: package/script names matching NonDownloadCommands words
|
||||
{
|
||||
name: "npm exec test — proxy runs (test is package arg, not subcommand)",
|
||||
pm: func() (*npmPackageManager, error) { return NewNpmPackageManager(DefaultNpmPackageManagerConfig()) },
|
||||
command: "npm exec test",
|
||||
isKnownNonDownloadCmd: false,
|
||||
isInstallationCommand: false,
|
||||
},
|
||||
{
|
||||
name: "npm update config — proxy runs (config is package name, not subcommand)",
|
||||
pm: func() (*npmPackageManager, error) { return NewNpmPackageManager(DefaultNpmPackageManagerConfig()) },
|
||||
command: "npm update config",
|
||||
isKnownNonDownloadCmd: false,
|
||||
isInstallationCommand: false,
|
||||
},
|
||||
{
|
||||
name: "npm publish --tag version — proxy runs (version is flag value, not subcommand)",
|
||||
pm: func() (*npmPackageManager, error) { return NewNpmPackageManager(DefaultNpmPackageManagerConfig()) },
|
||||
command: "npm publish --tag version",
|
||||
isKnownNonDownloadCmd: false,
|
||||
isInstallationCommand: false,
|
||||
},
|
||||
// Unknown commands default to proxy running (fail safe)
|
||||
{
|
||||
name: "unknown npm subcommand — proxy runs (fail safe)",
|
||||
|
||||
@@ -149,9 +149,13 @@ func (p *pipCommandParser) ParseCommand(args []string) (*ParsedCommand, error) {
|
||||
|
||||
if installCmdIndex == -1 {
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
continue
|
||||
}
|
||||
if slices.Contains(p.config.NonDownloadCommands, arg) {
|
||||
return &ParsedCommand{Command: command, IsKnownNonDownloadCommand: true}, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return &ParsedCommand{Command: command}, nil
|
||||
@@ -376,9 +380,13 @@ func (p *poetryCommandParser) ParseCommand(args []string) (*ParsedCommand, error
|
||||
|
||||
if installCmdIndex == -1 {
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
continue
|
||||
}
|
||||
if slices.Contains(p.config.NonDownloadCommands, arg) {
|
||||
return &ParsedCommand{Command: command, IsKnownNonDownloadCommand: true}, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return &ParsedCommand{Command: command}, nil
|
||||
|
||||
Reference in New Issue
Block a user