Scanning · SCA

SCA — Software composition analysis

Modern apps are mostly code you didn't write. SCA finds the known vulnerabilities hiding in your third-party dependencies — matched against public security advisories — so a flaw in a package you imported doesn't become a flaw in your product.

What SCA is

Software Composition Analysis inventories the open-source libraries your project depends on and checks each one against databases of known vulnerabilities (CVEs and GitHub advisories). Where SAST asks “is there a bug in the code we wrote?”, SCA asks “is there a known vulnerability in a package we imported?” Both matter — a huge share of real-world breaches trace back to an unpatched dependency.

How SCA works in CodesGuard

You don't run a separate SCA scan. In CodesGuard, SCA is derived from your SAST scans. When the SAST engine analyses your source, it also reads your dependency manifests and lockfiles (like composer.json, package.json and their lockfiles) and records every package your project depends on. The SCA module reads those results from each project's latest completed SAST scan and matches them against known advisories.

To populate SCA, run a SAST scan If the SCA page is empty, it will tell you: “SCA is derived from your SAST scans… Run a SAST scan to populate this view.” Just create and scan a SAST project — your dependencies are analysed automatically.

The dependency results arrive first: a project's dependency findings appear on the SCA page while its code analysis is still running, with a line saying so, and the scan's final results replace them when it ends. A scan whose code analysis stops (cancelled, or at a limit) keeps its complete dependency results.

The SCA overview

Open SCA from the sidebar. The top of the page summarizes your dependency risk across every project, and you can scope it to a single project or search by package, CVE or manifest.

app.codesguard.io/dashboard/sca
SCA overview with the metric tiles, severity mix, top packages by advisories and packages by ecosystem
SCA overview: vulnerable-package counts, the “Fix available” tally, and breakdowns by severity, package and ecosystem.

The metric tiles give you the headline numbers:

Vulnerable packages
Distinct dependencies with at least one advisory.
Critical + High
How many of those carry a serious advisory.
Advisories
Total advisories matched across your dependencies, with the package stores the advisory data covers and how many packages carry a known-exploited advisory.
Fix available
n/m: how many vulnerable packages have a single upgrade that clears every advisory. The version named is the smallest one above what you use that no advisory for that package covers. A package is not counted when no such version exists yet: its advisories say “No fix published”, or each names a fix that leaves another of its advisories open.
Ecosystems
How many package ecosystems your vulnerable dependencies come from, naming the three with the most.

Three charts break the same data down — Severity mix, Top packages by advisories, and By ecosystem (npm, Composer, Maven, Gradle, Python…). Clicking a segment filters the inventory table below it.

How sure each match is

Every finding says which version was checked and where that version came from, because that decides how much to trust it:

Locked
The exact version was read from your lockfile. This is what is installed, so the match is confirmed.
Pinned
The manifest declares an exact version, and no lockfile the scan read resolves it. Confirmed on what the manifest says.
Declared
The manifest declares a version range (such as ^7.4) and no lockfile the scan read pins it, so the installed version is unknown. The finding is marked for review and says, in the scan's own words, what would settle it: a lockfile when there is none, a refreshed lockfile when the one there does not list the package, or — when your lockfile is one the scan cannot read yet — that it is present but not read yet. Commit or upload the lockfile and rescan to turn the match into a confirmed one or make it disappear.

Packages your lockfile pulls in indirectly (a dependency of a dependency) are marked transitive; the finding points at the lockfile line, since there is no manifest line to edit.

The dependency inventory

Below the charts, every vulnerable dependency is listed in a sortable table. Each row shows the package with its ecosystem and the manifest file and line it is declared in, the installed version and how it was known, its first advisory ID (and how many more), the project it came from, and the upgrade that clears it. On a phone the rows stack and show the manifest's full path, so the same package in two manifests reads differently.

app.codesguard.io/dashboard/sca
SCA dependency inventory table with package, installed version, advisory, project and remediation columns
The dependency inventory, sortable by severity, priority, package, advisories, project or scan date.

Advisory detail

Expand any row to see the advisory in full. Each advisory ID opens the advisory's own public page:

