Core ConceptsSupply Chain

Supply Chain

Dependencies, BOMs, API inventory and the third-party services your code calls.

You will finish this page able to answer the three questions auditors and incident responders actually ask: what is in our software, which parts are exploitable, and who else is in our dependency and vendor graph.


Dependencies

The Dependencies page inventories every package Argus found across your scanned projects.

Columns: Package · Ecosystem · Version · Reachability · License · CVE

Facets: Vulnerabilities (Has vulnerabilities / No vulnerabilities) and Ecosystem (npm, pypi, maven, cargo, conan, gems).

Each row carries the installed version, the latest known version, whether the package is transitive, its license, its reachability verdict, and its vulnerability count. Open a dependency to see every finding attached to it and to act on them, assign, create a ticket, or hand a finding to the fix agent.

Export downloads CSV:

Name, Version, Latest, Ecosystem, Risk, Vulnerabilities, HasVulns, Transitive, Licenses, Project

25 rows per page.

What the inventory reflects

The inventory is the current state of your manifests, not a running total.

Every scan reconciles it. Packages the scan no longer finds are removed, so upgrading a dependency does not leave the old version behind, and a package you dropped stops appearing. Where a package is resolved from more than one source, a lockfile version always wins over a version range in a manifest, because a range is a floor and a lockfile is a record of what was installed.

Pruning fails closed. If a scan finishes without resolving any dependencies at all, Argus prunes nothing and keeps what it already had. A scanner that failed must never be able to empty your inventory. You will see the previous inventory unchanged rather than an empty page, and the scan itself will report the failure.

A package appearing at two versions is not necessarily an error. Nested dependency trees legitimately install two versions of one package, and both are reported when both are genuinely present.

Which ecosystems get advisory matching

Argus parses these lockfiles and matches them against OSV.dev:

package-lock.json · yarn.lock · pnpm-lock.yaml · requirements.txt · requirements-dev.txt · Pipfile.lock · go.sum · Gemfile.lock · Cargo.lock · composer.lock

Java and .NET packages appear in your SBOM (via the SBOM generator) and in container scan results, but Argus does not yet parse pom.xml, Gradle files or .csproj for advisory matching.

Reachability on dependencies

Every dependency finding is classified reachable, unreachable or unknown. This is the single biggest noise reduction available in SCA, an unreachable CVE stays visible but sinks to the bottom of your priority order. See Concepts.

Malicious packages and typosquats

Argus screens every dependency against OSV's malicious-package feed, and compares its name against a curated table of popular packages. A name one edit away from a popular package, the classic typosquat, and the slopsquat names AI assistants hallucinate, is flagged.

To stop a bad package before it is installed, wire the scan_package tool into your AI assistant. See Integrations.

Checking provenance

Argus can check whether a published package carries build provenance and a SLSA attestation:

curl -sS -X POST "$ARGUS_URL/api/graphql" \
  -H "Authorization: Bearer argus_live_xxx" \
  -H "content-type: application/json" \
  -d '{"query":"{ CheckProvenance(package: "sigstore", ecosystem: "npm") { supported hasProvenance signed slsaLevel builderId sourceRepo predicateTypes summary } }"}'

Bills of materials

The BOMs page holds every bill of materials Argus generated, one per project per scan.

Tabs: All · SBOM · AIBOM · CBOM · HBOM · QBOM Columns: Project · Type · Standard · Components · Updated · Export

What Argus generates

