Interlace ESLint
ESLint Interlace

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:

🌳Interactive AST Explorer
πŸ“JavaScript Code
🌳Abstract Syntax TreeClick nodes to inspect
β–ΌπŸ“¦Program
β–ΌπŸ“VariableDeclaration
β–ΌπŸ“ŽVariableDeclarator
🏷️Identifier"message"
πŸ’ŽLiteral"Hello!"
β–ΌπŸ“„ExpressionStatement
β–ΌπŸ“žCallExpression
β–ΆπŸ”—MemberExpression
🏷️Identifier"message"
πŸ’‘ Tip: ESLint rules use the AST to find patterns. A TemplateLiteral with SQL keywords and expressions = potential injection!

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

ConceptDescription
ASTAbstract Syntax Treeβ€”a structured representation of your code
ParserConverts source code text into an AST
RuleVisitor pattern that inspects specific AST node types
SpeedMicroseconds 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 NodeTypeSecurity Signal
`SELECT...`TemplateLiteralContains SQL keyword
${userId}TemplateElementDynamic user input
CombinedPattern MatchSQL 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 TypeExample CodeSecurity Use Case
CallExpressioneval(x)Dangerous function calls
MemberExpressionobj.innerHTMLDangerous property access
TemplateLiteral`${x}`String injection points
AssignmentExpressiona = bTaint tracking origins
NewExpressionnew 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:

AspectStatic AnalysisRuntime Testing
ExecutionNever runs codeMust execute code
Speed~50-200ms for entire projectSeconds to minutes
CoverageAll code pathsOnly executed paths
WhenOn every saveOn test run

πŸ”— External Resources

⚑ Key Takeaways

ConceptWhat to Remember
ASTCode as a tree structure, not text
ParsingSource β†’ AST happens once per file
Visitor PatternRules declare which nodes they care about
SpeedAnalysis is fast because code never runs
Pattern MatchingSecurity rules look for dangerous patterns in the tree
TopicWhat You'll Learn
Static Analysis LimitationsWhat AST analysis can and cannot detect
ESLint MCP IntegrationHow AI agents leverage these rules
Fixable vs. Non-Fixable RulesWhen automated fixes are possible

On this page