aletheia-firewall 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.helios-baseline CHANGED
@@ -1 +1 @@
1
- c6e045e5971e51f0a7b32304ed442101f6f4988100e87a0b08b8485e05f4af77
1
+ fea07f1e4d6c145de22dccff70a8069e3c9653441313df2f00bb60796abc9ec9
package/README.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # Aletheia Firewall
2
2
 
3
- Zero-dependency runtime firewall that blocks malicious npm modules at require-time through behavioral detection, Aho-Corasick signature scanning, and policy enforcement.
3
+ Zero-dependency, opt-in runtime enforcement layer for supported Node.js module-loading paths. It
4
+ scans modules as they are compiled/loaded (`require()` of `.js`/`.cjs`, and `import` of `file://`
5
+ ESM on supported Node versions) using Aho-Corasick signature matching, behavioral-sequence
6
+ correlation, and — opt-in — a narrow AST obfuscation tier, then enforces a signed policy. It is
7
+ defense-in-depth, **not** a comprehensive malware blocker: it covers specific module-loading paths
8
+ (see the coverage table below) and a documented set of techniques, with known gaps listed honestly.
4
9
 
5
10
  ## Install
6
11
 
@@ -55,11 +60,30 @@ Aletheia is a **runtime enforcement layer**: it watches what a dependency does o
55
60
  already in your CommonJS require graph, after `npm install` has finished. It is not an
56
61
  install-time scanner and does not intercept package installation.
57
62
 
58
- Detection is signature + behavioral, not AST-based, so payloads can evade static matching
59
- through string-splitting, encoding, or indirection. Our adversarial corpus documents this
60
- honestly: **95/125 (76%) of malicious payloads caught, 30 known bypasses, 0 false positives**
61
- on the current corpus run it yourself from the repository root with `npm run redteam`. Every bypass class is listed
62
- in [`red-team/README.md`](../../red-team/README.md).
63
+ Detection runs in tiers: **signature + behavioral by default**, plus an **opt-in AST obfuscation
64
+ tier** (`FW_ENABLE_AST=1`, off by default pending a broader benign-package soak). By default,
65
+ payloads can still evade static matching through string-splitting, encoding, or indirection; the
66
+ AST tier folds and structurally resolves many of those. Our adversarial corpus documents both
67
+ honestly:
68
+
69
+ | Configuration | Malicious caught | Known bypasses | False positives* |
70
+ |---|---|---|---|
71
+ | **Default** (`FW_ENABLE_AST` unset) | **105/142 (73.9%)** | 37 | 0 / 36 controls |
72
+ | **`FW_ENABLE_AST=1`** (opt-in) | **127/142 (89.4%)** | 15 | 0 / 36 controls |
73
+
74
+ \* "0 false positives" means zero across the **36 curated benign controls** — it is **not** a
75
+ measured general false-positive rate on arbitrary packages, which is one reason the AST tier ships
76
+ opt-in. Run them yourself from the repository root: `npm run redteam` (default) and
77
+ `npm run redteam:ast` (AST tier).
78
+
79
+ **Bounded, prioritized AST scanning.** The AST tier parses a bounded number of candidate spans per
80
+ file and scans them **highest-risk-first** (never in file order), so a payload can't be starved by a
81
+ flood of harmless decoy spans placed ahead of it. If a genuinely high-risk span is left unanalyzed
82
+ because the budget was exhausted, the scan is reported *incomplete* and `FW_AST_INCOMPLETE_POLICY`
83
+ decides what happens (see the environment-variable table).
84
+
85
+ Every bypass class is listed in [`red-team/README.md`](../../red-team/README.md) and the monorepo's
86
+ `docs/THREAT-COVERAGE.md`.
63
87
 
64
88
  ## Environment Variables
65
89
 
@@ -67,6 +91,10 @@ in [`red-team/README.md`](../../red-team/README.md).
67
91
  |----------|---------|-------------|
68
92
  | `FW_ENABLE_DETECTION` | `0` | Set to `1` to activate the firewall (required) |
69
93
  | `FW_ENABLE_BEHAVIORAL` | `1` | Set to `0` to disable the behavioral pass while keeping signature scanning active. Useful as an escape hatch if behavioral detection produces false positives. Note: several detections (credential exfiltration, dynamic-code/exec chains, base64→eval obfuscation) rely on the behavioral pass — disabling it falls back to signature-only coverage. |
94
+ | `FW_ENABLE_AST` | `0` | Set to `1` to enable the opt-in AST obfuscation tier (`src/ast-scan.js`). Folds/resolves bracket-, alias-, unicode-escape-, concat-, decode-, and constructor-based obfuscation of `eval`/`Function`/`require`. Raises malicious coverage from 73.9% to 89.4% on the corpus (see the table above). Off by default pending a broader benign-package soak; it is internally fail-open (any error falls back to signature+behavioral). |
95
+ | `FW_AST_INCOMPLETE_POLICY` | `quarantine` | Only relevant when `FW_ENABLE_AST=1`. Governs what happens when a module is so densely packed with high-risk obfuscation shapes that the AST tier could not fully analyze it (an *incomplete* scan — the span-exhaustion attack shape). Defaults to **fail-closed** (`quarantine`/`block`): an un-analyzable suspicious module is treated as block-tier, so the bypass is closed by default rather than only for operators who opt in. Set `observe` to opt down to WARN-only telemetry (module still runs) if you prefer availability. Only fires on pathological saturation (>256 rare high-risk spans in one module); ordinary large bundles never reach it. |
96
+ | `FW_ENABLE_CROSSFILE` | `0` | Set to `1` to enable cross-file behavioral correlation within a package (capabilities split across files). Off by default: it false-positives on large legitimate packages that legitimately split credential reads, metadata fetches, and code-gen/spawn across files. Intended for curated dependency sets and the registry batch scanner. |
97
+ | `FW_CACHE_POLICY` | `block` under `FW_MODE=enforce`, else `audit` | How `require.cache`/`Module._load` pre-seeding (a forged cache entry that bypasses `_compile` entirely) is handled: `block` refuses the substitution, `audit` allows it but logs, `allow` disables the check. |
70
98
  | `FW_TELEMETRY` | `0` | Set to `1` to start a telemetry worker that POSTs events to `FW_CONTROL_PORT`; with no control plane running it fails open and delivers nothing. |
