How AST & Static Analysis Work
Understanding the foundational technology behind ESLint and security linting
How AST & Static Analysis Work
The Gist: ESLint reads your code as a tree of nodes, like a family tree for your code. Security rules then walk through this tree looking for dangerous patternsβall without running a single line.
π³ Try It Live
Type JavaScript code below and watch it transform into an AST in real-time. Click nodes to inspect them:
What just happened? The parser converted your text into a structured tree. ESLint rules walk this tree looking for patterns like TemplateLiteral + SQL keywords = potential injection!
Understanding the Basics
| Concept | Description |
|---|---|
| AST | Abstract Syntax Treeβa structured representation of your code |
| Parser | Converts source code text into an AST |
| Rule | Visitor pattern that inspects specific AST node types |
| Speed | Microseconds per fileβruns on every save |
What is an Abstract Syntax Tree?
An Abstract Syntax Tree (AST) is a tree representation of your source code's structure. Instead of seeing code as text, the AST sees it as a hierarchy of meaningful components.
const message = "Hello, World!";
console.log(message);{
"type": "Program",
"body": [
{
"type": "VariableDeclaration",
"declarations": [{
"type": "VariableDeclarator",
"id": { "type": "Identifier", "name": "message" },
"init": { "type": "Literal", "value": "Hello, World!" }
}],
"kind": "const"
},
{
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": { "type": "Identifier", "name": "console" },
"property": { "type": "Identifier", "name": "log" }
},
"arguments": [{ "type": "Identifier", "name": "message" }]
}
}
]
}The Tree Structure
How ESLint Uses the AST
ESLint follows a systematic process to analyze your code:
Parsing
ESLint uses a parser (like @typescript-eslint/parser for TypeScript) to convert your source code into an AST. This happens file-by-file.
Traversal
ESLint walks through every node in the AST, visiting each one systematically (depth-first). This is called tree traversal.
Rule Matching
Each ESLint rule subscribes to specific node types it cares about. When the traverser visits a matching node, the rule's visitor function is called.
// A simplified ESLint rule structure
module.exports = {
create(context) {
return {
// Called for every CallExpression node
CallExpression(node) {
if (node.callee.name === 'eval') {
context.report({
node,
message: 'Avoid using eval()'
});
}
}
};
}
};Reporting
When a rule detects a pattern violation, it reports the issue with location information, message, and optional fix suggestions.
Security Rule Pattern Matching
Interlace security rules look for specific patterns in the AST that indicate vulnerabilities:
Example: SQL Injection Detection
// β Vulnerable - detected by AST pattern matching
const query = `SELECT * FROM users WHERE id = ${userId}`;The rule looks for:
What the Rule Sees
| AST Node | Type | Security Signal |
|---|---|---|
`SELECT...` | TemplateLiteral | Contains SQL keyword |
${userId} | TemplateElement | Dynamic user input |
| Combined | Pattern Match | SQL Injection Risk |
Visitor Pattern Deep Dive
ESLint rules use the Visitor Patternβa design pattern where you declare which node types you want to "visit":
π― Node Selectors
Rules subscribe to node types like CallExpression, Identifier, or MemberExpression.
π CSS-like Selectors
ESLint supports advanced selectors like CallExpression[callee.name='eval'].
β¬οΈ Enter & Exit
Rules can run code when entering a node (CallExpression) or leaving it (CallExpression:exit).
π Context API
The context object provides source code, scope analysis, and reporting utilities.
Common Node Types for Security Rules
| Node Type | Example Code | Security Use Case |
|---|---|---|
CallExpression | eval(x) | Dangerous function calls |
MemberExpression | obj.innerHTML | Dangerous property access |
TemplateLiteral | `${x}` | String injection points |
AssignmentExpression | a = b | Taint tracking origins |
NewExpression | new Function() | Dynamic code execution |
Scope Analysis
ESLint provides scope analysis to track variable bindings:
// ESLint knows these are different 'x' variables
let x = 1;
function foo() {
let x = 2; // Different scope
return x;
}Security insight: Scope analysis helps security rules track where dangerous values flow, distinguishing between safe local variables and potentially tainted external inputs.
Why Static Analysis is Fast
Static analysis is blazingly fast because it never executes code:
| Aspect | Static Analysis | Runtime Testing |
|---|---|---|
| Execution | Never runs code | Must execute code |
| Speed | ~50-200ms for entire project | Seconds to minutes |
| Coverage | All code paths | Only executed paths |
| When | On every save | On test run |
π External Resources
π³ AST Explorer
Full-featured AST exploration tool
π ESLint Custom Rules
Official guide to writing ESLint rules
π¬ TypeScript AST Viewer
Specialized for TypeScript ASTs
β‘ Key Takeaways
| Concept | What to Remember |
|---|---|
| AST | Code as a tree structure, not text |
| Parsing | Source β AST happens once per file |
| Visitor Pattern | Rules declare which nodes they care about |
| Speed | Analysis is fast because code never runs |
| Pattern Matching | Security rules look for dangerous patterns in the tree |
π Related Topics
| Topic | What You'll Learn |
|---|---|
| Static Analysis Limitations | What AST analysis can and cannot detect |
| ESLint MCP Integration | How AI agents leverage these rules |
| Fixable vs. Non-Fixable Rules | When automated fixes are possible |