Skip to content

feat(runtime): runtime package inventory, drift, unscanned deps and live advisories (#68) - #103

Merged
karthickmk2001 merged 2 commits into
mainfrom
feat/68-runtime-inventory
Nov 12, 2026
Merged

karthickmk2001 merged 2 commits into
mainfrom
feat/68-runtime-inventory

Conversation

@karthickmk2001

Copy link
Copy Markdown
Collaborator

Closes #68

What does this change, and why?

Before asking "was the vulnerable function called?", ask "is the vulnerable package even loaded in production, and at which version?" Probes already emit load lines; the Python probe from #55 maps sys.modules to distributions with packages_distributions(). This adds the server side, src/secfoo/runtime/inventory.py, which compares that inventory with the latest SCA scan. Docs are in docs/REFERENCE.md → Runtime package inventory.

Case (from the issue) Result
Vulnerable package loaded at the scanned version The SCA finding shows Loaded (via #57's sca_runtime).
Loaded at a different version than scanned Drift. The running version is checked against OSV (osv_lookups), and an advisory raises a runtime finding.
Loaded but absent from the scanned manifests Unscanned dependency. It's looked up in OSV, and an advisory raises a finding with source runtime.
Vulnerable package never loaded during the window Not loaded, shown with the observation hours (#51 rule 4).
  • Scanned set: the latest successful SCA report's Dependency Inventory, plus the project's sca_findings, with ecosystems normalised to OSV's names.
  • First seen in production: first_seen is tracked per (app, env, package, version). A package first seen more than --baseline-hours (default 24) after its app's first session is flagged new, even with no advisory.
  • Real-time advisories:
    • secfoo runtime recheck sends one OSV /v1/querybatch call per 1,000 running package versions and refreshes the cache. Any advisory the scans don't already account for raises a new_advisory runtime finding, with no new scan. The known advisories are snapshotted before the cache refresh, so a newly published one counts as new.
    • secfoo serve runs the same recheck in the background every --runtime-recheck-hours (default 6; 0 disables it). It only queries OSV once runtime data exists, and never stops the server.
  • Storage: a new runtime_findings table (kind drift / unscanned / new_advisory, package, version, advisory, scanned versions, the app/env running it, first/last seen). These findings can't live in sca_findings, because no scan run produced them. Re-running the analysis updates rows instead of duplicating them.
  • Export: secfoo runtime inventory --app <name> --env <env> --format cyclonedx writes a CycloneDX 1.5 runtime SBOM of what's actually loaded, with purls for PyPI, npm, Maven, NuGet, Go, Cargo, Gem and Composer and each package's runtime status. --format table|json are also available.

Node load lines are #56's job. This analysis is ecosystem-neutral and works on them unchanged.

Testing

  • pytest tests passes. 1258 passed locally. The only failures are the 4 existing Windows-only POSIX permission and symlink tests that fail on main too; Linux CI is unaffected.
  • ruff check . adds no new findings.
  • Added tests/test_runtime/test_inventory.py (9 tests), using fixture inventories (a real SCA report, plus load lines through Runtime evidence format, storage and secfoo runtime ingest #53's ingest) and fixture OSV responses, with no network:
    • all four cases in the table
    • the "new in production" flag
    • idempotent analysis
    • a recheck that finds a newly published advisory against a running version
    • no OSV request when there's no runtime data
    • the CycloneDX purls and statuses
    • the CLI's table, JSON and CycloneDX output

🤖 Generated with Claude Code

…ive advisories (#68)

secfoo runtime inventory compares what production loaded (load lines) with the latest SCA scan: Loaded at the scanned version, drift (running version checked in OSV), unscanned dependencies (OSV-checked, raised as runtime findings) and never-loaded vulnerable packages with the observation window; flags packages new after a baseline. secfoo runtime recheck (and a 6-hourly job in secfoo serve) queries OSV querybatch for every running version so a newly published advisory raises a finding with no new scan. --format cyclonedx writes a runtime SBOM.

Closes #68

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The setprofile fallback (3.10/3.11) made the kit's overhead loop exceed the 60 s per-step timeout on a slow runner. Use a smaller loop in the test and give each kit step 180 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@karthickmk2001
karthickmk2001 merged commit 030df3f into main Nov 12, 2026
6 checks passed
@karthickmk2001
karthickmk2001 deleted the feat/68-runtime-inventory branch November 12, 2026 14:58
@github-actions

Copy link
Copy Markdown

Leaderboard update

karthickmk2001 now has 76 points (rank #1, Silver). 24 more to reach Gold.

Full leaderboard

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Runtime package inventory: loaded versions, drift and live advisories

1 participant