How SAST Addresses the OWASP Top 10 Vulnerabilities

Static Code Analysis for Security: How SAST Addresses the OWASP Top 10 Vulnerabilities

Security vulnerabilities are easier to prevent than to fix. A SQL injection flaw caught in a code review costs minutes to correct. The same flaw discovered after a breach costs weeks of incident response, regulatory investigation, and remediation, plus whatever data was exfiltrated in between. Static code analysis is the discipline of finding these problems before the code runs, by examining source code structure, data flow, and patterns against known vulnerability signatures. Applied systematically, it converts security from a reactive practice into a built-in property of the development process.

The OWASP Top 10 is the authoritative catalog of the most critical web application security risks, updated by the Open Web Application Security Project based on real-world vulnerability data across thousands of applications. Every item on the list is detectable, to varying degrees, by static analysis tools. This guide maps each OWASP category to what static analysis can find, shows the vulnerable code pattern and its secure equivalent, and explains which tools apply each technique.

Find Injection Before Attackers Do

SMART TS XL traces vulnerabilities from JavaScript APIs through Java services to COBOL backends.

More Info

What Is Static Application Security Testing (SAST)?

Static Application Security Testing, SAST, analyzes source code, bytecode, or binary without executing the program. The analysis examines how data flows through the application, which security-sensitive operations that data reaches, and whether any path from an external input (user input, HTTP parameters, file contents, environment variables) leads to a dangerous operation (database query, system command, HTML output, cryptographic function) without appropriate validation or sanitization.

SAST is one layer in a complete application security program. Understanding where it fits requires comparing it to the alternatives:

ApproachWhen It RunsWhat It FindsWhat It Misses
SAST (Static)Before execution, on source codeCode-level vulnerabilities, injection patterns, cryptographic misuse, hardcoded secretsRuntime-only vulnerabilities, configuration issues in deployment
DAST (Dynamic)Against a running applicationRuntime behavior, authentication flaws, server configuration issuesCode-level patterns not triggered during testing
SCA (Software Composition Analysis)On dependency manifestsKnown CVEs in third-party librariesCustom code vulnerabilities
IAST (Interactive)During test execution with instrumentationRuntime data flows with high accuracyRequires running application, slower feedback

SAST provides the earliest feedback, it runs on code that has not yet been deployed, in the CI/CD pipeline or even in the IDE, before the vulnerability ever reaches a test environment. This earliness is its primary security value.

What Types of Threats Can Static Code Analysis Mitigate?

This is one of the most-searched questions about SAST. The direct answer:

Static code analysis can mitigate threats that manifest in source code patterns: injection vulnerabilities (SQL, command, XSS), cryptographic misuse, hardcoded credentials, insecure authentication implementations, broken access control in code logic, and data integrity failures. It cannot mitigate threats that arise from runtime configuration, network topology, or infrastructure setup, those require DAST, penetration testing, or infrastructure security scanning.

Static Analysis vs. Dynamic Analysis for OWASP Coverage

Dynamic analysis (DAST) and static analysis (SAST) find different subsets of OWASP vulnerabilities. Neither covers everything. The OWASP Web Security Testing Guide (WSTG) is the methodology framework for dynamic testing; SAST tools like CodeQL, Semgrep, and SonarQube address the source code layer.

OWASP Top 10 CategorySAST CoverageDAST Coverage
Broken Access ControlPartial, code logic gapsGood, runtime behavior testing
Cryptographic FailuresStrong, algorithm detectionWeak, hard to observe from outside
InjectionStrong, taint analysisStrong, active payload testing
Insecure DesignPartial, pattern detectionWeak, requires design knowledge
Security MisconfigurationPartial, config in codeStrong, live environment testing
Vulnerable ComponentsWeak, SCA is betterWeak, SCA is better
Authentication FailuresPartial, hardcoded creds, weak patternsStrong, session and auth testing
Data Integrity FailuresPartial, deserialization patternsWeak, hard to detect externally
Logging FailuresPartial, missing log statementsWeak, hard to observe absence
SSRFStrong, taint to HTTP callStrong, active request testing

The conclusion: SAST and DAST are complementary. For maximum OWASP coverage, run both. For budget-constrained teams starting from zero, SAST first, it provides the fastest developer feedback and the broadest injection coverage.

OWASP Top 10: What Static Analysis Finds and How

A01, Broken Access Control

