feat(configurator): compress share URLs with deflate (codec v2) - #427
Conversation
Adds deflate-raw compression to the share URL encoder via fflate. The encoder emits v2 (version byte 0x02 + deflateRaw payload) only when it's shorter than the uncompressed v1 — so tiny configs stay as v1. The decoder handles both v1 and v2 transparently, keeping all existing share URLs working. Typical reduction: 30–50% for configs with 10+ tokens. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyAfaeUcDGNkHYitTb9Nk9
Removes all v1 backward-compatibility paths — encoder always produces v2 (version byte 0x02 + deflateSync payload), decoder rejects anything that isn't v2. The inner payload is now purely token entries with no version byte. Fixes fflate import names (deflateSync/inflateSync, not *Raw*). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyAfaeUcDGNkHYitTb9Nk9
|
Warning Review limit reached
More reviews will be available in 50 minutes and 39 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe share codec now uses version 2 with deflate compression, updated size checks, and revised encode/decode framing. The package adds ChangesCodec v2 compression
Sequence Diagram(s)sequenceDiagram
participant encodeOverrides
participant deflateSync
participant readShareFromHash
participant inflateSync
encodeOverrides->>deflateSync: compress compact version 2 payload
deflateSync-->>encodeOverrides: compressed bytes for share code
readShareFromHash->>inflateSync: inflate version 2 payload
inflateSync-->>readShareFromHash: decoded overrides
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoCompress configurator share URLs using deflate (codec v2) Description
Diagram
High-Level Assessment
Files changed (4)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
6 rules 1.
|
Adds MAX_COMPRESSED_BYTES (32 KB) and MAX_DECOMPRESSED_BYTES (256 KB) limits to Ua() before and after inflateSync, so a crafted share URL cannot expand a tiny payload into an arbitrarily large decompressed buffer on the UI thread. Also tightens the compression-ratio test to assert sub-linear growth rather than just non-empty output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyAfaeUcDGNkHYitTb9Nk9
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@configurator/src/lib/codec.ts`:
- Around line 104-108: In codec.ts, the encode path in encodeOverrides currently
builds the share code with Ba(out) after deflateSync without checking the
compressed size, which can emit links that decode will later reject. Update
encodeOverrides to enforce the same MAX_COMPRESSED_BYTES limit before calling
Ba(out), using the existing compression logic and the same codec symbols so
oversized payloads are rejected before writing to window.location.hash.
- Around line 128-137: The decompression path in codec.ts still fully inflates
attacker-controlled input before MAX_DECOMPRESSED_BYTES is enforced, so update
the inflate logic in the config decode flow to cap output during decompression
rather than after it completes. Replace the current
inflateSync(rawBytes.subarray(1)) usage with a streaming or counting inflate
approach (or another decoder with a true max-output limit) inside the same
config parsing function so oversized payloads are rejected before full
expansion. Keep the existing warning/ignore behavior in the decode path, but
ensure the limit is applied while processing the compressed bytes, not after.
In `@configurator/tests/share.test.js`:
- Around line 85-86: The share tests are silently returning when the fixture has
too few knob tokens, which can mask missing codec coverage; in the affected
tests around the token filtering and codec assertions, replace the early return
with an explicit precondition assertion so the test fails if the fixture
shrinks. Use the existing test logic in the share test cases to keep the v2
codec assertions active rather than turning them into no-ops.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: deede58e-3984-40a1-8d24-e5f21ad9099c
⛔ Files ignored due to path filters (1)
configurator/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
configurator/package.jsonconfigurator/src/lib/codec.tsconfigurator/tests/codec.test.jsconfigurator/tests/share.test.js
| let i: Uint8Array; | ||
| try { | ||
| i = inflateSync(rawBytes.subarray(1)); | ||
| } catch { | ||
| console.warn("[codec] failed to decompress config; ignoring."); | ||
| return {}; | ||
| } | ||
| if (!i || i.length === 0) return {}; | ||
|
|
||
| if (i[0] !== 1) { | ||
| console.warn(`[codec] unknown config-code version ${i[0]} (expected 1); ignoring.`); | ||
| if (i.length > MAX_DECOMPRESSED_BYTES) { | ||
| console.warn(`[codec] decompressed payload too large (${i.length} B > ${MAX_DECOMPRESSED_BYTES} B); ignoring.`); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🌐 Web query:
Does fflate 0.8.3 provide a streaming or bounded inflate API that can abort once decompressed output exceeds a fixed byte limit?
💡 Result:
No. In fflate v0.8.3, there is no documented streaming or bounded-inflate API option that will automatically abort decompression once the decompressed output exceeds a fixed byte limit. What fflate does provide instead: 1) You can pass an output buffer via the out option, but it only truncates to fit that buffer; it does not provide an “abort when output exceeds N bytes” semantics for streamed inflate. The out option description says that if the decompression result is larger than the buffer, “it will be truncated to fit.” [1] 2) For streaming APIs, the terminate()/terminate capability exists to stop internal worker threads; but there is no API contract stating that fflate will stop automatically based on output-size thresholds. The terminate description says it terminates internal workers and subsequent push() calls silently fail. [2] So, to enforce a hard decompressed-byte cap with fflate 0.8.3, you would need to implement the limit yourself (e.g., count ondata bytes / track total output length and then call terminate() or otherwise stop processing once the cap is reached), because the library API itself does not expose a built-in “max uncompressed bytes” abort option.
Citations:
- 1: https://github.com/101arrowz/fflate/blob/master/docs/interfaces/InflateOptions.md
- 2: https://github.com/101arrowz/fflate/blob/master/docs/interfaces/UnzipDecoder.md
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## codec.ts excerpt\n'
nl -ba configurator/src/lib/codec.ts | sed -n '100,170p'
printf '\n## dependency version\n'
if [ -f configurator/package.json ]; then
rg -n '"fflate"|fflate' configurator/package.json configurator/pnpm-lock.yaml configurator/package-lock.json configurator/yarn.lock || true
fi
printf '\n## locate codec usages\n'
rg -n "inflateSync|decompress config|MAX_DECOMPRESSED_BYTES|c=" configurator/src -SRepository: codeslash-dev/SLASHED
Length of output: 220
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## codec.ts excerpt\n'
nl -ba configurator/src/lib/codec.ts | sed -n '100,170p'
printf '\n## dependency version\n'
if [ -f configurator/package.json ]; then
rg -n '"fflate"|fflate' configurator/package.json configurator/pnpm-lock.yaml configurator/package-lock.json configurator/yarn.lock || true
fi
printf '\n## locate codec usages\n'
rg -n "inflateSync|decompress config|MAX_DECOMPRESSED_BYTES|c=" configurator/src -SRepository: codeslash-dev/SLASHED
Length of output: 220
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## codec.ts excerpt\n'
sed -n '100,170p' configurator/src/lib/codec.ts
printf '\n## dependency version\n'
if [ -f configurator/package.json ]; then
grep -nE '"fflate"|fflate' configurator/package.json configurator/pnpm-lock.yaml configurator/package-lock.json configurator/yarn.lock 2>/dev/null || true
fi
printf '\n## locate codec usages\n'
grep -RInE 'inflateSync|decompress config|MAX_DECOMPRESSED_BYTES|c=' configurator/src 2>/dev/null || trueRepository: codeslash-dev/SLASHED
Length of output: 3899
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,40p' configurator/src/lib/codec.tsRepository: codeslash-dev/SLASHED
Length of output: 1316
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,40p' configurator/src/lib/codec.tsRepository: codeslash-dev/SLASHED
Length of output: 1316
Cap decompression before full inflation
inflateSync(rawBytes.subarray(1)) still fully expands attacker-controlled input before MAX_DECOMPRESSED_BYTES is checked. The 32 KB compressed cap doesn’t prevent a deflate bomb here, and fflate 0.8.3 doesn’t expose a built-in max-output limit. Use a streaming/counting inflate path or a different decoder if page-load DoS matters.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@configurator/src/lib/codec.ts` around lines 128 - 137, The decompression path
in codec.ts still fully inflates attacker-controlled input before
MAX_DECOMPRESSED_BYTES is enforced, so update the inflate logic in the config
decode flow to cap output during decompression rather than after it completes.
Replace the current inflateSync(rawBytes.subarray(1)) usage with a streaming or
counting inflate approach (or another decoder with a true max-output limit)
inside the same config parsing function so oversized payloads are rejected
before full expansion. Keep the existing warning/ignore behavior in the decode
path, but ensure the limit is applied while processing the compressed bytes, not
after.
…itions Encoder now rejects payloads whose compressed size exceeds MAX_COMPRESSED_BYTES (32 KB) before emitting, so the encoder and decoder are consistent — no URL can be generated that the same codec would later refuse to restore. Replaces silent early-return guards in v2 tests with explicit precondition assertions so CI fails if the token fixture shrinks below the required minimum. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyAfaeUcDGNkHYitTb9Nk9
Summary
fflateas a dependency and replaces the old binary-only codec with deflate-compressed encodingdeflateSync, prepends version byte0x02, then base64url-encodes0x02, decompresses withinflateSync, then parses token entries — anything else (including old v10x01) is silently rejectedTest plan
npm run test:unitinconfigurator/){}without throwing{}without throwing🤖 Generated with Claude Code
https://claude.ai/code/session_01KyAfaeUcDGNkHYitTb9Nk9
Generated by Claude Code
Summary by CodeRabbit
New Features
Bug Fixes