Skip to content

chore(release): @hasna/tai 0.1.4 — ship the merged Digest ReDoS fix (#17) - #18

Merged
andrei-hasna merged 1 commit into
mainfrom
release/0.1.4-41c731cc
Aug 1, 2026
Merged

chore(release): @hasna/tai 0.1.4 — ship the merged Digest ReDoS fix (#17)#18
andrei-hasna merged 1 commit into
mainfrom
release/0.1.4-41c731cc

Conversation

@andrei-hasna

@andrei-hasna andrei-hasna commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Closes the gap on todos 41c731cc.

0.1.3 is live on the registry and still carries the quadratic Digest response= rule. PR #17 fixed it and merged at 02:16:39Z as 66e0157e (head 64626683c, covered by a GO at that exact sha), but nothing published — so every public consumer still has the ReDoS. This bumps the version so it can ship. Version bump only; no source change.

Axis 1 — leak: does it still redact?

25-shape A/B corpus, 0.1.3 registry tarball vs this build, output drift ZERO byte-for-byte. The speedup was not bought by matching less. Positive controls (Digest response=, sk- key) redact in both; the known-open Cookie:/Set-Cookie: leak is still observable in both, which is what keeps a clean number from being hopeful silence.

Axis 2 — scale: how does runtime grow?

Stated separately and deliberately: 0.1.3 shipped as a security release with no axis for input scaling, and that is how the ReDoS got out.

Measured on station01, loadavg ~9-13, both versions interleaved in one process. Absolutes are quoted with their box — station01 load inflates them ~2.9x against an idle station02 — so the ratios are the portable result.

input 0.1.3 0.1.4
16 KiB 29.1 ms 0.87 ms
32 KiB 114.6 ms 1.75 ms
64 KiB 455.5 ms 3.51 ms
128 KiB 1814.7 ms 6.93 ms
per doubling x3.94, x3.97, x3.98 → QUADRATIC x2.00, x2.01, x1.97 → LINEAR

262x at 128 KiB on this box.

What this does NOT close

Named so silence does not imply coverage.

  • Cookie: / Set-Cookie: values are unredacted in every version (6200c4e4). Confirmed here: Cookie: session= and Set-Cookie: sessionid= both pass through untouched. Note the cookie name matters — Set-Cookie: auth= is masked incidentally by the generic auth= key rule, which will make a carelessly chosen control report a pass that says nothing about cookies.
  • The escaped-quote-JSON Digest leak (d841b3e1) is open and was NOT reproduced by this corpus.
  • src/mcp/index.ts:107 still has no maxBuffer. The fix removes the amplification, not the bound — unbounded input is now a generic bulk-input cost rather than a ReDoS.
  • This rule has ZERO discriminating test coverage. Measured: delete the rule entirely from SECRET_PATTERNS and the full suite still passes 31/31, rc=0. It is not dead code — of 10 adversarial Digest shapes, exactly 1 leaks without it:
    authorization: digest RESPONSE = 'quoted-single-0123456789'
      rule present: authorization: [REDACTED] = '[REDACTED]'
      rule deleted: authorization: [REDACTED] = 'quoted-single-0123456789'   <- LEAKS
    
    The other 9 are already masked downstream by the broader Authorization rule. The missing regression test is exactly that shape.
  • The rationale comment in src/redaction.ts asserts "no input that was redacted before stops being redacted". That absolute is falsified — across ~60k malformed inputs the change yields 19 leak regressions against 147 improvements. Net positive, not blocking, but the inner g mask can consume a trailing parameter the downstream Authorization rule needed, so over-masking is not uniformly the safe direction.

Release hygiene

@hasna/tai has no prepublishOnly (4efcbd8a), so npm publish ships whatever dist/ is on disk. Proved this tree equals a clean rebuild: two rm -rf dist rebuilds produced byte-identical output across all 18 files. Suite 31/31 and typecheck both rc=0, measured unpiped.

Agent: Aurelian


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

)

0.1.3 is live on the registry and still carries the quadratic Digest
`response=` rule. PR #17 fixed it and merged at 02:16:39Z as 66e0157,
but nothing published, so every public consumer still has the ReDoS.

TWO AXES, STATED SEPARATELY — 0.1.3 shipped as a security release with no
axis for input scaling, which is how the ReDoS got out.

SCALE. Measured on station01, loadavg ~9-13, both versions interleaved in
one process so the comparison is internally valid. Absolutes are quoted
with their box because station01 load inflates them ~2.9x against an idle
station02; the RATIOS are the portable result.

  0.1.3  29.1 / 114.6 / 455.5 / 1814.7 ms at 16/32/64/128 KiB
         x3.94, x3.97, x3.98 per doubling -> QUADRATIC
  0.1.4   0.87 /  1.75 /  3.51 /   6.93 ms
         x2.00, x2.01, x1.97 per doubling -> LINEAR
  262x at 128 KiB on this box

LEAK. 25-shape A/B corpus, output drift ZERO byte-for-byte, so the speedup
was not bought by matching less. Positive controls redact; the known-open
Cookie:/Set-Cookie: leak is still observable in both versions, which is
what keeps a clean number from being hopeful silence.

WHAT THIS DOES NOT CLOSE — named so silence does not imply coverage:
  - Cookie:/Set-Cookie: values are unredacted in every version (6200c4e4).
    Confirmed here: `Cookie: session=` and `Set-Cookie: sessionid=` both
    pass through untouched.
  - The escaped-quote-JSON Digest leak (d841b3e1) is open and was NOT
    reproduced by this release's corpus.
  - src/mcp/index.ts:107 still has no maxBuffer. The fix removes the
    amplification, not the bound: unbounded input is now a generic bulk
    cost rather than a ReDoS.
  - This rule has ZERO discriminating test coverage. Measured: delete the
    rule entirely from SECRET_PATTERNS and the full suite still passes
    31/31, rc=0. It is not dead code — of 10 adversarial Digest shapes, 1
    leaks without it (`authorization: digest RESPONSE = 'x'`, spaced
    equals + single quotes); the other 9 are already masked downstream by
    the broader Authorization rule. The missing regression test is exactly
    that shape.
  - The rationale comment in src/redaction.ts asserts "no input that was
    redacted before stops being redacted". That absolute is falsified:
    across ~60k malformed inputs the change yields 19 leak regressions
    against 147 improvements. Net positive and not blocking, but the inner
    `g` mask can consume a trailing parameter the downstream Authorization
    rule needed, so over-masking is not uniformly the safe direction.

Version bump only; no source change in this commit.

Agent: Aurelian
@andrei-hasna
andrei-hasna merged commit b75e651 into main Aug 1, 2026
2 checks passed
@andrei-hasna
andrei-hasna deleted the release/0.1.4-41c731cc branch August 1, 2026 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant