AST-based evasion detection for OpenClaw skills.
Catches obfuscation, dynamic eval, and encoding tricks that regex patterns miss.
OpenClaw's built-in skill scanner uses regex patterns to detect dangerous code. But regex has a fundamental weakness: it matches strings, not structure. A malicious skill author can bypass regex by obfuscating their code.
This plugin adds a TypeScript AST analysis layer that catches structural evasion patterns:
| Rule ID | Severity | What it catches |
|---|---|---|
dynamic-import |
🔴 critical | import(variable), import(\${template}`)` — dynamic module loading |
dynamic-require |
🔴 critical | require(expr) with non-literal args — concatenated module names |
indirect-eval |
🔴 critical | globalThis["eval"](...), (0, eval)(...), window.Function(...) |
prototype-pollution |
🔴 critical | __proto__ assignment, constructor.prototype, Object.setPrototypeOf() |
These all bypass regex but are caught by this plugin:
// Concatenated require — regex sees no module name
const cp = require("child_" + "process");
cp.exec("whoami");
// Variable import — regex can't match the specifier
const target = getPayload();
await import(target);
// Indirect eval via comma operator — regex won't match "eval" as a call
(0, eval)("malicious code here");
// globalThis bracket notation — bypasses direct "eval" keyword check
globalThis["eval"]("malicious code here");
// Prototype pollution
obj["__proto__"]["isAdmin"] = true;openclaw plugins install @kobepaw/openclaw-skill-scannerAdd to your OpenClaw config:
{
"plugins": {
"entries": {
"skill-scanner": {
"enabled": true,
"blockOnDetection": false
}
}
}
}| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable/disable AST scanning |
blockOnDetection |
boolean | false |
Block skill install on findings (warn-only by default) |
The plugin uses TypeScript's built-in parser (typescript package) — zero additional dependencies beyond what's already in your OpenClaw install.
When a skill is scanned:
- TypeScript's
createSourceFileparses the source into an AST - Four structural detectors walk the AST looking for dangerous patterns
- Findings are reported with file path, line number, and evidence snippet
- Deduplication: one finding per rule per file keeps output actionable, not noisy
- Graceful degradation: if parsing fails, a
warn-levelast-parse-errorfinding is emitted
- Complements, doesn't replace — works alongside existing regex scanners as a second layer
- Zero new dependencies — TypeScript is already a project dependency in most OpenClaw setups
- Correct ScriptKind mapping —
.tsx→TSX,.jsx→JSX,.js/.mjs/.cjs→JS, everything else→TS - One finding per ruleId per file — signal over noise
process.binding()native access not detectedsetTimeout("code", 0)string eval not detectedFunction.prototype.constructor("code")()not detected- Legitimate
require(path.join(__dirname, "config"))will flag asdynamic-require— acceptable trade-off for security
Issues and PRs welcome: github.com/kobepaw/openclaw-skill-scanner
Apache-2.0 © kobepaw