Qodana / Static Code Analysis Guide / What Is Taint Analysis?

What Is Taint Analysis?

When you’re building applications that handle untrusted inputs, especially anything exposed to the public internet, you’re constantly navigating the boundary between what’s trustworthy and what isn’t. Taint analysis exists to enforce that boundary.

It’s one of the most powerful techniques in static code analysis, and Qodana uses it to help developers spot vulnerabilities early, long before they ever appear in production.

This guide breaks down how taint analysis works, why it matters, and how it fits into modern development and CI pipelines.

The core idea behind Taint Analysis

Taint analysis traces the flow of untrusted data (“tainted”) through your application to determine whether it can reach sensitive or dangerous operations (“sinks”) without proper validation or sanitization.

In other words, it answers a simple question:

Can external input reach a dangerous place in my code?

If yes, that’s a vulnerability.

This includes classic issues like SQL injection, path traversal, command injection, and insecure deserialization - but also modern framework-specific issues where data flows through layers of abstractions, async calls, serializers, or template engines.

What counts as “tainted”?

Tainted data is anything your application cannot inherently trust. Typically, this includes:

  • User input from forms, JSON bodies, cookies or headers
  • CLI arguments
  • Deserialized or decoded objects
  • Messages from queues or external systems
  • Data pulled from partially trusted sources (e.g., internal APIs that still may receive user-controlled data)

The precise definitions vary per language and framework, and static analysis tools such as Qodana maintain language-specific models to capture these flows accurately.

Sources, sinks, and sanitizers: The three pillars

Although taint analysis can get deeply technical, nearly every engine uses the same conceptual model.

Sources

A source is where tainted data enters the program. For example:

val username = request.getParameter("username")

Sinks

A sink is an operation that becomes dangerous if it receives untrusted input. Examples include:

  • Database queries
  • Command execution
  • File-system operations
  • HTML rendering
  • Logging frameworks e.g. vulnerable to log injection

URL or redirect constructors

Sanitizers

Sanitizers are operations that validate, escape, filter, or structurally transform dangerous input. Proper sanitization stops the taint flow.

A good taint engine understands not just simple sanitization (e.g., escapeHtml()) but also contextual sanitization (e.g., SQL escaping differs from HTML escaping).

Why Taint Analysis is difficult

Real-world systems rarely operate in simple straight-line code. Modern applications include:

  • Dependency injection and reflection
  • Multithreading and asynchronous execution
  • Framework conventions and annotations
  • Nested lambdas and higher-order functions
  • Serialization layers
  • 3rd-party libraries

A taint engine must track the flow across all of these. That means understanding function arguments, return values, object fields, collections, streams, higher-level framework behaviors, and custom frameworks without explicit annotations.

Qodana’s language models include deep, framework-aware rules for JVM languages, letting it track taint through Spring, Ktor, Jakarta EE, Micronaut, and more

How Taint Analysis works in practice

1. Build a graph of data flow

Static analysis engines transform your code into representations such as:

  • AST(Abstract syntax tree)
  • Control-flow graphs
  • Data-flow graphs
  • Interprocedural call graphs

These graphs let the engine reason about how data moves across method calls and classes.

2. Propagate taint

The engine tracks where tainted values go, through variable assignments, method returns, objects, collections, streams, and coroutines.

3. Check for paths to sinks

If tainted data reaches a sink without passing through a recognized sanitizer, Qodana flags a vulnerability.

4. Filter out false positives

This is where a mature engine matters, real-world code can generate noisy analysis. Qodana incorporates:

  • Framework-aware heuristics
  • Custom sanitization rules
  • Type inference
  • Nullability and flow-based analysis
  • Constant propagation

So, it catches real bugs without overwhelming developers.

Example: SQL Injection in Kotlin

val id = request.getParameter("id") // source
val query = "SELECT * FROM users WHERE id = $id" // taint flows
jdbcTemplate.execute(query) // sink

Qodana would flag this because untrusted input reaches the SQL execution without sanitization.

A safe version might use prepared statements:

jdbcTemplate.query("SELECT * FROM users WHERE id = ?", id)

Now the sink is protected by a framework-level sanitizer.

How Qodana extends Taint Analysis

Qodana builds upon the JetBrains IDE engine, the same engine that powers IntelliJ IDEA’s intelligent inspections, and extends it for CI, team workflows, and large-scale repositories.

Qodana’s taint analysis includes:

  1. Cross-repository taint tracking
  2. IDE-to-CI consistency (no “surprises” at build time)
  3. Support for Java & Kotlin frameworks
  4. Detection of dangerous dependency usage
  5. Custom policies for high-security environments
  6. Shift-left workflows (IDE → PR → CI → dashboard)

It also integrates taint analysis results into broader security features:

✓ Vulnerability analysis
✓ Malicious dependency detection
✓ License compliance
✓ Security trend reports
✓ Organization-wide risk dashboards

This gives teams a single, unified security view of their code.

See Qodana’s taint analysis in action with this WebGoat demo by JetBrains security expert, Greg.

Even if you think your input is safe, taint analysis often exposes surprising flows that developers didn’t realize existed.

Best practices when using Taint Analysis

Good taint analysis complements good engineering habits. For example:

  • Validate input as early as possible
  • Prefer framework-level sanitization (e.g., prepared statements)
  • Avoid dynamically building queries, file paths, or command strings
  • Review CI results regularly in dashboards
  • Track risk trends over time
  • Encourage developers to fix taint warnings during PR review

Teams using taint analysis consistently see fewer high-impact vulnerabilities and faster remediation times.

Taint analysis: A key tool in modern secure development

As applications scale and architectures get more distributed, taint analysis becomes essential. It provides a systematic way to prevent injection flaws and unsafe flows - especially in environments where developers rely on AI-assisted code generation.

Since Qodana’s inception, we’ve had Taint Analysis for PHP but now support has expanded. Qodana’s taint engine brings this capability directly into your IDE, CI/CD pipelines, and organization-wide dashboards, helping teams detect vulnerabilities early, at the point of development.

Watch the Taint Analysis livestream to learn more!