Description
_extract_packages_from_requirements keeps the version string for any version specifier, so a lower bound like pillow>=10.0.0 is treated as if it were the exact pin pillow==10.0.0. The CVEs of that floor version are then attributed to the dependency, producing a CRITICAL finding for a requirements file that does not pin anything.
The same file already contains the correct logic in another extractor, which is what makes this look like an oversight rather than a design choice.
Reproduction
$ cat requirements.txt
pillow>=10.0.0
$ skillspector scan . --no-llm --format json -o out.json
$ jq -r '.issues[] | select(.category=="Supply Chain") | "[\(.severity)] \(.finding)"' out.json
[LOW] pillow>=10.0.0
[CRITICAL] pillow==10.0.0
Both findings come from line 1. The LOW one is correct — the dependency is unpinned. The CRITICAL one reports a pin that does not exist in the file.
Version: SkillSpector v2.3.13.
Cause
skillspector/nodes/analyzers/static_patterns_supply_chain.py:429
m = re.match(r"^([a-zA-Z][a-zA-Z0-9._-]*)(?:\[.*?\])?\s*(?:([=<>!~]=?)\s*([\d.*]+))?", line)
if m:
name = m.group(1)
version = m.group(3) if m.group(2) else None # <-- any operator counts as a pin
The regex captures the operator in group 2, but the guard only checks that some operator matched. >=, != and ~= all fall through as exact versions.
Line 497 of the same file, in _extract_packages_from_setup_py, gets it right:
version = m.group(3) if m.group(2) in ("==", "<=") else None
Suggested fix
Apply the same guard at line 429:
version = m.group(3) if m.group(2) in ("==", "<=") else None
<= is arguably also not a pin, but keeping the two extractors consistent seems like the right first step; tightening both is a separate decision.
Impact
Any requirements.txt that uses floors — the common convention for libraries — generates false CRITICAL supply-chain findings that never go away, since the "fix" the report implies (pin to a newer version) is not what the file was expressing. In a batch of 46 findings I triaged across three skill collections, this produced the only CRITICAL in the set, and it was a false positive.
Related
#233 covers a different defect in the same area (OSV queried without a version specifier, so historical CVEs surface even for an exact pin that is clean). This one is upstream of that: the specifier operator is misread before the query is built. Fixing #233 alone would not fix this case, since the analyzer would still be asking about the wrong version.
Description
_extract_packages_from_requirementskeeps the version string for any version specifier, so a lower bound likepillow>=10.0.0is treated as if it were the exact pinpillow==10.0.0. The CVEs of that floor version are then attributed to the dependency, producing a CRITICAL finding for a requirements file that does not pin anything.The same file already contains the correct logic in another extractor, which is what makes this look like an oversight rather than a design choice.
Reproduction
Both findings come from line 1. The LOW one is correct — the dependency is unpinned. The CRITICAL one reports a pin that does not exist in the file.
Version: SkillSpector v2.3.13.
Cause
skillspector/nodes/analyzers/static_patterns_supply_chain.py:429The regex captures the operator in group 2, but the guard only checks that some operator matched.
>=,!=and~=all fall through as exact versions.Line 497 of the same file, in
_extract_packages_from_setup_py, gets it right:Suggested fix
Apply the same guard at line 429:
<=is arguably also not a pin, but keeping the two extractors consistent seems like the right first step; tightening both is a separate decision.Impact
Any
requirements.txtthat uses floors — the common convention for libraries — generates false CRITICAL supply-chain findings that never go away, since the "fix" the report implies (pin to a newer version) is not what the file was expressing. In a batch of 46 findings I triaged across three skill collections, this produced the only CRITICAL in the set, and it was a false positive.Related
#233 covers a different defect in the same area (OSV queried without a version specifier, so historical CVEs surface even for an exact pin that is clean). This one is upstream of that: the specifier operator is misread before the query is built. Fixing #233 alone would not fix this case, since the analyzer would still be asking about the wrong version.