app.codesguard.io/dashboard/sca
Expanded SCA advisory showing its linked ID, aliases, CWE and OWASP tags, CVSS, exploit likelihood and the upgrade that clears it
An expanded advisory: its identifier (a link to its public page) and aliases, classification tags, severity and exploit likelihood, and the upgrade that clears it.
  • Advisory identifiers — the GHSA ID and any aliases such as the CVE (e.g. GHSA-f2wf-25xc-69c9 / CVE-2022-31091). The ID links to the advisory's own reference page. A package that is itself malicious (not merely vulnerable) carries a Malicious package badge and is always rated critical — whatever score the advisory itself gives, because a compromised release is not a bug to upgrade past later. The advisory's own score is still shown beside it. An advisory whose ID has no public page of its own is shown as plain text, without a link.
  • Your own package — a package your upload defines itself (a workspace member, a module of the same build) or whose name is under your private registry scope is never checked against public advisories. When a package of the same name is published on the public registry, it is listed with a Your own package badge: the advisory shown is the public namesake's, and it means an install that resolves the name from the public registry instead of from yours would install someone else's code. The fix is to serve it only from your own registry. If another project in your upload installs the public package of that name from the registry, that copy is not yours: it is checked like any public package, and the bill of materials lists both — yours marked as your own.
  • Unmaintained package — a package its advisory source marks as no longer maintained carries an Unmaintained package badge. It is not a known flaw: nothing found in it will be fixed any more, so the fix is to replace it rather than upgrade it.
  • Versions — the version that was checked, how it was found (locked, pinned or declared), the versions the advisory says are fixed, and the single upgrade that clears every advisory on the package when one exists. When the manifest line does not write the version itself, the detail says where it is set — another line of your build files, or a parent or bill of materials — and for a transitive package, which packages pulled it in.
  • Summary — a plain description of the vulnerability.
  • CVSS — the advisory's score and its full CVSS vector as text, for CVSS version 3 and version 4, so the scoring can be checked rather than trusted.
  • Classification — CWE and OWASP tags.
  • Exploit data — how likely the advisory is to be exploited and whether it is known to be exploited (see Exploit data), and whether your code imports and calls the package. A dependency finding is a published advisory matched on a version, so it carries no false-positive score: it is not sent for AI review.
  • Affected functions — where the advisory's fix names the functions it changed, those functions, and the first place your code calls one of them through the package (for example src/render.js:3 · _.template). When no such call is found the advisory says so and stays listed: calls the package makes inside itself are not followed, so “no direct call” never clears a finding.
  • Remediation — the upgrade that clears every advisory on the package when one exists (“Upgrade to 7.4.5”), else each advisory's fixed versions, or “No fix published”.
  • Links — jump to the underlying SAST analysis or the project.
Provenance you can trust A Sources row shows exactly which SAST projects and scans the SCA view was derived from — so you always know where a dependency finding came from and how fresh it is. A project whose latest scan predates dependency details is marked rescan needed, and its dependency findings are still listed — each marked found before dependency details existed — rescan, with a link to it on the analysis page. A project that has never been scanned, or whose latest scan failed, says so instead of showing nothing.

Supply-chain signals

Beyond published advisories, each package is checked for signs that matter before a flaw is ever published. They are listed with the dependency findings, under their own family:

Possible typosquat
A package that is not among the 20,000 most-used packages of its registry and whose name is one character or separator away from a popular one (expres beside express). Your scan policy fails on it by default; check it is the package you meant.
Runs code when installed
A package that runs its own code at install time — an install script, a Composer plugin, a Python package built from source — with the permissions of whoever installs it.
End of life
A release line past the end of its support: it no longer gets security fixes, whether or not an advisory names it yet.
Behind the latest release
At the fetched tier, how many major releases a package is behind the newest one on its registry.

Container images

Upload a container image (a docker save archive, .tar) with your code and its root filesystem is read: the operating-system packages from its package database, matched against the distribution's own advisories for its release, and the language packages installed in it — Java archives, Python packages, Node packages, Go programs. At the fetched tier, a Dockerfile's runtime base image is pulled from its registry and read the same way.

Distributions checked
Debian, Ubuntu, Alpine, Red Hat Enterprise Linux (and UBI), Rocky Linux, AlmaLinux, SUSE Linux Enterprise, openSUSE Leap and Tumbleweed, and Azure Linux.
Anything else
An image of another distribution (Wolfi, Chainguard, Amazon Linux), or without a package database, is listed as read with its operating-system packages not checked, and why — never as a clean image.

Alerts and trends

Every hour, each project's stored dependencies are checked again against the newest advisory data — no upload. When that finds new vulnerable packages, the people and webhook you set under Organization → Dependency alerts are told: an e-mail summary, and a JSON delivery signed with your secret (X-CodesGuard-Signature). Choose the lowest severity that alerts, and whether a newly known-exploited advisory alerts whatever its severity. The Vulnerable packages tile shows the trend over the last weeks, and the Fix available tile how long fixes have taken.

