Guides / vulnerability-and-osv-analysis · Edit on GitHub

Vulnerability and OSV Supply-Chain Analysis

Introduction

Traditional Software Composition Analysis (SCA) tools generate overwhelming false-positive fatigue: they scan dependency manifests (pom.xml, package.json, Cargo.toml) and flag every CVE matching a package version—regardless of whether the vulnerable function is ever imported, called, or exposed to external input.

rgctl provides an end-to-end supply-chain reachability and exploitability pipeline that bridges dependency triage with deep graph analysis:

OSV Advisory JSON
       │
       ▼
[P0] vuln triage         Normalize advisory & extract affected version ranges and methods
       │
       ▼
[P1] deps check          Match package manifests & optional bundled JARs / node_modules
       │                 ↳ Fast Exit: not_affected if dependency absent or not in range
       ▼
[P2/P3] find & callers   Query import statements & verify if affected methods are ever called
       │
       ▼
[P4] blast-radius        Classify call paths against external attack boundaries (REST, CLI, web)
       │
       ▼
[P5] taint               Trace data flow from untrusted sources to the vulnerable sink (CFG/PDG)
       │
       ▼
[P6] vuln analyze        Synthesize exploitability verdict & generate machine-readable OpenVEX

All security commands are available as top-level verbs (vuln, deps, taint) and grouped under the security namespace (security vuln, security deps, security taint).


Use Cases

  • Eliminating Vulnerability False Positives: Prove with code graph evidence that a vulnerable dependency method is never in the execution path, allowing safe deferral without patching.
  • Automated OpenVEX Generation: Produce standardized, machine-readable OpenVEX documents justifying why specific CVEs are not_affected or not_exploitable for compliance audits and SBOM pipelines.
  • Incident Response Triage: Rapidly determine which microservices actually call a newly disclosed 0-day vulnerability (such as a Jackson or Log4Shell deserialization sink).
  • Sink-First Taint Verification: Confirm whether untrusted external HTTP request parameters can flow through the call graph into a security-sensitive sink.

Exploitability Verdicts

The orchestrator (vuln analyze) evaluates multi-phase evidence to produce one of four definitive verdicts:

Exploitability VerdictOpenVEX StatusCondition
not_affectednot_affectedThe dependency is not present in the project manifests/bundles, or its installed version is outside the advisory's vulnerable range.
not_exploitablenot_affected (vulnerable_code_not_in_execute_path)The dependency is installed in a vulnerable version range, but the code graph proves the affected methods/classes are never imported or called.
exploitableaffectedThe vulnerable method is called, reaches an external boundary, and forward data-flow taint confirms a reachable path from untrusted input.
under_investigationunder_investigationThe dependency is present and called, but evidence is inconclusive (e.g. CFG was not built, or the primary sink cannot be definitively resolved).

Indexing Prerequisites

The depth of analysis determines the required indexing flags:

# 1. Fast path (deps-only triage & manifest matching):
rgctl discover .

# 2. Deep reachability, sink-first taint, and firm OpenVEX justifications:
rgctl discover . --with-cfg --with-taint

[!IMPORTANT] Why --with-cfg matters: Without control-flow graphs (--with-cfg), deps check and caller searches still work, but taint cannot execute and OpenVEX cannot definitively assert that vulnerable code is not in the execution path. Always index with --with-cfg for reachability audits.


Step-by-Step Walkthrough

Step 0: Normalize the Advisory (vuln triage)

Parse an OSV advisory JSON file (e.g., from GHSA, CVE, or PyPA) to extract affected packages, ecosystems, version boundaries, and affected function signatures:

rgctl -f json vuln triage --osv ./advisory.json

JSON Output:

{
  "id": "GHSA-j276-x3v5-9988",
  "summary": "Jackson-databind vulnerable to unsafe deserialization",
  "packages": [
    {
      "ecosystem": "Maven",
      "name": "com.fasterxml.jackson.core:jackson-databind",
      "introduced": "2.0.0",
      "fixed": "2.9.10.8"
    }
  ],
  "affected_methods": [
    "com.fasterxml.jackson.databind.ObjectMapper.readValue"
  ],
  "versions_array_not_authoritative": false
}

Step 1: Check Dependency Manifests (deps check)

Match the advisory against your project manifests (pom.xml, package.json, Cargo.toml, go.mod, etc.) and optional binary bundles (--include-jars, --include-node-modules):

rgctl -r "$REPO" -f json deps check --osv ./advisory.json --include-jars lib

Output when not affected (Fast Exit):

{
  "osv_id": "GHSA-j276-x3v5-9988",
  "verdict": "not_affected",
  "checked": ["pom.xml", "lib/custom-lib.jar"],
  "matched": []
}

Output when candidate is affected:

{
  "osv_id": "GHSA-j276-x3v5-9988",
  "verdict": "affected_candidate",
  "checked": ["pom.xml"],
  "matched": [
    {
      "ecosystem": "Maven",
      "name": "com.fasterxml.jackson.core:jackson-databind",
      "version": "2.9.8",
      "in_range": true,
      "provenance": "pom.xml"
    }
  ]
}

If deps check returns not_affected, vuln analyze terminates early, immediately generating an OpenVEX document with not_affected (component_not_present).


Step 2: Check Import Presence and Method Callers

If the dependency is installed, query the graph to determine if your code actually imports or calls the affected symbols:

# Check if package imports exist in the graph
rgctl -r "$REPO" -f json find --package 'com.fasterxml.jackson.core:jackson-databind' -t import

# Find callers of the affected method
rgctl -r "$REPO" -f json callers ObjectMapper --methods readValue