Broken access control is the top OWASP risk. Static analysis addresses the code-level manifestations: missing authorization checks on functions and endpoints, object references that expose internal IDs without validation, and hardcoded role assignments that bypass intended permission structures.

What static analysis detects: Methods that handle sensitive operations without a corresponding authorization check; direct object references where the object ID comes from user input without access validation; force-browsing patterns where authenticated state is checked but object ownership is not.

java

// Vulnerable: no ownership check -- any authenticated user can access any order
@GetMapping("/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId) {
    return orderRepository.findById(orderId).orElseThrow();
}

// Secure: verify the order belongs to the requesting user
@GetMapping("/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId,
                      @AuthenticationPrincipal UserDetails user) {
    Order order = orderRepository.findById(orderId).orElseThrow();
    if (!order.getOwnerId().equals(user.getUserId())) {
        throw new AccessDeniedException("Order does not belong to requesting user");
    }
    return order;
}

What static analysis cannot catch: Runtime access control failures where the logic is correct but the data used to make the decision is compromised. DAST and penetration testing are needed for these.

A02, Cryptographic Failures

Weak cryptography is reliably detectable by static analysis because the vulnerable patterns, MD5, SHA-1, DES, ECB mode, hardcoded keys, are lexically identifiable in source code.

python

# Vulnerable: MD5 for password hashing (broken algorithm)
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()

# Vulnerable: hardcoded encryption key
KEY = b"mysecretkey12345"
cipher = AES.new(KEY, AES.MODE_ECB)  # ECB mode also vulnerable

# Secure: bcrypt for passwords, environment-sourced keys
import bcrypt, os
password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))

# Secure: AES-GCM with environment-sourced key
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
key = os.environ["ENCRYPTION_KEY"].encode()
aesgcm = AESGCM(key)

Static analysis rules flag: MD5/SHA-1 for security-sensitive purposes, DES/3DES/RC4/ECB mode, hardcoded cryptographic keys and secrets, HTTP instead of HTTPS for sensitive data transmission, and disabled certificate verification (verify=False in Python requests, setHostnameVerifier(ALLOW_ALL_HOSTNAME_VERIFIER) in Java).

A03, Injection

Injection, SQL, OS command, LDAP, XSS, template injection, is the category where interprocedural taint analysis provides its most direct value. The vulnerability requires tracking untrusted input from its source through function calls to a dangerous sink.

javascript

// Vulnerable: direct string interpolation in SQL (SQL injection)
app.get('/users', async (req, res) => {
    const name = req.query.name;
    const result = await db.query(`SELECT * FROM users WHERE name = '${name}'`);
    res.json(result.rows);
});

// Secure: parameterized query
app.get('/users', async (req, res) => {
    const name = req.query.name;
    const result = await db.query('SELECT * FROM users WHERE name = $1', [name]);
    res.json(result.rows);
});

php

// Vulnerable: unescaped output (XSS)
echo "Welcome, " . $_GET['username'];

// Secure: context-appropriate escaping
echo "Welcome, " . htmlspecialchars($_GET['username'], ENT_QUOTES, 'UTF-8');

csharp

// Vulnerable: command injection in C#
var process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = "/c " + userInput;
process.Start();

// Secure: avoid shell interpretation, validate and whitelist inputs
var allowedCommands = new HashSet<string> { "report", "export" };
if (!allowedCommands.Contains(userInput))
    throw new ArgumentException("Invalid command");

Tools that perform interprocedural taint analysis for injection: CodeQL (most precise), Semgrep with taint mode, Snyk Code, SonarQube with security rules.

A04, Insecure Design

Insecure design is the most difficult OWASP category for static analysis because it concerns architectural decisions rather than code patterns. Static analysis can flag the symptoms:

Missing rate limiting logic on authentication endpoints, absence of account lockout after failed attempts, business logic that skips validation steps, and functions that perform privileged operations without audit logging. These are detectable as missing patterns, static analysis that reports on what is absent rather than what is present.

Some SAST tools support custom rules that can encode organizational security design requirements: every controller method must call an authorization function, every database write must be preceded by input validation, every external API call must use a timeout. These custom rules convert design requirements into enforceable code constraints.

A05, Security Misconfiguration

Security misconfiguration in code includes: disabled security features, permissive CORS headers, missing security response headers, debug mode enabled in production, and verbose error messages that expose stack traces.

python