71
99
  | `FW_CONTROL_PORT` | `3000` | Port for the control plane telemetry ingestion endpoint (`fw-control`). Used by the telemetry worker when `FW_TELEMETRY=1`. |
72
100
  | `FW_MODE` | `dev` | `enforce` fails closed (`process.exit(1)`) when not preloaded via `--require`; `dev` (default) warns loudly and continues. See the root README's "Enforcement mode vs Development mode" section. |
@@ -127,7 +155,8 @@ To reproduce, run the 900-module gate from the GitHub repo: `npm run gate`.
127
155
 
128
156
  ## Known Bypasses
129
157
 
130
- This firewall provides defense-in-depth but cannot catch all threats. Documented bypasses require dynamic or AST-level analysis:
158
+ This firewall provides defense-in-depth but cannot catch all threats. "Status" below is split by
159
+ tier: default (signature + behavioral) and `FW_ENABLE_AST=1` (the opt-in AST tier).
131
160
 
132
161
  | Technique | Status |
133
162
  |-----------|--------|
@@ -138,11 +167,14 @@ This firewall provides defense-in-depth but cannot catch all threats. Documented
138
167
  | `.env`/credential read + network call | **BLOCKED** |
139
168
  | `eval` + `child_process.exec` | **BLOCKED** |
140
169
  | `curl \| bash` in host project's npm scripts | **BLOCKED** (root scripts only; not dependency install hooks) |
141
- | Bracket eval: `this["ev"+"al"]` | **BYPASSES** needs AST analysis |
142
- | String concat: `global["ev"+"al"]` | **BYPASSES** needs taint tracking |
143
- | Variable-alias eval: `const fn = eval; fn("code")` | **BYPASSES** needs runtime Proxy / taint tracking |
144
- | Array join: `["ch","ild"].join("")` | **BYPASSES (per-module)** may be caught by cross-module state |
145
- | Prototype chain: `eval.constructor` | **BYPASSES** needs runtime instrumentation |
170
+ | Bracket eval: `this["ev"+"al"]` | Default: **BYPASSES** · `FW_ENABLE_AST=1`: **BLOCKED** (bracket key folded, resolved eval access) |
171
+ | String concat: `global["ev"+"al"]` | Default: **BYPASSES** · `FW_ENABLE_AST=1`: **BLOCKED** (concat folded) |
172
+ | Variable-alias eval: `const fn = eval; fn("code")` | Default: **BYPASSES** · `FW_ENABLE_AST=1`: **BLOCKED** (alias tracking) |
173
+ | Array join: `["ch","ild"].join("")` | Default: **BYPASSES** · `FW_ENABLE_AST=1`: **BLOCKED** (join folded, re-matched) |
174
+ | Unicode-escape eval: `eval(...)` | Default: **BYPASSES** · `FW_ENABLE_AST=1`: **BLOCKED** (tokenizer decodes escapes) |
175
+ | Prototype chain: `Object.getPrototypeOf(fn).constructor`, `constructor.constructor` | Default: **BYPASSES** · `FW_ENABLE_AST=1`: **BLOCKED** (constructor-chase resolved) |
176
+ | AST span-exhaustion: real payload hidden behind >40 decoy spans | `FW_ENABLE_AST=1`: **BLOCKED** (highest-risk-first span scheduling; incomplete scans governed by `FW_AST_INCOMPLETE_POLICY`) |
177
+ | WASM payloads, env-sourced config, network+process-exec taint chains, low-and-slow C2 | **BYPASSES even with `FW_ENABLE_AST=1`** — architectural limits of static/AST analysis; need runtime/dataflow instrumentation |
146
178
 
147
179
  See the monorepo's `docs/THREAT-COVERAGE.md` for the full, test-backed protection/bypass matrix.
148
180
 
package/index.js CHANGED
@@ -195,6 +195,11 @@ const fwMode = resolveFwMode();
195
195
  // in the npm manifest and are security-critical, so they must be covered here — omitting
196
196
  // them let a tampered aho-corasick silently defeat detection while self-integrity passed.
197
197
  path.join(__dirname, 'src', 'aho-corasick.js'),
198
+ // ast-scan.js (Phase 3) is required by detector.js and feeds signal positions directly into
199
+ // detector.js's block-tier decisions — equally security-critical, same reasoning as
200
+ // aho-corasick.js above. This list is duplicated in three other places that must stay in
201
+ // lockstep — see the self-integrity-lockstep test in .agent/scripts/__tests__/.
202
+ path.join(__dirname, 'src', 'ast-scan.js'),
198
203
  path.join(__dirname, 'sync-worker.js'),
199
204
  ];
200
205
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aletheia-firewall",
3
- "version": "0.5.0",
4
- "description": "Zero-dependency runtime firewall that blocks malicious npm modules at require-time.",
3
+ "version": "0.6.0",
4
+ "description": "Zero-dependency, opt-in runtime enforcement for supported Node.js module-loading paths (require()/file:// ESM): signature, behavioral, and opt-in AST obfuscation detection with signed-policy enforcement. Defense-in-depth, not a comprehensive malware blocker.",
5
5
  "main": "index.js",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Aletheia contributors",