BOMStatusWhat is in it
SBOMGenerated on every scanThe full component inventory of the repository, name, version, purl, license, scope, and genuine artifact hashes recovered from lockfile integrity data. Emitted as CycloneDX 1.6 and SPDX 2.3
CBOMGenerated when cryptography is detectedOne component per detected cryptographic algorithm family, AES, RSA, SHA family, ECDSA, Diffie-Hellman, MD5, each with the real file:line locations where it was found, and NIST post-quantum classification. Detection is source-pattern based, so treat it as an inventory, not an exhaustive crypto audit. Marked beta
QBOMGenerated when quantum-vulnerable cryptography is detectedThe strict subset of the CBOM that is quantum-vulnerable, RSA, ECDSA and Diffie-Hellman. Adds a quantum-readiness verdict, a harvest-now-decrypt-later exposure flag, and references to FIPS 203/204/205. Marked beta
HBOMGenerated when infrastructure-as-code is presentContainer and cloud-infrastructure inventory derived from your Terraform resources, Docker Compose services, Dockerfile base images and Kubernetes workloads. This is deployed infrastructure, explicitly not physical hardware
AIBOMComing soonNot yet produced as a reliable AI/ML model inventory. Do not rely on it

A specialised BOM is only written when it has content, an empty CBOM or HBOM is omitted rather than padded with a zero-component record.

Exporting

Every BOM offers the same five formats:

FormatFile
CycloneDX JSON<project>-<subtype>-cyclonedx.json
CycloneDX XML<project>-<subtype>-cyclonedx.xml
SPDX JSON<project>-<subtype>-spdx.json
SPDX Tag-Value<project>-<subtype>.spdx
CSV<project>-<subtype>-components.csv

The CSV columns are Name, Version, Type, PURL, License, Scope, Hash.

Export from the API:

curl -sS -X POST "$ARGUS_URL/api/graphql" \
  -H "Authorization: Bearer argus_live_xxx" \
  -H "content-type: application/json" \
  -d '{
    "query": "query Export($id: ID!, $f: String!) { ExportBOM(id: $id, format: $f) { filename mimeType content } }",
    "variables": { "id": "<bom-id>", "f": "cyclonedx-json" }
  }'

Valid format values: cyclonedx-json, cyclonedx-xml, spdx-json, spdx-tv, csv.

Comparing two BOMs

Use Compare on the BOMs page, or the API, to diff two BOMs and see what was added, removed and version-bumped:

{
  GetBOMDiff(bomId1: "<older>", bomId2: "<newer>") {
    added removed updated unchanged
    components { name changeType oldVersion newVersion }
  }
}

Importing a BOM

Bring in a BOM produced elsewhere, a vendor's SBOM, or one from a build system Argus does not scan:

mutation {
  ImportBOM(
    projectId: "<project-id>"
    content: "<the BOM document as a string>"
    format: "cyclonedx"
    bomSubtype: "SBOM"
  ) { id project components bomType status }
}

bomSubtype accepts SBOM, CBOM, QBOM, HBOM, AIBOM.


VEX

VEX (Vulnerability Exploitability eXchange) tells downstream consumers which CVEs actually affect your product. Argus drives its VEX statements from reachability, so an unreachable dependency CVE is published as not_affected with a proper justification instead of alarming everyone who consumes your SBOM.

Generating VEX

BOMs → VEX ▾ → Generate VEX, or:

curl -sS -X POST "$ARGUS_URL/api/graphql" \
  -H "Authorization: Bearer argus_live_xxx" \
  -H "content-type: application/json" \
  -d '{
    "query": "query Vex($p: ID!, $f: String) { GenerateVex(projectId: $p, format: $f) { filename mimeType content } }",
    "variables": { "p": "<project-id>", "f": "openvex" }
  }'

Formats: openvex (OpenVEX v0.2.0, the default) and cyclonedx-vex (CycloneDX 1.6 VEX).

How a finding maps to a statement:

FindingVEX statusJustification
Marked false positivenot_affectedcomponent_not_present
Resolved / fixedfixed-
Unreachablenot_affectedCode-not-reachable, with the evidence string
Open and reachableaffected-

VEX statements key on a vulnerability identifier, CVE-…, GHSA-…, GO-…, PYSEC-…, RUSTSEC-… or OSV-…. Findings without one are omitted.

Importing VEX

BOMs → VEX ▾ → Import VEX consumes an upstream VEX document and suppresses the CVEs its supplier has declared not-affected:

mutation {
  ImportVex(projectId: "<project-id>", content: "<vex document>") {
    statements matchedCves suppressed message
  }
}

Third-party inventory