rgctl handles package namespace resolution across ecosystems, including the Xalan dual path (resolving xalan:* to both Apache packages and JDK internal aliases like com.sun.org.apache.xalan.internal).


Step 3: Classify Attack Boundaries (blast-radius --classify-boundary)

If the method is called, determine whether callers are exposed to external entrypoints:

rgctl -r "$REPO" -f json blast-radius ObjectMapper.readValue --classify-boundary

Boundary catalogs automatically detect:

  • Java / Jakarta: Spring @RestController, @PostMapping, JAX-RS @Path, Servlet doGet/doPost.
  • Python: Flask @app.route, Django views, FastAPI endpoints.

Step 4: Sink-First Taint Propagation (taint)

Trace data flow from untrusted external sources directly into the vulnerable sink method using the CFG/PDG graph:

rgctl -r "$REPO" -f json taint \
  --sink ObjectMapper.readValue \
  --source external \
  --depth 8

JSON Output:

{
  "sink": "ObjectMapper.readValue",
  "sink_resolution": "resolved",
  "sink_callers": ["OrderController.submitOrder"],
  "paths": [
    {
      "source": "HttpServletRequest.getInputStream",
      "sink": "ObjectMapper.readValue",
      "hops": [
        "OrderController.submitOrder",
        "OrderService.parsePayload",
        "ObjectMapper.readValue"
      ]
    }
  ],
  "honesty": "PDG confirmed forward taint path from external boundary"
}

If no path exists, taint confirms that untrusted inputs cannot flow into the sink.


Step 5: Full Orchestrated Analysis & OpenVEX (vuln analyze)

Run the complete orchestrated pipeline with a single command:

rgctl -r "$REPO" -f json vuln analyze --osv ./advisory.json --include-jars lib

JSON Output:

{
  "schema_version": "1",
  "osv_id": "GHSA-j276-x3v5-9988",
  "deps_verdict": "affected_candidate",
  "exploitability": "not_exploitable",
  "phases": [
    "triage",
    "deps_check",
    "package_resolve",
    "caller_scan",
    "sink_taint",
    "openvex"
  ],
  "notes": "Vulnerable package is present in pom.xml, but affected method ObjectMapper.readValue has 0 call sites in the repository.",
  "openvex": {
    "@context": "https://openvex.dev/ns/v0.2.0",
    "@id": "urn:openvex:rgctl:analysis:GHSA-j276-x3v5-9988",
    "author": "rgctl-security",
    "timestamp": "2026-10-10T03:30:00Z",
    "statements": [
      {
        "vulnerability": {
          "name": "GHSA-j276-x3v5-9988"
        },
        "products": [
          {
            "@id": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.9.8"
          }
        ],
        "status": "not_affected",
        "justification": "vulnerable_code_not_in_execute_path",
        "impact_statement": "Affected method has zero callers in application call graph"
      }
    ]
  }
}

The security Namespace

All vulnerability and dependency commands are available under the security parent command:

Top-Level CommandCanonical security EquivalentDescription
rgctl vuln triagergctl security vuln triageNormalize OSV advisory
rgctl deps checkrgctl security deps checkMatch manifests and bundles
rgctl vuln analyzergctl security vuln analyzeOrchestrate analysis & OpenVEX
rgctl taintrgctl security taintSink-first external taint flow

Declarative Taint Rule Overlays

rgctl uses a declarative YAML taint engine (crates/rgctl-analysis/rules/taint/). You can supply project-specific rules or custom vulnerability sink overlays:

  1. Place rule files in .rgctl/taint-rules.d/my-rules.yaml.
  2. Or pass rules directly via the CLI:
rgctl -f json taint \
  --sink MyCustomValidator.decode \
  --source external \
  --taint-rules ./custom-rules.yaml

Example Taint Rule Format:

id: CWE-502-deserialization
name: Unsafe Object Deserialization
severity: high
cwe: 502
sources:
  - kind: external
    pattern: "HttpServletRequest.*"
sinks:
  - pattern: "ObjectMapper.readValue"
    arg_indices: [0, 1]
sanitizers:
  - pattern: "SecurityValidator.sanitize"

CLI Flag Reference

vuln triage Options

FlagDefaultDescription
--osv <PATH>RequiredPath to OSV advisory JSON file.

deps check Options

FlagDefaultDescription
--osv <PATH>RequiredPath to OSV advisory JSON file.
--include-jars <DIR>[]Directories containing bundled .jar archives to inspect.
--include-node-modules <DIR>[]Directories containing bundled node_modules/ to inspect.

vuln analyze Options

FlagDefaultDescription
--osv <PATH>RequiredPath to OSV advisory JSON file.
--include-jars <DIR>[]Directories containing bundled .jar archives.
--include-node-modules <DIR>[]Directories containing bundled node_modules/.

taint Options

FlagDefaultDescription
--sink <SYMBOL>RequiredTarget sink function or method (e.g. ObjectMapper.readValue).
--source <KIND>externalTaint source kind (only external supported in v1).
--depth <N>8Maximum caller hops to traverse upstream.
--taint-rules <PATH>—Path to custom YAML taint ruleset file.

Working with jq

# 1. Extract exploitability verdict and notes
rgctl -f json vuln analyze --osv ./advisory.json \
  | jq '{osv_id, exploitability, notes}'

# 2. Extract OpenVEX statements directly
rgctl -f json vuln analyze --osv ./advisory.json \
  | jq '.openvex.statements[] | {status, justification, impact: .impact_statement}'

# 3. Check matched dependencies
rgctl -f json deps check --osv ./advisory.json \
  | jq '.matched[] | {name, version, in_range, provenance}'

Related Documentation