# Vulnerable: Flask debug mode enables interactive debugger in production
app = Flask(__name__)
app.run(debug=True)  # exposes console access if error occurs

# Vulnerable: overly permissive CORS
from flask_cors import CORS
CORS(app, origins="*")  # allows any origin

# Secure: environment-controlled debug, restricted CORS
import os
debug_mode = os.environ.get("FLASK_DEBUG", "false").lower() == "true"
CORS(app, origins=os.environ.get("ALLOWED_ORIGINS", "").split(","))
app.run(debug=debug_mode)

Static analysis rules flag: debug mode set to True in source, wildcard CORS origins, missing security headers in HTTP response configurations, disabled SSL certificate verification, and default credentials in configuration files.

A06, Vulnerable and Outdated Components

This category is primarily addressed by Software Composition Analysis (SCA) rather than traditional SAST. SCA scans package.json, pom.xml, requirements.txt, and similar manifests against vulnerability databases (National Vulnerability Database, GitHub Advisory Database).

SAST contributes by identifying deprecated API usage, calls to library functions that have been superseded due to security flaws, or direct use of vulnerable patterns that even up-to-date libraries no longer recommend.

Tools specifically for A06: npm audit, pip-audit, snyk test, OWASP Dependency-Check, GitHub Dependabot, and Mend (formerly WhiteSource).

A07, Identification and Authentication Failures

Static analysis finds the code-level authentication anti-patterns: hardcoded passwords, weak password validation, session tokens generated with non-cryptographic randomness, missing session invalidation on logout, and JWT implementations that accept the none algorithm.

javascript

// Vulnerable: JWT accepting 'none' algorithm -- allows signature bypass
const decoded = jwt.verify(token, secret, { algorithms: ['HS256', 'none'] });

// Vulnerable: hardcoded admin credentials
if (username === 'admin' && password === 'admin123') {
    grantAccess();
}

// Secure: algorithm whitelist, no hardcoded credentials
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
    algorithms: ['HS256']  // explicit allowlist only
});

csharp

// Vulnerable: weak random for session token generation in C#
var sessionToken = new Random().Next().ToString();

// Secure: cryptographically secure random
using var rng = RandomNumberGenerator.Create();
var bytes = new byte[32];
rng.GetBytes(bytes);
var sessionToken = Convert.ToBase64String(bytes);

A08, Software and Data Integrity Failures

This category covers insecure deserialization and unverified software updates. Static analysis detects: Java ObjectInputStream deserializing data from untrusted sources, Python pickle.loads() on external data, PHP unserialize() with user-controlled input, and YAML parsers using unsafe loaders.

python

# Vulnerable: pickle deserialization of untrusted data
import pickle
data = pickle.loads(request.data)  # arbitrary code execution risk

# Vulnerable: unsafe YAML loader
import yaml
config = yaml.load(user_input)  # yaml.load without Loader is unsafe

# Secure: safe alternatives
import json
data = json.loads(request.data)  # JSON cannot execute code

import yaml
config = yaml.safe_load(user_input)  # safe_load disables arbitrary object creation

A09, Security Logging and Monitoring Failures

Logging failures are detectable by static analysis as absent patterns: sensitive operations, authentication events, access control decisions, data modifications, that proceed without accompanying log statements. Static analysis rules can require that certain function calls always co-occur with audit log calls.

What static analysis flags: logging of passwords or tokens (logging sensitive data is also a vulnerability), exception handlers that silently swallow errors without logging, and catch blocks that log the raw exception message (which may contain sensitive data).

java

// Vulnerable: swallowed exception, no logging
try {
    authenticateUser(username, password);
} catch (Exception e) {
    // silent failure -- no log, no audit trail
}

// Vulnerable: logging sensitive data
log.info("User logged in with password: " + password);

// Secure: log the event, not the credential
try {
    authenticateUser(username, password);
    auditLog.info("Authentication success for user: {}", username);
} catch (AuthenticationException e) {
    auditLog.warn("Authentication failure for user: {}", username);
    throw e;  // do not swallow
}

A10, Server-Side Request Forgery (SSRF)

SSRF is a strong case for interprocedural taint analysis. The vulnerability requires tracking user-controlled input through to an HTTP request, often through multiple function calls. A tool that only analyzes individual functions cannot detect SSRF when the URL construction and the HTTP call are in different functions.

python

# Vulnerable: user-controlled URL in HTTP request (SSRF)
import requests

def fetch_resource(url):
    return requests.get(url).content  # no validation

