Skip to content

Latest commit

 

History

History

README.md

Vulnerability Scanning: CVE Database Lookups, SAST Pattern Matching, and Severity-Based Release Gating

Your CI pipeline runs three security scanners. Snyk finds a CRITICAL CVE in log4j-core 2.14.0. SonarQube finds a hardcoded password. Trivy finds 15 vulnerabilities in your node:latest container. Each tool generates its own report, its own severity scale, its own dashboard. Nobody consolidates them. This example builds a unified vulnerability scanning pipeline using Conductor that runs SAST code analysis, SCA dependency scanning against a real CVE database (Log4Shell, Spring4Shell, Jackson deserialization, Commons Text), and container image analysis -- then triages all findings into a single prioritized report that gates releases when critical vulnerabilities are present.

The Pipeline

vs_scan_code  -->  vs_scan_dependencies  -->  vs_scan_containers  -->  vs_triage_findings

The scan workers operate independently on different inputs. The triage worker consolidates dependencyFindings and codeFindings into a unified risk assessment.

SAST Code Scanning

ScanCodeWorker scans source code strings against four vulnerability patterns using compiled java.util.regex.Pattern:

Pattern Name Regex What It Catches
SQL_INJECTION (?i)(execute|query).*\\+.*\\b(request|input|param) String concatenation in SQL queries
HARDCODED_SECRET (?i)(password|secret|apikey|api_key)\\s*=\\s*"[^"]+" Credentials in source code
INSECURE_RANDOM new\\s+Random\\(\\) java.util.Random instead of SecureRandom
EVAL_INJECTION (?i)(eval|exec)\\s*\\( Dynamic code execution

The SQL injection pattern specifically looks for string concatenation (+) between a query method and user input -- catching db.execute("SELECT * FROM users WHERE id=" + request.getParameter("id")) but not parameterized queries. Clean code like int x = 42; System.out.println(x); returns zero findings.

Dependency Scanning Against a CVE Database

ScanDependenciesWorker checks dependencies against an embedded vulnerability database with real CVE entries:

Artifact CVE CVSS Severity Fixed In
log4j-core CVE-2021-44228 10.0 CRITICAL 2.17.0
spring-core CVE-2022-22965 9.8 CRITICAL 5.3.18
jackson-databind CVE-2020-36518 7.5 HIGH 2.13.2.1
commons-text CVE-2022-42889 9.8 CRITICAL 1.10.0

Version comparison uses a real semver-aware algorithm that splits on . and -, parsing each segment as an integer:

static int compareVersions(String v1, String v2) {
    String[] p1 = v1.split("[.-]");
    String[] p2 = v2.split("[.-]");
    for (int i = 0; i < Math.max(p1.length, p2.length); i++) {
        int a = 0, b = 0;
        if (i < p1.length) try { a = Integer.parseInt(p1[i]); } catch (NumberFormatException ignored) {}
        if (i < p2.length) try { b = Integer.parseInt(p2[i]); } catch (NumberFormatException ignored) {}
        if (a != b) return a - b;
    }
    return 0;
}

So log4j-core:2.14.0 is vulnerable (below 2.17.0), but log4j-core:2.21.0 is clean. Dependencies not in the database (like guava:33.0.0) return zero findings.

Container Image Analysis

ScanContainersWorker evaluates container images based on base image characteristics:

Image Type Estimated Vulns Recommendation
:latest or no tag 15 "Pin to specific version"
distroless 0 "No action needed"
alpine 3 "No action needed"
Other 8 "No action needed"

The usesLatestTag flag is set when the image ends with :latest or contains no : at all.

Triage and Release Gating

TriageFindingsWorker merges dependency and code findings, counts by severity, and makes a release decision:

boolean blockRelease = critical > 0 || high > 2;

Any CRITICAL finding blocks the release. More than 2 HIGH findings also block. The worker validates CVSS scores are in the [0.0, 10.0] range and rejects findings with invalid scores.

An optional minimumSeverity filter (CRITICAL, HIGH, MEDIUM, LOW) returns only findings at or above the threshold. The severity ordering is CRITICAL > HIGH > MEDIUM > LOW -- setting minimumSeverity: "CRITICAL" filters out all HIGH/MEDIUM/LOW findings from filteredFindings while still counting them in totalFindings.

Test Coverage

MainExampleTest (21 tests):

  • Dependency scanning: known vulnerable version detected, updated version clean, clean deps return zero, empty list OK, missing/invalid dependencies type, missing artifact ID, missing version, CVSS score included and validated, CVSS validation boundaries (0.0, 5.5, 10.0 valid; -0.1, 10.1 invalid)
  • Code scanning: SQL injection detected, clean code returns zero, missing code input
  • Container scanning: :latest tag detected (15 vulns), distroless is clean (0 vulns), missing image
  • Triage: missing findings, empty = LOW risk, CRITICAL blocks release, CRITICAL-only filter, invalid severity name, invalid CVSS rejected

VulnScanIntegrationTest (3 tests):

  • Full pipeline with vulnerable deps (log4j + jackson) producing CRITICAL risk and release block
  • Full pipeline with clean deps producing LOW risk and no block
  • CRITICAL-only filter in pipeline: 2 total findings but only 1 passes CRITICAL filter

24 tests total covering the full scanning pipeline, CVE database lookups, and release gating logic.


How to run: See RUNNING.md | Production guidance: See PRODUCTION.md