Skip to content

osv-scanner panics on empty license field (index out of range) when --licenses is enabled #2968

Description

@sl4x0

Summary

osv-scanner scan --licenses <allowlist> crashes with an unrecoverable panic (index out of range) when scanning packages whose license field is empty. An empty license string reaches spdx.Satisfies("", allowlist), the tokenizer produces an empty token list, and tokens.next() dereferences ts.tokens[0] without a bounds check.

This is reachable from valid parser output, not malformed input: the APK extractor emits Licenses: []string{record["L"]} and, when an Alpine package record omits the L: field, that yields []string{""}. The license enricher preserves it and it flows into the SPDX evaluator. With no recover() anywhere in the scan path, the panic aborts the whole scan.

Affected code

internal/spdx/satisfies.go:72-77:

// next returns the next token in the list of tokens, removing it from the list in the process
func (ts *tokens) next() string {
	token := ts.tokens[0]
	ts.tokens = ts.tokens[1:]

	return token
}

No bounds check exists before ts.tokens[0], unlike peek() (:63-69) which does guard with len(ts.tokens) == 0.

Call chain: Satisfies("", ...) -> parse (:157) -> parseOr -> parseAnd -> parseExpression (:216) -> nextAndIsNextNextValid (:92) -> next (:73) -> panic.

Reproduction

At HEAD d87a911 (go 1.26+):

go test ./internal/spdx -run TestPocSatisfiesEmptyLicense -v

Observed output (poc_satisfies_test.go calling spdx.Satisfies("", []string{"MIT"})):

panic: runtime error: index out of range [0] with length 0
github.com/google/osv-scanner/v2/internal/spdx.(*tokens).next(...)
        C:/.../internal/spdx/satisfies.go:73

End-to-end: scan an Alpine repository/container image containing a package whose lib/apk/db/installed record omits the L: field with osv-scanner scan --licenses MIT <target>. The empty license reaches spdx.Satisfies("") and the process terminates.

Impact

  • Complete denial of the scan whenever --licenses is enabled and any scanned package lacks a license field.
  • CI pipelines fail consistently until the package or flag is removed.
  • Remaining packages in the scan are never evaluated.

Root cause / suggested fix

  • tokenise can legitimately produce zero tokens (empty input). next() must not index ts.tokens[0] unconditionally - return an error (or EOF token) when the stream is exhausted.
  • Skip empty license strings before evaluating (e.g. treat "" as no license / not satisfied rather than crashing).
  • Add regression tests for empty license expressions.
  • Consider a top-level recover() around scan execution so one malformed package record cannot terminate the whole process.

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