Supply-chain scanning
cyscan supply walks your tree, finds every supported lockfile, and emits findings from three independent matchers:
- OSV advisory match against a bundled snapshot
- Typosquat heuristic (Levenshtein ≤ 2 against popular packages)
- Policy rules authored in YAML with a
dependency:block
All three run in one pass; findings are merged, deduped by severity, and printed like any other scan.
Quick run
cyscan supply .
Example output:
[crit] CBR-SUPPLY-GHSA-mh6f-8j2x-4483 package-lock.json:0:0
event-stream was compromised with malicious code
│ event-stream@3.3.6
[high] CBR-SUPPLY-GHSA-vqm2-6jp7-jqvx requirements.txt:0:0
urllib3 Proxy-Authorization header leaks across redirects
│ urllib3@1.26.5
[low ] CBR-SUPPLY-TYPOSQUAT package-lock.json:0:0
reakt resembles popular package react
│ reakt@0.1.0
3 finding(s)
Supported lockfiles
| Ecosystem | Files parsed |
|---|---|
| Rust | Cargo.lock |
| Node.js | package-lock.json (v1 + v2/v3), yarn.lock (classic) |
| Go | go.sum |
| Python | requirements.txt (pinned with ==), poetry.lock, Pipfile.lock |
Lockfiles are preferred over manifests because they carry exact pinned versions. A manifest with ranges ("axios": "^1.6.0") depends on resolver state cyscan doesn't replicate; if a project has no lockfile, nothing is scanned — commit the lock.
Advisory matcher
The OSV snapshot ships in rules/advisories/*.jsonl alongside the binary and is refreshed nightly by the release pipeline. Every release picks up the newest cut.
Match logic:
(ecosystem, name)lookup by hash.- For each advisory's
affectedentry, check everyrange.eventsstream against the dep's version using semver. - For non-semver versions (Go
+incompatiblepseudo-versions, git SHAs) falls back to exact-list match.
Skipping OSV
cyscan supply . --no-advisories # policy + typosquat only
Useful when your org maintains its own advisory feed and doesn't want OSV noise.
Offline environments
The default is already offline — cyscan never phones home. Pass --offline to suppress the "snapshot is N days old" warning that otherwise fires on air-gapped builds.
cyscan supply . --offline
Typosquat heuristic
For each dependency, cyscan computes Levenshtein distance to every name in the bundled popular-packages list for that ecosystem. A distance ≤ 2 triggers a low-severity finding — it's advisory, not a gate.
[low ] CBR-SUPPLY-TYPOSQUAT package-lock.json
reakt resembles popular package react
False positives happen (e.g. a fork called requests2). Suppress with a custom rule or filter in your CI step.
Writing policy rules
A rule with a dependency: block matches against Dependency objects rather than source code:
id: CBR-DEP-NO-GPL-NPM
title: "Disallow GPL/AGPL licenses in Node dependencies"
severity: high
dependency:
ecosystem: npm
name_pattern: ".+" # everything — filter by license via another rule
message: |
License policy bans GPL-family licenses. Find a permissive alternative
or negotiate an exception with Legal.
dependency: fields
| Field | Type | Notes |
|---|---|---|
ecosystem | npm / pypi / crates.io / go | Case-insensitive |
name | exact name (case-insensitive) | |
name_pattern | regex | Mutually exclusive with name |
version.min | semver | Inclusive lower bound |
version.max | semver | Inclusive upper bound |
All fields are AND-ed within one rule.
Example — block a specific compromised release
id: CBR-DEP-EVENT-STREAM-MALWARE
title: "event-stream 3.3.6 was trojaned"
severity: critical
dependency:
ecosystem: npm
name: event-stream
version: { min: "3.3.6", max: "3.3.6" }
cwe: [CWE-506]
message: |
This exact release was compromised. Upgrade past the fix.
SARIF output
cyscan supply . --format sarif produces a SARIF 2.1.0 document where each supply-chain finding carries:
"properties": {
"packageCoordinate": "event-stream@3.3.6"
}
The Cybrium platform consumes this field to join findings back to its reachability index — see Integrating with the platform.
Updating the advisory snapshot
The snapshot is pinned to the release you installed. To pick up a newer one:
brew upgrade cyscan
# or for cargo / raw binary:
# re-run the install steps for the latest release
Enterprises that scan thousands of repos typically pin a specific release internally and bump it monthly — the snapshot updates are included in patch releases.