Metadata-Version: 2.4
Name: oneport-depcheck
Version: 1.2.0
Summary: Dependency CVE gate: deterministic detection via OSV.dev, LLM-triaged exploitability so the 3 vulns that matter surface above the 40 that don't
Author-email: Oneport <eng@oneport.dev>
License: MIT
Project-URL: Homepage, https://oneport.dev
Project-URL: Repository, https://github.com/oneport/oneport-depcheck
Project-URL: Changelog, https://github.com/oneport/oneport-depcheck/blob/main/CHANGELOG.md
Keywords: security,dependencies,cve,osv,sca,vulnerability,reachability
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: click>=8.1.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: oneport-account>=0.1.0
Requires-Dist: packaging>=23.0
Requires-Dist: tomli>=2.0.0; python_version < "3.11"
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: respx>=0.21.0; extra == "dev"

# oneport-depcheck

**Dependency CVE gate — deterministic detection, LLM-triaged exploitability.**

Every scanner can tell you `requests==2.19.1` has a CVE. Depcheck tells you
whether *your code* can actually hit it — so the 3 vulnerabilities that matter
surface above the 40 that don't.

```
1. [HIGH] [REACHABLE] requests==2.19.1  CVE-2018-18074
   requirements.txt:2
   Why: fetch.py:8 calls requests.get() on user-supplied URLs that can
        redirect cross-origin, leaking the Authorization header — live.
   Fix: upgrade to 2.20.0 - Minor upgrade (2.19 -> 2.20) - low breakage risk.
        requests==2.20.0
```

## Architecture: detection is deterministic, judgment is LLM

1. **Deterministic core (no model, ever).** Manifests and lockfiles are parsed
   locally; each pinned `package==version` is checked against
   [OSV.dev](https://osv.dev) (free, public, no key). Severity is computed from
   the advisory's CVSS vector — same input, same output, every time. The model
   is **never** asked whether a package is vulnerable. License risks
   (GPL/AGPL in a proprietary repo) are flagged the same way.
2. **LLM triage (the judgment layer).** For each *confirmed* CVE, depcheck
   greps your codebase for actual usage of the package (imports, call sites,
   file:line) and asks the model one question: is the vulnerable path
   exploitable *here*? Verdicts: `REACHABLE`, `LIKELY-UNREACHABLE`, `DEV-ONLY`.
   Dev-only deps and never-imported packages are classified deterministically
   without a model call.
3. **Upgrade guidance.** Safe target version (clears every CVE for the
   package), a one-line what-might-break note from the semver gap, and the
   exact manifest line change as a committable suggestion.

## Install

```bash
pip install oneport-depcheck
oneport-account login <token>   # free token at https://oneport.dev (metered proxy)
```

The reachability triage runs through OnePort's managed proxy — no API key to
manage. No login at all? `scan --no-llm` still runs full deterministic detection.

## Usage

```bash
oneport-depcheck scan                       # scan ./ manifests
oneport-depcheck scan path/to/repo --fix    # + consolidated fix plan
oneport-depcheck scan --format json         # machine-readable
oneport-depcheck scan --format sarif        # GitHub Code Scanning
oneport-depcheck scan --min-severity high   # hide LOW/MEDIUM
oneport-depcheck scan --fail-on any         # CI: exit 1 on any CVE
oneport-depcheck scan --fail-on reachable   # CI: exit 1 only if exploitable (default)
oneport-depcheck scan --post https://github.com/org/repo/pull/42
```

`--post` posts inline PR comments on the exact manifest lines (with one-click
committable `suggestion` blocks for pin bumps) plus a sticky summary table
that updates in place on re-runs (hidden HTML marker — no server needed).

### Supported manifests

`requirements.txt` (+ `requirements-dev.txt` variants) · `poetry.lock` ·
`Pipfile.lock` · `package.json` · `package-lock.json` (v1–v3)

Unpinned requirements can't be checked against a version database; depcheck
lists them as skipped rather than silently claiming they're clean.

### CI gate

```yaml
- run: pip install oneport-depcheck oneport-account
- run: oneport-account login ${{ secrets.ONEPORT_TOKEN }}
- run: oneport-depcheck scan --fail-on reachable
```

Exit codes: `0` pass · `1` gate failed · `2` usage/auth/OSV error.
`UNTRIAGED` findings fail the `reachable` gate — unverified is not safe.

### Waivers — accept a known advisory on the record

Some CVEs you've triaged and accepted (a false positive, an advisory that doesn't
apply to how you use the package, or a real one you can't fix until next quarter).
Approve them narrowly instead of turning the gate off, in
`.oneport/depcheck-waivers.yml`:

```yaml
waivers:
  - vuln: "CVE-2024-1234"          # CVE / GHSA id (exact or glob); matched against
                                   # the finding's id AND its aliases
    reason: "not reachable — we never call the affected parser (JIRA-123)"
    expires: "2026-06-30"
  - package: "lodash"              # ...or waive by package (glob)
    vuln: "GHSA-*"                 # optionally narrowed to certain advisories
    reason: "pinned by an upstream we don't control; upgrade tracked in OPS-7"
```

A waived finding is still shown (marked `[WAIVED]`) and still exported to SARIF as
a `suppressions` entry, but it never trips `--fail-on`. Expired waivers are
ignored, so the finding re-blocks — an approval can't outlive its reason.

### Team guidelines

`.oneport/guidelines.md` (shared with all Oneport tools) is injected into the
triage prompt:

```bash
oneport-depcheck learn "dev-dependency CVEs are warn-only"
```

DEV-ONLY findings never fail `--fail-on reachable` by design.

## Privacy

Serverless — see [PRIVACY.md](PRIVACY.md). Package names/versions go to
OSV.dev and the package registries (public data); usage snippets of vulnerable
packages go to the model on **your** API key. Nothing else leaves the machine.

## What this is not

- Not a guess engine: every vulnerability reported has an OSV.dev id you can
  look up. If OSV doesn't know it, depcheck doesn't report it.
- Not a replacement for upgrading: `LIKELY-UNREACHABLE` means *lower priority
  right now*, not *never fix*.