def api_endpoint(request):
    target = request.json().get("url")    # attacker controls this
    return fetch_resource(target)          # SSRF across function boundary

# Secure: allowlist validation before making the request
from urllib.parse import urlparse

ALLOWED_HOSTS = {"api.internal.example.com", "cdn.example.com"}

def fetch_resource(url: str) -> bytes:
    parsed = urlparse(url)
    if parsed.hostname not in ALLOWED_HOSTS:
        raise ValueError(f"URL host not allowed: {parsed.hostname}")
    return requests.get(url, timeout=5).content

Static Code Analysis Tools for OWASP Security

The table below maps the major SAST tools to the OWASP categories they address most effectively:

ToolPrimary LanguagesOWASP StrengthsApproach
CodeQLJava, JS/TS, Python, C/C++, Go, RubyA03 Injection, A10 SSRF (deep taint)Interprocedural semantic analysis
Semgrep30+ languagesA03, A02, A07 (pattern-based + taint mode)Pattern matching + shallow taint
Snyk CodeJava, JS/TS, Python, C#A03, A07, A08ML-based taint analysis
SonarQube30+ languagesA02, A03, A05, A07, A09Rule-based + data flow
Checkmarx30+ languagesFull OWASP coverageInterprocedural taint
VeracodeJava, .NET, JS, PHPFull OWASP coverageBytecode + taint analysis
OWASP ZAPLanguage-agnosticA01, A05, A07 (runtime)DAST, dynamic testing
SMART TS XLCOBOL, JCL, Java, Python, RPG, SQLCross-language taint, dependency riskCross-language structural + taint

How SMART TS XL Addresses Security in Enterprise Codebases

Enterprise security programs face a challenge that single-language SAST tools cannot solve: the attack surface spans languages. A web application may accept user input in JavaScript, process it in Java, pass it through a message queue to a COBOL program that executes SQL against a DB2 database. The injection vulnerability exists across four language boundaries. No individual language scanner sees the complete taint path.

SMART TS XL’s static code analysis covers every language in this chain simultaneously. When untrusted input flows from a JavaScript API handler through a Java service into a COBOL program that constructs a dynamic SQL query, SMART TS XL traces that path across the full cross-language call graph, the same interprocedural taint tracking that CodeQL applies within Java, applied across Java, COBOL, and SQL together.

The application dependency mapping capability provides the security-relevant view of which components in the system interact with external inputs, which reach privileged operations, and what the complete attack surface of the system actually looks like. This architectural security view is the foundation for threat modeling, you cannot model threats against a system whose structure you do not understand.

The impact analysis capability serves security remediation programs: when a vulnerability is found in a component, impact analysis identifies every other component that depends on the vulnerable one, scoping the remediation effort accurately before any code is changed. For large legacy codebases where a COBOL program with a security flaw is included by hundreds of other programs, knowing the complete remediation scope before starting is the difference between a managed fix and a cascading incident.

For organizations managing legacy modernization programs, security analysis of the legacy codebase is a prerequisite, not an afterthought. Migrating vulnerable COBOL programs to Java produces vulnerable Java programs. SMART TS XL’s security analysis ensures that the vulnerabilities are identified and remediated as part of the modernization process, not discovered in the migrated system.

Integrating SAST Into the Development Lifecycle

Static security analysis provides its maximum value when it runs at the point of decision, where code is being written and reviewed, not after it has been deployed.

In the IDE: SonarLint, Snyk’s IDE extensions, and CodeQL’s VS Code extension surface findings inline as developers write code. A SQL injection flag that appears the moment a developer types the vulnerable pattern costs seconds to fix.

In pull requests: SAST integrated into GitHub Actions, GitLab CI, or Jenkins runs on every pull request and posts findings as inline code review comments. The developer sees the finding in context, alongside the code that caused it.

As a quality gate: SonarQube’s quality gate model blocks merges when new critical security hotspots are introduced. This makes security not an optional review step but a structural requirement of the merge process.

On a scheduled basis: Deep interprocedural analysis, CodeQL, Checkmarx, full Semgrep rule sets, is typically too slow for per-commit execution but runs nightly or weekly on the main branch, finding vulnerabilities that require full call graph analysis to detect.

The layered approach, fast pattern-based rules in the IDE and on commits, deep taint analysis on pull requests and nightly, provides both the immediacy developers need and the thoroughness security programs require.