Skip to main content
The pre-scanner runs BEFORE the JavaScript parser (acorn) to catch DoS attacks that could crash or hang the parser itself. It enforces mandatory security limits that cannot be disabled.

Basic Usage

import { PreScanner, createPreScannerConfig } from '@enclave-vm/ast';

// Create pre-scanner with AgentScript config (strictest)
const scanner = new PreScanner(createPreScannerConfig('agentscript'));

const result = scanner.scan(userCode);
if (!result.valid) {
  console.log('Pre-scan failed:', result.issues);
  // Don't even attempt to parse - could DoS the parser
}

Why Pre-Scanning?

The JavaScript parser itself can be vulnerable to:
  • Memory exhaustion - Extremely large files
  • Stack overflow - Deeply nested expressions
  • CPU exhaustion - Complex regex patterns
  • Parsing hangs - Malformed Unicode sequences
Pre-scanning catches these before parsing begins.

Mandatory Limits

These limits protect against parser crashes and cannot be overridden:
LimitMaximumPurpose
ABSOLUTE_MAX_INPUT_SIZE100MBPrevents memory exhaustion
ABSOLUTE_MAX_NESTING200 levelsPrevents parser stack overflow
ABSOLUTE_MAX_LINE_LENGTH100,000 charsPrevents minified/obfuscated DoS
ABSOLUTE_MAX_LINES1,000,000Prevents extremely long files
ABSOLUTE_MAX_STRING5MBPrevents huge embedded strings
ABSOLUTE_MAX_REGEX_LENGTH1,000 charsPrevents ReDoS via complex patterns

Pre-Scanner Presets

ConfigAgentScriptSTRICTSECURESTANDARDPERMISSIVE
maxInputSize100KB500KB1MB5MB10MB
maxLineLength2,0005,0008,00010,00050,000
maxLines1,0002,0005,00010,000100,000
maxNestingDepth20304050100
regexModeblockanalyzeanalyzeanalyzeallow
blockBidiPatternsYESYESYESNONO
blockInvisibleCharsYESYESNONONO

Using Presets

import { createPreScannerConfig, PreScanner } from '@enclave-vm/ast';

// AgentScript (strictest)
const agentScript = new PreScanner(createPreScannerConfig('agentscript'));

// Other presets
const strict = new PreScanner(createPreScannerConfig('strict'));
const secure = new PreScanner(createPreScannerConfig('secure'));
const standard = new PreScanner(createPreScannerConfig('standard'));
const permissive = new PreScanner(createPreScannerConfig('permissive'));

Regex Handling Modes

The pre-scanner supports three regex handling modes:

Block Mode

Block ALL regex literals (AgentScript default, maximum security):
const scanner = new PreScanner(createPreScannerConfig('agentscript'));
// regexMode: 'block'

// This will fail:
scanner.scan('const pattern = /foo/;');

Analyze Mode

Allow regex but analyze for ReDoS patterns:
const scanner = new PreScanner(createPreScannerConfig('secure'));
// regexMode: 'analyze'

// Safe patterns allowed:
scanner.scan('const pattern = /^[a-z]+$/;'); // OK

// Dangerous patterns blocked:
scanner.scan('const evil = /(a+)+/;'); // Blocked - nested quantifier

Allow Mode

Allow all regex without analysis (Permissive only):
const scanner = new PreScanner(createPreScannerConfig('permissive'));
// regexMode: 'allow'

// All regex allowed (not recommended for untrusted code)
scanner.scan('const pattern = /(a+)+/;'); // OK

ReDoS Detection

The pre-scanner detects dangerous regex patterns that cause exponential backtracking:
PatternScoreExampleRisk
Nested quantifier90(a+)+Exponential backtracking
Star in repetition85(a+){2,}Exponential backtracking
Repetition in star85(a{2,})+Exponential backtracking
Overlapping alternation80(a|ab)+Exponential backtracking
Greedy backtracking75(.*a)+Polynomial backtracking
Multiple greedy70.*foo.*barPolynomial backtracking

Manual ReDoS Analysis

import { analyzeForReDoS } from '@enclave-vm/ast';

// Analyze a pattern for ReDoS vulnerabilities
const result = analyzeForReDoS('(a+)+', 'catastrophic');
// {
//   vulnerable: true,
//   score: 90,
//   vulnerabilityType: 'nested_quantifier'
// }

Unicode Security

The pre-scanner detects Unicode-based attacks:

Bidirectional Text (Trojan Source)

const scanner = new PreScanner({
  ...createPreScannerConfig('secure'),
  blockBidiPatterns: true,
});

// Blocks code with BiDi control characters that can
// make code appear different than it executes

Invisible Characters

const scanner = new PreScanner({
  ...createPreScannerConfig('strict'),
  blockInvisibleChars: true,
});

// Blocks zero-width spaces, invisible formatting, etc.

Custom Configuration

const scanner = new PreScanner({
  maxInputSize: 200 * 1024,    // 200KB
  maxLineLength: 3000,
  maxLines: 2000,
  maxNestingDepth: 25,
  regexMode: 'analyze',
  blockBidiPatterns: true,
  blockInvisibleChars: true,
});