Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

Code Security Scan (SAST/SCA)

⏱ ~25 min (SAST + SCA) · 📍 Module 2 of 7

1234567

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.

Why is Code Security Important?

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.

Common Code Security Issues

SAST Findings:

  • 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

SCA Findings:

  • Known Vulnerabilities - CVEs in dependencies
  • Outdated Packages - Dependencies with security updates
  • License Issues - Non-compliant licenses
  • Supply Chain Risks - Malicious packages

Tools Used in This Module

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.

Learning Objectives

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

Security Checklist

  • SAST integrated into CI/CD pipeline
  • SCA scanning for all dependencies
  • Regular dependency updates
  • Security hotspots addressed
  • False positive management
  • Security metrics tracking

References

Solutions (spoilers — open only when stuck)

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 in code/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.json

Tip: npm view axios versions lists the available versions. At the time of writing 1.15.2 was clean; check the latest before committing.