Exploit data

Severity says how bad a vulnerability would be; exploit data says how likely it is to be used against you. Each advisory whose CVE is known to the two public sources below shows:

Known exploited
The CVE is in the U.S. CISA catalog of vulnerabilities exploited in the wild — shown as a Known exploited badge with the date it was added, and whether it is known to be used in ransomware campaigns.
Exploit likelihood
The FIRST.org Exploit Prediction Scoring System's probability that the CVE is exploited in the next 30 days, with its percentile among all scored CVEs and the date of the scores. An advisory with no score says Unscored — a score is never assumed.

Sort the list by Priority to see known-exploited advisories first, then the most likely to be exploited, then the most severe. The Exploit filter shows only known-exploited advisories, or those at or above a likelihood (10, 50 or 90 %), and the Advisories tile counts how many are known exploited. Your scan policy can fail scans on either fact. The exploit data is refreshed with the advisory data, and stored results are checked against it again without a new upload.

Licenses

Every package shows its license as the file that pins it declares it — “License: MIT” — written as an SPDX expression, or “License: unknown” with the reason: the file records no licenses (most manifests and some lockfiles), the lockfile entry declares none, or the declared text is not an SPDX license. A license is never guessed. npm and Composer lockfiles record licenses; for other ecosystems the license is shown as unknown with that reason.

With a license policy (Organization → License policy; part of the Enterprise plan), a package whose license you deny, or mark for review, becomes a license finding — “License denied by policy: <package>” — listed with your other findings. The policy has allow, review and deny lists of SPDX licenses, a rule for licenses on no list and one for unknown licenses, and whether packages used only to build or test are judged. Saving it judges your scans again at once. See Scan policy and License policy.

Stating impact (VEX)

Not every advisory on a package affects your product: the vulnerable function may never be called. State impact on an advisory records what it means for you, in the standard VEX vocabulary:

Not affected
With a justification — the vulnerable component or code is not included, the vulnerable code is never run, it cannot be reached by an attacker's input, or a mitigation is already in place. The advisory is hidden everywhere — the list, the totals, the report and the gate — while the statement is in force. A malicious package is never hidden.
Affected
With what is being done about it.
Fixed
The fix is in place.
Under investigation
You are looking into it.

A statement can expire (in 30, 90 or 180 days, or never). It is kept across scans until it expires — or until the advisory's fixed versions change, when the advisory comes back so you can look again. Withdraw it at any time. Owners, admins and pentesters can state impact; viewers see the statements but cannot make them. Download every statement in force as OpenVEX from the Export menu.

Exports

Export on the SCA page downloads, for the project and filters you have chosen:

Vulnerable list — CSV or JSON
The vulnerable dependencies as listed.
Bill of materials — CycloneDX or SPDX
Every package the project's latest scan read, vulnerable or not: name, version, its standard package identifier, its license, and whether it ships or is used only to build. A package of your own is marked as yours, so a reader does not take it for the public package of the same name. Choose a project first: a bill of materials is one project's packages.
VEX statements — OpenVEX
Every statement in force.
Dependency report — PDF
The dependency findings with their exploit data and your statements, ending with the scan's bill of materials.

A single scan's report, results file and bill of materials are also on the scan page — see SAST → Downloads.

What the scan did not check

