DEV Community

Rushabh Shah
Rushabh Shah

Posted on Originally published at depwarden.in

What is SAST? A developer's guide to static application security testing

Static Application Security Testing (SAST) is the practice of analyzing source code — or bytecode, or compiled binaries — for security vulnerabilities without executing the program. A SAST tool reads your code the way a security-aware compiler would: following execution paths, tracking data flow, and flagging patterns that match known vulnerability classes. The key word is static: analysis happens offline, on source files, with no running server or test environment required.

That property makes SAST the right tool for catching security bugs when they're cheapest to fix: at the moment a developer writes the code, on every commit, in the CI pipeline before a PR is merged.

How SAST works: three technical approaches

1. Pattern matching (regex-based)

The simplest approach is pattern matching: define a regular expression for a dangerous construct (eval(, System.exec(, password =) and flag every match. This is fast, easy to write rules for, and works across any language without a parser. The downside is false positives: eval( appears in safe code too, and a regex can't distinguish whether the argument to eval is attacker-controlled or a hardcoded constant.

2. Abstract Syntax Tree (AST) analysis

More sophisticated tools parse source code into an AST — a structured tree representing the code's syntax — and analyze patterns within that structure. An AST rule can express "find all method calls to query() where the first argument is a concatenation involving a non-constant value," which is far more precise than a regex. AST analysis understands code shape rather than raw text.

3. Taint analysis / data flow

The most powerful approach is taint analysis: the tool tracks data from untrusted sources (HTTP request parameters, form inputs, user-uploaded content) through the code until it reaches a dangerous sink (database queries, shell commands, HTML output). A taint finding fires only when there is a confirmed path from source to sink with no sanitization in between.

Taint analysis is how serious SQL injection, XSS, and command injection detections work. It requires building a data flow graph across the codebase, which is more expensive than pattern matching but dramatically reduces false positives. DepWarden's SAST engine combines all three: pattern rules for broad coverage, tree-sitter AST rules for structural precision on supported languages, and an intra-file taint pass that only fires injection-class rules when user-controlled data actually reaches the sink.

The vulnerability categories SAST covers

SAST tools primarily find code-level vulnerabilities — flaws in the application's own logic:

  • Injection (SQL, NoSQL, LDAP, OS command) — user input reaches a dangerous API call without sanitization. CWE-89, CWE-78.
  • Cross-Site Scripting (XSS) — user input is reflected into HTML output without escaping, enabling script execution in the victim's browser. CWE-79.
  • Insecure deserialization — attacker-controlled data is passed to a deserializer that can instantiate arbitrary objects. CWE-502.
  • Hardcoded credentials — API keys, passwords, tokens, private keys committed directly in source. CWE-798.
  • Weak cryptography — MD5 or SHA-1 for password hashing, ECB mode encryption, 1024-bit RSA keys, hardcoded initialization vectors, DES/RC4 usage. CWE-327, CWE-326.
  • Insecure randomness — Math.random() or rand() for security-sensitive values like tokens or session IDs. CWE-338.
  • Path traversal — user input reaches a file system call without sanitization, allowing arbitrary file access. CWE-22.
  • SSRF — user-controlled URLs are fetched server-side, enabling access to internal services and cloud metadata endpoints. CWE-918.
  • Insecure configuration — DEBUG = True in Django settings, CSRF protection disabled, security headers absent, CORS wildcard with credentials. CWE-16.

SAST, DAST, and SCA: the three-scanner model

These scanner types are complementary, not interchangeable:

Scanner What it finds When it runs
SAST Bugs in your own source code CI, pre-commit, every PR
DAST Bugs exposed at runtime in a deployed app Staging environment, release gate
SCA Vulnerabilities in open-source dependencies CI, pre-commit, every PR

SAST catches bugs in the code you write. SCA catches bugs in the code you import. DAST catches bugs that only manifest when the application is running — misconfigured servers, race conditions, issues SAST can't reach statically. A mature security pipeline uses all three; SAST and SCA run on every commit.

Integrating SAST into CI/CD

The most valuable property of SAST is that it runs without a deployed application. Upload a source zip in the browser, or connect a GitHub, GitLab, Bitbucket or Azure DevOps repo branch and schedule recurring scans so new findings surface without you doing anything.

The key configuration choice is --fail-on. Setting it to critical only blocks the most severe findings; high blocks high and critical. Starting with critical and moving to high once you've baselined your codebase is the practical approach. Failing on medium is usually too noisy until you've tuned suppressions for your codebase.

Managing false positives

The main challenge with SAST is false positives, which create alert fatigue that causes developers to ignore the scanner entirely. Good tooling reduces false positives through:

Taint gating: Only fire injection-class rules when the data flow is confirmed. A rule that matches eval( pattern-only fires on every eval call; a taint-gated rule fires only when the argument comes from user input.

Confidence levels: Separate HIGH-confidence (taint-confirmed, specific pattern, no plausible safe use) from MEDIUM and LOW (pattern-only, more context needed). Treat them differently in CI — block on HIGH, review MEDIUM.

Suppression markers: When a specific finding is safe by design, document it in code:

// depwarden-ignore: eval-user-input — content is from a curated config, not user input
eval(configExpression);
Enter fullscreen mode Exit fullscreen mode

Not-patterns: Rules can include patterns that cancel the match. A "hardcoded password" rule can exclude lines that reference process.env or os.environ, since those are reading from environment variables, not hardcoding.

What DepWarden's SAST covers

DepWarden's static analysis covers:

  • 14 languages: JavaScript, TypeScript, Python, Java, Go, PHP, Ruby, C, C++, C#, Rust, Kotlin, Swift, Shell/Bash
  • IaC: Terraform/HCL, YAML (GitHub Actions, Kubernetes), Dockerfile, config/secret files
  • 300+ rules organized into packs:
    • Core injection, XSS, secrets, weak crypto (applies to all languages)
    • Framework-specific: Django, Spring Boot, Express, Rails, Laravel, ASP.NET Core
    • Advanced cryptography: ECB mode, DES, RC4, hardcoded IV, bcrypt cost, TLS version
    • Shell script security: eval injection, wget/curl pipe exec, world-writable files
    • Mobile: Android WebView, iOS keychain, SSL pinning bypass
    • API & web: JWT algorithm confusion, SSRF, open redirect, GraphQL introspection

Every finding includes CWE and OWASP Top 10 mapping, severity (CRITICAL/HIGH/MEDIUM/LOW), confidence level, remediation guidance, and a redacted code snippet that never exposes actual credentials.

Findings are separated from SCA dependency results and filterable by severity, confidence, category, language, and status. The engine runs in under 10 seconds for most projects.

See also: SAST vs SCA — why you need both, catching typosquats in CI, CVSS, EPSS and KEV guide.

Top comments (0)