AST (Abstract Syntax Tree) analysis uses tree-sitter to parse JavaScript, TypeScript, and Python files. This catches obfuscated malicious patterns that regex-based scanning cannot detect.
vexscan scan ./plugins --astRegex patterns fail on obfuscated code:
// Regex detects this:
eval('malicious code');
// Regex CANNOT detect these:
window['eval']('malicious code'); // Computed property
const e = eval;
e('malicious code'); // Variable aliasing
window['ev' + 'al']('malicious code'); // String concatenation
window["\x65\x76\x61\x6c"]('malicious'); // Escape sequences
(0, eval)('malicious code'); // Comma operator
const {exec: run} = require('child_process');
run('ls'); // Destructured aliasAST analysis understands the code structure and catches all of these.
┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Source │────▶│ Tree-sitter │────▶│ Scope │────▶│ Detectors │
│ Code │ │ Parser │ │ Tracker │ │ │
└─────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
- Parsing: Tree-sitter builds an AST from the source code
- Scope Tracking: Variable bindings are tracked to resolve aliases
- Detection: Specialized detectors analyze specific node types
- Reporting: Findings include the obfuscation technique used
Detects dangerous functions accessed via bracket notation:
// Detected:
window['eval']('code');
globalThis['eval']('code');
window['Function']('return this')();
global['exec']('rm -rf /');
// Not detected (safe):
obj['foo']; // Not a dangerous function
window['console'].log(); // console is safeHow it works: The detector checks if the object is a global (window, globalThis, global) and if the property
being accessed is a dangerous function (eval, Function, exec, etc.).
Detects when dangerous functions are assigned to variables:
// Detected:
const e = eval;
e('malicious');
const fn = eval;
const x = fn; // Chain tracked
x('malicious'); // Still detected!
// Scope tracking:
function outer() {
const e = eval;
function inner() {
e('code'); // Detected via scope chain
}
}How it works: The scope tracker maintains a map of variable bindings. When a variable is assigned to eval or
another dangerous function, it's marked. Later calls using that variable are flagged.
Detects property names built via concatenation:
// Detected:
window['ev' + 'al']('code');
window['exe' + 'c']('cmd');
global['eva' + 'l']('x');
// Static resolution:
const a = 'ev';
const b = 'al';
window[a + b]('code'); // Resolved to 'eval'How it works: The detector identifies binary + expressions inside subscript access. It statically resolves string
literals and checks if the result is a dangerous function name.
Detects property names using escape sequences:
// Detected:
window["\x65\x76\x61\x6c"]('code'); // \x65\x76\x61\x6c = "eval"
window["\u0065\u0076\u0061\u006c"](); // Unicode escapes
window['\x65val']('x'); // Partial escape
// Decoded equivalents:
// \x65 = 'e', \x76 = 'v', \x61 = 'a', \x6c = 'l'How it works: The detector decodes \xHH and \uHHHH escape sequences in string literals, then checks if the
decoded result matches a dangerous function name.
Detects the indirect eval pattern:
// Detected:
(0, eval)('code');
(1, eval)('code');
(null, eval)('code');
// Why this matters:
// Direct: eval('code') - runs in local scope
// Indirect: (0, eval)('code') - runs in global scope
// Attackers use this to escape sandbox restrictionsHow it works: The detector identifies sequence expressions (comma operator) where the last element is a dangerous function, and this expression is being called.
Detects dangerous imports with aliased names:
// Detected:
const {exec: run} = require('child_process');
run('rm -rf /');
const {execSync: execute} = require('child_process');
execute('whoami');
// ES modules too:
import {exec as run} from 'child_process';How it works: The detector tracks destructuring patterns from dangerous modules (child_process, fs, etc.). When
a renamed export is later called, it's flagged.
AstAnalyzerConfig {
enable_javascript: true, // Analyze .js, .mjs, .cjs, .jsx
enable_typescript: true, // Analyze .ts, .tsx, .mts, .cts
enable_python: true, // Analyze .py (limited support)
max_file_size: 1_000_000, // 1 MB limit
max_scope_depth: 10, // Max alias chain depth
}The scope tracker resolves variable references across scopes:
const e = eval; // Binding: e -> DangerousFunction("eval")
function foo() {
const f = e; // Binding: f -> Alias("e") -> "eval"
function bar() {
f('code'); // Resolved: f -> e -> eval
} // ^^^^^^^^^^^^^^^^
} // Scope chain lookupThe tracker maintains:
- Current scope stack
- Variable bindings per scope
- Chain resolution (up to
max_scope_depthhops)
AST findings include additional metadata:
{
"rule_id": "AST-EXEC-001",
"title": "Computed property access to dangerous function",
"metadata": {
"technique": "computed_property",
"dangerous_function": "eval",
"object": "window",
"ast_analyzed": "true"
}
}AST analysis is more expensive than regex:
| File Size | Regex | AST |
|---|---|---|
| 10 KB | ~1ms | ~5ms |
| 100 KB | ~5ms | ~30ms |
| 1 MB | ~50ms | ~300ms |
For this reason, AST analysis is opt-in (--ast). Use it when:
- Scanning untrusted third-party code
- Maximum security is required
- Obfuscation is suspected
- Dynamic evaluation: Cannot analyze runtime-generated code
- Complex flows: Deep data flow across files not tracked
- Encrypted payloads: Encoded strings only detected, not decrypted
- Python support: Currently limited compared to JavaScript/TypeScript
vexscan scan ./obfuscated-plugin --ast
CRITICAL AST-EXEC-001 Computed property access to dangerous function
File: src/loader.js:15
Snippet: window['eval'](payload)
Technique: computed_property
Remediation: Do not use computed access for dangerous functions
CRITICAL AST-EXEC-002 Variable aliasing of dangerous function
File: src/util.js:8
Snippet: const e = eval; ... e(code)
Technique: variable_aliasing
Chain: e -> eval
Remediation: Do not alias eval or similar functions
Found 2 issues (2 critical)
- Static Analysis - Regex-based detection (faster, less comprehensive)
- Encoding Detection - How obfuscated strings are decoded
- Rules Reference - All AST rules (AST-EXEC-, AST-SHELL-)