What the dependency check does not cover CodesGuard checks the dependencies declared in your source code — the manifests and lockfiles in your upload — and the container images in it; when fetching is allowed it also reads the base image a Dockerfile names. It reports versions with a published vulnerability (an image's operating-system packages included), packages an advisory lists as malicious, your own package names that the public registry also publishes, look-alike names of popular packages, packages that run code when installed, packages marked unmaintained or past their end of life and — when fetching is allowed — major releases behind the latest, the versions of the CI workflow actions you use, and — with a license policy — licenses. Where an advisory's fix names the functions it changed, the finding says whether your code calls one of them; no call found never clears a finding. It does not inspect what an install script does, judge how a CI workflow is configured, or read an image held only in a registry. Every PDF report states the same boundary.

A clean page is only worth something if you know what was looked at. So the SCA page never says “Supply chain clear” unless every project is current and at least one package was actually checked against the advisory data — and when it does say it, it also says what that rests on: how many manifests and lockfiles were read, how many packages were checked, which ecosystems were read, and which ecosystems are present in your code but not read (for example a SUSE image's package database, which is listed as seen but not read). Below the inventory, a coverage block says, per ecosystem, how many packages were found in your dependency files, how many were checked, and — for every package that was not checked — why:

  • Version managed outside this file — a Maven or Gradle version that comes from a parent or a bill of materials the upload does not contain.
  • Version comes from a variable this scan could not resolve — a build file variable that no file in the upload assigns.
  • Version could not be read — a spec no version grammar can read, such as a git address, a local path or a development branch. The spec is shown as written.
  • No advisory data loaded for this ecosystem — the page also prints “<ecosystem>: 0 advisories loaded”, so an empty result for that ecosystem is never mistaken for a clean one.
  • Dependency file not read yet / a lockfile exists but is not read yet — a file we recognise but do not read (for example a Bun binary lockfile bun.lockb written by a Bun version whose layout is not read — those of Bun 1.0 and 1.1 are; upload the text bun.lock instead) is listed by name under unsupported files; the manifest beside it is still checked, and any range-based finding there names that lockfile as the thing that would settle it.
  • Dependency file could not be read — a manifest that is not well-formed; the reason is shown.
  • Dependency file not read — a manifest or lockfile the scan saw but did not open, listed by path with why: it sits in a folder the scan skips (tests, build output, translations), under a path your project is set to ignore, in a bundled third-party asset folder, or it is larger than the size the scan reads for one dependency file. A .gitignore never hides a dependency file, and a large repository never pushes one out of the scan. Installed copies of packages (node_modules/, vendor/) are not listed one by one; they are read through the lockfile that installed them.

Packages used only to build or test your project are checked too. By default, an ordinary vulnerability in such a package is left out of the findings and counted instead — the SCA page and the scan page say how many build-time advisories were hidden. A malicious package is always reported. To list every advisory on build and test packages, turn on Report build-time dependencies in full in the project's analysis settings; it applies from the next scan.

If the dependency analysis itself did not finish during a scan, the scan page and the scans list carry a “dependency analysis did not complete” warning and the SCA page treats that project as not checked, rather than reporting it clean. When any part of the analysis did not finish, the scan's gate reads “Gate not decided — analysis incomplete” instead of passing: what the unfinished part would have found is unknown. A scan whose finished parts already fail your scan policy still fails.

Where the advisory data comes from

Dependency advisory data © GitHub, Inc. and contributors (GitHub Advisory Database, CC-BY 4.0); © the Python Packaging Authority and contributors (PyPA Advisory Database, CC-BY 4.0); © the OSS-Fuzz project and contributors (OSS-Fuzz vulnerability database, CC-BY 4.0); © the Go Authors (Go Vulnerability Database, CC-BY 4.0), with additional records from public vulnerability databases; exploit data: Exploit Prediction Scoring System (EPSS) by FIRST.org, Inc.; Known Exploited Vulnerabilities Catalog by the U.S. Cybersecurity and Infrastructure Security Agency (CISA).

Container images are checked against the distributions' own advisory data: © Red Hat, Inc. (Red Hat security data, CC-BY 4.0); © Canonical Ltd. and contributors (Ubuntu Security Notices, CC-BY-SA 4.0); © AlmaLinux OS (AlmaLinux errata, MIT); © Microsoft Corporation (Azure Linux Vulnerability Data, MIT); and the Debian Security Tracker, the Alpine Linux security database, the Rocky Linux errata and the SUSE and openSUSE security advisories. A scan that checked an image's packages carries these credits in its own line.

Every state of the SCA page ends with a footer: what the latest scans read and checked, the date of the advisory data the scans used (“Advisory data as of <date>.”) — or “Advisory data date not recorded” when a scan predates that stamp — and the credit line above, always. When any project's scan could not use the configured advisory data and fell back to the copy built into the scanner, or used data older than the freshness limit, the footer names every such project, not only the one whose date is shown. Every PDF report states the same date sentence for its scan, with the credit line. Each advisory links to its own reference page, and shows its published and last-modified dates alongside its severity score.

Fixing dependency issues

  1. Prioritise

    Sort by Priority — known-exploited advisories first, then the most likely to be exploited, then the most severe; filter on whether your code uses the package.

  2. Apply the remediation

    Follow the advisory's upgrade guidance — bump the package to the patched version in your manifest.

  3. Re-scan

    Upload the updated code and run a new SAST scan. SCA refreshes from that latest scan, and the resolved advisories drop off. An advisory that does not affect you can instead be stated not affected.

Access & plans

SCA is available on plans that include it; when it's part of your plan it runs automatically alongside SAST at no extra step. If SCA isn't in your plan, the module appears locked with an upgrade prompt — see Billing & plans. A license policy is part of the Enterprise plan; on other plans the saved lists are kept and not applied.