Skip to content

output/vertical: pkg name/version not sanitized — residual GHA workflow-command injection after #2750 #2792

@evilgensec

Description

@evilgensec

Component: internal/output/vertical.go
Affects: --format=vertical output
Class: GitHub Actions workflow-command injection (incomplete fix of #2749 / PR #2750)

Background

PR #2750 sanitised source-file path strings in vertical.go to prevent \r/\n bytes reaching the GHA runner. Three fmt.Fprintf call sites in the same file were not covered:

Function Unsanitized fields
printVerticalVulnerabilitiesForPackages pkgSourceName, pkg.InstalledVersion
printVerticalLicenseViolations pkg.Name, pkg.InstalledVersion
printVerticalPkgDeprecatedSummary pkg.Name, pkg.InstalledVersion

pkg.InstalledVersion and pkg.Name come from user-controlled lock files. A crafted lock file can encode a CR byte inside the version field using the standard JSON \r escape; Go's encoding/json decodes it before osv-scanner processes it. The raw byte then reaches stdout, and when the output is consumed by the GHA runner, it is treated as a workflow directive.

--format=table (default) is not affected — go-pretty strips control characters. --format=gh-annotations is not affected — it has explicit sanitisation from PR #2669.

Impact

An attacker with pull-request access (fork PR) can silence ::error::CVE-… annotations produced by osv-scanner by injecting ::stop-commands:: into a package version field in the lock file, making the scan appear clean to reviewers.

Fix

Apply SanitizeForWorkflowCommand to pkg.Name, pkgSourceName, and pkg.InstalledVersion at the three affected call sites. PR #2791 implements this fix.

I'm also filing this through the appropriate security channel (Google OSS-VRP) in parallel.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions