From 9f82619eca2373cdd20255a746117927dbd58d50 Mon Sep 17 00:00:00 2001 From: adilburaksen Date: Wed, 15 Jul 2026 00:55:24 +0300 Subject: [PATCH] fix(security): sanitize package identity in workflow-parsed log sinks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several Info/Warn log sites emit package name, version and ecosystem derived from lockfile/SBOM contents to stdout without applying output.SanitizeForWorkflowCommand. On the pull_request-triggered google/osv-scanner-action path an attacker controls those values, and a package name may contain \r/\n (e.g. a package-lock.json path key or a CycloneDX component name), letting them inject ::error::, ::add-mask::, ::stop-commands:: and other GitHub Actions workflow commands into the runner's stdout. This mirrors the existing guard on pkg/osvscanner/scan.go:158 and extends it to the remaining package-identity sinks: - pkg/osvscanner/filter.go:48 (short-commit skip warning) — reachable with no config via a git dependency whose commit hash is < 40 chars - pkg/osvscanner/filter.go:104 (ignored-package notice) - pkg/osvscanner/vulnerability_result.go:110,113 (license override notices) SanitizeForWorkflowCommand is an identity function on strings that contain no \r or \n, so there is no behaviour change for typical package metadata. --- pkg/osvscanner/filter.go | 5 +++-- pkg/osvscanner/vulnerability_result.go | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/osvscanner/filter.go b/pkg/osvscanner/filter.go index 49f0a8218b9..21927dd8a56 100644 --- a/pkg/osvscanner/filter.go +++ b/pkg/osvscanner/filter.go @@ -9,6 +9,7 @@ import ( "github.com/google/osv-scanner/v2/internal/config" "github.com/google/osv-scanner/v2/internal/imodels" "github.com/google/osv-scanner/v2/internal/imodels/results" + "github.com/google/osv-scanner/v2/internal/output" "github.com/google/osv-scanner/v2/pkg/models" "github.com/ossf/osv-schema/bindings/go/osvconstants" "github.com/ossf/osv-schema/bindings/go/osvschema" @@ -45,7 +46,7 @@ func filterUnscannablePackages(scanResults *results.ScanResults, actions Scanner // Short commit hashes (< 40 hex chars) are rejected by the OSV API with // "Invalid hash". Skip them with a warning rather than aborting the scan. case imodels.Commit(psr) != "" && len(imodels.Commit(psr)) < 40: - cmdlogger.Warnf("Skipping %s: short commit hash %q cannot be queried; OSV API requires a full 40-character SHA.", imodels.Name(psr), imodels.Commit(psr)) + cmdlogger.Warnf("Skipping %s: short commit hash %q cannot be queried; OSV API requires a full 40-character SHA.", output.SanitizeForWorkflowCommand(imodels.Name(psr)), imodels.Commit(psr)) if actions.ShowAllPackages { filteredPsr = append(filteredPsr, psr) @@ -101,7 +102,7 @@ func filterIgnoredPackages(scanResults *results.ScanResults) { if reason == "" { reason = "(no reason given)" } - cmdlogger.Infof("Package %s has been filtered out because: %s", pkgString, reason) + cmdlogger.Infof("Package %s has been filtered out because: %s", output.SanitizeForWorkflowCommand(pkgString), output.SanitizeForWorkflowCommand(reason)) continue } diff --git a/pkg/osvscanner/vulnerability_result.go b/pkg/osvscanner/vulnerability_result.go index 32fe18f4825..a1c1a3954d5 100644 --- a/pkg/osvscanner/vulnerability_result.go +++ b/pkg/osvscanner/vulnerability_result.go @@ -107,10 +107,10 @@ func buildVulnerabilityResults( if actions.ScanLicensesSummary || len(actions.ScanLicensesAllowlist) > 0 { if override, entry := configToUse.ShouldOverridePackageLicense(p); override { if entry.License.Ignore { - cmdlogger.Infof("ignoring license for package %s/%s/%s", pkg.Package.Ecosystem, pkg.Package.Name, pkg.Package.Version) + cmdlogger.Infof("ignoring license for package %s/%s/%s", output.SanitizeForWorkflowCommand(pkg.Package.Ecosystem), output.SanitizeForWorkflowCommand(pkg.Package.Name), output.SanitizeForWorkflowCommand(pkg.Package.Version)) p.Licenses = []string{} } else { - cmdlogger.Infof("overriding license for package %s/%s/%s with %s", pkg.Package.Ecosystem, pkg.Package.Name, pkg.Package.Version, strings.Join(entry.License.Override, ",")) + cmdlogger.Infof("overriding license for package %s/%s/%s with %s", output.SanitizeForWorkflowCommand(pkg.Package.Ecosystem), output.SanitizeForWorkflowCommand(pkg.Package.Name), output.SanitizeForWorkflowCommand(pkg.Package.Version), output.SanitizeForWorkflowCommand(strings.Join(entry.License.Override, ","))) p.Licenses = entry.License.Override } }