Skip to content

fix: address Sighthound CWE-78 command injection findings (top 5) - #47242

Merged
pelikhan merged 4 commits into
mainfrom
copilot/sighthound-security-fix
Jul 22, 2026
Merged

fix: address Sighthound CWE-78 command injection findings (top 5)#47242
pelikhan merged 4 commits into
mainfrom
copilot/sighthound-security-fix

Conversation

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Five Critical-severity CWE-78 findings from the Sighthound scan flagged exec.Command calls where dynamic values (executable paths, package names, relative paths) were passed without explicit validation or suppression annotations.

Changes

  • pkg/gitutil/gitutil.go — add #nosec G204 with rationale: relPath is produced by filepath.Rel and already guarded by a ..-prefix check; no shell execution.

  • pkg/workflow/dependabot.go — add filepath.IsAbs(npmPath) guard before invoking npm (defense-in-depth; exec.LookPath normally returns an absolute path, but this makes it explicit) + #nosec G204.

  • pkg/workflow/pip_validation.go — add #nosec G204; pkgName is already validated against the PEP 508 allowlist via validatePipPackageName before reaching the exec.Command call; pipCmd is one of the hardcoded strings "pip" / "pip3".

  • pkg/cli/upgrade_command.go — add filepath.IsAbs(exe) guard before re-launching the process; exe always comes from os.Executable() or a rename of it, but the check makes the invariant enforceable + #nosec G204.

  • pkg/cli/runner_guard.go — apply filepath.Clean to scanPath after the existing ..-escape check; tighten #nosec G204 comment to document both gitRoot (absolute, from git rev-parse) and scanPath (cleaned, escape-validated relative path) guarantees.

Copilot AI linked an issue Jul 22, 2026 that may be closed by this pull request
- gitutil.go: add #nosec G204 annotation; relPath validated by filepath.Rel and checked against '..' prefix
- dependabot.go: add filepath.IsAbs validation for npmPath + #nosec G204
- pip_validation.go: add #nosec G204; pkgName pre-validated by validatePipPackageName
- upgrade_command.go: add filepath.IsAbs validation for exe + #nosec G204
- runner_guard.go: apply filepath.Clean to scanPath; improve #nosec G204 comment

Closes #47208

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix security findings in github/gh-aw fix: address Sighthound CWE-78 command injection findings (top 5) Jul 22, 2026
Copilot AI requested a review from pelikhan July 22, 2026 08:32
@pelikhan
pelikhan marked this pull request as ready for review July 22, 2026 08:36
Copilot AI review requested due to automatic review settings July 22, 2026 08:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Addresses #47208 by documenting validated exec.Command usage and adding path safeguards.

Changes:

  • Adds G204 suppression rationales.
  • Validates dynamic executable paths.
  • Normalizes Runner Guard scan paths.
Show a summary per file
File Description
pkg/gitutil/gitutil.go Documents safe Git argument handling.
pkg/workflow/dependabot.go Requires an absolute npm path.
pkg/workflow/pip_validation.go Documents package validation.
pkg/cli/upgrade_command.go Validates the relaunch executable path.
pkg/cli/runner_guard.go Cleans the scanner path.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment thread pkg/cli/runner_guard.go
relDir, relErr := filepath.Rel(gitRoot, workflowDir)
if relErr == nil && relDir != ".." && !strings.HasPrefix(relDir, ".."+string(filepath.Separator)) {
scanPath = relDir
scanPath = filepath.Clean(relDir)
Comment thread pkg/cli/upgrade_command.go Outdated
Comment on lines +489 to +491
// #nosec G204 -- exe is validated as an absolute path above and originates from
// os.Executable() or the known install path; newArgs is a copy of the current process
// argv (os.Args[1:]) plus a single hardcoded flag, containing no untrusted user input.
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

No test files were added or modified in this PR. Test Quality Sentinel skipped. This PR (#47242) addresses Sighthound CWE-78 command injection findings in 5 production files but contains no test additions or modifications.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #47242 does not have the 'implementation' label and has only 27 new lines of code in business logic directories (threshold: 100).

@github-actions github-actions Bot mentioned this pull request Jul 22, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: CWE-78 command injection fixes

Overall the changes are well-structured. The defense-in-depth guards (filepath.IsAbs, filepath.Clean) are sensible additions, and most #nosec G204 suppressions are accurately documented.

Two issues are flagged in existing inline comments:

  1. runner_guard.go line 60filepath.Clean does not prevent option/flag injection. A user-controlled workflowDir like --help produces a scanPath starting with -, interpretable as a Docker CLI flag. Consider prefixing with ./ or adding -- before the path in the Docker command.

  2. upgrade_command.go line 491 — The #nosec rationale claims newArgs contains no untrusted input, but newArgs = os.Args[1:] is user-controlled. Shell injection is not possible here (exec.Command uses argv directly), but the comment is misleading and should be corrected.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 21.8 AIC · ⌖ 4.53 AIC · ⊞ 5K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs — requesting one targeted fix before merge.

📋 Key Themes & Highlights

Key Themes

  • Defense-in-depth inconsistency: pip_validation.go uses a bare "pip"/"pip3" command name without prior exec.LookPath + filepath.IsAbs validation, unlike dependabot.go which correctly resolves and validates the path. The #nosec rationale is therefore weaker than claimed.
  • Option injection (pre-existing): The existing Copilot comments on runner_guard.go (line 60) and upgrade_command.go (line 491) correctly note that filepath.IsAbs / filepath.Clean do not guard against flag-injection via values starting with -. These are real gaps worth addressing.

Positive Highlights

  • dependabot.go: the exec.LookPath + filepath.IsAbs pattern is the right approach and well-documented.
  • upgrade_command.go: filepath.IsAbs guard on exe is a good defense-in-depth addition.
  • gitutil.go: the #nosec rationale is accurate and well-scoped.
  • ✅ All suppression comments now explain why the call is safe, not just that it is.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 26 AIC · ⌖ 4.67 AIC · ⊞ 6.7K
Comment /matt to run again

// Include --pre flag to check for pre-release versions (alpha, beta, rc)
// #nosec G204 -- pipCmd is one of the hardcoded values "pip" or "pip3"; pkgName is
// validated above by validatePipPackageName against the strict PyPI PEP 508 allowlist.
cmd := exec.Command(pipCmd, "index", "versions", pkgName, "--pre")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] pipCmd is used as a bare command name ("pip" / "pip3"), relying on PATH resolution at exec time — unlike dependabot.go, which calls exec.LookPath and validates filepath.IsAbs before the #nosec. The #nosec rationale here says the values are "hardcoded" but omits that they are resolved via PATH, making the defense-in-depth story inconsistent across files.

💡 Suggested fix

Apply the same pattern used in dependabot.go: resolve with exec.LookPath, validate with filepath.IsAbs, then pass the resolved absolute path to exec.Command.

@copilot please address this.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REQUEST_CHANGES — Two medium-severity gaps in the new hardening logic need fixes before this merges.

Blocking findings

1. pip_validation.go: pipCmd not resolved via exec.LookPath

This PR correctly adds exec.LookPath + filepath.IsAbs for npmPath in dependabot.go, but omits the same treatment for pip/pip3 in pip_validation.go. The nosec comment argues pipCmd is hardcoded, but the resolved binary is still PATH-dependent at exec time — the scanner will likely flag this too, and it is inconsistent with the pattern applied elsewhere in this PR.

2. upgrade_command.go: exeOverride branch skips filepath.EvalSymlinks

The new filepath.IsAbs guard is only a partial fix. When exeOverride is non-empty it bypasses the EvalSymlinks call that the os.Executable() branch uses. An absolute-path symlink still resolves to an unintended binary. EvalSymlinks should be applied to exe regardless of source.

The two pre-existing comments (option injection in runner_guard.go, nosec rationale accuracy in upgrade_command.go) remain valid and unaddressed.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 38.6 AIC · ⌖ 4.83 AIC · ⊞ 5.6K
Comment /review to run again

// Include --pre flag to check for pre-release versions (alpha, beta, rc)
// #nosec G204 -- pipCmd is one of the hardcoded values "pip" or "pip3"; pkgName is
// validated above by validatePipPackageName against the strict PyPI PEP 508 allowlist.
cmd := exec.Command(pipCmd, "index", "versions", pkgName, "--pre")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pipCmd resolved via PATH without exec.LookPath, inconsistent with the npmPath pattern added in this same PR: a tampered PATH could cause an attacker-controlled binary to be executed.

💡 Suggested fix

Mirror the dependabot.go treatment from this PR:

resolvedPip, err := exec.LookPath(pipCmd)
if err != nil {
    return fmt.Errorf("pip command %q not found: %w", pipCmd, err)
}
if !filepath.IsAbs(resolvedPip) {
    return fmt.Errorf("pip path is not absolute: %s", resolvedPip)
}
cmd := exec.Command(resolvedPip, "index", "versions", pkgName, "--pre")

The nosec comment correctly notes pipCmd is a hardcoded string, but the resolved binary is still PATH-dependent. The defense-in-depth absolute-path validation applied to npmPath in dependabot.go in this same PR should be applied here consistently to satisfy the scanner and reduce risk from a compromised environment.

Comment thread pkg/cli/upgrade_command.go Outdated

// #nosec G204 -- exe is validated as an absolute path above and originates from
// os.Executable() or the known install path; newArgs is a copy of the current process
// argv (os.Args[1:]) plus a single hardcoded flag, containing no untrusted user input.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exeOverride branch skips filepath.EvalSymlinks, leaving a symlink attack surface that the os.Executable() branch explicitly guards against: filepath.IsAbs only confirms the path is absolute, not that the final resolved binary is the intended one.

💡 Suggested fix

Apply filepath.EvalSymlinks before the IsAbs check regardless of which branch provides exe:

if exeOverride != "" {
    exe = exeOverride
} else {
    exe, err = os.Executable()
    // ...existing error handling...
}

// Resolve symlinks for both branches before validation
exe, err = filepath.EvalSymlinks(exe)
if err != nil {
    return fmt.Errorf("failed to resolve executable symlinks: %w", err)
}
if !filepath.IsAbs(exe) {
    return fmt.Errorf("executable path is not absolute: %s", exe)
}

The nosec comment states exe "originates from os.Executable() or the known install path" but exeOverride is a caller-controlled string; symlink resolution is a separate concern from path absoluteness.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

- runner_guard.go: prefix scanPath with './' and convert to forward
  slashes for the Linux container, preventing option injection when
  workflowDir begins with '-' (e.g. '--help')
- upgrade_command.go: fix #nosec G204 rationale to accurately state
  that exec.Command bypasses the shell entirely (execve), so
  shell-injection is impossible regardless of argv content

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Both review comments have been addressed in commit fix: address review feedback on runner_guard and upgrade_command:

  • runner_guard.go: scanPath is now converted to a containerScanPath by prepending ./ and calling filepath.ToSlash, so a workflowDir beginning with - (e.g. --help) can never be interpreted as a flag by runner-guard.
  • upgrade_command.go: The #nosec G204 rationale now accurately documents that os.Args[1:] is user-controlled, and explains why shell-injection is still impossible — exec.Command calls execve(2) directly without a shell.

Copilot AI requested a review from pelikhan July 22, 2026 09:32
@pelikhan
pelikhan merged commit aeb6e06 into main Jul 22, 2026
23 checks passed
@pelikhan
pelikhan deleted the copilot/sighthound-security-fix branch July 22, 2026 11:15
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.83.0

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.

[sighthound] Security findings in github/gh-aw

3 participants