Dependency Vulnerability Analysis¶
Version: 3.1
When you build a WSO2 product, wire in the dependency-vulnerability scans described below. Tool-by-tool tutorials are in the tools' own documentation, linked below. This page covers the policy, the CI wiring, and the triage workflow. The supply-chain framing (version pinning, lock files, manifest guards, release signing) is in Secure Coding Guide: Software Supply Chain Failures.
The rule¶
A known-vulnerable component never ships in a release. Either the vulnerable code path is not reachable from product code (documented and approved by a designated security reviewer for the product) or the component is upgraded / replaced / removed. A finding that is neither fixed nor formally accepted blocks the release. Defer is not an option.
When scans run¶
Configure your CI to run scans at three points:
- Every pull request: fast subset; build fails on any new high-severity finding.
- Daily on
main: full database refresh that surfaces findings discovered overnight. - Before every release: full scan with the latest vulnerability database, with the report attached to the release artifact.
CI owns the scan; manual runs are for local debugging only.
Tooling by stack¶
- Java (Maven, Ivy, Gradle): OWASP Dependency Check (Maven plugin reference, releases).
- Go:
govulncheckreading the Go vulnerability database. Call-path analysis means findings reflect what is actually reachable from product code, not every vulnerable function in the dependency graph. - JavaScript / npm:
npm auditorpnpm auditfor React portals and admin UIs shipped with WSO2 products.
Output format: use SARIF wherever the tool supports it. SARIF uploads cleanly to the GitHub Security tab and integrates with the SAST dashboard. See the SARIF specification and GitHub's SARIF support.
CI configuration¶
Java (Dependency Check Maven plugin)¶
Pin the plugin version in the parent POM. Recommended configuration:
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>10.0.4</version>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS>
<suppressionFiles>
<suppressionFile>dependency-check-suppressions.xml</suppressionFile>
</suppressionFiles>
<formats>
<format>HTML</format>
<format>SARIF</format>
</formats>
<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>
</configuration>
<executions>
<execution><goals><goal>check</goal></goals></execution>
</executions>
</plugin>
failBuildOnCVSS=7fails on High and Critical. Your build should aim for zero unsuppressed findings at this threshold.nvdApiKeyis required for non-throttled runs. Obtain a free NVD API key and store as theNVD_API_KEYCI secret.- Never use Maven version ranges (
[1.0,)),LATEST, orRELEASEin product POMs. Dependency Check can only assess what Maven resolves at build time; a moving version target makes findings unreproducible.
Go (govulncheck)¶
- name: govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck -format sarif ./... > govulncheck.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: govulncheck.sarif
govulncheck does not support a suppression file. Track accepted findings in the project's SECURITY.md using the same rationale-and-review pattern as Java. The Go security team's recommendation is to fix or upgrade rather than suppress.
For release-validation against a built binary: govulncheck -mode binary ./bin/your-binary.
JavaScript (npm audit / pnpm audit)¶
CI fails on any unsuppressed high or critical advisory. The audit allow-list lives in .audit-ignore.json with one entry per accepted advisory:
{
"frontend": {
"GHSA-XXXX-XXXX-XXXX": {
"reason": "Transitive of eslint; dev-only, not shipped to production bundle.",
"accepted_by": "[email protected]",
"review_date": "2026-04-01"
}
}
}
The PR-builder workflow reads the allow-list and exits non-zero on any high/critical advisory not present in it.
Lock files must be committed (package-lock.json, pnpm-lock.yaml). CI installs with npm ci / pnpm install --frozen-lockfile. Never use semver ranges (^, ~) in production package.json; pin exactly, lock the transitives.
Triage workflow¶
When a new finding lands above the severity threshold:
- Severity ≥ High blocks the merge until either fixed or formally accepted.
- Fix path: upgrade the dependency; or pin a known-good earlier version if upstream has no fixed release yet; or remove the dependency if the project no longer needs it.
- Accept path: a designated security reviewer for the product confirms that the vulnerable code path is not reachable from product code (or that impact is otherwise mitigated), records the rationale in the suppression file / allow-list, and links to the review record. Every acceptance carries an expiry date, and you re-review at expiry.
- Defer is not an option. A finding without an explicit accept is not "deferred", it is "blocking".
Suppression rules¶
- Each suppression names a specific CVE / GHSA, a specific GAV (or package), and a rationale that points at a review record.
- Wildcard suppressions on entire packages are not acceptable.
- Every accepted finding names the security reviewer who approved it and an expiry date.
Example Dependency Check suppression:
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
<suppress>
<notes>
CVE-2024-XXXXX applies only to the FTP server functionality of
commons-net, which this product does not use. Confirmed by
security review YYYY-MM-DD.
</notes>
<gav regex="true">^commons-net:commons-net:.*$</gav>
<cve>CVE-2024-XXXXX</cve>
</suppress>
</suppressions>
PR-builder steps recap¶
- Java:
mvn dependency-check:check(fails on CVSS ≥ 7). - Go:
govulncheck ./...(fails on any reachable vulnerability). - JS:
npm audit --audit-level=high(with.audit-ignore.jsonfilter).
Each step uploads its SARIF output for the GitHub Security tab and attaches the human-readable report as a build artifact.