⏱ ~25 min (SAST + SCA) · 📍 Module 2 of 7
Important
This module is the only one with two placeholders. .github/workflows/code-scan.yml
ships with both a sast-scan and an sca-scan placeholder, because Code Scan covers
two complementary techniques. You can:
- Pick one (replace the entire
jobs:block with a single tool's job — quickest), or - Pick one of each (paste both a SAST tool's job and an SCA tool's job into the same
jobs:block, making sure each job has a unique key — full coverage).
This workshop module covers Static Application Security Testing (SAST) and Software Composition Analysis (SCA) to identify security vulnerabilities in application code and dependencies.
Vulnerabilities in your code or your dependencies are the path attackers take to gain unauthorized access, steal data, or disrupt your systems. Two complementary techniques cover the bulk of the surface:
- SAST (Static Application Security Testing) — analyzes source code without executing it, looking at patterns, data flow and control flow to surface issues like injection, XSS or unsafe APIs.
- SCA (Software Composition Analysis) — analyzes your open-source dependencies to flag known CVEs, outdated packages and license issues.
- SQL Injection - Unsafe database queries
- Cross-Site Scripting (XSS) - Unvalidated user input
- Command Injection - Direct execution of system commands with user data
- Path Traversal - Unsafe file access with user-controlled paths
- Authentication Flaws - Weak authentication mechanisms
- Authorization Issues - Missing access controls
- Hardcoded Secrets - Credentials in source code
- Input Validation - Missing or improper validation
- Known Vulnerabilities - CVEs in dependencies
- Outdated Packages - Dependencies with security updates
- License Issues - Non-compliant licenses
- Supply Chain Risks - Malicious packages
| Tool | Type | What it does | Notes |
|---|---|---|---|
| CodeQL | SAST | GitHub's semantic code analysis engine for deep security analysis | Action · Docs |
| Semgrep | SAST | Fast pattern-based scanner with extensive rule sets | Docs · Community rules |
| osv-scanner | SCA | Lockfile-based vulnerability scanner backed by OSV.dev | Action · Docs |
| OWASP Dependency Check | SCA | OWASP SCA tool for known vulnerabilities in dependencies | The workshop action uses bundled NVD data (--noupdate), so no API key is required to run the demo. For production you'd typically install dependency-check directly and pass --nvdApiKey to refresh the feed. |
Note: Different ecosystems have specialized SAST tools (e.g., ESLint with security plugins for JavaScript, Bandit for Python, Brakeman for Ruby on Rails) that can provide more targeted analysis alongside general-purpose scanners.
By the end of this module, you will:
- Understand the difference between SAST and SCA
- Learn to configure and run security scanners
- Understand common vulnerability patterns
- Learn to integrate security scanning into CI/CD
- SAST integrated into CI/CD pipeline
- SCA scanning for all dependencies
- Regular dependency updates
- Security hotspots addressed
- False positive management
- Security metrics tracking
- OWASP Top Ten — the canonical list of the most critical web application security risks; most SAST rule packs map back to it.
- OWASP Application Security Verification Standard (ASVS) — concrete requirements you can audit your code against.
- GitHub: About code scanning — how SARIF uploads surface in the Security → Code scanning tab.
- OSV.dev — the vulnerability database that powers
osv-scanner; useful for manually checking a single package. - Snyk: SAST vs DAST vs SCA — quick comparison of the three flavours of code/dependency analysis.
The bait for this module lives in two places: a vulnerable code pattern in
code/src/simple-app.js(SAST) and a known-vulnerable dependency incode/package.json(SCA). The CI failures are intentional.
CodeQL (SAST) — js/command-line-injection at code/src/simple-app.js:42
What CodeQL flagged: exec(\cat "${filePath}"`, …)flows the user-controlledfilePath from the request body straight into a shell command. The TODO comment on the line above (// TODO: Tech debt - should use fs.readFile instead of shell command for security`) hints at the intended fix.
Reading the output: the snippet's Summarize findings step prints ruleId | message | path:line — exactly the format you need.
Fix — replace the exec(...) with fs.readFile(...) confined to a safe base directory (the path.resolve + startsWith guard also clears any follow-up path-traversal rule):
if (filePath) {
const fs = require('fs');
const path = require('path');
const safeBase = path.resolve(__dirname, 'public');
const resolved = path.resolve(safeBase, filePath);
if (!resolved.startsWith(safeBase + path.sep)) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid path' }));
return;
}
fs.readFile('./config.json', 'utf8', (configErr, configData) => {
const config = configErr ? { debug: false } : JSON.parse(configData);
fs.readFile(resolved, 'utf8', (error, stdout) => {
if (error) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: error.message }));
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ config: config, content: stdout }));
});
});
}Heads-up on CI wall-clock: each CodeQL run takes 4-5 minutes (init + database create + analyze). It's not stuck.
Semgrep (SAST) — javascript.lang.security.audit.detect-child-process at code/src/simple-app.js:42
Reading the output: the snippet's Summarize findings step parses the SARIF and prints ruleId | message | path:line directly to the job log — no GHAS required. If your fork has GHAS enabled, the SARIF is also uploaded to the Code Scanning tab for a richer UI.
Same root cause as CodeQL: the exec(\cat "${filePath}"`, …)` line.
Fix — identical to the CodeQL fix above.
osv-scanner (SCA) — multiple CVEs on axios@1.6.0 in code/package.json
What osv-scanner flagged: axios@1.6.0 has multiple known CVEs (SSRF, DoS, credential leakage, etc.). The snippet's Summarize findings step prints them as CVE-… | Package … is vulnerable to ….
Fix — bump axios to a recent patched version. Don't pick the lowest version that closes a single CVE; pick the latest stable patch level (CVEs in the same package keep coming):
"dependencies": {
- "axios": "1.6.0"
+ "axios": "1.15.2"
}Then regenerate the lockfile so it stays in sync with package.json:
cd code
rm -f package-lock.json
npm install --package-lock-only
cd ..
git add code/package.json code/package-lock.jsonTip:
npm view axios versionslists the available versions. At the time of writing1.15.2was clean; check the latest before committing.