Skip to content

[BUG] SC4 has no notion of version ranges: "^1.8.3" becomes an exact pin, and an unversioned dep gets the package's worst-ever advisory as CRITICAL #318

Description

@Mark2Mac

Summary

SC4 (known vulnerable dependencies) models a dependency as either "exact pin" or "no version at all". Real manifests are mostly ranges, and both branches mishandle them:

  1. Range read as a pin — npm: _extract_packages_from_package_json does m.group(2).lstrip("^~>=<"), so "shell-quote": "^1.8.3" is reported as shell-quote==1.8.3 with that version's advisories. Python: _extract_packages_from_requirements keeps m.group(3) for any operator, so mcp>=1.1.0 is reported as mcp==1.1.0 (this is [BUG] requirements.txt: any version specifier is treated as an exact pin (pillow>=10.0.0 reported as pillow==10.0.0 with its CVEs) #294 / PR fix(supply-chain): treat only == / <= as version pins in requirements.txt (#294) #302, still present in v2.5.0 at static_patterns_supply_chain.py:430).
  2. No version read as "worst ever"_extract_packages_from_pyproject correctly drops the version for non-pin operators (m.group(2) in ("==", "<=")), so requires = ["setuptools>=61"] yields version=None. query_batch then builds a name-only OSV query, which returns every advisory ever filed for the package, and _sc4_from_osv takes the worst severity of that set. Result: CRITICAL, on a floor that excludes every affected release.

So the guard that avoids FP class 1 produces FP class 2.

Reproduction

$ printf '[build-system]\nrequires = ["setuptools>=61"]\n' > pyproject.toml
$ skillspector scan . --no-llm
# SC4 CRITICAL — "Known Vulnerable Dependency: setuptools — N advisory(ies)"
$ cat package.json
{
  "name": "r",
  "dependencies": {
    "shell-quote": "^1.8.3"
  }
}
$ skillspector scan . --no-llm
# SC4 CRITICAL — finding "shell-quote==1.8.3"   (the manifest never pins 1.8.3)

Both reproduced on v2.3.13; the code paths are unchanged in v2.5.0.

Side note found while minimising the second case: _extract_packages_from_package_json is a
line-based state machine over JSON, so a single-line package.json is silently skipped — no
dependency is extracted at all. Worth parsing the JSON instead.

Field data

Scanning 65 skill/plugin units, SC4 produced 12 findings, 5 CRITICAL. All 12 were artefacts of the two behaviours above — no manifest in the corpus pinned a vulnerable version. Two of the affected files are first-party Anthropic marketplaces; a user who trusts the CRITICAL label is being told to fix a non-problem, and a user who learns to ignore it loses SC4 entirely.

Suggested fix

  • Parse the specifier into (operator, version) and keep it. For a floor (>=, ^, ~), report only advisories whose fixed version is above the floor; for a caret/tilde range, resolve the range's upper bound before matching.
  • When the version genuinely cannot be resolved, do not report the package as vulnerable. Emit at most an INFO/LOW "unpinned dependency — could not verify against OSV", with the advisory count as context. CRITICAL should mean "you depend on a vulnerable version", not "this package once had a CVE".
  • Severity should never be derived from the worst advisory of a version-less query.

Related: #294 (+ PR #302) covers the requirements.txt half of behaviour 1; this issue is the general case plus behaviour 2.

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