Complete Hands-On Lab Guide: Fundamentals -> Basic -> Intermediate -> Advanced
Generation / verification date: 2026-10-03
Primary scanner: OWASP Dependency-Check 13.0.0
Primary build ecosystem: Java + Maven
Capstone: real intentionally vulnerable GitHub training repository
Audience: developers, DevOps engineers, DevSecOps engineers, application security engineers, security champions, trainers
Safety boundary: The labs intentionally use known-vulnerable dependency versions so students can observe real scanner findings. Do not deploy the vulnerable lab applications, expose them to untrusted input, or reproduce exploit payloads. This guide teaches identification, analysis, prioritization, remediation, reporting, and secure integration – not exploitation.
Learning Path
Fundamentals
-> Understand Dependencies
-> Identify Dependencies
-> Scan Dependencies
-> Find Vulnerabilities
-> Interpret CVE / CWE / CVSS / CPE
-> Analyze Risk with NVD / EPSS / KEV
-> Generate Reports
-> Manage Dependencies
-> Handle False Positives
-> Remediate
-> Integrate into Build and CI/CD
-> Apply Security Gates
-> Generate SBOMs
-> Add Supply-Chain Context
-> Work with VEX / Provenance / Reachability
-> Complete End-to-End Capstone
LAB ENVIRONMENT AND VERSION SNAPSHOT
Verified Tooling Snapshot – 2026-10-03
| Tool / Integration | Verified version or current interface used in this guide | Purpose |
|---|---|---|
| OWASP Dependency-Check | 13.0.0 | Primary SCA vulnerability scanner |
| Dependency-Check Maven plugin | 13.0.0 | Build and CI integration |
| Dependency-Check Gradle plugin | 13.0.0 | Referenced for Gradle users |
| Dependency-Check Docker image | owasp/dependency-check:13.0.0 | Reproducible CLI path |
| NVD integration | NVD API 2.0 | Vulnerability source used by Dependency-Check |
| Maven Versions Plugin | 2.22.0 | Find and apply dependency updates |
| Syft | 1.52.0 | CycloneDX / SPDX SBOM generation and license inventory |
| Grype | 0.119.0 | Complementary SBOM vulnerability scan |
| vexctl | 0.4.4 | OpenVEX create / validate workflow |
| CycloneDX | 1.7 current specification referenced | SBOM / VEX-capable standard |
| SPDX | 3.0.1 current specification; Syft lab emits SPDX JSON supported by Syft | SBOM / software metadata standard |
| GitHub Actions checkout | actions/checkout@v6 | CI checkout |
| GitHub setup-java | actions/setup-java@v4 | CI JDK setup |
| GitHub artifact upload | actions/upload-artifact@v4 | Preserve evidence |
| GitHub SARIF upload | github/codeql-action/upload-sarif@v4 | Publish SARIF to code scanning |
| GitHub artifact attestation | actions/attest@v4 | Provenance / attestation lab |
Important current Dependency-Check facts
- Dependency-Check 13.0.0 is the current release used by this manual.
- The Maven plugin requires Maven 3.8.1+ and Java 11+; the labs use JDK 17 for a stable classroom baseline.
- The first vulnerability-data population is much slower than later incremental updates. For operational CI, OWASP strongly recommends mirroring NVD data rather than depending on every build to populate directly from NVD.
- An NVD API key is strongly recommended for direct NVD API access. In CI, use the plugin setting
nvdApiKeyEnvironmentVariable; do not put the secret directly on a debug-logged command line. - Current Dependency-Check report formats are: HTML, XML, CSV, JSON, JUNIT, SARIF, JENKINS, GITLAB, and ALL.
- The CISA Known Exploited Vulnerabilities (KEV) updater/analyzer is enabled by default in current Dependency-Check configuration.
Workstation Prerequisites
Use macOS, Linux, Windows with WSL, or a training VM. Verify:
git --version
java -version
mvn -version
curl --version
jq --version
Recommended classroom baseline:
- Git
- JDK 17
- Maven 3.9.x (minimum required by Dependency-Check Maven plugin is 3.8.1)
curljq- A browser for HTML reports
- Docker optional
- Syft and Grype for SBOM labs
vexctlfor VEX lab
Install Dependency-Check CLI – macOS Homebrew option
brew update
brew install dependency-check
dependency-check --version
Install / run Dependency-Check – pinned Docker option
mkdir -p "$HOME/OWASP-Dependency-Check/data" odc-reports
docker pull owasp/dependency-check:13.0.0
docker run --rm --volume "$(pwd)":/src --volume "$HOME/OWASP-Dependency-Check/data":/usr/share/dependency-check/data --volume "$(pwd)/odc-reports":/report owasp/dependency-check:13.0.0 --version
Optional NVD API key
Request a key from NVD, then expose only the variable name to Maven:
export NVD_API_KEY='REPLACE_WITH_YOUR_KEY'
Do not commit this value.
Install complementary tools
macOS Homebrew:
brew install syft
brew tap anchore/grype
brew install grype
brew install vexctl
syft version
grype version
vexctl version
Official Anchore installer alternative:
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sudo sh -s -- -b /usr/local/bin
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sudo sh -s -- -b /usr/local/bin
Shared Lab Project
The first modules progressively build this safe local project. It contains a vulnerable dependency but the application only logs a fixed string. Do not feed it untrusted input.
Create the project:
mkdir -p sca-lab/src/main/java/lab
cd sca-lab
cat > pom.xml <<'EOF'
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>sca-lab</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>11</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<log4j.version>2.14.1</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
EOF
cat > src/main/java/lab/App.java <<'EOF'
package lab;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class App {
private static final Logger LOG = LogManager.getLogger(App.class);
public static void main(String[] args) {
LOG.info("SCA lab started");
}
}
EOF
mvn -B clean package
If the build succeeds, continue.
MODULE 1 – SCA FUNDAMENTALS
1. Open-Source Software (OSS)
1. What You Need to Know
Open-source software is software whose source is available under a license that permits specified forms of use, modification, and redistribution. In modern applications, OSS commonly arrives through package managers rather than by copying source code manually.
In this project, Apache Log4j is an OSS dependency resolved from Maven repositories.
2. Why It Matters in SCA
SCA starts with inventory. You cannot reason about vulnerabilities, licenses, or maintenance state until you know which third-party components and versions are present.
3. What We Will Do in the Lab
Identify the Log4j dependency declared in pom.xml, resolve it with Maven, and locate the downloaded package and metadata on disk.
4. Lab Setup
Start in the shared sca-lab directory. The initial pom.xml already declares log4j-core:2.14.1.
5. Step-by-Step Lab
👨🏫 Trainer Note
Do not discuss Log4Shell in depth yet. The learning goal is simply: “this application depends on external OSS, with an exact identity and version.”
💻 Student Action
- Open
pom.xmland locate thelog4j-coredependency. - Ask Maven to resolve and list dependencies.
- Locate the downloaded JAR and its POM in the local Maven repository.
- Inspect the package metadata and project URL/license metadata in the dependency POM.
💡 Explanation
Maven coordinates give a reproducible identity: group ID + artifact ID + version. The local repository proves where Maven materialized that external component on this workstation.
6. Commands
🖥️ Command
grep -n -A4 -B2 'log4j-core' pom.xml
mvn -B dependency:list
find "$HOME/.m2/repository/org/apache/logging/log4j/log4j-core/2.14.1" -maxdepth 1 -type f -print
grep -E '<name>|<url>|<license>|<artifactId>|<version>' "$HOME/.m2/repository/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.pom" | head -30
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
[INFO] The following files have been resolved:
[INFO] org.apache.logging.log4j:log4j-core:jar:2.14.1:compile
...
~/.m2/repository/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar
~/.m2/repository/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.pom
8. What to Look For
🔍 Check
Verify the exact GAV org.apache.logging.log4j:log4j-core:2.14.1, the JAR location, and metadata that points to the upstream project. The local Maven cache is not the package “owner”; it is only your resolved copy.
9. Practical Exercise
🧪 Exercise
Choose one additional dependency already present transitively, locate its JAR under ~/.m2/repository, and write down its group, artifact, version, upstream URL, and license metadata.
10. Expected Result
✅ Success Criteria
You can identify at least one OSS component by exact coordinates, version, local artifact path, upstream metadata, and license metadata.
11. Key Takeaway
- SCA begins with component identity and version.
- Package-manager metadata is a primary inventory source.
- Local presence does not imply your organization authored the package.
2. Third-Party Dependencies
1. What You Need to Know
A third-party dependency is code your project obtains from another project/vendor. It can be open source or proprietary. Package managers make adding one easy, which is why dependency governance matters.
2. Why It Matters in SCA
Every added dependency expands the software you must track for vulnerabilities, licenses, provenance, maintenance, and transitive dependencies.
3. What We Will Do in the Lab
Add Apache HttpClient as a second direct dependency, confirm its version, build the project, and scan the resulting dependency list.
4. Lab Setup
Continue in sca-lab. We will insert org.apache.httpcomponents:httpclient:4.5.13 as a direct Maven dependency.
5. Step-by-Step Lab
👨🏫 Trainer Note
The point is not that HttpClient 4.5.13 is the “recommended” version. It is used here to make dependency-tree behavior easy to observe. Later labs teach updates.
💻 Student Action
- Insert HttpClient into the
dependenciesblock. - Build the project.
- List dependencies.
- Copy runtime dependencies into
target/dependencyso the CLI can scan a concrete directory.
💡 Explanation
A direct dependency is one explicitly requested by the application. Maven will also resolve any dependencies required by that component.
6. Commands
🖥️ Command
python3 - <<'PY2'
from pathlib import Path
p = Path('pom.xml')
s = p.read_text()
needle = ' </dependencies>'
block = """ <dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
"""
if 'httpclient</artifactId>' not in s:
s = s.replace(needle, block + needle)
p.write_text(s)
PY2
mvn -B clean package
mvn -B dependency:list
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
ls -1 target/dependency | head -30
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
[INFO] BUILD SUCCESS
...
httpclient-4.5.13.jar
httpcore-....jar
commons-codec-....jar
commons-logging-....jar
log4j-api-2.14.1.jar
log4j-core-2.14.1.jar
8. What to Look For
🔍 Check
Confirm that httpclient-4.5.13.jar appears and that additional JARs appeared even though you did not explicitly add them. Those additional JARs set up the next lab on transitive dependencies.
9. Practical Exercise
🧪 Exercise
Add one harmless library of your choice, run mvn dependency:list, then remove the dependency and prove it disappears after mvn clean package.
10. Expected Result
✅ Success Criteria
The project builds, the new dependency is visible with its exact version, and you can distinguish the explicit POM entry from files Maven resolved on its behalf.
11. Key Takeaway
- A single dependency declaration can pull in multiple components.
- SCA scope is larger than the lines manually added to a manifest.
3. Direct vs Transitive Dependencies
1. What You Need to Know
A direct dependency appears explicitly in your project manifest. A transitive dependency is required by another dependency and is brought in automatically.
For example:
Application
-> httpclient (direct)
-> httpcore (transitive)
-> commons-logging (transitive)
-> commons-codec (transitive)
2. Why It Matters in SCA
A vulnerability may exist in a library your team never typed into pom.xml. SCA must therefore inspect the resolved dependency graph, not only direct declarations.
3. What We Will Do in the Lab
Use Maven’s dependency tree to prove which components are direct and which are transitive.
4. Lab Setup
The shared project must contain both log4j-core and httpclient direct dependencies from Topics 1-2.
5. Step-by-Step Lab
👨🏫 Trainer Note
Ask students to predict the tree before running the command. This quickly exposes the common misconception that “if it is not in my POM, it is not my dependency.”
💻 Student Action
- Print the dependency tree.
- Filter the HttpClient branch.
- Compare indentation depth.
- Compare the tree with direct declarations in
pom.xml.
💡 Explanation
Maven tree indentation encodes dependency relationships. The first level under the project is direct; deeper levels are transitive unless separately declared.
6. Commands
🖥️ Command
mvn -B dependency:tree
mvn -B dependency:tree -Dincludes=org.apache.httpcomponents:httpclient,org.apache.httpcomponents:httpcore,commons-logging:commons-logging,commons-codec:commons-codec
grep -n '<dependency>' pom.xml
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
com.example:sca-lab:jar:1.0.0
+- org.apache.logging.log4j:log4j-core:jar:2.14.1:compile
| \- org.apache.logging.log4j:log4j-api:jar:2.14.1:compile
\- org.apache.httpcomponents:httpclient:jar:4.5.13:compile
+- org.apache.httpcomponents:httpcore:jar:...:compile
+- commons-logging:commons-logging:jar:...:compile
\- commons-codec:commons-codec:jar:...:compile
8. What to Look For
🔍 Check
Your exact transitive versions may differ if upstream metadata changes, but the hierarchy should show HttpClient below the application and several dependencies below HttpClient.
9. Practical Exercise
🧪 Exercise
Choose one transitive dependency and find which direct dependency introduced it. Then write the path in Application -> Direct -> Transitive form.
10. Expected Result
✅ Success Criteria
You can produce a dependency path for at least one transitive component and explain why removing the parent dependency would affect the child.
11. Key Takeaway
- Transitive dependencies are part of your risk surface.
- Dependency paths matter when deciding how to remediate a finding.
4. Dependency Trees
1. What You Need to Know
A dependency tree is the resolved graph produced by a package/build tool. It shows hierarchy, versions, scopes, and often conflict resolution.
2. Why It Matters in SCA
When Dependency-Check flags a component, the dependency tree answers an operational question: “How did this component get into our build?”
3. What We Will Do in the Lab
Generate full and filtered trees, save them as evidence, and map a Dependency-Check finding back to its dependency path.
4. Lab Setup
Create evidence/ in the shared project. Topic 6 will generate the first Dependency-Check report; for now save the tree so it can be compared later.
5. Step-by-Step Lab
👨🏫 Trainer Note
Keep tree output as evidence before remediation. It becomes extremely useful when demonstrating that an upgrade or exclusion removed a vulnerable transitive dependency.
💻 Student Action
- Generate a complete dependency tree and save it.
- Produce a verbose tree to expose omitted/conflict-resolved versions where Maven reports them.
- Search for Log4j and HttpClient.
- After Topic 6, return and map report dependencies to the paths recorded here.
💡 Explanation
The tree is build-tool truth about resolution. Dependency-Check report identity is scanner truth about what it analyzed. Comparing both helps detect missing coverage or unexpected components.
6. Commands
🖥️ Command
mkdir -p evidence
mvn dependency:tree | tee evidence/dependency-tree-before.txt
mvn dependency:tree -Dverbose | tee evidence/dependency-tree-verbose-before.txt
grep -E 'log4j|httpclient|httpcore|commons-codec|commons-logging' evidence/dependency-tree-before.txt
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
[INFO] com.example:sca-lab:jar:1.0.0
[INFO] +- org.apache.logging.log4j:log4j-core:jar:2.14.1:compile
[INFO] | \- org.apache.logging.log4j:log4j-api:jar:2.14.1:compile
[INFO] \- org.apache.httpcomponents:httpclient:jar:4.5.13:compile
...
8. What to Look For
🔍 Check
Confirm the saved file is non-empty, records exact versions, and distinguishes direct from transitive indentation. Later, the same file becomes part of the before/after comparison.
9. Practical Exercise
🧪 Exercise
Select any component from target/dependency, find it in the tree, and record its complete path from the application root.
10. Expected Result
✅ Success Criteria
For a component selected from disk, you can show exactly which direct dependency or application declaration caused it to be present.
11. Key Takeaway
- Trees explain dependency introduction paths.
- Save before/after trees as remediation evidence.
5. Dependency Management
1. What You Need to Know
Dependency management means controlling which packages and versions enter a build. Activities include adding, removing, updating, centralizing versions, resolving conflicts, and keeping repeatable manifests.
Maven does not provide a universal native lockfile equivalent to package-lock.json. Teams commonly pin explicit versions, use dependencyManagement, BOMs, repository controls, and reproducible build practices.
2. Why It Matters in SCA
Uncontrolled version drift makes findings difficult to reproduce and remediation difficult to prove.
3. What We Will Do in the Lab
Centralize selected versions in Maven properties, demonstrate an update, demonstrate removal, and verify resolution each time.
4. Lab Setup
Continue with sca-lab. Make a backup before changing the POM.
5. Step-by-Step Lab
👨🏫 Trainer Note
Avoid saying “latest is always safest.” Updating is a change that must be tested. Security remediation should use a vetted fixed version compatible with the application.
💻 Student Action
- Back up the manifest.
- Move HttpClient’s version into a property.
- Display available dependency updates using the current Versions Maven Plugin.
- Temporarily remove HttpClient and prove its branch disappears.
- Restore the POM for later labs.
💡 Explanation
Version properties centralize decisions. display-dependency-updates reports newer releases but does not prove compatibility, maintenance quality, or security by itself.
6. Commands
🖥️ Command
cp pom.xml evidence/pom-topic5-before.xml
mvn org.codehaus.mojo:versions-maven-plugin:2.22.0:display-dependency-updates
cp pom.xml /tmp/sca-lab-pom.xml
python3 - <<'PY2'
from pathlib import Path
p=Path('pom.xml')
s=p.read_text()
start=s.index(' <dependency>
<groupId>org.apache.httpcomponents</groupId>')
end=s.index(' </dependency>', start)+len(' </dependency>
')
p.write_text(s[:start]+s[end:])
PY2
mvn -B dependency:tree | tee evidence/tree-without-httpclient.txt
cp /tmp/sca-lab-pom.xml pom.xml
mvn -B dependency:tree | tee evidence/tree-restored.txt
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
[INFO] The following dependencies in Dependencies have newer versions:
...
[INFO] BUILD SUCCESS
# When removed, the httpclient branch is absent.
# After restore, it is present again.
8. What to Look For
🔍 Check
Look for the difference between “update available” and “update approved.” Also verify that removing a direct dependency removes its transitive branch unless another dependency still requires those components.
9. Practical Exercise
🧪 Exercise
Pick one dependency, identify an available update, record the current and candidate versions, and list one compatibility test you would run before accepting it.
10. Expected Result
✅ Success Criteria
You can add/remove/update a dependency deliberately, confirm the resulting tree, and explain why dependency management is more than blindly selecting the newest version.
11. Key Takeaway
- Pin and review dependency versions deliberately.
- Build-tool resolution evidence should accompany dependency changes.
MODULE 2 – VULNERABILITY FUNDAMENTALS
6. Vulnerable Dependencies
1. What You Need to Know
A vulnerable dependency is a component/version associated with one or more publicly disclosed vulnerabilities. Dependency-Check attempts to identify the component (often using evidence mapped to CPEs) and then associates known vulnerabilities.
2. Why It Matters in SCA
The practical SCA job is not merely “a CVE exists.” You need the affected component, version, severity, evidence, dependency path, and an actionable remediation path.
3. What We Will Do in the Lab
Run the first real Dependency-Check scan against the Maven project and generate all supported report formats. Then locate Log4j 2.14.1 and its vulnerabilities.
4. Lab Setup
Create reports/before. If you have an NVD API key, keep it in NVD_API_KEY. The command intentionally sets failBuildOnCVSS=11 so discovery succeeds even with critical findings; CVSS maximum is 10.
5. Step-by-Step Lab
👨🏫 Trainer Note
The first data population can be much slower than later runs. For classroom scale this is acceptable; for CI/enterprise use, teach the NVD-mirror pattern in Module 7.
💻 Student Action
- Run Dependency-Check 13.0.0 through the Maven plugin.
- Open the HTML report.
- Find
log4j-core-2.14.1. - Record at least one CVE, its severity/CVSS, CPE/GAV evidence, and references.
- Save the report as baseline evidence.
💡 Explanation
Dependency-Check does not patch anything. Its job here is detection and reporting. Remediation is a deliberate dependency update followed by a re-scan.
6. Commands
🖥️ Command
mkdir -p reports/before
mvn -B org.owasp:dependency-check-maven:13.0.0:check -Dformat=ALL -Dodc.outputDirectory=reports/before -DfailBuildOnCVSS=11 -DnvdApiKeyEnvironmentVariable=NVD_API_KEY
find reports/before -maxdepth 1 -type f -print | sort
jq '[.dependencies[].vulnerabilities[]?] | length' reports/before/dependency-check-report.json
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
[INFO] Analysis Started
...
[INFO] Analysis Complete
[INFO] BUILD SUCCESS
reports/before/dependency-check-report.html
reports/before/dependency-check-report.json
reports/before/dependency-check-report.xml
reports/before/dependency-check-report.csv
reports/before/dependency-check-report.sarif
...
8. What to Look For
🔍 Check
Open the HTML report and verify the scanner analyzed the expected Maven dependencies. For Log4j Core 2.14.1, current vulnerability data should include the well-known Log4Shell family, including CVE-2021-44228. Do not treat the total finding count as a constant; NVD records and analyzer logic can change.
9. Practical Exercise
🧪 Exercise
Choose one vulnerable component and create a four-column note: component/version, CVE, CVSS/severity, candidate remediation. Do not yet apply the change.
10. Expected Result
✅ Success Criteria
You have a real generated Dependency-Check baseline report and can trace at least one finding to a concrete dependency/version and remediation candidate.
11. Key Takeaway
- Scanner findings must map to real component versions.
- Baseline reports are evidence; preserve them before remediation.
- Counts are data-driven, not values to pre-fill in a training manual.
7. CVE
1. What You Need to Know
CVE is a public identifier for a specific disclosed vulnerability, such as CVE-2021-44228. The CVE identifier is a stable handle; richer analysis comes from records and references maintained by sources such as NVD and the CNA/vendor.
2. Why It Matters in SCA
CVE IDs let developers, scanners, advisories, ticketing systems, and external intelligence refer to the same vulnerability.
3. What We Will Do in the Lab
Extract a CVE from the Dependency-Check JSON report, then verify the same CVE using NVD’s API 2.0 and authoritative references.
4. Lab Setup
Topic 6 must have generated reports/before/dependency-check-report.json. curl and jq are required.
5. Step-by-Step Lab
👨🏫 Trainer Note
Use Log4Shell because it is real, well documented, and expected for the intentionally vulnerable Log4j version. Do not demonstrate exploitation.
💻 Student Action
- Search the report for
CVE-2021-44228. - Query NVD API 2.0 by CVE ID.
- Inspect descriptions, metrics, weaknesses, configurations/affected products, and references.
- Compare scanner data with NVD data rather than assuming the report is the sole authority.
💡 Explanation
Dependency-Check consumes vulnerability data and produces a useful local association. NVD remains an authoritative vulnerability enrichment source, while upstream vendor advisories are important for exact fix guidance.
6. Commands
🖥️ Command
grep -n 'CVE-2021-44228' reports/before/dependency-check-report.json | head
curl -s 'https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2021-44228' | jq '.vulnerabilities[0].cve | {id, published, lastModified, descriptions, metrics, weaknesses, references}' > evidence/CVE-2021-44228-nvd.json
jq '.id, .published, .lastModified' evidence/CVE-2021-44228-nvd.json
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
"CVE-2021-44228"
"2021-12-10T..."
"..."
# The full saved object contains descriptions, metrics, weaknesses and references.
8. What to Look For
🔍 Check
Verify the CVE ID matches in both places. Review NVD references and affected-product data; do not infer a fixed version from CVSS alone.
9. Practical Exercise
🧪 Exercise
Repeat the process for a second CVE in your generated report. Record one NVD reference and one upstream/vendor reference where available.
10. Expected Result
✅ Success Criteria
Two scanner CVEs have been independently verified against current authoritative data, with references recorded.
11. Key Takeaway
- CVE is an identifier, not a risk score.
- Verify important findings against authoritative records and vendor guidance.
8. CWE
1. What You Need to Know
CWE (Common Weakness Enumeration) describes classes of software weaknesses. A CVE is a specific vulnerability instance; a CWE is a category describing the underlying weakness pattern.
2. Why It Matters in SCA
CWE helps teams group recurring defect patterns and connect dependency findings to broader secure-development learning.
3. What We Will Do in the Lab
Locate CWE information associated with CVE-2021-44228 in NVD/Dependency-Check data and compare CVE vs CWE.
4. Lab Setup
Use the saved NVD object from Topic 7 and the Dependency-Check JSON baseline.
5. Step-by-Step Lab
👨🏫 Trainer Note
A vulnerability can have more than one weakness mapping, and mappings can change as records are enriched. Teach students to read the current data rather than memorizing one number.
💻 Student Action
- Extract the NVD weakness array.
- Search the Dependency-Check report around the CVE for
cwes. - Write a one-line distinction between the specific CVE and the weakness class(es).
💡 Explanation
The CVE answers “which disclosed vulnerability?” CWE answers “what kind of weakness?” They are related but not interchangeable.
6. Commands
🖥️ Command
jq '.weaknesses' evidence/CVE-2021-44228-nvd.json
jq '.. | objects | select(.name? == "CVE-2021-44228") | {name,cwes}' reports/before/dependency-check-report.json
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
[
{
"source": "...",
"type": "...",
"description": [
{"lang":"en","value":"CWE-..."}
]
}
]
# Dependency-Check may expose a cwes array for the vulnerability.
8. What to Look For
🔍 Check
Look for one or more CWE identifiers. Current NVD data for Log4Shell has accumulated multiple weakness mappings over time; treat the record as data, not a single timeless textbook label.
9. Practical Exercise
🧪 Exercise
Pick another CVE from the report, record its CWE mapping if available, and explain in one sentence why two CVEs may share a CWE.
10. Expected Result
✅ Success Criteria
You can distinguish a vulnerability instance from a weakness category and retrieve current mappings rather than guessing them.
11. Key Takeaway
- CVE = specific disclosed vulnerability.
- CWE = weakness category/classification.
- Mappings can be multiple and evolve.
9. CVSS
1. What You Need to Know
CVSS expresses technical severity using a score and vector. It is not a complete business-risk decision. This lab focuses on reading the score, severity, and vector – not manually calculating CVSS.
2. Why It Matters in SCA
A security gate often starts from CVSS, but prioritization should later add exploitation evidence, reachability/usage, exposure, and business context.
3. What We Will Do in the Lab
Extract CVSS data for two findings and compare their base scores/vectors.
4. Lab Setup
Use reports/before/dependency-check-report.json and NVD API data. Log4Shell currently has an NVD CVSS v3.1 base score of 10.0 with vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H.
5. Step-by-Step Lab
👨🏫 Trainer Note
Students should read vector dimensions at a high level. Do not turn this into a CVSS calculator exercise.
💻 Student Action
- Extract
severity,cvssv3, andcvssv4fields when present. - Select two findings with different scores.
- Compare attack vector, privileges required, user interaction, and impacts.
- Record which is technically more severe without claiming it must be fixed first in every business context.
💡 Explanation
CVSS helps normalize technical severity. It does not tell you whether vulnerable code is reachable or whether the asset is internet-exposed.
6. Commands
🖥️ Command
jq '[.dependencies[].vulnerabilities[]? | {name,severity,cvssv3,cvssv4}]' reports/before/dependency-check-report.json > evidence/cvss-findings.json
jq '.[] | select(.name=="CVE-2021-44228")' evidence/cvss-findings.json
jq '[.[] | select(.cvssv3.baseScore? != null)] | sort_by(.cvssv3.baseScore) | [.[0], .[-1]]' evidence/cvss-findings.json
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
{
"name": "CVE-2021-44228",
"severity": "CRITICAL",
"cvssv3": {
"baseScore": 10.0,
"attackVector": "NETWORK",
"attackComplexity": "LOW",
"privilegesRequired": "NONE",
"userInteraction": "NONE",
"...": "..."
}
}
8. What to Look For
🔍 Check
Note that the report severity and cvssv3.baseSeverity fields may differ in source semantics for some records. Use the source and vector shown in the report/NVD, not just a color badge.
9. Practical Exercise
🧪 Exercise
Pick two current findings and write a two-row comparison with CVE, CVSS version, base score, vector, and severity. Do not add your own composite score.
10. Expected Result
✅ Success Criteria
You can read and compare CVSS values and explain why CVSS is an input to prioritization rather than the entire prioritization decision.
11. Key Takeaway
- CVSS measures technical severity, not complete organizational risk.
- Preserve the vector/source when documenting a score.
10. CPE
1. What You Need to Know
CPE (Common Platform Enumeration) is a standardized naming scheme used to identify products/platforms. Dependency-Check analyzes evidence from a dependency and tries to identify one or more matching CPEs before associating NVD vulnerabilities.
2. Why It Matters in SCA
Incorrect product identification can produce false positives or false negatives. OWASP explicitly recommends checking whether the identified CPE is correct when reviewing reports.
3. What We Will Do in the Lab
Inspect the CPE and evidence shown for Log4j, then trace the relationship dependency -> CPE -> vulnerability.
4. Lab Setup
Use the HTML and JSON reports from Topic 6.
5. Step-by-Step Lab
👨🏫 Trainer Note
Emphasize CPE confidence/evidence. A CVE match is only as useful as the component identification behind it.
💻 Student Action
- Open the HTML report and expand
log4j-core. - Locate the CPE/evidence section.
- Extract CPEs from JSON.
- Compare the product identity in the CPE with the Maven GAV.
💡 Explanation
Package coordinates (GAV/PURL) and CPE are different identity systems. Dependency-Check uses collected evidence to bridge package identity to vulnerability applicability data.
6. Commands
🖥️ Command
jq '.dependencies[] | select(.fileName | test("log4j-core")) | {fileName,packages,vulnerabilityIds,vulnerabilities}' reports/before/dependency-check-report.json > evidence/log4j-dependency.json
jq '.. | .cpe? // empty' evidence/log4j-dependency.json | head -30
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
"cpe:2.3:a:apache:log4j:2.14.1:..."
...
# Exact representation can vary by report schema and analyzer evidence.
8. What to Look For
🔍 Check
The CPE vendor/product/version should logically correspond to the dependency you intended to scan. If it names an unrelated product, treat that as a likely identification problem requiring review/suppression or hints rather than blindly accepting the CVE.
9. Practical Exercise
🧪 Exercise
Select a second dependency. Verify whether Dependency-Check assigned a CPE and whether that CPE appears semantically correct.
10. Expected Result
✅ Success Criteria
You can explain and inspect the chain: concrete dependency evidence -> product identity/CPE -> matched vulnerabilities.
11. Key Takeaway
- Validate component identity before trusting a vulnerability association.
- CPE is one important bridge between dependency evidence and NVD applicability.
11. NVD
1. What You Need to Know
The U.S. National Vulnerability Database enriches CVE records with metadata such as CVSS, weakness information, references, and CPE-based applicability. Current Dependency-Check uses NVD API 2.0 for direct API updates and maintains a local vulnerability database/cache.
2. Why It Matters in SCA
Scanner quality depends on current vulnerability data. CI reliability also depends on how that data is updated and cached.
3. What We Will Do in the Lab
Inspect Dependency-Check’s update behavior, query NVD API 2.0 directly, and practice the update-only/purge workflow.
4. Lab Setup
An NVD API key is recommended for direct API use. Do not expose it in logs. The lab uses nvdApiKeyEnvironmentVariable for Maven.
5. Step-by-Step Lab
👨🏫 Trainer Note
For a single classroom workstation, direct updates are fine. For operational CI, use an NVD mirror/cache and update it separately from application builds.
💻 Student Action
- Run Dependency-Check
update-only. - Observe data-source timestamps in the report JSON.
- Query the NVD 2.0 endpoint directly for a known CVE.
- Learn
purgeonly as a troubleshooting/reset action, not something to run on every build.
💡 Explanation
Dependency-Check stores data locally so every scan does not download the full NVD corpus. Current configuration also updates other sources such as KEV and hosted suppressions.
6. Commands
🖥️ Command
mvn -B org.owasp:dependency-check-maven:13.0.0:update-only -DnvdApiKeyEnvironmentVariable=NVD_API_KEY
jq '.scanInfo.dataSource' reports/before/dependency-check-report.json
curl -s 'https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2021-44228' | jq '.totalResults, .vulnerabilities[0].cve.id'
# Troubleshooting/reset only - do not make this a normal CI step:
# mvn org.owasp:dependency-check-maven:13.0.0:purge
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
[INFO] Checking for updates
[INFO] ...
1
"CVE-2021-44228"
# Data-source timestamps are visible in the generated report metadata.
8. What to Look For
🔍 Check
Verify the report records when vulnerability data was checked/updated. A successful scan against stale cached data is not equivalent to a current scan; monitor update health in CI.
9. Practical Exercise
🧪 Exercise
Document how your organization would separate “vulnerability data refresh” from “application scan” so NVD availability is not a single-build bottleneck.
10. Expected Result
✅ Success Criteria
You can query NVD API 2.0 directly, explain Dependency-Check’s local update/cache role, and describe why a mirror/cache is recommended for operational CI.
11. Key Takeaway
- Current Dependency-Check integrates with NVD API 2.0.
- Data freshness and update reliability are part of SCA operations.
12. EPSS
1. What You Need to Know
EPSS (Exploit Prediction Scoring System) estimates the probability that a published CVE will be exploited in the wild during the next 30 days. It complements CVSS rather than replacing it.
Dependency-Check does not directly provide current EPSS as a primary report signal. In this lab we enrich a Dependency-Check CVE using FIRST’s authoritative EPSS API.
2. Why It Matters in SCA
A high-severity vulnerability with very low exploitation likelihood can deserve different operational treatment than a heavily exploited issue, depending on exposure and business context.
3. What We Will Do in the Lab
Take a CVE detected by Dependency-Check, fetch its current EPSS probability and percentile from FIRST, then review it beside CVSS and dependency context.
4. Lab Setup
Requires curl, jq, and at least one CVE from Topic 6.
5. Step-by-Step Lab
👨🏫 Trainer Note
EPSS changes over time. Never hardcode an EPSS value in the lab answer key. Fetch it when the student performs the exercise.
💻 Student Action
- Query FIRST for CVE-2021-44228.
- Save the response with the lab evidence.
- Record CVSS and EPSS side by side.
- Add only contextual facts: direct/transitive, runtime use, environment exposure. Do not invent a combined score.
💡 Explanation
CVSS and EPSS answer different questions. CVSS expresses technical severity; EPSS predicts near-term exploitation probability based on the EPSS model. Context determines action.
6. Commands
🖥️ Command
curl -s 'https://api.first.org/data/v1/epss?cve=CVE-2021-44228' | tee evidence/CVE-2021-44228-epss.json | jq '.data[0]'
jq -r '.data[0] | "CVE=\(.cve) EPSS=\(.epss) percentile=\(.percentile)"' evidence/CVE-2021-44228-epss.json
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
{
"cve": "CVE-2021-44228",
"epss": "<current probability returned by FIRST>",
"percentile": "<current percentile returned by FIRST>",
"date": "<current model date>"
}
8. What to Look For
🔍 Check
Confirm the response date/model data is current at execution time. Treat epss as a probability value and percentile as relative position in the scored CVE population.
9. Practical Exercise
🧪 Exercise
Query EPSS for two CVEs from your report and compare: CVSS, EPSS, direct/transitive status, and runtime context. State which one you would investigate first and why, without creating a proprietary score.
10. Expected Result
✅ Success Criteria
Your prioritization note clearly distinguishes CVSS, EPSS, and application context and cites live FIRST data.
11. Key Takeaway
- EPSS is dynamic; fetch it at decision time.
- Combine signals conceptually, not by inventing an unsupported formula.
13. Known Exploited Vulnerabilities (KEV)
1. What You Need to Know
CISA’s Known Exploited Vulnerabilities catalog contains vulnerabilities for which there is evidence of exploitation in the wild. Current Dependency-Check includes a KEV update/analyzer and uses CISA’s JSON feed by default.
2. Why It Matters in SCA
KEV status is a strong prioritization signal because it represents observed exploitation, but it still does not eliminate the need to check whether the affected component and vulnerable condition are actually present in your environment.
3. What We Will Do in the Lab
Verify whether a Dependency-Check CVE is in CISA KEV using both the scanner context and the authoritative CISA JSON catalog.
4. Lab Setup
Use CVE-2021-44228 as the primary demonstration. It is in CISA KEV. The CISA feed URL is also the default current Dependency-Check KEV URL.
5. Step-by-Step Lab
👨🏫 Trainer Note
Do not create a homegrown numeric risk score. The exercise is a signal comparison, not a scoring formula.
💻 Student Action
- Query the CISA KEV JSON feed for CVE-2021-44228.
- Save the matching entry.
- Inspect Dependency-Check report details for KEV context where shown.
- Compare two hypothetical decision rows: High CVSS/not KEV vs lower CVSS/KEV. Do not declare a universal winner; explain why KEV changes urgency.
💡 Explanation
KEV is evidence of real-world exploitation. A moderate/lower technical severity can still become operationally urgent when exploitation is confirmed and the organization is exposed.
6. Commands
🖥️ Command
curl -sL 'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json' | jq '.vulnerabilities[] | select(.cveID=="CVE-2021-44228")' | tee evidence/CVE-2021-44228-kev.json
jq '{cveID,vendorProject,product,vulnerabilityName,dateAdded,dueDate,knownRansomwareCampaignUse}' evidence/CVE-2021-44228-kev.json
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability data changes over time.
{
"cveID": "CVE-2021-44228",
"vendorProject": "Apache",
"product": "Log4j2",
"vulnerabilityName": "...",
"dateAdded": "...",
"dueDate": "...",
"knownRansomwareCampaignUse": "..."
}
8. What to Look For
🔍 Check
A non-empty object proves KEV membership at execution time. If you query another CVE and jq emits nothing, verify spelling and current catalog status before concluding it is not in KEV.
9. Practical Exercise
🧪 Exercise
Choose one CVE from your report that is not in KEV and compare it to the KEV finding using a table with: CVSS, KEV yes/no, EPSS, dependency path, runtime usage, exposure. Do not calculate a composite score.
10. Expected Result
✅ Success Criteria
You can independently verify KEV membership and explain why observed exploitation materially changes prioritization without pretending KEV alone decides business risk.
11. Key Takeaway
- KEV is an observed-exploitation signal.
- Dependency-Check now consumes KEV data, but authoritative CISA verification remains useful.
- Prioritization stays contextual.
MODULE 3 – SBOM
14. Software Bill of Materials (SBOM)
1. What You Need to Know
An SBOM is a machine-readable inventory of software components and related metadata. It lets teams answer “what components are in this artifact/application?” independently of a one-time vulnerability scan.
Dependency-Check does not directly replace a general-purpose SBOM generator. For SBOM creation this lab uses Syft; Dependency-Check remains the central vulnerability scanner.
2. Why It Matters in SCA
An SBOM allows teams to re-evaluate existing software when new vulnerabilities appear, even if the application was built weeks or months ago. It also supports supplier transparency and compliance workflows.
3. What We Will Do in the Lab
Generate an SBOM from the resolved JAR directory, inspect component names/versions/PURLs/licenses, then connect the inventory back to Dependency-Check findings.
4. Lab Setup
From sca-lab, refresh target/dependency and ensure Syft is installed.
5. Step-by-Step Lab
👨🏫 Trainer Note
Do not imply that generating an SBOM proves an application is secure. An SBOM is inventory/evidence; vulnerability interpretation is a separate process.
💻 Student Action
- Copy resolved Maven dependencies into a concrete directory.
- Generate a Syft native JSON inventory plus CycloneDX and SPDX SBOMs.
- Count components.
- Find Log4j in the SBOM.
- Match its version/PURL to the Dependency-Check report.
💡 Explanation
The same component can be represented by Maven GAV, PURL, CPE, CycloneDX component objects, or SPDX package objects. A mature program maintains the ability to reconcile these identities.
6. Commands
🖥️ Command
mkdir -p reports/sbom
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
syft scan dir:target/dependency -o syft-json=reports/sbom/sbom.syft.json -o cyclonedx-json=reports/sbom/sbom.cdx.json -o spdx-json=reports/sbom/sbom.spdx.json
jq '.artifacts | length' reports/sbom/sbom.syft.json
jq '.components[] | select(.name | test("log4j"; "i")) | {name,version,purl,licenses}' reports/sbom/sbom.cdx.json
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
<component count>
{
"name": "log4j-core",
"version": "2.14.1",
"purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
"licenses": [ ... ]
}
8. What to Look For
🔍 Check
Confirm the vulnerable version is present in both the SBOM and Dependency-Check baseline. If a component is present in one inventory and unexpectedly absent in the other, investigate scan scope/analyzer coverage.
9. Practical Exercise
🧪 Exercise
Select three SBOM components and map each to the dependency tree. Mark each as direct or transitive.
10. Expected Result
✅ Success Criteria
You have machine-readable SBOM artifacts and can reconcile at least three components with the Maven dependency graph.
11. Key Takeaway
- SBOM = reusable component inventory.
- SCA findings become more operationally useful when tied to stable component identities such as PURLs.
15. CycloneDX
1. What You Need to Know
CycloneDX is a BOM standard designed for software supply-chain use cases. Current CycloneDX 1.7 supports rich component, dependency, vulnerability, service, and VEX-related data structures.
2. Why It Matters in SCA
CycloneDX is widely consumed by vulnerability-management and supply-chain tools, making it a practical interchange format for SBOM workflows.
3. What We Will Do in the Lab
Generate CycloneDX JSON, inspect specification metadata and component objects, and validate that dependency versions are represented.
4. Lab Setup
Syft must be installed and target/dependency must contain resolved JARs.
5. Step-by-Step Lab
👨🏫 Trainer Note
The exact CycloneDX spec version emitted by Syft can be controlled by its output capabilities. Inspect specVersion rather than assuming it.
💻 Student Action
- Generate CycloneDX JSON.
- Inspect
bomFormat,specVersion, and component count. - Extract name/version/PURL/license fields.
- Save a compact component inventory for review.
💡 Explanation
CycloneDX uses component objects and Package URLs to make dependencies portable across tools. The BOM itself is not a vulnerability verdict.
6. Commands
🖥️ Command
syft scan dir:target/dependency -o cyclonedx-json=reports/sbom/sbom.cdx.json
jq '{bomFormat,specVersion,serialNumber,version,componentCount:(.components|length)}' reports/sbom/sbom.cdx.json
jq -r '.components[] | [.name,.version,(.purl // ""),((.licenses // [])|tostring)] | @tsv' reports/sbom/sbom.cdx.json > evidence/cyclonedx-components.tsv
head -20 evidence/cyclonedx-components.tsv
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
{
"bomFormat": "CycloneDX",
"specVersion": "<version emitted by Syft>",
"serialNumber": "urn:uuid:...",
"version": 1,
"componentCount": <number>
}
log4j-core 2.14.1 pkg:maven/...
8. What to Look For
🔍 Check
Verify bomFormat is CycloneDX and the application dependencies are represented with versions. Record the actual specVersion produced on your machine.
9. Practical Exercise
🧪 Exercise
Find log4j-core, httpclient, and one transitive dependency in the CycloneDX file and record their PURLs.
10. Expected Result
✅ Success Criteria
You can generate and read a CycloneDX SBOM and identify component identity/version information without a graphical UI.
11. Key Takeaway
- CycloneDX is a portable supply-chain inventory format.
- Inspect the generated spec version and component identities rather than assuming them.
16. SPDX
1. What You Need to Know
SPDX is a standard for exchanging software/package, licensing, provenance, security, and other supply-chain metadata. The current SPDX specification is 3.0.1, while tools may emit supported earlier serializations such as SPDX 2.3 JSON depending on their implementation.
2. Why It Matters in SCA
Different customers and ecosystems request different SBOM standards. Teams should be able to produce and consume the format their downstream process requires.
3. What We Will Do in the Lab
Generate an SPDX JSON SBOM with Syft, inspect its package structures, and compare its basic organization to CycloneDX.
4. Lab Setup
Use the same resolved dependency directory so CycloneDX and SPDX represent comparable input.
5. Step-by-Step Lab
👨🏫 Trainer Note
Do not claim one format is universally “better.” Compare what the generated artifacts actually contain and what your consumer accepts.
💻 Student Action
- Generate SPDX JSON.
- Inspect document/spec metadata.
- Extract packages and versions.
- Compare one Log4j record with the CycloneDX record.
💡 Explanation
CycloneDX typically exposes a components collection. SPDX 2.x-style JSON exposes packages and relationships. SPDX 3.x introduces a substantially expanded model; the emitted format depends on tool support.
6. Commands
🖥️ Command
syft scan dir:target/dependency -o spdx-json=reports/sbom/sbom.spdx.json
jq '{spdxVersion,dataLicense,name,packageCount:(.packages|length)}' reports/sbom/sbom.spdx.json
jq '.packages[] | select(.name | test("log4j"; "i")) | {name,versionInfo,licenseConcluded,licenseDeclared,externalRefs}' reports/sbom/sbom.spdx.json
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
{
"spdxVersion": "SPDX-...",
"dataLicense": "...",
"name": "...",
"packageCount": <number>
}
{
"name": "log4j-core",
"versionInfo": "2.14.1",
"licenseConcluded": "...",
"externalRefs": [ ... ]
}
8. What to Look For
🔍 Check
Confirm package identity and version are equivalent to the CycloneDX representation even though field names/structure differ.
9. Practical Exercise
🧪 Exercise
Create a two-column note for one package showing the CycloneDX fields and the SPDX fields that express name, version, package identity, and license.
10. Expected Result
✅ Success Criteria
You can generate/read an SPDX SBOM and explain the practical structural difference from the CycloneDX file produced from the same inputs.
11. Key Takeaway
- SBOM standards are interchange formats, not vulnerability scanners.
- Choose/validate formats based on consumer requirements and supported schema versions.
MODULE 4 – LICENSE & COMPLIANCE
17. License Compliance
1. What You Need to Know
Dependencies carry license obligations in addition to vulnerability risk. Automated tools can inventory declared/detected license metadata, but legal interpretation and policy decisions require an organizational compliance process.
2. Why It Matters in SCA
A component can be vulnerability-free and still be unacceptable under an organization’s distribution, attribution, copyleft, commercial, or contractual policy.
3. What We Will Do in the Lab
Extract license information from the generated SBOM and compare it with Maven POM metadata for a selected package.
4. Lab Setup
Syft SBOMs from Module 3 must exist.
5. Step-by-Step Lab
👨🏫 Trainer Note
State clearly: this lab identifies metadata and potential review needs; it is not legal advice and does not make final compatibility determinations.
💻 Student Action
- Inspect license metadata for all Syft artifacts.
- Filter the Log4j component.
- Inspect the upstream Maven POM license block as a second source.
- Record discrepancies/unknowns instead of silently treating them as permissive.
💡 Explanation
License scanners rely on metadata/files and can be incomplete. Compliance workflows need evidence, policy, and human/legal review for ambiguous cases.
6. Commands
🖥️ Command
jq -r '.artifacts[] | [.name,.version,((.licenses // [])|tostring)] | @tsv' reports/sbom/sbom.syft.json | tee evidence/licenses.tsv
grep -n -A12 '<licenses>' "$HOME/.m2/repository/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.pom"
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
log4j-core 2.14.1 [...license metadata...]
httpclient 4.5.13 [...license metadata...]
...
<licenses>
<license>
...
</license>
</licenses>
8. What to Look For
🔍 Check
Flag unknown, multiple, or ambiguous licenses for review. Do not convert “no detected license” into “public domain” or “no obligations.”
9. Practical Exercise
🧪 Exercise
Select five dependencies and classify their detected license status as identified, multiple/complex, or unknown/review required. Do not make legal compatibility conclusions.
10. Expected Result
✅ Success Criteria
A license inventory exists, and ambiguous metadata is routed to review rather than silently accepted.
11. Key Takeaway
- License visibility is part of SCA.
- Automated detection is evidence, not final legal advice.
18. Open-Source License Types
1. What You Need to Know
Common license families include permissive licenses such as MIT, Apache License 2.0 (SPDX: Apache-2.0), and BSD; stronger copyleft licenses such as GPL; library-oriented copyleft such as LGPL; file-level copyleft such as MPL; and public-domain/equivalent mechanisms such as CC0/Unlicense depending on jurisdiction/policy.
This is a practical identification lab, not a legal course.
2. Why It Matters in SCA
Different license types can trigger different notice, source-distribution, modification, linking, patent, and redistribution considerations.
3. What We Will Do in the Lab
Inventory the licenses actually present in the lab dependencies, normalize obvious SPDX identifiers where the tool provides them, and flag anything unknown.
4. Lab Setup
Use reports/sbom/sbom.syft.json and reports/sbom/sbom.spdx.json.
5. Step-by-Step Lab
👨🏫 Trainer Note
Do not tell students that all licenses in the same broad family have identical obligations. The goal is recognition and escalation.
💻 Student Action
- List unique detected license values.
- Compare them with SPDX license expressions where available.
- Build a small recognition table for MIT, Apache-2.0, BSD, GPL, LGPL, MPL, and public-domain/equivalent concepts.
- Mark which of those are actually found in this project.
💡 Explanation
SPDX identifiers improve machine readability, but a scanner may produce declared text, detected text, SPDX expressions, or unknown values depending on evidence.
6. Commands
🖥️ Command
jq -r '.artifacts[] | (.licenses // [])[]? | (.spdxExpression // .value // "UNKNOWN")' reports/sbom/sbom.syft.json | sort -u
jq -r '.packages[] | [.name,.versionInfo,(.licenseDeclared // "NOASSERTION")] | @tsv' reports/sbom/sbom.spdx.json | head -30
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
Apache-2.0
...
package-name version Apache-2.0
package-name version NOASSERTION
...
8. What to Look For
🔍 Check
Look for SPDX-style expressions and NOASSERTION/unknown cases. Unknown is a review state, not a license type.
9. Practical Exercise
🧪 Exercise
Create a seven-row table (MIT, Apache-2.0, BSD, GPL, LGPL, MPL, public-domain/equivalent) with one-sentence practical characteristics and whether your lab inventory contains an example.
10. Expected Result
✅ Success Criteria
You can recognize major license families, retrieve license metadata from real components, and know when to escalate uncertainty.
11. Key Takeaway
- License family recognition helps triage compliance review.
- Never equate automated detection with legal approval.
19. License Risk & License Conflicts
1. What You Need to Know
License risk is the possibility that dependency obligations conflict with project distribution/business requirements or organizational policy. The workflow is:
Dependency -> Detected/Declared License -> Project Distribution Model -> Policy/Compatibility Review
A technical tool can surface evidence; it cannot replace legal/compliance interpretation.
2. Why It Matters in SCA
Teams need a repeatable way to stop problematic dependency choices before release, just as they gate severe vulnerabilities.
3. What We Will Do in the Lab
Create a simple policy-review file, feed the lab license inventory into it, and flag components for review without issuing legal conclusions.
4. Lab Setup
Create an example project policy that treats unknown licenses and strong-copyleft licenses as REVIEW_REQUIRED for this fictional training organization. This is a workflow example, not a legal rule.
5. Step-by-Step Lab
👨🏫 Trainer Note
Make the distinction explicit: a policy flag is not a statement that a license is “bad” or legally incompatible.
💻 Student Action
- Generate the current license inventory.
- Create a training policy list.
- Search inventory for terms that trigger review.
- Record why a human review is required.
💡 Explanation
Organizations often express license acceptance rules in dedicated governance tools. Here we model the decision path with a simple file so students understand detection vs policy vs legal decision.
6. Commands
🖥️ Command
cat > evidence/license-review-policy.txt <<'EOF'
TRAINING POLICY ONLY - NOT LEGAL ADVICE
REVIEW_REQUIRED_IF: UNKNOWN
REVIEW_REQUIRED_IF: NOASSERTION
REVIEW_REQUIRED_IF: GPL
REVIEW_REQUIRED_IF: AGPL
REVIEW_REQUIRED_IF: LGPL
EOF
grep -Ei 'UNKNOWN|NOASSERTION|GPL|AGPL|LGPL' evidence/licenses.tsv || true
cat evidence/license-review-policy.txt
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
TRAINING POLICY ONLY - NOT LEGAL ADVICE
REVIEW_REQUIRED_IF: UNKNOWN
...
# Matching dependencies, if any, are candidates for review; no automatic legal verdict is produced.
8. What to Look For
🔍 Check
Confirm that the policy only creates a review queue. Check the project’s own license/distribution context before any compatibility conclusion.
9. Practical Exercise
🧪 Exercise
Invent a fictional dependency with a detected GPL-family license in a proprietary distribution scenario. Describe the questions you would send to legal/compliance rather than deciding compatibility yourself.
10. Expected Result
✅ Success Criteria
You can separate three layers: technical license detection, organizational policy flagging, and legal/compliance decision-making.
11. Key Takeaway
- License policy is organizational context, not universal law.
- Unknown/ambiguous license data should be visible and reviewable.
MODULE 5 – DEPENDENCY SECURITY
20. Dependency Vulnerability Scanning
1. What You Need to Know
Dependency vulnerability scanning identifies known vulnerabilities associated with software components. This is the core OWASP Dependency-Check lab: scan a concrete directory, scan a Maven project, generate multiple reports, and review remediation candidates.
2. Why It Matters in SCA
A mature SCA practice can scan both build metadata and produced/resolved artifacts. Comparing scopes helps detect blind spots.
3. What We Will Do in the Lab
Run both the Dependency-Check CLI/Docker directory scan and Maven project scan, then compare their component/finding coverage.
4. Lab Setup
Ensure target/dependency exists. Choose local CLI or Docker; both use current 13.0.0 syntax. Preserve a persistent data directory for Docker.
5. Step-by-Step Lab
👨🏫 Trainer Note
The Docker scan of JARs and the Maven plugin scan of the project are intentionally two views. Finding counts may not be identical because analyzers/evidence differ.
💻 Student Action
- Resolve/copy dependencies.
- Scan the directory with current CLI/Docker syntax.
- Scan the project with the Maven plugin.
- Generate HTML + JSON + SARIF.
- Review component, vulnerability, CPE, CVSS, KEV, and references.
- Identify a candidate remediation version from upstream/current package data.
💡 Explanation
Scanning is useful only when it ends in a review/remediation workflow. A generated report sitting unread is not a security control.
6. Commands
🖥️ Command
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
mkdir -p odc-reports/directory
docker run --rm --volume "$(pwd)/target/dependency":/src:ro --volume "$HOME/OWASP-Dependency-Check/data":/usr/share/dependency-check/data --volume "$(pwd)/odc-reports/directory":/report owasp/dependency-check:13.0.0 --project "SCA Directory Scan" --scan /src --out /report --format HTML --format JSON --format SARIF --prettyPrint --failOnCVSS 11
mvn -B org.owasp:dependency-check-maven:13.0.0:check -Dformat=HTML,JSON,SARIF -Dodc.outputDirectory=reports/project-scan -DfailBuildOnCVSS=11 -DnvdApiKeyEnvironmentVariable=NVD_API_KEY
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
# Directory scan output directory:
dependency-check-report.html
dependency-check-report.json
dependency-check-report.sarif
# Maven scan output directory:
dependency-check-report.html
dependency-check-report.json
dependency-check-report.sarif
8. What to Look For
🔍 Check
Compare dependency counts and vulnerability counts using jq. Investigate differences rather than treating them as scanner errors automatically.
9. Practical Exercise
🧪 Exercise
Run both methods on the same lab state. Record: number of dependencies analyzed, number with vulnerabilities, highest observed severity, and one remediation candidate for each scan.
10. Expected Result
✅ Success Criteria
You can perform repeatable CLI/directory and build-tool/project scans, generate reports, and turn at least one finding into a remediation plan.
11. Key Takeaway
- Scan scope matters.
- Multiple report formats support different consumers.
- Findings need ownership and remediation, not just generation.
21. Dependency Version Management – Before/After Remediation
1. What You Need to Know
The safest proof that an upgrade addressed a known vulnerable dependency is a controlled before/after workflow: preserve the baseline, update to a vetted version, rebuild, re-scan, and compare.
2. Why It Matters in SCA
Security remediation must be demonstrable. Merely changing a version string is insufficient if the resolved graph still contains a vulnerable version or the build breaks.
3. What We Will Do in the Lab
Upgrade the intentionally vulnerable Log4j 2.14.1 to the current Log4j 2.26.1 release verified for this guide, rebuild, re-run Dependency-Check, and compare findings.
4. Lab Setup
Ensure reports/before exists. Make a Git commit or backup before modification. Apache’s current Log4j release stream should be re-checked whenever this manual is reused later.
5. Step-by-Step Lab
👨🏫 Trainer Note
Avoid teaching the historical “2.17.1 is always the final safe version” shortcut. New vulnerabilities and fixes have appeared since Log4Shell. Use current vendor guidance and a currently supported release line.
💻 Student Action
- Preserve the before POM and reports.
- Replace the Log4j version property.
- Build and generate a new tree.
- Re-run Dependency-Check into a separate
reports/afterdirectory. - Prove
2.14.1is absent from the resolved tree. - Compare the specific CVE and overall counts without assuming they go to zero.
💡 Explanation
An upgrade can remove old CVEs and still introduce/retain other findings. Success means the targeted vulnerable version and targeted vulnerability condition are remediated and the application remains compatible.
6. Commands
🖥️ Command
cp pom.xml evidence/pom-before-remediation.xml
python3 - <<'PY2'
from pathlib import Path
p=Path('pom.xml')
s=p.read_text()
s=s.replace('<log4j.version>2.14.1</log4j.version>',
'<log4j.version>2.26.1</log4j.version>')
p.write_text(s)
PY2
mvn -B clean package
mvn dependency:tree | tee evidence/dependency-tree-after.txt
mkdir -p reports/after
mvn -B org.owasp:dependency-check-maven:13.0.0:check -Dformat=ALL -Dodc.outputDirectory=reports/after -DfailBuildOnCVSS=11 -DnvdApiKeyEnvironmentVariable=NVD_API_KEY
grep -n 'log4j.*2.14.1' evidence/dependency-tree-after.txt || true
grep -n 'CVE-2021-44228' reports/after/dependency-check-report.json || true
printf 'before vulnerabilities: '
jq '[.dependencies[].vulnerabilities[]?] | length' reports/before/dependency-check-report.json
printf 'after vulnerabilities: '
jq '[.dependencies[].vulnerabilities[]?] | length' reports/after/dependency-check-report.json
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
[INFO] BUILD SUCCESS
...
# No resolved Log4j 2.14.1 line should remain.
# The targeted CVE should no longer be associated with the upgraded Log4j dependency.
before vulnerabilities: <measured value>
after vulnerabilities: <measured value>
8. What to Look For
🔍 Check
Do not expect the entire report to become empty. Focus first on the targeted component/version/CVE, then review any remaining or newly discovered findings.
9. Practical Exercise
🧪 Exercise
Choose one additional outdated direct dependency. Use vendor/repository information plus the Versions Maven Plugin to propose and test an upgrade, then scan again.
10. Expected Result
✅ Success Criteria
The application builds, the targeted old version is absent, the targeted vulnerability is no longer reported for that component, and before/after evidence is preserved.
11. Key Takeaway
- Remediation is a verified state transition, not an edit.
- Re-scan after every dependency security change.
22. Outdated & Abandoned Dependencies
1. What You Need to Know
An outdated dependency has a newer release. An abandoned/unmaintained dependency is a broader maintenance-risk judgment based on signals such as release activity, unresolved security issues, maintainer status, archived repositories, or explicit deprecation.
Vulnerability scanning alone cannot prove that a package is healthy or maintained.
2. Why It Matters in SCA
A package can have no known CVEs today yet still be risky because nobody is maintaining it, security fixes are unlikely, or the ecosystem has moved to a replacement.
3. What We Will Do in the Lab
List available Maven updates, inspect project/repository metadata, and create a maintenance-review checklist.
4. Lab Setup
Use the current Maven Versions Plugin 2.22.0. Internet access is required to resolve newer versions.
5. Step-by-Step Lab
👨🏫 Trainer Note
Teach “outdated” and “abandoned” as separate labels. Newer release availability is machine-detectable; abandonment usually needs contextual evidence.
💻 Student Action
- Display dependency updates.
- Save the result.
- Inspect upstream project URL/SCM metadata for one dependency.
- Review official release/repository activity manually.
- Mark the dependency
maintained,uncertain, orreview requiredwith evidence.
💡 Explanation
Do not automate a conclusion from “last release date” alone. Stable libraries may legitimately release infrequently.
6. Commands
🖥️ Command
mvn org.codehaus.mojo:versions-maven-plugin:2.22.0:display-dependency-updates | tee evidence/dependency-updates.txt
grep -E 'newer versions|->|log4j|httpclient' evidence/dependency-updates.txt || true
# Inspect metadata already downloaded by Maven:
grep -E '<url>|<connection>|<developerConnection>|<tag>' "$HOME/.m2/repository/org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.pom" | head -30
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
[INFO] The following dependencies in Dependencies have newer versions:
[INFO] group:artifact ........................ old -> new
...
<url>...</url>
<connection>scm:git:...</connection>
8. What to Look For
🔍 Check
Separate findings into: vulnerable, outdated, and maintenance concern. One component can have any combination of those states.
9. Practical Exercise
🧪 Exercise
Pick one dependency with an available major-version upgrade. Review its upstream project status and answer: maintained? supported release line? migration cost? security reason to move?
10. Expected Result
✅ Success Criteria
Your maintenance review includes evidence and does not label a component abandoned solely because it is old.
11. Key Takeaway
- “No CVE” does not mean “low maintenance risk.”
- Update detection and maintainer-health review are complementary practices.
MODULE 6 – SUPPLY CHAIN SECURITY
23. Malicious Packages & Supply-Chain Attacks
1. What You Need to Know
SCA often focuses on known vulnerable packages, but a dependency can also be malicious from the start, become compromised, or receive a malicious update. Dependency-Check is not a malware-detector or complete package-trust system.
2. Why It Matters in SCA
A newly published malicious package may have no CVE and therefore evade vulnerability-only workflows.
3. What We Will Do in the Lab
Use a controlled, non-malicious exercise to practice verifying exact package coordinates, repository source, hashes, and unexpected dependency changes. We do not install look-alike or known malicious packages.
4. Lab Setup
Use the already downloaded Maven artifacts only. No malicious package is downloaded or executed.
5. Step-by-Step Lab
👨🏫 Trainer Note
The exercise should teach defensive verification, not how to publish or weaponize malicious packages.
💻 Student Action
- Record hashes for resolved JARs.
- Record exact GAV/PURL identities.
- Change nothing, regenerate the inventory, and compare hashes.
- Discuss which supply-chain attack classes a CVE scanner would not necessarily detect.
💡 Explanation
Hashes prove byte identity at a point in time; repository controls/signatures/provenance add stronger source assurance. A matching hash does not prove the software was benign in the first place.
6. Commands
🖥️ Command
find target/dependency -name '*.jar' -type f -print | sort | while IFS= read -r file; do
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$file"
else
shasum -a 256 "$file"
fi
done | tee evidence/dependency-sha256.txt
syft scan dir:target/dependency -o syft-json=evidence/supply-chain-inventory.json
jq -r '.artifacts[] | [.name,.version,(.purl // "")] | @tsv' evidence/supply-chain-inventory.json | sort > evidence/package-identities.tsv
head evidence/dependency-sha256.txt
head evidence/package-identities.tsv
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
<sha256> target/dependency/package-a.jar
<sha256> target/dependency/package-b.jar
...
package-a version pkg:maven/...
...
8. What to Look For
🔍 Check
Look for unexpected packages, version changes, repository/source changes, or byte changes between controlled builds. None of those automatically proves maliciousness, but they are investigation triggers.
9. Practical Exercise
🧪 Exercise
Write three examples of supply-chain compromise that a CVE-based scanner could miss initially: malicious new package, compromised maintainer release, and build-system tampering. For each, name one additional control.
10. Expected Result
✅ Success Criteria
You can explain Dependency-Check’s boundary and produce integrity/inventory evidence without downloading malware.
11. Key Takeaway
- Vulnerability scanning is not malware detection.
- Exact identity, provenance, repository governance, and integrity controls complement SCA.
24. Software Supply Chain Security
1. What You Need to Know
Software supply-chain security protects the end-to-end path from developer/source through dependency resolution, build, artifact creation, distribution, and deployment.
Developer
-> Source Code
-> Dependencies
-> Package Repository
-> Build System
-> CI/CD
-> Artifact
-> Deployment
2. Why It Matters in SCA
SCA is one control in a larger chain. A perfect dependency-vulnerability report cannot detect a stolen signing key, compromised build runner, malicious source commit, or altered artifact after scanning.
3. What We Will Do in the Lab
Map the lab’s current controls to each supply-chain stage and identify gaps.
4. Lab Setup
Use the dependency tree, Dependency-Check report, SBOM, hashes, and Git metadata already generated.
5. Step-by-Step Lab
👨🏫 Trainer Note
Keep the model concrete: ask “what evidence/control do we have at this stage?” instead of listing security buzzwords.
💻 Student Action
- Record current Git commit.
- Record dependency inventory and scan report.
- Record artifact hash.
- Record SBOM.
- Map each artifact/evidence item to the supply-chain stage it protects or observes.
💡 Explanation
SCA primarily addresses dependency inventory, known vulnerabilities, and some license visibility. Provenance/attestation and build integrity operate at different stages.
6. Commands
🖥️ Command
mkdir -p evidence/supply-chain
git rev-parse HEAD 2>/dev/null | tee evidence/supply-chain/source-commit.txt || true
cp evidence/dependency-tree-after.txt evidence/supply-chain/ 2>/dev/null || true
cp reports/after/dependency-check-report.json evidence/supply-chain/ 2>/dev/null || true
cp reports/sbom/sbom.cdx.json evidence/supply-chain/ 2>/dev/null || true
find target -maxdepth 1 -name '*.jar' -type f -exec shasum -a 256 {} \; | tee evidence/supply-chain/artifact-sha256.txt
find evidence/supply-chain -type f -maxdepth 1 -print
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
evidence/supply-chain/source-commit.txt
evidence/supply-chain/dependency-tree-after.txt
evidence/supply-chain/dependency-check-report.json
evidence/supply-chain/sbom.cdx.json
evidence/supply-chain/artifact-sha256.txt
8. What to Look For
🔍 Check
Identify at least one gap at each stage. Examples: developer identity, branch protection, repository allowlist, runner hardening, artifact signing, attestation verification, deployment policy.
9. Practical Exercise
🧪 Exercise
Create a stage/control/evidence/gap table for the eight stages in the model.
10. Expected Result
✅ Success Criteria
Your table clearly shows where Dependency-Check fits and where different supply-chain controls are needed.
11. Key Takeaway
- SCA is dependency-focused; supply-chain security is end-to-end.
- Evidence should follow the artifact from source to deployment.
MODULE 7 – SCA + DEVSECOPS
25. SCA in CI/CD Pipelines
1. What You Need to Know
CI/CD integration makes SCA repeatable on changes rather than dependent on a person remembering to scan. The pipeline should build, scan, retain reports, and optionally publish SARIF/security results.
2. Why It Matters in SCA
Fast feedback prevents vulnerable dependency changes from quietly reaching later environments and creates auditable evidence for every build.
3. What We Will Do in the Lab
Create a current GitHub Actions workflow using checkout v6, setup-java v4, Dependency-Check Maven plugin 13.0.0, upload-artifact v4, and CodeQL SARIF upload v4.
4. Lab Setup
The repository needs GitHub Actions enabled. Add an NVD API key as a GitHub Actions secret named NVD_API_KEY if using direct NVD access. For production-scale use, prefer an internal NVD mirror/cache.
5. Step-by-Step Lab
👨🏫 Trainer Note
The workflow uses an example CVSS threshold of 7.0 only to demonstrate a gate. Organizations must set policy based on their context.
💻 Student Action
- Create
.github/workflows/sca.yml. - Build on push and pull request.
- Run Dependency-Check.
- Preserve HTML/JSON/SARIF even if the gate fails.
- Upload SARIF to GitHub code scanning when permissions allow.
💡 Explanation
The scanner step may fail intentionally when findings meet the threshold. if: always() on evidence steps ensures reports are preserved for investigation.
6. Commands
🖥️ Command
mkdir -p .github/workflows
cat > .github/workflows/sca.yml <<'YAML'
name: SCA - OWASP Dependency-Check
on:
push:
pull_request:
jobs:
sca:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: maven
- name: Build
run: mvn -B -DskipTests package
- name: OWASP Dependency-Check
run: >-
mvn -B org.owasp:dependency-check-maven:13.0.0:check
-Dformat=HTML,JSON,SARIF
-Dodc.outputDirectory=target/dependency-check
-DfailBuildOnCVSS=7
-DnvdApiKeyEnvironmentVariable=NVD_API_KEY
- name: Upload Dependency-Check reports
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: dependency-check-reports
path: target/dependency-check/
- name: Upload SARIF
if: ${{ always() && hashFiles('target/dependency-check/dependency-check-report.sarif') != '' }}
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: target/dependency-check/dependency-check-report.sarif
YAML
cat .github/workflows/sca.yml
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
# GitHub Actions UI should show steps similar to:
Checkout
Set up Java
Build
OWASP Dependency-Check
Upload Dependency-Check reports
Upload SARIF
# If a finding meets the example CVSS 7 threshold, the scan step/job is expected to fail.
8. What to Look For
🔍 Check
Confirm report artifacts are retained after a failing scan. If SARIF upload fails on a forked PR, review GitHub token permission restrictions rather than disabling the security gate.
9. Practical Exercise
🧪 Exercise
Run once with failBuildOnCVSS=11 to observe inventory mode, then restore the example 7 gate and observe the policy failure on the vulnerable baseline branch.
10. Expected Result
✅ Success Criteria
The workflow scans every change, produces evidence, and demonstrates pass/fail behavior without losing reports when the gate triggers.
11. Key Takeaway
- CI makes SCA repeatable and auditable.
- Preserve evidence even on failure.
- Pin scanner/plugin versions for reproducibility.
26. SCA Policies, Gates & Remediation
1. What You Need to Know
An SCA policy converts findings into actions: fail, allow temporarily with a documented exception, suppress a validated false positive, or remediate. A suppression should not silently convert a real vulnerability into “safe.”
2. Why It Matters in SCA
Without policy, reports accumulate but engineering behavior does not change. Exceptions also need expiration/ownership so they do not become permanent debt.
3. What We Will Do in the Lab
Demonstrate a CVSS build gate, inspect a temporary training suppression mechanism, then remove the exception and complete the real remediation/re-scan workflow.
4. Lab Setup
Use a fresh branch or restore the vulnerable Log4j 2.14.1 baseline for the gate demonstration. Use suppression schema 1.4. The suppression below is explicitly a training exception demonstration, not a claim that Log4Shell is a false positive.
5. Step-by-Step Lab
👨🏫 Trainer Note
Prefer generating real false-positive rules from the HTML report’s Suppress button after validating the CPE/CVE mismatch. The temporary rule here exists only because a deterministic false positive cannot be guaranteed in every classroom run.
💻 Student Action
- Run with threshold 7 and observe failure.
- Create an expiring training suppression for one real finding.
- Re-run with the suppression to observe policy mechanics.
- Delete the training suppression.
- Apply the real dependency upgrade.
- Re-scan and verify the vulnerability is gone for the correct reason.
💡 Explanation
A suppression changes reporting/policy treatment; it does not change vulnerable bytes. Remediation changes the dependency itself.
6. Commands
🖥️ Command
# Inventory mode - never fails based on CVSS:
mvn -B org.owasp:dependency-check-maven:13.0.0:check -Dformat=HTML,JSON -Dodc.outputDirectory=reports/policy-inventory -DfailBuildOnCVSS=11 -DnvdApiKeyEnvironmentVariable=NVD_API_KEY
# Example gate - organization-specific policy:
set +e
mvn -B org.owasp:dependency-check-maven:13.0.0:check -Dformat=HTML,JSON -Dodc.outputDirectory=reports/policy-gate -DfailBuildOnCVSS=7 -DnvdApiKeyEnvironmentVariable=NVD_API_KEY
GATE_RC=$?
set -e
printf 'gate exit code: %s
' "$GATE_RC"
# For a real false positive, use the HTML report Suppress button to generate XML.
# Current suppression schema: dependency-suppression.1.4.xsd
# Do NOT suppress CVE-2021-44228 as remediation.
# Example invocation once a VALIDATED suppression file exists:
mvn -B org.owasp:dependency-check-maven:13.0.0:check -Dformat=HTML,JSON -Dodc.outputDirectory=reports/policy-with-suppressions -DfailBuildOnCVSS=7 -DsuppressionFiles=dependency-check-suppressions.xml -DfailBuildOnUnusedSuppressionRule=true -DnvdApiKeyEnvironmentVariable=NVD_API_KEY
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
gate exit code: <non-zero when a CVSS >= 7 finding is present>
# A validated suppression can remove a false-positive finding from gate evaluation.
# An unused suppression can be configured to fail the build, helping clean stale exceptions.
8. What to Look For
🔍 Check
For every suppression require: finding identity, justification, approver/owner, expiration where appropriate, and evidence that the component identification or exploitability assessment is valid.
9. Practical Exercise
🧪 Exercise
From the HTML report, open a finding’s Suppress dialog and inspect the generated XML. Do not commit the rule unless you can justify it as a false positive or approved exception. Explain why failBuildOnUnusedSuppressionRule=true is useful.
10. Expected Result
✅ Success Criteria
You can demonstrate inventory -> gate -> review -> exception/suppression mechanics -> remediation -> re-scan, while clearly distinguishing suppression from fixing the dependency.
11. Key Takeaway
- Gates enforce policy; thresholds are organization-specific.
- Suppressions need evidence, ownership, and lifecycle management.
- Remediation is preferred when the vulnerability is real.
27. SCA Reporting, Risk & Compliance
1. What You Need to Know
Different consumers need different report forms. HTML is human-friendly; JSON/XML are machine-friendly; CSV supports simple analysis; SARIF integrates with code-scanning platforms; JUnit can integrate with test-style gates; SBOM files support inventory and downstream vulnerability/compliance workflows.
2. Why It Matters in SCA
A professional SCA program must preserve evidence that developers, security, DevOps, auditors, and compliance teams can independently consume.
3. What We Will Do in the Lab
Generate all Dependency-Check formats, generate SBOMs, produce a small metrics file, and assemble an evidence directory.
4. Lab Setup
Use the remediated project state and keep before/after reports separate.
5. Step-by-Step Lab
👨🏫 Trainer Note
Do not fabricate summary numbers. Every metric in the evidence file must be calculated from the generated reports or left blank pending execution.
💻 Student Action
- Generate
ALLDependency-Check formats. - Generate CycloneDX/SPDX SBOMs.
- Calculate dependency and vulnerability counts from JSON.
- Copy dependency tree, update report, and hashes into evidence.
- Define which audience consumes which artifact.
💡 Explanation
Reports are security evidence only if they are tied to a specific source revision/build and their generation process is trustworthy.
6. Commands
🖥️ Command
mkdir -p evidence/final
mvn -B org.owasp:dependency-check-maven:13.0.0:check -Dformat=ALL -Dodc.outputDirectory=evidence/final/dependency-check -DfailBuildOnCVSS=11 -DnvdApiKeyEnvironmentVariable=NVD_API_KEY
syft scan dir:target/dependency -o cyclonedx-json=evidence/final/sbom.cdx.json -o spdx-json=evidence/final/sbom.spdx.json
REPORT=evidence/final/dependency-check/dependency-check-report.json
{
printf 'dependencies='; jq '[.dependencies[]] | length' "$REPORT"
printf 'vulnerabilities='; jq '[.dependencies[].vulnerabilities[]?] | length' "$REPORT"
printf 'critical='; jq '[.dependencies[].vulnerabilities[]? | select((.severity // "") == "CRITICAL")] | length' "$REPORT"
printf 'high='; jq '[.dependencies[].vulnerabilities[]? | select((.severity // "") == "HIGH")] | length' "$REPORT"
printf 'medium='; jq '[.dependencies[].vulnerabilities[]? | select((.severity // "") == "MEDIUM")] | length' "$REPORT"
printf 'low='; jq '[.dependencies[].vulnerabilities[]? | select((.severity // "") == "LOW")] | length' "$REPORT"
} | tee evidence/final/metrics.txt
find evidence/final -type f -print | sort
7. Expected Output
📋 Expected Output
The block below is representative output, not a claim that your machine will produce identical lines, ordering, scores, or counts. Vulnerability and package metadata changes over time.
dependencies=<measured>
vulnerabilities=<measured>
critical=<measured>
high=<measured>
medium=<measured>
low=<measured>
evidence/final/dependency-check/dependency-check-report.html
...
evidence/final/sbom.cdx.json
evidence/final/sbom.spdx.json
evidence/final/metrics.txt
8. What to Look For
🔍 Check
Validate values directly against the HTML report. If a vulnerability has an unknown/unscored severity, do not force it into low/medium/high; record it separately in a real compliance process.
9. Practical Exercise
🧪 Exercise
Create an audience matrix: developer, security, DevOps, compliance. Assign HTML/JSON/XML/SARIF/SBOM/metrics/evidence artifacts and explain the purpose of each.
10. Expected Result
✅ Success Criteria
A source/build can be accompanied by consistent, machine-readable and human-readable SCA evidence with numbers derived from real execution.
11. Key Takeaway
- Reports serve different consumers.
- Evidence must be generated from the actual build, not pre-filled examples.
- SBOM and vulnerability reports complement each other.
MODULE 8 – ADVANCED SCA
28. Dependency Confusion
1. What You Need to Know
Dependency confusion happens when a build intended to resolve an internal package can instead resolve a package with the same or a confusingly similar identity from a public repository. The risk comes from repository precedence, fallback behavior, naming, and weak controls around internal namespaces.
This lab does not publish any package to a public repository. We simulate an internal component only in the local Maven repository.
2. Why It Matters in SCA
Traditional SCA may identify vulnerabilities in the dependency that was resolved, but it does not by itself prove that the build resolved the intended publisher or repository. Resolution policy and provenance controls are required alongside vulnerability scanning.
3. What We Will Do in the Lab
Create a harmless local Maven artifact named like an internal package, resolve it locally, inspect Maven repository configuration, and identify controls that prevent public fallback for internal namespaces.
4. Lab Setup
You need Java, jar, Maven, and the shared lab directory. Maven Install Plugin 3.2.0 is used explicitly so the lab does not depend on whatever plugin version Maven happens to select.
5. Step-by-Step Lab
👨🏫 Trainer Note
Do not create or upload a look-alike package to Maven Central, npm, PyPI, or any other public registry. The objective is defensive repository-resolution analysis.
💻 Student Action
- Create an empty harmless JAR.
- Install it under an internal-looking Maven coordinate.
- Resolve the exact coordinate from the local repository.
- Inspect effective Maven settings and known repositories.
- Write controls that would prevent an unintended public source from satisfying internal coordinates.
💡 Explanation
The package identity is not enough. Teams also need confidence in the repository, publisher, expected digest, and build policy that produced the dependency.
6. Commands
🖥️ Command
mkdir -p advanced/dependency-confusion/empty evidence/dependency-confusion
jar --create \
--file advanced/dependency-confusion/payment-utils-1.0.0.jar \
-C advanced/dependency-confusion/empty .
mvn org.apache.maven.plugins:maven-install-plugin:3.2.0:install-file \
-Dfile=advanced/dependency-confusion/payment-utils-1.0.0.jar \
-DgroupId=com.acme.internal \
-DartifactId=payment-utils \
-Dversion=1.0.0 \
-Dpackaging=jar \
-DgeneratePom=true
mvn org.apache.maven.plugins:maven-dependency-plugin:3.11.0:get \
-Dartifact=com.acme.internal:payment-utils:1.0.0 \
-Dtransitive=false
mvn help:effective-settings > evidence/dependency-confusion/effective-settings.xml
mvn dependency:list-repositories | tee evidence/dependency-confusion/repositories.txt
sha256sum advanced/dependency-confusion/payment-utils-1.0.0.jar \
| tee evidence/dependency-confusion/payment-utils.sha256
On macOS, if sha256sum is unavailable:
shasum -a 256 advanced/dependency-confusion/payment-utils-1.0.0.jar \
| tee evidence/dependency-confusion/payment-utils.sha256
7. Expected Output
📋 Expected Output
The block below is representative only.
[INFO] Installing ...payment-utils-1.0.0.jar to .../.m2/repository/com/acme/internal/payment-utils/1.0.0/...
[INFO] BUILD SUCCESS
...
com.acme.internal:payment-utils:jar:1.0.0
8. What to Look For
🔍 Check
Inspect effective-settings.xml and repositories.txt for mirrors, internal repository managers, public repositories, and unexpected fallback paths. In production, internal coordinates should normally be governed by a controlled repository/proxy policy instead of trusting whichever repository answers first.
9. Practical Exercise
🧪 Exercise
Write a one-page defensive resolution policy containing at least: reserved internal namespace, controlled repository manager, public repository allowlist, exact versioning/lock strategy where available, provenance or digest validation, and CI monitoring for unexpected package sources.
10. Expected Result
✅ Success Criteria
You can demonstrate that com.acme.internal:payment-utils:1.0.0 was created and resolved locally, identify the repository configuration that controls resolution, and explain how a production build should prevent public fallback for internal identities.
11. Key Takeaway
- Dependency confusion is primarily a resolution and provenance problem.
- Never publish a malicious look-alike package for training.
- SCA must be complemented by repository and provenance controls.
29. Typosquatting
1. What You Need to Know
Typosquatting uses a package name that visually resembles a legitimate package: one character changed, omitted, repeated, or substituted. A developer can accidentally add the wrong dependency even when the package itself has no known CVE yet.
2. Why It Matters in SCA
A vulnerability scanner cannot guarantee that a newly published look-alike package is legitimate. Package identity review is therefore part of software supply-chain hygiene.
3. What We Will Do in the Lab
Compare an approved coordinate with a deliberately fake look-alike string, build a tiny coordinate allowlist check, and avoid installing the fake package.
4. Lab Setup
No network or package installation is required for the look-alike example.
5. Step-by-Step Lab
👨🏫 Trainer Note
The second coordinate below is only a text string for comparison. Do not search for, install, or execute an unknown look-alike package.
💻 Student Action
- Create an approved package list.
- Create a requested package list containing a look-alike.
- Compare the two lists.
- Fail the simple policy check when a coordinate is not approved.
- Relate this to dependency-review controls in CI.
💡 Explanation
An allowlist does not solve every software supply-chain problem, but it makes the identity check explicit and gives students a safe way to see the risk.
6. Commands
🖥️ Command
mkdir -p advanced/typosquatting evidence/typosquatting
cat > advanced/typosquatting/approved.txt <<'LIST'
org.apache.logging.log4j:log4j-core
org.apache.logging.log4j:log4j-api
LIST
cat > advanced/typosquatting/requested.txt <<'LIST'
org.apache.logging.log4j:log4j-core
org.apache.logging.log4j:log4j-c0re
LIST
printf '%s\n' '--- approved' '+++ requested'
diff -u advanced/typosquatting/approved.txt advanced/typosquatting/requested.txt || true
while IFS= read -r coordinate; do
if grep -Fxq "$coordinate" advanced/typosquatting/approved.txt; then
printf 'APPROVED %s\n' "$coordinate"
else
printf 'REVIEW %s\n' "$coordinate"
fi
done < advanced/typosquatting/requested.txt \
| tee evidence/typosquatting/package-review.txt
7. Expected Output
📋 Expected Output
APPROVED org.apache.logging.log4j:log4j-core
REVIEW org.apache.logging.log4j:log4j-c0re
8. What to Look For
🔍 Check
Look for single-character substitutions such as 0 versus o, inserted separators, changed organization/group names, or unexpected publishers. The check is about identity, not vulnerability score.
9. Practical Exercise
🧪 Exercise
Export the actual Maven dependency list from the lab project and compare every direct coordinate with a short approved direct-dependency file that you create yourself. Do not approve transitive packages automatically; record which direct package introduced them.
mvn dependency:list -DexcludeTransitive=true \
| tee evidence/typosquatting/direct-dependencies.txt
10. Expected Result
✅ Success Criteria
The safe look-alike is flagged for review without installing or executing it, and you can explain why zero known CVEs would not prove package legitimacy.
11. Key Takeaway
- Name similarity is a supply-chain signal, not a CVE signal.
- Validate publisher/group, repository, version, and provenance.
- Do not install suspicious packages merely to test them.
30. Package Hijacking
1. What You Need to Know
Package hijacking occurs when an attacker gains control of a legitimate package identity or release path, for example through a maintainer account compromise, stolen publishing token, abandoned package transfer, or compromised CI release credential.
2. Why It Matters in SCA
The coordinate can be legitimate while a new release is not. A CVE database may not know about the compromise when the malicious release first appears.
3. What We Will Do in the Lab
Inspect package metadata for repository/maintainer clues, record the exact artifact digest we received, and create a release-review checklist that would detect unexpected ownership or source changes.
4. Lab Setup
Use log4j-core already downloaded by Maven. No malicious version is used.
5. Step-by-Step Lab
👨🏫 Trainer Note
This is a monitoring and integrity lab. We do not simulate account compromise or publish any release.
💻 Student Action
- Locate the local JAR and POM.
- Record the exact SHA-256 digest.
- Inspect POM metadata such as project URL and SCM fields when present.
- Record the approved source/repository expectation.
- Define alerts for publisher, repository, signing, or release-process changes.
💡 Explanation
Version pins help reproducibility but do not prove publisher integrity. Digests and attestations bind the bytes; provenance binds the artifact to a build/source claim.
6. Commands
🖥️ Command
mkdir -p evidence/package-hijacking
LOG4J_JAR="$HOME/.m2/repository/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar"
LOG4J_POM="$HOME/.m2/repository/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.pom"
ls -l "$LOG4J_JAR" "$LOG4J_POM"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$LOG4J_JAR" | tee evidence/package-hijacking/log4j-core-2.14.1.sha256
else
shasum -a 256 "$LOG4J_JAR" | tee evidence/package-hijacking/log4j-core-2.14.1.sha256
fi
grep -E '<url>|<scm>|<connection>|<developerConnection>|<tag>' "$LOG4J_POM" \
| head -n 30 \
| tee evidence/package-hijacking/pom-source-metadata.txt
7. Expected Output
📋 Expected Output
Representative only:
<64-hex-digest> .../log4j-core-2.14.1.jar
<url>...</url>
<scm>...</scm>
The exact metadata varies by POM and version.
8. What to Look For
🔍 Check
Compare the expected group/artifact, source repository, project URL, release process, and digest with organizational records. Unexpected ownership, repository migration, sudden unsigned releases, or a new publication path deserve review even if a vulnerability scanner is clean.
9. Practical Exercise
🧪 Exercise
Create evidence/package-hijacking/release-review.md with the fields: package, approved registry, approved source repository, maintainers/owner source, expected signing/provenance mechanism, exact version, digest, release date, and reviewer decision.
10. Expected Result
✅ Success Criteria
You have an integrity record for the exact artifact and can list controls that reduce package-hijack risk: MFA, protected publisher tokens, least-privilege CI credentials, signed/provenanced releases, repository proxying/quarantine, and change monitoring.
11. Key Takeaway
- A legitimate package name does not guarantee a legitimate new release.
- Digest and provenance checks complement SCA.
- Monitor publisher and release-path changes.
31. Package Provenance
1. What You Need to Know
Package provenance answers: Where did this artifact come from, what source/build produced it, and can we verify that claim? At minimum, record package repository, version, source metadata, and integrity digest. Stronger workflows add signed build attestations.
2. Why It Matters in SCA
Dependency-Check finds known vulnerable components; it is not a general build-provenance verifier. Provenance adds confidence that scanned and deployed bytes came from an expected build process.
3. What We Will Do in the Lab
Record local package provenance evidence and review a current GitHub Actions attestation pattern for a built JAR and its SBOM.
4. Lab Setup
Use the shared Maven project. GitHub artifact attestations require a GitHub-hosted workflow context and appropriate repository permissions; the local portion of the lab works without GitHub.
5. Step-by-Step Lab
👨🏫 Trainer Note
Treat an attestation as a signed claim whose subject digest must match the artifact you verify. It does not automatically make unsafe source code safe.
💻 Student Action
- Build the lab JAR.
- Generate an SBOM.
- Hash the JAR and SBOM locally.
- Record source revision if the project is in Git.
- Inspect the GitHub Actions attestation example.
- If using GitHub, generate and verify attestations in your own repository.
💡 Explanation
The local evidence gives you reproducible identifiers. GitHub’s actions/attest@v4 can create build provenance and SBOM attestations for workflow-produced subjects.
6. Commands
🖥️ Command
mkdir -p evidence/provenance
mvn -B package -DskipTests
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
syft scan dir:target/dependency \
-o cyclonedx-json=evidence/provenance/sbom.cdx.json
ARTIFACT=target/sca-lab-1.0.0.jar
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$ARTIFACT" evidence/provenance/sbom.cdx.json \
| tee evidence/provenance/sha256.txt
else
shasum -a 256 "$ARTIFACT" evidence/provenance/sbom.cdx.json \
| tee evidence/provenance/sha256.txt
fi
git rev-parse HEAD 2>/dev/null | tee evidence/provenance/source-commit.txt || true
Current GitHub Actions pattern for an SBOM attestation:
permissions:
id-token: write
contents: read
attestations: write
steps:
- uses: actions/checkout@v6
# Build the artifact and generate a JSON SBOM before this step.
- name: Attest artifact and SBOM
uses: actions/attest@v4
with:
subject-path: 'target/sca-lab-1.0.0.jar'
sbom-path: 'evidence/provenance/sbom.cdx.json'
After the GitHub workflow creates the attestation, verify the downloaded artifact using GitHub CLI:
gh attestation verify target/sca-lab-1.0.0.jar --repo OWNER/REPOSITORY
7. Expected Output
📋 Expected Output
Local output contains real SHA-256 digests calculated from your files. The GitHub verification command, when run against a successfully attested artifact from your repository, should report a verified attestation for the artifact digest.
8. What to Look For
🔍 Check
Confirm that the attestation subject digest matches the exact artifact being verified and that repository/workflow/source identity is the one your policy expects. Do not treat a matching filename as proof.
9. Practical Exercise
🧪 Exercise
Add an evidence/provenance/provenance-record.md containing source commit, artifact path, artifact SHA-256, SBOM SHA-256, package manager, repository source, and attestation verification result if GitHub was used.
10. Expected Result
✅ Success Criteria
You can tie a build artifact and SBOM to exact digests and explain how signed attestations strengthen that chain of evidence.
11. Key Takeaway
- Provenance identifies the expected origin/build of an artifact.
- A digest identifies exact bytes; an attestation makes a verifiable claim about them.
- Dependency-Check complements provenance; it does not replace it.
32. VEX
1. What You Need to Know
VEX, Vulnerability Exploitability eXchange, communicates the status of a vulnerability in the context of a specific product. Common lifecycle states include under_investigation, affected, not_affected, and fixed, depending on the VEX format and statement semantics.
An SBOM says what components are present. VEX communicates a vulnerability impact assessment for the product using those components.
2. Why It Matters in SCA
A component can be present while the vulnerable behavior is not exploitable in a particular product. That conclusion must be evidence-based. VEX provides a machine-readable way to communicate the assessment without deleting the component from the SBOM.
3. What We Will Do in the Lab
Use current vexctl to create an OpenVEX under_investigation statement for the real Log4Shell finding used earlier in the lab, then validate and inspect the document.
4. Lab Setup
Install vexctl 0.4.4 or a later explicitly reviewed version. On Homebrew systems:
brew install vexctl
vexctl version
Dependency-Check does not directly provide a complete VEX authoring/consumption workflow. vexctl is the complementary tool in this lab.
5. Step-by-Step Lab
👨🏫 Trainer Note
Do not mark a real application not_affected just to silence a scanner. A not_affected statement needs a defensible justification or impact statement under OpenVEX rules.
💻 Student Action
- Create an
under_investigationstatement. - Validate it strictly.
- Inspect product, vulnerability, status, author/timestamp metadata.
- Explain what evidence would be required before publishing a later
not_affected,affected, orfixedstatus. - Explain how a consumer can understand the status lifecycle without deleting the component from the SBOM.
💡 Explanation
The lab uses the real CVE-2021-44228 finding and starts with the neutral under_investigation state so no unsupported not_affected claim is made. In production, the product PURL and vulnerability identifier must refer to the exact product/component context you assessed.
6. Commands
🖥️ Command
mkdir -p evidence/vex
vexctl create \
--product='pkg:maven/com.example/sca-lab@1.0.0' \
--vuln='CVE-2021-44228' \
--status='under_investigation' \
> evidence/vex/under-investigation.vex.json
vexctl validate --strict evidence/vex/under-investigation.vex.json
jq '.statements[] | {vulnerability, products, status}' \
evidence/vex/under-investigation.vex.json
CVE-2021-44228 is the real Log4j finding used in the earlier vulnerable-version lab. The initial under_investigation state does not make an unsupported claim that the application is unaffected. If a later assessment concludes not_affected, OpenVEX requires the appropriate evidence-backed justification or impact statement; if remediation removes/fixes the condition, publish a subsequent status for the correctly versioned remediated product.
7. Expected Output
📋 Expected Output
vexctl validate --strict should exit successfully for valid documents. jq should show an OpenVEX document containing the product PURL, vulnerability identifier, timestamp, and selected status.
8. What to Look For
🔍 Check
Check that status is tied to the correct product and vulnerability, the document is valid, and any not_affected statement has evidence plus a valid justification/impact explanation. A VEX assertion is not a substitute for investigation.
9. Practical Exercise
🧪 Exercise
Take one real finding from your Dependency-Check report and draft, but do not automatically approve, a VEX investigation record containing: product, CVE, initial status under_investigation, evidence owner, reachability observations, environment assumptions, and next decision date.
10. Expected Result
✅ Success Criteria
You can distinguish SBOM inventory from VEX exploitability/status communication and validate a machine-readable OpenVEX document.
11. Key Takeaway
- SBOM answers “what is present”; VEX communicates vulnerability status in product context.
not_affectedrequires evidence, not convenience.- Dependency-Check remains the finder; VEX is complementary assessment metadata.
33. Reachability Analysis
1. What You Need to Know
There is a major difference between “the dependency exists” and “the vulnerable code path is actually reachable from this application.” True reachability analysis usually requires source/bytecode call-graph analysis, runtime evidence, or specialized SAST/SCA capabilities.
OWASP Dependency-Check does not provide true code-level reachability analysis. It detects known vulnerabilities in identified dependencies; it should not be described as proving exploitability or call-path reachability.
2. Why It Matters in SCA
Presence-based findings are intentionally conservative. Reachability evidence can help prioritize work or support a VEX assessment, but it must not be confused with a dependency simply appearing in the graph.
3. What We Will Do in the Lab
Use simple source and bytecode inspection to demonstrate usage evidence for Log4j versus another dependency. This is a teaching approximation, not a full reachability engine.
4. Lab Setup
Use the shared lab project in which App.java uses Log4j. If httpclient is present in the POM but not called by the source, it provides a useful contrast.
5. Step-by-Step Lab
👨🏫 Trainer Note
Be precise with wording: grep and jdeps can provide evidence of references/dependencies, but they do not prove whether every vulnerable method is reachable under all runtime paths.
💻 Student Action
- Confirm both dependencies exist in the Maven tree.
- Search source for Log4j and HttpClient usage.
- Build the application.
- Copy runtime dependencies.
- Run
jdepsagainst the JAR. - Compare package presence with source/bytecode references.
💡 Explanation
This exercise teaches why an SCA finding is not the same thing as a proven exploitable call path.
6. Commands
🖥️ Command
mkdir -p evidence/reachability
mvn dependency:tree | tee evidence/reachability/dependency-tree.txt
grep -RniE 'log4j|LogManager|Logger' src || true
grep -RniE 'HttpClient|HttpClients|org\.apache\.http' src || true
mvn -B package -DskipTests
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
jdeps -verbose:class \
--class-path 'target/dependency/*' \
target/sca-lab-1.0.0.jar \
| tee evidence/reachability/jdeps.txt
grep -Ei 'log4j|httpclient|httpcore' evidence/reachability/jdeps.txt || true
7. Expected Output
📋 Expected Output
Your output should show the dependency tree contains the declared packages. Source or jdeps output may show references to Log4j because the sample application uses it. If HttpClient is not referenced by the compiled application, it may not appear in class-level reference output even though Maven resolved it.
Exact lines depend on the current source and compiler.
8. What to Look For
🔍 Check
Separate these statements:
Dependency present -> established by package manager / SBOM / SCA
Class referenced -> partial static evidence
Vulnerable method reachable -> requires stronger reachability analysis
Runtime exploitable -> additionally depends on configuration, input, environment, defenses, and exploit preconditions
9. Practical Exercise
🧪 Exercise
For one actual Dependency-Check finding, create evidence/reachability/assessment.md and record: component presence, direct/transitive path, source references found, bytecode references found, vulnerable API/method from authoritative advisory, environment assumptions, and whether further specialist reachability analysis is required.
10. Expected Result
✅ Success Criteria
You can show a dependency can exist without proving its vulnerable code path is reachable, and you do not attribute code-level reachability capability to Dependency-Check.
11. Key Takeaway
- Dependency presence is not the same as vulnerable-code reachability.
jdeps/source search are teaching evidence, not a full reachability engine.- Use specialized analysis plus context before making exploitability claims.
34. Vulnerability Prioritization
1. What You Need to Know
Prioritization is the process of deciding what to investigate and remediate first. No single number is sufficient. Useful signals include CVSS, EPSS, CISA KEV status, dependency path/usage, internet exposure, environment, business importance, exploit preconditions, and whether a supported fix exists.
Do not add these signals together into an invented score unless your organization has a formally defined and validated method. This lab keeps the signals visible and separate.
2. Why It Matters in SCA
A very high CVSS finding may be unreachable or isolated in a non-production tool, while a lower-scored vulnerability known to be exploited can deserve urgent attention. Context changes action.
3. What We Will Do in the Lab
Build a prioritization worksheet directly from Dependency-Check results, enrich selected CVEs with live EPSS and KEV data, and document contextual decisions without creating a proprietary score.
4. Lab Setup
Use the JSON Dependency-Check report generated earlier, curl, jq, FIRST EPSS API, and the official CISA KEV JSON feed.
5. Step-by-Step Lab
👨🏫 Trainer Note
Priority and severity are not synonyms. Preserve the authoritative source values and capture the decision separately.
💻 Student Action
- Export findings from the real report.
- Choose two real CVEs from the report if available.
- Fetch current EPSS data for each.
- Check each against current CISA KEV.
- Add direct/transitive, usage, exposure, environment, business, and fix-availability context.
- Record the remediation decision without calculating a made-up composite score.
💡 Explanation
CVSS describes vulnerability severity characteristics. EPSS estimates exploitation probability over the next 30 days. KEV indicates evidence of known exploitation. Application context tells you whether and how the component affects your system.
6. Commands
🖥️ Command
mkdir -p evidence/prioritization
REPORT=evidence/final/dependency-check/dependency-check-report.json
jq -r '
[.dependencies[] as $d
| ($d.vulnerabilities // [])[]
| [$d.fileName,
.name,
(.severity // "UNSCORED"),
(.cvssv3.baseScore // .cvssv4.baseScore // "")]
| @tsv]
| .[]' "$REPORT" \
| tee evidence/prioritization/findings.tsv
# Replace with a REAL CVE selected from findings.tsv.
CVE=CVE-2021-44228
curl -fsS "https://api.first.org/data/v1/epss?cve=${CVE}" \
| tee "evidence/prioritization/${CVE}-epss.json" \
| jq '.data[] | {cve, epss, percentile, date}'
curl -fsS \
https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json \
-o evidence/prioritization/cisa-kev.json
jq --arg cve "$CVE" \
'.vulnerabilities[] | select(.cveID == $cve) |
{cveID, vendorProject, product, vulnerabilityName, dateAdded, dueDate, requiredAction}' \
evidence/prioritization/cisa-kev.json
Create the worksheet:
cat > evidence/prioritization/worksheet.tsv <<'TSV'
CVE CVSS EPSS EPSS_percentile KEV direct_or_transitive used_or_unknown environment exposure business_context fix_available decision evidence
TSV
7. Expected Output
📋 Expected Output
The EPSS command returns the current API value at execution time. The KEV command returns an object only if the selected CVE is present in the current catalog. Do not copy a numeric EPSS value from this manual because it can change daily.
For CVE-2021-44228, the authoritative sources should identify the real Log4j vulnerability; current metadata is retrieved during the lab rather than fabricated here.
8. What to Look For
🔍 Check
Compare signals without collapsing them:
Finding A: High CVSS + not in KEV + low/unknown usage context
Finding B: Lower CVSS + in KEV + exposed/used context
Use actual findings if your scan contains such a pair. If it does not, treat the lines above only as an unlabeled decision scenario – do not invent CVE IDs or scores to force the comparison.
9. Practical Exercise
🧪 Exercise
Complete at least two rows in worksheet.tsv using only measured or verified values. Add a short paragraph explaining why your remediation order follows from the evidence and organizational context.
10. Expected Result
✅ Success Criteria
Every priority decision can be traced to real scanner output, current external intelligence, and explicit application/business context. No proprietary score was invented.
11. Key Takeaway
- CVSS, EPSS, KEV, and application context answer different questions.
- Known exploitation can materially change urgency.
- Prioritization is contextual and evidence-based.
35. SBOM Vulnerability Management
1. What You Need to Know
An SBOM is useful beyond build time. Stored SBOMs can be re-scanned as new vulnerability intelligence appears, even when an application is not rebuilt that day.
2. Why It Matters in SCA
A component that was clean on release day can receive a CVE later. Keeping a build-specific SBOM makes historical inventory searchable and enables continuous vulnerability management.
3. What We Will Do in the Lab
Generate a CycloneDX SBOM, scan that SBOM with the complementary Grype scanner, store hashes and vulnerability output, remediate a dependency, regenerate the SBOM, and compare the two inventories.
4. Lab Setup
Use Syft 1.52.0 and Grype 0.119.0, or later versions that you have explicitly revalidated. Dependency-Check remains the primary scanner in this course; Grype is used here because it supports scanning a stored SBOM directly.
5. Step-by-Step Lab
👨🏫 Trainer Note
The result sets from two scanners need not be identical because data sources, matching logic, package identity, and update timing differ. Treat differences as investigation inputs, not proof that one tool is automatically wrong.
💻 Student Action
- Copy resolved dependencies.
- Generate a build-specific CycloneDX SBOM.
- Hash and archive it.
- Scan the stored SBOM with Grype.
- Remediate/update the project.
- Generate a new SBOM.
- Diff component versions and re-scan.
💡 Explanation
This creates the full inventory -> intelligence -> remediation -> updated inventory loop.
6. Commands
🖥️ Command
Before remediation:
mkdir -p evidence/sbom-vuln/before evidence/sbom-vuln/after
rm -rf target/dependency
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
syft scan dir:target/dependency \
-o cyclonedx-json=evidence/sbom-vuln/before/sbom.cdx.json
grype sbom:evidence/sbom-vuln/before/sbom.cdx.json \
-o json \
> evidence/sbom-vuln/before/grype.json
jq -r '.components[] | [.group, .name, .version] | @tsv' \
evidence/sbom-vuln/before/sbom.cdx.json \
| sort \
> evidence/sbom-vuln/before/components.tsv
After your approved dependency remediation/update:
rm -rf target/dependency
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
syft scan dir:target/dependency \
-o cyclonedx-json=evidence/sbom-vuln/after/sbom.cdx.json
grype sbom:evidence/sbom-vuln/after/sbom.cdx.json \
-o json \
> evidence/sbom-vuln/after/grype.json
jq -r '.components[] | [.group, .name, .version] | @tsv' \
evidence/sbom-vuln/after/sbom.cdx.json \
| sort \
> evidence/sbom-vuln/after/components.tsv
diff -u \
evidence/sbom-vuln/before/components.tsv \
evidence/sbom-vuln/after/components.tsv || true
Hash evidence:
if command -v sha256sum >/dev/null 2>&1; then
sha256sum evidence/sbom-vuln/*/sbom.cdx.json \
| tee evidence/sbom-vuln/sbom-hashes.txt
else
shasum -a 256 evidence/sbom-vuln/*/sbom.cdx.json \
| tee evidence/sbom-vuln/sbom-hashes.txt
fi
7. Expected Output
📋 Expected Output
You should have two SBOMs with real component/version lists, two Grype result files, a component diff, and cryptographic hashes. Exact vulnerabilities and counts depend on the dependencies and intelligence databases at execution time.
8. What to Look For
🔍 Check
Confirm that the remediated component version changed in the components.tsv diff and that the SBOM scan result reflects current vulnerability intelligence. If a finding remains, investigate whether another dependency path/version still introduces it.
9. Practical Exercise
🧪 Exercise
Pretend the source repository is temporarily unavailable. Using only before/sbom.cdx.json, answer: Which Log4j/Jackson/HttpClient versions were shipped, and what does the current SBOM vulnerability scan report for them today?
10. Expected Result
✅ Success Criteria
You can perform vulnerability reassessment from a stored SBOM, regenerate inventory after remediation, and preserve verifiable before/after evidence.
11. Key Takeaway
- SBOMs enable vulnerability reassessment after release.
- Re-scan stored inventories when intelligence changes.
- Regenerate and archive SBOMs after remediation.
36. SCA vs Software Supply Chain Security
1. What You Need to Know
SCA is a dependency-focused security discipline. Software supply-chain security is broader and includes source integrity, repository controls, dependency resolution, build isolation, provenance, signing/attestation, CI/CD identity, artifact distribution, and deployment controls.
2. Why It Matters in SCA
Dependency-Check is valuable, but treating one scanner as the entire supply-chain program leaves major risks uncovered.
3. What We Will Do in the Lab
Map every artifact created in this course to either SCA, broader supply-chain security, or both, and place Dependency-Check in the end-to-end model.
4. Lab Setup
Use the evidence/ directory produced by previous labs.
5. Step-by-Step Lab
👨🏫 Trainer Note
The goal is not to diminish SCA; it is to define its boundary accurately so controls can be layered correctly.
💻 Student Action
- Review the comparison table.
- Inventory evidence files created so far.
- Classify each control/artifact.
- Identify gaps not covered by Dependency-Check.
- Draw the complete supply-chain flow.
💡 Explanation
A mature program keeps vulnerability/component analysis strong while adding integrity and trust controls around the rest of the lifecycle.
6. Commands
🖥️ Command
mkdir -p evidence/sca-vs-supply-chain
find evidence -type f -print | sort \
| tee evidence/sca-vs-supply-chain/evidence-inventory.txt
cat > evidence/sca-vs-supply-chain/control-map.tsv <<'TSV'
artifact_or_control SCA Supply_Chain why
Dependency-Check vulnerability report yes yes Known-vulnerability component analysis
Dependency tree yes yes Dependency inventory/path
SBOM yes yes Component inventory and exchange
License inventory yes yes Dependency governance
Repository resolution policy no yes Controls dependency origin
Package digest partial yes Exact artifact integrity
Build provenance attestation no yes Build/source origin claim
CI identity and permissions no yes Pipeline trust boundary
VEX complementary yes Product vulnerability status communication
Reachability analysis complementary yes Exploitability/context evidence
TSV
column -t -s $'\t' evidence/sca-vs-supply-chain/control-map.tsv 2>/dev/null \
|| cat evidence/sca-vs-supply-chain/control-map.tsv
Practical comparison:
| SCA | Software Supply Chain Security |
|---|---|
| Dependency-focused | End-to-end lifecycle |
| Known vulnerability detection | Vulnerability + integrity + trust controls |
| Dependency inventory | Source/build/artifact provenance |
| License visibility | Publisher/repository governance |
| SBOM creation/consumption | SBOM + attestations/signing |
| Direct/transitive dependency risk | Build system and CI/CD identity risk |
| Version/remediation workflow | Release/distribution/deployment integrity |
| Component intelligence | Package origin and tamper resistance |
7. Expected Output
📋 Expected Output
The control map should clearly show which artifacts belong to SCA and which require broader supply-chain controls. There is intentional overlap: an SBOM is useful to both.
8. What to Look For
🔍 Check
Dependency-Check belongs primarily in the SCA/known-vulnerability component-analysis layer. It does not prove publisher identity, build integrity, package provenance, or code-level reachability.
9. Practical Exercise
🧪 Exercise
Add three missing supply-chain controls to control-map.tsv for your organization. For each, name the pipeline stage, owner, evidence artifact, and failure/exception process.
10. Expected Result
✅ Success Criteria
You can explain exactly what Dependency-Check covers and identify the additional controls required for an end-to-end supply-chain security program.
11. Key Takeaway
- SCA is essential but narrower than supply-chain security.
- Dependency-Check is a component/vulnerability tool, not an end-to-end trust platform.
- Layer inventory, vulnerability, provenance, build integrity, and deployment controls.
37. SCA Integration with DevSecOps
1. What You Need to Know
DevSecOps makes security checks repeatable parts of delivery rather than one-off manual activities. For SCA, the useful pattern is dependency resolution -> scan -> analysis -> SBOM -> gate -> evidence -> remediation -> re-scan.
2. Why It Matters in SCA
A scan that only runs during training quickly becomes stale. Automation makes the same control run on pull requests, merges, releases, and scheduled reassessments.
3. What We Will Do in the Lab
Run the complete local workflow through one script, preserve evidence, and map it to the GitHub Actions workflow from Topic 25.
4. Lab Setup
Use the shared Maven project, mvn, jq, Syft, and the Dependency-Check Maven plugin 13.0.0. Set NVD_API_KEY when available.
5. Step-by-Step Lab
👨🏫 Trainer Note
The sample gate of CVSS 7.0 is a teaching policy, not an industry-standard universal threshold. Organizations must define their own policy and exception process.
💻 Student Action
- Build and resolve dependencies.
- Generate dependency tree and Dependency-Check reports.
- Generate SBOM.
- Extract findings/metrics.
- Apply a security gate.
- Remediate and re-run when the gate fails.
- Preserve source/build/evidence identity.
- Run the equivalent sequence in CI.
💡 Explanation
This is the final integrated workflow before the capstone uses a separate real training repository.
6. Commands
🖥️ Command
Create an end-to-end local driver:
cat > run-sca.sh <<'SCRIPT'
#!/usr/bin/env bash
set -euo pipefail
OUT="evidence/devsecops/$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$OUT/dependency-check" "$OUT/sbom"
mvn -B package -DskipTests
mvn -B dependency:tree | tee "$OUT/dependency-tree.txt"
rm -rf target/dependency
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
mvn -B org.owasp:dependency-check-maven:13.0.0:check \
-Dformat=HTML,JSON,SARIF \
-Dodc.outputDirectory="$OUT/dependency-check" \
-DfailBuildOnCVSS=11 \
${NVD_API_KEY:+-DnvdApiKeyEnvironmentVariable=NVD_API_KEY}
syft scan dir:target/dependency \
-o cyclonedx-json="$OUT/sbom/sbom.cdx.json" \
-o spdx-json="$OUT/sbom/sbom.spdx.json"
REPORT="$OUT/dependency-check/dependency-check-report.json"
jq '{
dependencies: (.dependencies | length),
vulnerabilities: ([.dependencies[].vulnerabilities[]?] | length),
critical: ([.dependencies[].vulnerabilities[]? | select(.severity == "CRITICAL")] | length),
high: ([.dependencies[].vulnerabilities[]? | select(.severity == "HIGH")] | length),
medium: ([.dependencies[].vulnerabilities[]? | select(.severity == "MEDIUM")] | length),
low: ([.dependencies[].vulnerabilities[]? | select(.severity == "LOW")] | length)
}' "$REPORT" | tee "$OUT/metrics.json"
printf '%s\n' "$OUT"
SCRIPT
chmod +x run-sca.sh
./run-sca.sh
Then run the explicit teaching gate against the same project:
mvn -B org.owasp:dependency-check-maven:13.0.0:check \
-Dformat=HTML,JSON,SARIF \
-Dodc.outputDirectory=evidence/devsecops/gate \
-DfailBuildOnCVSS=7.0 \
-DnvdApiKeyEnvironmentVariable=NVD_API_KEY
If you do not have an NVD API key, omit the last property for a learning run. For repeated/CI operation, follow OWASP guidance to use a supported NVD API configuration or internal NVD mirror rather than repeatedly forcing cold public updates.
7. Expected Output
📋 Expected Output
The first script creates timestamped dependency tree, HTML/JSON/SARIF reports, CycloneDX/SPDX SBOMs, and measured metrics. The gate command exits according to the actual findings at the selected threshold.
A failure is a successful demonstration of the gate when a real finding meets/exceeds policy.
8. What to Look For
🔍 Check
Trace one finding through the complete flow:
Developer
-> Git/GitHub
-> Build
-> Dependency Resolution
-> SCA
-> Vulnerability Analysis
-> SBOM
-> Security Gate
-> Report/Evidence
-> Remediation
-> Re-scan
-> Deployment decision
Make sure the CI artifact paths and local evidence paths refer to the same logical outputs.
9. Practical Exercise
🧪 Exercise
Break the gate intentionally using the known vulnerable training version from earlier, capture the failing evidence, remediate to an approved fixed/current version, run the workflow again, and document the before/after decision. Never introduce a fake vulnerability merely to make a gate fail.
10. Expected Result
✅ Success Criteria
You can run SCA locally and in CI, preserve evidence, enforce a policy threshold, remediate a real dependency finding, and verify the result with a new scan/SBOM.
11. Key Takeaway
- Automate the same evidence-producing workflow used during investigation.
- Gates should be policy-driven, reviewable, and repeatable.
- Re-scan after every remediation or approved exception.
MODULE 9 – FINAL CAPSTONE
Complete End-to-End SCA Security Lab
Capstone Objective
This capstone combines dependency discovery, dependency trees, OWASP Dependency-Check, CVE/CWE/CVSS/CPE analysis, NVD verification, EPSS, KEV, outdated dependency review, license review, SBOM generation, remediation, re-scanning, policy gates, CI/CD, and final security evidence.
The target is the public educational repository:
https://github.com/hv-oblikas/vulnerable-demo-app
It is intentionally vulnerable and explicitly marked for security testing only. Do not deploy it, expose it to the Internet, or execute exploit examples from its source/README. This capstone only resolves dependencies and analyzes security metadata.
Capstone Prerequisites
Required:
Git
Java 11+ (Java 17 recommended for this lab)
Maven 3.8.1+
curl
jq
OWASP Dependency-Check Maven Plugin 13.0.0
Syft 1.52.0 or later explicitly revalidated version
Useful/optional:
NVD API key in NVD_API_KEY
Grype 0.119.0 for complementary SBOM scanning
GitHub repository for CI/CD exercise
Verify tools before starting:
git --version
java -version
mvn -version
jq --version
curl --version | head -n 1
syft version
grype version
Capstone Step 1 – Clone and Pin the Real Training Project
💻 Student Action
Clone the project, record the exact commit, and create an evidence directory outside the Java subproject.
🖥️ Command
git clone --depth 1 https://github.com/hv-oblikas/vulnerable-demo-app.git
cd vulnerable-demo-app
mkdir -p capstone-evidence/{before,after,investigation,ci,final}
git rev-parse HEAD | tee capstone-evidence/source-commit.txt
git status --short | tee capstone-evidence/source-status-before.txt
sed -n '1,220p' README.md | tee capstone-evidence/repository-readme-extract.txt
cd java
🔍 Check
Confirm the README identifies the repository as intentionally vulnerable/security-testing-only and that source-commit.txt contains a real Git commit SHA.
✅ Success Criteria
The training source is pinned to a specific revision and no application/exploit payload has been executed.
Capstone Step 2 – Identify Direct Dependencies
💻 Student Action
Inspect the Maven manifest and extract declared dependencies.
🖥️ Command
cp pom.xml ../capstone-evidence/before/pom.xml
cat pom.xml | tee ../capstone-evidence/before/pom-reviewed.xml
mvn -B dependency:list -DexcludeTransitive=true \
| tee ../capstone-evidence/before/direct-dependencies.txt
🔍 Check
Look for the exact group, artifact, and version of every direct dependency. At the 2026-10-03 research snapshot, this repository intentionally declared vulnerable Java packages including Log4j, Jackson Databind, and Commons IO. Always trust the POM you actually cloned over a cached tutorial description.
✅ Success Criteria
You can identify every direct Maven dependency and its version from the real cloned revision.
Capstone Step 3 – Generate and Read the Dependency Tree
🖥️ Command
mvn -B dependency:tree \
| tee ../capstone-evidence/before/dependency-tree.txt
mvn -B dependency:tree -Dverbose \
| tee ../capstone-evidence/before/dependency-tree-verbose.txt
🔍 Check
Mark each direct dependency and any transitive dependencies. Record which parent introduces each transitive package.
🧪 Exercise
Create ../capstone-evidence/investigation/dependency-paths.md with at least three examples in this form:
Application
-> direct dependency
-> transitive dependency
If a dependency has no transitive child in this exact POM, say so rather than inventing one.
✅ Success Criteria
You can explain the difference between a package listed directly in pom.xml and one resolved transitively.
Capstone Step 4 – Resolve and Copy the Actual Dependency Artifacts
🖥️ Command
rm -rf target/dependency
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
find target/dependency -maxdepth 1 -type f -print | sort \
| tee ../capstone-evidence/before/resolved-artifacts.txt
🔍 Check
The files in target/dependency are the actual JARs used for SBOM/license/integrity exercises. Compare their versions with the dependency tree.
✅ Success Criteria
The resolved artifacts are present locally and match the package-manager view.
Capstone Step 5 – Run the Initial OWASP Dependency-Check Scan
👨🏫 Trainer Note
The first NVD data population can take substantial time. An NVD API key helps but is not a substitute for an organizational NVD mirror/cache strategy in CI. Do not repeatedly delete the local Dependency-Check data cache between exercises.
🖥️ Command
NVD_ARGS=()
if [ -n "${NVD_API_KEY:-}" ]; then
NVD_ARGS+=("-DnvdApiKeyEnvironmentVariable=NVD_API_KEY")
fi
mvn -B org.owasp:dependency-check-maven:13.0.0:check \
-Dformat=ALL \
-Dodc.outputDirectory=../capstone-evidence/before/dependency-check \
-DfailBuildOnCVSS=11 \
"${NVD_ARGS[@]}"
📋 Expected Output
Do not expect exact vulnerability counts from this manual. A successful non-gating scan should create multiple reports, for example:
../capstone-evidence/before/dependency-check/dependency-check-report.html
../capstone-evidence/before/dependency-check/dependency-check-report.json
../capstone-evidence/before/dependency-check/dependency-check-report.xml
../capstone-evidence/before/dependency-check/dependency-check-report.csv
../capstone-evidence/before/dependency-check/dependency-check-report.sarif
...
List what your run actually generated:
find ../capstone-evidence/before/dependency-check -maxdepth 1 -type f -print | sort
🔍 Check
Open the HTML report in a browser and identify dependencies with findings. Verify the project/dependency names instead of scanning only the CVE column.
✅ Success Criteria
Dependency-Check completed, current vulnerability data was used, and machine-readable plus human-readable evidence exists.
Capstone Step 6 – Extract Real Findings from JSON
🖥️ Command
REPORT=../capstone-evidence/before/dependency-check/dependency-check-report.json
jq -r '
.dependencies[] as $d
| ($d.vulnerabilities // [])[]
| [$d.fileName,
.name,
(.severity // "UNSCORED"),
(.cvssv3.baseScore // .cvssv4.baseScore // ""),
((.cwes // []) | join(","))]
| @tsv' "$REPORT" \
| tee ../capstone-evidence/before/findings.tsv
If a field is absent in the current report schema, inspect the JSON rather than guessing:
jq '.dependencies[] | select(.vulnerabilities != null) | .vulnerabilities[0]' "$REPORT" | head -n 120
🔍 Check
Every CVE, score, severity, and CWE recorded in your notes must come from generated evidence or an authoritative source.
✅ Success Criteria
findings.tsv contains real scan findings from your run, not copied example counts.
Capstone Step 7 – Investigate a Real CVE
Use a real CVE that appears in your report. If CVE-2021-44228 is present for the pinned training revision, it is a useful investigation target.
🖥️ Command
CVE=CVE-2021-44228
grep -F "$CVE" ../capstone-evidence/before/findings.tsv \
| tee ../capstone-evidence/investigation/${CVE}-dependency-check.txt
If your cloned revision no longer contains that finding, choose a CVE that actually appears in findings.tsv and update CVE.
🔍 Check
Record dependency/version, direct/transitive path, severity, score, and references from the HTML/JSON report.
✅ Success Criteria
You are investigating a CVE produced by your actual scan.
Capstone Step 8 – Verify CVE/CWE/CVSS/CPE in NVD
🖥️ Command
curl -fsS \
"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=${CVE}" \
-o "../capstone-evidence/investigation/${CVE}-nvd.json"
jq '.vulnerabilities[0].cve |
{id,
sourceIdentifier,
published,
lastModified,
metrics,
weaknesses,
configurations,
references}' \
"../capstone-evidence/investigation/${CVE}-nvd.json" \
> "../capstone-evidence/investigation/${CVE}-nvd-summary.json"
jq . "../capstone-evidence/investigation/${CVE}-nvd-summary.json" | head -n 220
🔍 Check
Identify:
CVE ID
CWE(s), when NVD provides them
CVSS version/score/vector
CPE configuration/ranges
published/last-modified dates
references
Do not assume every CVE has every field or that every data provider agrees on every metric.
✅ Success Criteria
You can trace the scanner finding to current authoritative NVD metadata and explain dependency -> CPE identification -> CVE association.
Capstone Step 9 – Fetch Current EPSS
🖥️ Command
curl -fsS "https://api.first.org/data/v1/epss?cve=${CVE}" \
-o "../capstone-evidence/investigation/${CVE}-epss.json"
jq '.data[] | {cve, epss, percentile, date}' \
"../capstone-evidence/investigation/${CVE}-epss.json"
🔍 Check
Record the EPSS score, percentile, and date exactly as returned at execution time. Do not use a stale value from slides or a prior lab run.
✅ Success Criteria
The finding now has a current exploitation-probability signal alongside CVSS.
Capstone Step 10 – Check CISA KEV
🖥️ Command
curl -fsS \
https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json \
-o ../capstone-evidence/investigation/cisa-kev.json
jq --arg cve "$CVE" '
.vulnerabilities[]
| select(.cveID == $cve)
| {cveID,
vendorProject,
product,
vulnerabilityName,
dateAdded,
dueDate,
requiredAction,
knownRansomwareCampaignUse}' \
../capstone-evidence/investigation/cisa-kev.json \
| tee "../capstone-evidence/investigation/${CVE}-kev.json"
🔍 Check
An empty result means the CVE was not found in the current downloaded KEV catalog at that moment. A returned object means CISA currently lists it as known exploited. Preserve the catalog file/date with the evidence.
✅ Success Criteria
KEV status is based on the official current catalog, not memory.
Capstone Step 11 – Identify Outdated Dependencies
🖥️ Command
mvn -B -Dstyle.color=never \
org.codehaus.mojo:versions-maven-plugin:2.22.0:display-dependency-updates \
| tee ../capstone-evidence/before/dependency-updates.txt
🔍 Check
For each direct dependency, compare installed version with the versions currently offered by Maven repositories. Review release notes/vendor support before selecting an upgrade. “Latest” and “compatible/supported” are not always the same thing.
🧪 Exercise
Create ../capstone-evidence/investigation/outdated-review.tsv:
package current_version candidate_version maintenance_status release_notes_reviewed decision
Populate it from actual command/source evidence.
✅ Success Criteria
You can distinguish a known-vulnerable package from a merely old package and can identify maintenance risk that CVE scanning alone may miss.
Capstone Step 12 – Review License Information
🖥️ Command
syft scan dir:target/dependency \
-o cyclonedx-json=../capstone-evidence/before/sbom.cdx.json \
-o spdx-json=../capstone-evidence/before/sbom.spdx.json
jq -r '
.components[]
| [.group,
.name,
.version,
((.licenses // [])
| map(.license.id // .license.name // .expression // "UNKNOWN")
| join(","))]
| @tsv' ../capstone-evidence/before/sbom.cdx.json \
| tee ../capstone-evidence/before/licenses.tsv
If the generated CycloneDX representation differs, inspect one component and adapt the query to the real document:
jq '.components[0]' ../capstone-evidence/before/sbom.cdx.json
🔍 Check
Record detected licenses and unknowns. Technical detection is not legal advice. Escalate unclear or incompatible licensing to the organization’s legal/compliance process.
✅ Success Criteria
You have machine-generated license evidence tied to the same resolved dependency set.
Capstone Step 13 – Generate and Inspect CycloneDX SBOM
🖥️ Command
jq '{bomFormat, specVersion, serialNumber, metadata, componentCount: (.components | length)}' \
../capstone-evidence/before/sbom.cdx.json
jq -r '.components[] | [.type, .group, .name, .version, (.purl // "")] | @tsv' \
../capstone-evidence/before/sbom.cdx.json \
| tee ../capstone-evidence/before/cyclonedx-components.tsv
🔍 Check
Confirm CycloneDX document format/spec version and the component PURLs/versions that represent the build inventory.
✅ Success Criteria
You can locate a vulnerable component and its exact version/PURL in the CycloneDX SBOM.
Capstone Step 14 – Generate and Inspect SPDX SBOM
The SPDX file was generated in Step 12.
🖥️ Command
jq '{spdxVersion, dataLicense, SPDXID, name, documentNamespace}' \
../capstone-evidence/before/sbom.spdx.json
jq -r '
.packages[]?
| [.name,
(.versionInfo // ""),
(.licenseDeclared // ""),
(.licenseConcluded // "")]
| @tsv' ../capstone-evidence/before/sbom.spdx.json \
| tee ../capstone-evidence/before/spdx-packages.tsv
🔍 Check
Compare at least three packages between CycloneDX and SPDX. Focus on package identity/version/license representation rather than declaring one format “better.”
✅ Success Criteria
You can read package/version/license data from both standards.
Capstone Step 15 – Create a Prioritization Record
🖥️ Command
cat > ../capstone-evidence/investigation/prioritization.tsv <<'TSV'
CVE component version CVSS CVSS_vector EPSS EPSS_date KEV direct_or_transitive usage environment_exposure business_context fix_available decision evidence
TSV
💻 Student Action
Fill at least three real rows from your evidence. Keep CVSS, EPSS, and KEV separate. Add dependency usage and environment/business context manually from what you actually know.
🔍 Check
No invented combined risk score should appear.
✅ Success Criteria
Each remediation decision is traceable to evidence and context.
Capstone Step 16 – Remediate the Vulnerable Dependencies
👨🏫 Trainer Note
The repository README may contain old “safe version” suggestions. Do not assume those are the best versions in October 2026. Use current package-manager/vendor information and test compatibility.
🖥️ Command
First preserve the original POM and review available releases:
cp pom.xml ../capstone-evidence/before/pom-pre-remediation.xml
mvn -B -Dstyle.color=never \
org.codehaus.mojo:versions-maven-plugin:2.22.0:display-dependency-updates \
| tee ../capstone-evidence/before/dependency-updates-review.txt
For this isolated training project, update release dependencies to the latest releases Maven currently resolves:
mvn -B \
org.codehaus.mojo:versions-maven-plugin:2.22.0:use-latest-releases
git diff -- pom.xml \
| tee ../capstone-evidence/after/pom-remediation.diff
cp pom.xml ../capstone-evidence/after/pom.xml
Review the changed versions before proceeding:
grep -nE '<artifactId>|<version>' pom.xml \
| tee ../capstone-evidence/after/dependency-version-review.txt
🔍 Check
Do not equate “automatically changed” with “approved.” In a real application, run unit/integration/regression tests and review breaking changes. For this SCA capstone, the objective is to replace intentionally vulnerable dependencies with currently resolved releases and prove the vulnerability result changed.
✅ Success Criteria
The POM diff contains real dependency version changes selected from current Maven repository data.
Capstone Step 17 – Re-resolve, Rebuild the Inventory, and Re-scan
🖥️ Command
rm -rf target/dependency
mvn -B dependency:tree \
| tee ../capstone-evidence/after/dependency-tree.txt
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
mvn -B org.owasp:dependency-check-maven:13.0.0:check \
-Dformat=ALL \
-Dodc.outputDirectory=../capstone-evidence/after/dependency-check \
-DfailBuildOnCVSS=11 \
"${NVD_ARGS[@]}"
syft scan dir:target/dependency \
-o cyclonedx-json=../capstone-evidence/after/sbom.cdx.json \
-o spdx-json=../capstone-evidence/after/sbom.spdx.json
mvn -B -Dstyle.color=never \
org.codehaus.mojo:versions-maven-plugin:2.22.0:display-dependency-updates \
| tee ../capstone-evidence/after/dependency-updates.txt
🔍 Check
Confirm the old vulnerable package versions no longer appear in the after dependency tree/SBOM unless another dependency still introduces them.
✅ Success Criteria
Fresh Dependency-Check and SBOM evidence exists for the remediated dependency set.
Capstone Step 18 – Measure Before vs After from Real Reports
Create a small metrics helper. It intentionally does not invent an “outdated” count because Maven’s text update output can vary; record that count only after reviewing the actual update reports.
🖥️ Command
cat > ../capstone-evidence/final/report-metrics.sh <<'SCRIPT'
#!/usr/bin/env bash
set -euo pipefail
report="$1"
jq -r '
def vulns: [.dependencies[].vulnerabilities[]?];
[
(.dependencies | length),
(vulns | length),
([vulns[] | select((.severity // "") == "CRITICAL")] | length),
([vulns[] | select((.severity // "") == "HIGH")] | length),
([vulns[] | select((.severity // "") == "MEDIUM")] | length),
([vulns[] | select((.severity // "") == "LOW")] | length)
] | @tsv' "$report"
SCRIPT
chmod +x ../capstone-evidence/final/report-metrics.sh
../capstone-evidence/final/report-metrics.sh \
../capstone-evidence/before/dependency-check/dependency-check-report.json \
| tee ../capstone-evidence/final/metrics-before.tsv
../capstone-evidence/final/report-metrics.sh \
../capstone-evidence/after/dependency-check/dependency-check-report.json \
| tee ../capstone-evidence/final/metrics-after.tsv
printf '%s\n' 'dependencies vulnerabilities critical high medium low'
printf 'before: '; cat ../capstone-evidence/final/metrics-before.tsv
printf 'after: '; cat ../capstone-evidence/final/metrics-after.tsv
🔍 Check
Open both HTML reports and validate the calculated counts. Record any unscored/unknown-severity finding separately if present.
✅ Success Criteria
Before/after numbers come from the generated reports on your machine.
Capstone Step 19 – Fill the Required Before vs After Table
Do not pre-fill this table with example numbers. Copy the values from Step 18 and your actual update/suppression review.
| Metric | Before Remediation | After Remediation |
|---|---|---|
| Dependencies | <measured> | <measured> |
| Vulnerabilities | <measured> | <measured> |
| Critical | <measured> | <measured> |
| High | <measured> | <measured> |
| Medium | <measured> | <measured> |
| Low | <measured> | <measured> |
| Outdated dependencies | <count from actual dependency-updates review> | <count from actual dependency-updates review> |
| Suppressed findings | <actual count; 0 if none> | <actual count; 0 if none> |
🧪 Exercise
Save the completed table as ../capstone-evidence/final/before-after.md. Under it, list every changed dependency version and which finding(s) disappeared, remained, or changed after remediation.
✅ Success Criteria
No number in the final table is fabricated or copied from the repository README.
Capstone Step 20 – Configure and Test a Security Gate
The following CVSS 7.0 threshold is a lab policy example, not a universal recommendation.
🖥️ Command
set +e
mvn -B org.owasp:dependency-check-maven:13.0.0:check \
-Dformat=HTML,JSON,SARIF \
-Dodc.outputDirectory=../capstone-evidence/final/gate \
-DfailBuildOnCVSS=7.0 \
"${NVD_ARGS[@]}"
GATE_RC=$?
set -e
printf 'Dependency-Check gate exit code: %s\n' "$GATE_RC" \
| tee ../capstone-evidence/final/gate-result.txt
🔍 Check
Interpret the result from the current findings:
exit code 0 -> no finding violated this configured threshold
non-zero -> one or more findings violated the policy or the scan itself failed
Read the console/report before assuming every non-zero exit is a vulnerability gate; operational failures can also make a build fail.
🧪 Exercise
If the gate still fails for a real finding, investigate and remediate it or document a time-bounded, reviewed exception/suppression only when technically justified. Re-run until the final policy result is understood.
✅ Success Criteria
You can demonstrate a deterministic pass/fail policy based on real scan output and distinguish remediation from suppression.
Capstone Step 21 – Integrate SCA into GitHub Actions
Create the workflow from the repository root, not the java directory:
🖥️ Command
cd ..
mkdir -p .github/workflows
cat > .github/workflows/sca.yml <<'YAML'
name: SCA Security
on:
push:
pull_request:
workflow_dispatch:
permissions:
contents: read
security-events: write
jobs:
sca:
runs-on: ubuntu-latest
defaults:
run:
working-directory: java
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: maven
- name: Resolve dependencies and capture tree
run: |
mkdir -p target/security-evidence
mvn -B dependency:tree | tee target/security-evidence/dependency-tree.txt
mvn -B dependency:copy-dependencies -DoutputDirectory=target/dependency
- name: OWASP Dependency-Check
shell: bash
run: |
ARGS=()
if [ -n "${NVD_API_KEY:-}" ]; then
ARGS+=("-DnvdApiKeyEnvironmentVariable=NVD_API_KEY")
fi
mvn -B org.owasp:dependency-check-maven:13.0.0:check \
-Dformat=HTML,JSON,SARIF \
-Dodc.outputDirectory=target/security-evidence/dependency-check \
-DfailBuildOnCVSS=7.0 \
"${ARGS[@]}"
- name: Install Syft
id: syft
uses: anchore/sbom-action/download-syft@v0
with:
syft-version: v1.52.0
- name: Generate CycloneDX SBOM
run: |
${{ steps.syft.outputs.cmd }} scan dir:target/dependency \
-o cyclonedx-json=target/security-evidence/sbom.cdx.json
- name: Upload SCA evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: sca-security-evidence
path: |
java/target/security-evidence
if-no-files-found: warn
- name: Upload SARIF to GitHub code scanning
if: always()
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: java/target/security-evidence/dependency-check/dependency-check-report.sarif
YAML
cp .github/workflows/sca.yml capstone-evidence/ci/sca.yml
👨🏫 Trainer Note
actions/checkout@v6, actions/setup-java@v4, actions/upload-artifact@v4, and github/codeql-action/upload-sarif@v4 are the verified current major action lines used in this lab snapshot. For hardened production supply-chain controls, organizations may pin third-party/action dependencies to reviewed full commit SHAs and manage update automation.
🔍 Check
Push this workflow only to an authorized training repository. If SARIF upload is restricted by repository plan/settings or pull-request permissions, preserve SARIF as the uploaded workflow artifact and document the platform limitation.
✅ Success Criteria
A push/PR runs dependency resolution -> Dependency-Check -> security gate -> SBOM -> evidence upload, and the workflow result reflects actual findings.
Capstone Step 22 – Generate the Final Security Evidence Package
🖥️ Command
mkdir -p capstone-evidence/final/package
cp capstone-evidence/source-commit.txt capstone-evidence/final/package/
cp capstone-evidence/final/before-after.md capstone-evidence/final/package/ 2>/dev/null || true
cp capstone-evidence/investigation/prioritization.tsv capstone-evidence/final/package/ 2>/dev/null || true
cp capstone-evidence/ci/sca.yml capstone-evidence/final/package/
cp capstone-evidence/before/dependency-tree.txt capstone-evidence/final/package/dependency-tree-before.txt
cp capstone-evidence/after/dependency-tree.txt capstone-evidence/final/package/dependency-tree-after.txt
cp capstone-evidence/before/sbom.cdx.json capstone-evidence/final/package/sbom-before.cdx.json
cp capstone-evidence/after/sbom.cdx.json capstone-evidence/final/package/sbom-after.cdx.json
cp capstone-evidence/before/sbom.spdx.json capstone-evidence/final/package/sbom-before.spdx.json
cp capstone-evidence/after/sbom.spdx.json capstone-evidence/final/package/sbom-after.spdx.json
cp capstone-evidence/before/dependency-check/dependency-check-report.json capstone-evidence/final/package/dependency-check-before.json
cp capstone-evidence/after/dependency-check/dependency-check-report.json capstone-evidence/final/package/dependency-check-after.json
cp capstone-evidence/before/dependency-check/dependency-check-report.html capstone-evidence/final/package/dependency-check-before.html
cp capstone-evidence/after/dependency-check/dependency-check-report.html capstone-evidence/final/package/dependency-check-after.html
cp capstone-evidence/final/gate-result.txt capstone-evidence/final/package/ 2>/dev/null || true
find capstone-evidence/final/package -type f -print | sort \
| tee capstone-evidence/final/package/manifest.txt
if command -v sha256sum >/dev/null 2>&1; then
(cd capstone-evidence/final/package && sha256sum * > SHA256SUMS.txt)
else
(cd capstone-evidence/final/package && shasum -a 256 * > SHA256SUMS.txt)
fi
💻 Student Action
Add capstone-evidence/final/package/final-report.md with these sections:
1. Source repository + pinned commit
2. Dependency inventory summary
3. Initial vulnerabilities
4. CVE/CWE/CVSS/CPE investigation
5. NVD verification
6. EPSS result/date
7. KEV result/date
8. Outdated dependency review
9. License observations
10. CycloneDX/SPDX observations
11. Remediation changes
12. Before vs after table
13. Security gate result
14. Suppressions/exceptions, if any
15. CI/CD evidence
16. Remaining risk and next actions
🔍 Check
Every factual number must point to a generated file. Every live-intelligence value should include retrieval date. Every exception should have owner/reason/expiry. Every remediation should have before/after dependency identity.
✅ Success Criteria
The final evidence package can be reviewed by a developer, AppSec engineer, DevOps engineer, or auditor without requiring you to verbally reconstruct what happened.
Capstone Completion Checklist
A student is complete only when all applicable boxes can be checked from actual evidence:
- [ ] Cloned a real GitHub training project and recorded the exact commit.
- [ ] Identified direct dependencies.
- [ ] Generated and interpreted the dependency tree.
- [ ] Ran OWASP Dependency-Check 13.0.0.
- [ ] Generated HTML and machine-readable reports.
- [ ] Identified real CVEs from the scan.
- [ ] Reviewed CVSS severity/vector.
- [ ] Reviewed CWE where available.
- [ ] Reviewed CPE/configuration evidence where available.
- [ ] Verified selected CVEs against NVD API 2.0.
- [ ] Retrieved current EPSS values from FIRST.
- [ ] Checked selected CVEs against current CISA KEV.
- [ ] Identified outdated dependencies with Versions Maven Plugin 2.22.0.
- [ ] Reviewed dependency licenses.
- [ ] Generated CycloneDX SBOM.
- [ ] Generated SPDX SBOM.
- [ ] Remediated vulnerable dependency versions.
- [ ] Re-ran Dependency-Check.
- [ ] Regenerated SBOMs.
- [ ] Compared measured before/after results.
- [ ] Configured and tested a security gate.
- [ ] Integrated SCA into GitHub Actions.
- [ ] Preserved security evidence and hashes.
- [ ] Documented final risk/decisions without fabricated metrics.
Troubleshooting Guide
Dependency-Check First Run Is Very Slow
Cause: initial NVD data population can be large.
Actions:
1. Keep the local Dependency-Check data cache between runs.
2. Use an NVD API key according to your organization's secret-management policy.
3. For CI/enterprise use, follow OWASP guidance for an internal NVD mirror/cache strategy.
4. Do not launch many cold scanners against public NVD simultaneously.
NVD API Rate Limit / HTTP Errors
Do not add blind high-frequency retries. Confirm network access, API key handling, proxy configuration, NVD service status, and update/mirroring strategy.
failBuildOnCVSS Makes the Lab Stop Before Reports Are Reviewed
Use a non-gating threshold such as 11 during discovery, then run a second explicit gate with the policy threshold. Never leave 11 as the real production policy merely because it is convenient for training.
A Dependency Has a CVE That Does Not Seem to Match
Check the Dependency-Check evidence/CPE mapping, product/vendor identity, versions, and authoritative advisory. If it is a genuine false positive, create a narrowly scoped, documented, expiring suppression. Do not suppress a true vulnerable component because the gate is inconvenient.
JSON Query Fails
Report schemas evolve. Inspect the real object first:
jq 'keys' dependency-check-report.json
jq '.dependencies[0]' dependency-check-report.json
Then adjust the query to the generated version instead of forcing old field assumptions.
Syft Finds Fewer/More Components Than Maven
Compare scan target. Scanning target/dependency inventories resolved JARs; scanning the repository may inventory manifests, source metadata, lock files, and generated artifacts differently. Record what was scanned.
Grype and Dependency-Check Results Differ
This can be normal. Compare vulnerability sources, component identity, CPE/PURL matching, package type, database update timestamps, severity source, and suppression/VEX behavior. Investigate; do not average the scanners.
GitHub SARIF Upload Fails
Check repository permissions/settings and whether the workflow context is allowed to write security-events. Preserve SARIF with actions/upload-artifact@v4 even when code-scanning upload is unavailable.
Security Gate Still Fails After Upgrade
Check for:
another vulnerable direct dependency
old version still present transitively
multiple versions resolved
new CVEs affecting the upgraded version
incorrect CPE match / false positive
build or update failure rather than gate failure
Use dependency tree plus HTML/JSON report to trace the exact path.
Trainer Debrief Questions
- Which finding changed priority the most after EPSS/KEV/context review, and why?
- Which dependency was direct versus transitive?
- Did any scanner finding require CPE validation?
- Did remediation remove the vulnerable version from both dependency tree and SBOM?
- What did the security gate enforce, and what did it not prove?
- Which supply-chain risks remain even after a clean Dependency-Check scan?
- Which evidence would you retain for an audit/release record?
- When would VEX be appropriate, and what evidence would be required?
- Why can SCA not be described as true reachability analysis?
- How would you make NVD data ingestion reliable at organizational CI scale?
Final Learning Outcome
After completing this manual, the student should be able to move from:
I do not know what a dependency vulnerability is.
to:
I can identify application dependencies,
understand direct and transitive dependencies,
run OWASP Dependency-Check,
interpret CVE/CVSS/CPE/CWE findings,
investigate vulnerabilities,
use EPSS and KEV as complementary priority signals,
review dependency licenses,
generate and inspect CycloneDX and SPDX SBOMs,
understand dependency confusion, typosquatting, package hijacking and provenance,
explain VEX and reachability boundaries,
remediate vulnerable dependencies,
configure security gates,
integrate SCA into CI/CD,
and participate in an organization's software supply-chain security process.
Verified Reference Baseline – 2026-10-03
The commands and versions in this training guide were checked against current authoritative/project sources at generation time. Re-validate pinned versions when reusing the guide in the future.
OWASP Dependency-Check
- Project/release/documentation: https://owasp.org/www-project-dependency-check/
- GitHub: https://github.com/dependency-check/DependencyCheck
- Maven plugin: https://dependency-check.github.io/DependencyCheck/dependency-check-maven/
- CLI arguments: https://dependency-check.github.io/DependencyCheck/dependency-check-cli/arguments.html
- Suppression documentation: https://dependency-check.github.io/DependencyCheck/general/suppression.html
Verified lab line: Dependency-Check 13.0.0.
NVD / NIST
- NVD: https://nvd.nist.gov/
- NVD developer/API resources: https://nvd.nist.gov/developers
- CVE API 2.0 base: https://services.nvd.nist.gov/rest/json/cves/2.0
EPSS / FIRST
- EPSS: https://www.first.org/epss/
- EPSS API: https://api.first.org/data/v1/epss
CISA KEV
- Catalog: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- JSON feed used by lab: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
SBOM Standards and Tools
- CycloneDX: https://cyclonedx.org/
- SPDX: https://spdx.dev/
- Syft: https://github.com/anchore/syft
- Grype: https://github.com/anchore/grype
Verified lab snapshots: Syft 1.52.0; Grype 0.119.0; CycloneDX current specification family 1.7; SPDX current specification 3.0.1 at generation time. Generated SPDX output version is determined by the selected Syft output implementation and should be read from the actual file.
Maven
- Versions Maven Plugin: https://www.mojohaus.org/versions/versions-maven-plugin/
- Maven Dependency Plugin: https://maven.apache.org/plugins/maven-dependency-plugin/
- Maven Install Plugin: https://maven.apache.org/plugins/maven-install-plugin/
Verified lab snapshots: Versions Maven Plugin 2.22.0; Maven Dependency Plugin 3.11.0; Maven Install Plugin 3.2.0.
VEX
- OpenVEX/vexctl: https://github.com/openvex/vexctl
Verified lab snapshot: vexctl 0.4.4.
GitHub Actions
- GitHub Actions docs: https://docs.github.com/actions
- Artifact attestations: https://docs.github.com/actions/security-for-github-actions/using-artifact-attestations
Verified current action lines used by this guide include actions/checkout@v6, actions/setup-java@v4, actions/upload-artifact@v4, github/codeql-action/upload-sarif@v4, and actions/attest@v4. The capstone also uses the official Anchore anchore/sbom-action/download-syft@v0 action and pins Syft itself to v1.52.0 for repeatability.
Capstone Training Repository
The repository is intentionally vulnerable and states it is for security testing only. The capstone records git rev-parse HEAD so the exact source used by each student is auditable.
Final Quality Assurance Checklist
- [ ] All 37 required topics are present as visible numbered sections.
- [ ] Every numbered topic has What You Need to Know, Why It Matters, Lab objective, setup, steps, commands, expected output, checks, exercise, expected result, and key takeaway.
- [ ] Fundamental -> basic -> intermediate -> advanced progression is preserved.
- [ ] OWASP Dependency-Check remains the central SCA scanner.
- [ ] Complementary tools are explicitly identified rather than misattributed to Dependency-Check.
- [ ] CVE, CWE, CVSS, CPE, NVD, EPSS, and KEV are all practiced.
- [ ] CycloneDX and SPDX SBOMs are generated and inspected.
- [ ] License/compliance workflows are practical and clearly separated from legal advice.
- [ ] Dependency confusion, typosquatting, package hijacking, provenance, VEX, reachability, and prioritization are covered safely.
- [ ] CI/CD, gates, suppressions, remediation, and re-scan are demonstrated.
- [ ] Report formats and security evidence are generated.
- [ ] The capstone uses a real GitHub training project.
- [ ] No exploit payload execution is required.
- [ ] No vulnerability counts are fabricated.
- [ ] Expected-output blocks are explicitly representative where values can change.
- [ ] Before/after metrics must be populated from actual execution.
- [ ] Commands and versions were researched against current sources on 2026-10-03.