The Third Parties page lists the external services your code actually calls, discovered from your source during scanning.

KPIs: Vendors tracked · Critical risk · With vulnerabilities · Data sharing

Filters: Risk level (Critical / High / Medium / Low) · Classification (Analytics, Authentication, CDN, Payment, Communication, Storage, AI/ML, Security, External API) · Projects

Each vendor card opens a detail sheet with Overview, API Endpoints and Projects tabs.

Read the banner. This page is detected services, not a security assessment. The vendor, its category and the projects that call it come from your scanned code and are real. TLS version, authentication, data-sharing and vulnerability status are not independently verified and read Not assessed. "0 with vulnerabilities" means "not evaluated", not "clean".

Export downloads CSV: Vendor, Category, Risk, Endpoints, DataShared, AuthRequired, TLS, HasVulns, Projects, LastSeen.


API inventory

Scanning also extracts the HTTP routes your own code exposes. The API Inventory page lists them.

Columns: Method · Endpoint · Project · Auth · Category · PII · Last seen · Risk

Filters: HTTP Method (GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD) · Risk · PII (Contains PII) · Authentication (Authenticated, Open / no auth, Not determined). Counts are computed over the whole result set, not just the visible page.

The three authentication states

Argus reports what it can prove, and says so when it cannot:

StateMeaning
AuthenticatedAn auth construct was found that is attributable to this specific route.
Open / no authThe route was analysed and no authentication was found. This is a finding.
Not determinedAuthentication machinery exists in the file, but Argus could not tie it to this route. Not a finding, an unanswered question.

Why the third state matters. Before 2026-08-07 the inventory had only two buckets, and anything not provably authenticated was labelled Open / no auth. On a live estate of 447 endpoints that reported 383 as unauthenticated; the true figure was 96, with 287 simply undetermined. A scanner that cannot distinguish "I checked and it is open" from "I could not check" produces false alarms at scale, and a queue full of them is a queue nobody works.

Risk scoring is deliberately more conservative than the label. An endpoint whose auth is undetermined is still scored as though it were open, because erring toward higher risk is the right default for a security tool. So you may see a Not determined endpoint carrying a High or Critical risk. That is intended. The risk assumes the worst; the label refuses to assert something unproven.

Open an endpoint to see:

  • Overview: risk level, category, auth, PII, third party, last seen, status, namespace
  • Remediate: the findings linked to this endpoint, each with Assign to Agent, Assign and Create ticket

Bulk actions: Send to Jira (creates tickets for the selected endpoints' linked findings) and Export selected.

Export downloads CSV: Method, Endpoint, Authenticated, PII, Risk, ThirdParty, Category, Status, Project, LastSeen.

25 rows per page.


Compliance evidence packs

Argus assembles a framework-mapped evidence pack from your project's BOMs and its latest completed scan. Everything in the pack is derived from scan data. Nothing is asserted.

FrameworkKey
CERT-InCERT-In (default)
US Executive Order 14028EO-14028
NIST SSDFNIST-SSDF
EU Cyber Resilience ActEU-CRA

Download from Compliance, or over REST:

curl -sS "$ARGUS_URL/api/compliance/<project-id>?framework=EU-CRA&download=1" \
  -H "Authorization: Bearer argus_live_xxx" \
  -o evidence-pack.json

Drop &download=1 to get the same JSON inline. The same data is available over GraphQL as GenerateCompliancePack(projectId, framework).

A pack contains the BOM inventory across all subtypes, a scan attestation naming the tools that ran and the findings they produced, and the per-control mapping with its evidence.


SARIF export

For any tool that speaks SARIF, GitHub code scanning, IDE plugins, your own dashboards:

curl -sS -X POST "$ARGUS_URL/api/graphql" \
  -H "Authorization: Bearer argus_live_xxx" \
  -H "content-type: application/json" \
  -d '{"query":"query($p: ID!){ ExportSarif(projectId: $p) { filename mimeType content } }","variables":{"p":"<project-id>"}}'

Resolved and false-positive findings are excluded.


Next