agent-sanitizer 2.66.0 → 2.66.1
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/THREAT-MODEL.md +36 -30
- package/package.json +3 -1
package/THREAT-MODEL.md
CHANGED
|
@@ -1020,38 +1020,44 @@ up to the constant factors a parse pays, and one sort where findings or splice
|
|
|
1020
1020
|
ranges arrive in an order the caller does not promise (`foldConfusables`,
|
|
1021
1021
|
`mergeRanges`). The bound that matters is that nothing is _quadratic_ — a sort
|
|
1022
1022
|
of the findings in a 1 MB paste is milliseconds, a quadratic rescan of it is
|
|
1023
|
-
minutes — and quadratic is what the two
|
|
1023
|
+
minutes — and quadratic is what the two tiers below are built to catch,
|
|
1024
1024
|
because neither can see what the other does.
|
|
1025
1025
|
|
|
1026
|
-
**
|
|
1027
|
-
regexploit over an inventory that `scripts/extract-js-regexes.mjs`
|
|
1028
|
-
walking the TypeScript AST of every shipped `.mjs` (not by matching
|
|
1029
|
-
with a regex), and fails on any pattern with super-linear
|
|
1030
|
-
`RegExp(…)` built at runtime must be statically resolvable or
|
|
1031
|
-
exemption, so a new dynamic pattern is a red build rather than a
|
|
1032
|
-
`tests/secrets/test_redos_static_guard.py` is the twin over the Python
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1026
|
+
**Deterministic — every pattern, no clock.** `tests/test_redos_js_static_guard.py`
|
|
1027
|
+
drives regexploit over an inventory that `scripts/extract-js-regexes.mjs`
|
|
1028
|
+
extracts by walking the TypeScript AST of every shipped `.mjs` (not by matching
|
|
1029
|
+
regex source with a regex), and fails on any pattern with super-linear
|
|
1030
|
+
backtracking. A `RegExp(…)` built at runtime must be statically resolvable or
|
|
1031
|
+
carry a written exemption, so a new dynamic pattern is a red build rather than a
|
|
1032
|
+
hole. `tests/secrets/test_redos_static_guard.py` is the twin over the Python
|
|
1033
|
+
engine. In `pnpm lint`, `eslint-plugin-redos` (recheck) and
|
|
1034
|
+
`eslint-plugin-regexp`'s `no-super-linear-backtracking` and
|
|
1035
|
+
`no-super-linear-move` read every shipped regex literal — the last of them
|
|
1036
|
+
reports the shape that carries no backtracking at all, an **unanchored**
|
|
1037
|
+
pattern retried at every start offset (`(?=.*[A-Z])(?=.*[0-9])` against a long
|
|
1038
|
+
value carrying neither class). `test/regex-recheck.test.mjs` hands every
|
|
1039
|
+
pattern a shipped module actually compiles at runtime to recheck. Every recheck
|
|
1040
|
+
verdict runs with its wall-clock incubation and attack timeouts off, so it
|
|
1041
|
+
rests on step counts alone and no machine's speed can change it.
|
|
1042
|
+
|
|
1043
|
+
**Budgets — everything that is not a pattern.** Work that is not a regex at
|
|
1044
|
+
all — a string rebuilt once per finding, a prefix re-sliced inside a loop, a
|
|
1045
|
+
re-walk of an already-walked node, a parser's own look-back — no pattern
|
|
1046
|
+
analyzer will ever report. `test/algorithmic-complexity.test.mjs` runs each
|
|
1047
|
+
entry point ONCE, after a warm-up, over an adversarial input sized so the
|
|
1048
|
+
linear implementation costs tens to hundreds of milliseconds, against an
|
|
1049
|
+
absolute CPU ceiling at least ten times that reading (`assertUnderBudget` in
|
|
1050
|
+
`test/helpers/budget.mjs`, twin `tests/_budget.py`). A quadratic at that size
|
|
1051
|
+
overshoots the ceiling by another order of magnitude, so no runner is slow or
|
|
1052
|
+
busy enough to blur the two; a constant-factor slowdown passes, by design. Each
|
|
1053
|
+
case also asserts, at the size it measures, that the input still reaches the
|
|
1054
|
+
code the case is about — a length gate answering first is how such a case
|
|
1055
|
+
quietly degrades into timing nothing. `tests/secrets/test_algorithmic_complexity.py`
|
|
1056
|
+
is the twin over the Python engine. `test/hook-latency.test.mjs` holds the
|
|
1057
|
+
three blocking hook paths to absolute ceilings per 256 KB call, read
|
|
1058
|
+
uninstrumented in CI. Every test verdict in the repo that reads a clock goes
|
|
1059
|
+
through that one helper (`test/real-clock-thresholds.test.mjs` and its Python
|
|
1060
|
+
twin enforce it).
|
|
1055
1061
|
|
|
1056
1062
|
**Idioms new code is held to.** Each is the fix for a defect one of the gates
|
|
1057
1063
|
caught:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-sanitizer",
|
|
3
|
-
"version": "2.66.
|
|
3
|
+
"version": "2.66.1",
|
|
4
4
|
"description": "Defend an agent against hidden-content injection: strip payload-capable invisible Unicode and ANSI, splice out human-invisible HTML, and flag data-exfil URLs in untrusted text before any model sees it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -62,11 +62,13 @@
|
|
|
62
62
|
"esbuild": "0.28.1",
|
|
63
63
|
"eslint": "10.4.0",
|
|
64
64
|
"eslint-plugin-redos": "4.5.0",
|
|
65
|
+
"eslint-plugin-regexp": "3.3.1",
|
|
65
66
|
"fast-check": "4.8.0",
|
|
66
67
|
"globals": "17.6.0",
|
|
67
68
|
"lint-staged": "^17.0.5",
|
|
68
69
|
"micromark-extension-gfm-autolink-literal": "2.1.0",
|
|
69
70
|
"prettier": "^3.0.0",
|
|
71
|
+
"recheck": "4.5.0",
|
|
70
72
|
"rehype-parse": "9.0.1",
|
|
71
73
|
"remark-gfm": "4.0.1",
|
|
72
74
|
"smol-toml": "^1.7.1",
|