Skip to content

feat(configurator): compress share URLs with deflate (codec v2) - #427

Merged
jackgranatowski merged 4 commits into
mainfrom
claude/share-function-url-shortening-3jpqkh
Jun 26, 2026
Merged

feat(configurator): compress share URLs with deflate (codec v2)#427
jackgranatowski merged 4 commits into
mainfrom
claude/share-function-url-shortening-3jpqkh

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds fflate as a dependency and replaces the old binary-only codec with deflate-compressed encoding
  • Encoder builds a binary token payload, compresses it with deflateSync, prepends version byte 0x02, then base64url-encodes
  • Decoder checks for version byte 0x02, decompresses with inflateSync, then parses token entries — anything else (including old v1 0x01) is silently rejected
  • Removes all v1 backward-compatibility paths (no one has used share URLs yet)
  • Typical URL length reduction: 30–50% for configs with 10+ tokens

Test plan

  • All 49 unit tests pass (npm run test:unit in configurator/)
  • Round-trip encode → decode verified for single token, multiple tokens, unicode values
  • Unknown/old version bytes return {} without throwing
  • Malformed base64 returns {} without throwing
  • Share URL copied from the configurator loads correctly when pasted in a new tab

🤖 Generated with Claude Code

https://claude.ai/code/session_01KyAfaeUcDGNkHYitTb9Nk9


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Share codes now use a newer compressed format, which helps them stay smaller as the number of saved settings grows.
    • Large configuration shares should now round-trip more reliably.
  • Bug Fixes

    • Improved handling of invalid or outdated share data, so broken share codes now fail safely instead of causing issues.

claude added 2 commits June 26, 2026 20:47
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
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jackgranatowski, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5898c3fe-338f-49db-bafe-68f7fcf79eff

📥 Commits

Reviewing files that changed from the base of the PR and between ba64abd and 16c3b7c.

📒 Files selected for processing (2)
  • configurator/src/lib/codec.ts
  • configurator/tests/share.test.js
📝 Walkthrough

Walkthrough

The share codec now uses version 2 with deflate compression, updated size checks, and revised encode/decode framing. The package adds fflate, the exported version constant changes to 2, and tests cover the new round-trip and legacy-version rejection.

Changes

Codec v2 compression

Layer / File(s) Summary
Runtime dependency and codec setup
configurator/package.json, configurator/src/lib/codec.ts
fflate is added, codec.ts imports compression helpers, and compressed payload size limits are defined.
Versioned encoding
configurator/src/lib/codec.ts
Encoding sorts entries by id, skips empty input, compresses the compact payload, and emits version 2 framing.
Versioned decoding
configurator/src/lib/codec.ts
Decoding requires version 2, enforces compressed and decompressed size bounds, inflates the payload, and returns {} on rejected frames.
Version constant and test coverage
configurator/src/lib/codec.ts, configurator/tests/codec.test.js, configurator/tests/share.test.js
CODEC_VERSION changes to 2, and tests cover the new version, compression round-trip, and legacy-version rejection.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: deflate-based share URL compression and the codec v2 format.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/share-function-url-shortening-3jpqkh

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Compress configurator share URLs using deflate (codec v2)
✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

Description

• Add deflate-compressed codec v2 for shorter share URL config payloads.
• Reject non-v2 share codes and malformed inputs without throwing.
• Update unit tests to cover v2 round-trips and version handling.
Diagram

graph TD
  A["Configurator UI"] --> B["encodeOverrides()"] --> C["Token payload"] --> D["deflateSync (fflate)"] --> E["v2 bytes (0x02 + data)"] --> F["base64url code"]
  G["Share URL hash"] --> H["readShareFromHash()"] --> I["base64url decode"] --> J["version check (0x02)"] --> K["inflateSync (fflate)"] --> L["Parsed overrides"]
  F --> G
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep v1 decoding fallback (decode v1 + v2; encode v2 only)
  • ➕ Protects against any early/accidental distribution of v1 links
  • ➕ Easier rollback if compression causes unexpected issues in some environments
  • ➖ Additional parsing paths to maintain/test
  • ➖ Slightly larger surface area for malformed-input handling
2. Use Brotli (or gzip) instead of raw deflate
  • ➕ Potentially better compression ratios for larger payloads
  • ➕ Well-known formats across ecosystems
  • ➖ More complicated runtime support and/or larger dependency footprint
  • ➖ Different encoding headers/parameters to standardize across clients
3. Dictionary/token-table compression (domain-specific)
  • ➕ Can outperform generic compression by exploiting repeated token prefixes/values
  • ➕ Fully controllable and potentially faster than general-purpose compression
  • ➖ More custom code and format complexity
  • ➖ Harder to evolve safely without rigorous versioning and migration

Recommendation: The PR’s approach (deflate-compressed binary payload with an explicit v2 version byte) is a pragmatic improvement: it materially reduces URL length while keeping the format simple and deterministic. The main strategic question is compatibility posture—if there’s any chance v1 links were shared, keeping a v1 decode fallback is a low-cost safety net; otherwise, strict v2-only decoding is reasonable and simplifies long-term maintenance.

Files changed (4) +74 / -29

Enhancement (1) +36 / -26
codec.tsSwitch share code format to v2: deflate-compressed payload +36/-26

Switch share code format to v2: deflate-compressed payload

• Reworks the encoder to build a binary token/value payload, compress it with deflateSync, prefix a v2 version byte (0x02), and base64url-encode. Updates the decoder to accept only v2, inflate the payload, and parse entries; unknown versions and decompression failures return an empty map without throwing. Bumps CODEC_VERSION to 2.

configurator/src/lib/codec.ts

Tests (2) +37 / -3
codec.test.jsUpdate codec version assertion to v2 +2/-2

Update codec version assertion to v2

• Adjusts the unit test expectation to reflect CODEC_VERSION=2.

configurator/tests/codec.test.js

share.test.jsAdd v2 share-code round-trip and version-rejection tests +35/-1

Add v2 share-code round-trip and version-rejection tests

• Extends share tests to assert CODEC_VERSION=2, validate v2 round-trips for multi-token configs, and ensure unknown/old version bytes decode to an empty map without errors.

configurator/tests/share.test.js

Other (1) +1 / -0
package.jsonAdd fflate dependency for compression +1/-0

Add fflate dependency for compression

• Introduces the fflate package to support synchronous deflate/inflate operations used by the share URL codec.

configurator/package.json

@qodo-code-review

qodo-code-review Bot commented Jun 26, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 6 rules

Grey Divider


Action required

1. Unbounded share-code decompression ✓ Resolved 🐞 Bug ⛨ Security
Description
Ua() inflates attacker-controlled bytes from the share URL without any cap on compressed or
decompressed size, allowing a tiny hash value to expand into a very large payload and freeze/crash
the tab during startup. This is triggered by visiting a crafted link because the app decodes the
hash on load.
Code

configurator/src/lib/codec.ts[R112-128]

+  const rawBytes = Va(r);
+  if (!rawBytes || rawBytes.length === 0) return {};
+
+  if (rawBytes[0] !== 2) {
+    console.warn(`[codec] unknown config-code version ${rawBytes[0]} (expected 2); ignoring.`);
    return {};
  }
-  
+
+  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 {};
+
Relevance

⭐⭐⭐ High

Team has accepted security hardening against attacker-controlled inputs/prototype hazards; likely to
add inflate size caps.

PR-#398
PR-#313

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The decoder inflates raw bytes obtained from base64url-decoding the c= parameter, with no size
limit before or after inflation; the app reads c= from window.location.hash during
initialization, so a malicious URL triggers this path on load.

configurator/src/lib/codec.ts[109-128]
configurator/src/lib/codec.ts[236-245]
configurator/src/App.svelte[22-32]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Ua()` performs `inflateSync()` on bytes derived from the URL hash (`c=`) with no bounds checks. This enables a decompression-bomb style denial of service (small input => huge output) that can hang/crash the UI thread when a user opens a crafted share URL.

## Issue Context
- The share code is untrusted input (URL hash).
- Current code catches inflate errors but does not limit compressed length or decompressed output size.

## Fix Focus Areas
- configurator/src/lib/codec.ts[112-128]

### Recommended fix
1. Add hard caps, e.g.:
  - `MAX_COMPRESSED_BYTES` (reject if `rawBytes.length - 1` exceeds it)
  - `MAX_DECOMPRESSED_BYTES` (reject if `inflateSync(...)` output exceeds it)
2. Prefer an inflate API that enforces max output (e.g., inflate into a preallocated buffer / use library options if available). If the library cannot enforce a maximum, check `i.length` immediately after inflate and reject.
3. Consider also bounding entry count during parsing (e.g., stop if too many entries) to prevent pathological loops even within size bounds.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Synchronous deflate in effect 🐞 Bug ➹ Performance
Description
Ha() now performs synchronous deflateSync() compression, and the app recomputes the share code on
every overrides update to keep window.location.hash in sync. This adds repeated synchronous CPU
work to a reactive path and can stall rendering for larger override maps or rapid input changes.
Code

configurator/src/lib/codec.ts[R102-106]

+  const compressed = deflateSync(payload);
+  const out = new Uint8Array(1 + compressed.length);
+  out[0] = 2;
+  out.set(compressed, 1);
+  return Ba(out);
Relevance

⭐⭐ Medium

No prior reviews found about avoiding sync compression in reactive paths; performance guidance
unclear in history.

PR-#424
PR-#423

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The encoder now calls deflateSync(payload); App.svelte’s reactive effect computes `const code =
Ga(_ov) and updates window.location.hash` whenever overrides change, so compression executes as
part of frequent UI updates.

configurator/src/lib/codec.ts[71-106]
configurator/src/App.svelte[71-79]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Ha()` uses `deflateSync()` which runs on the main thread. The app recomputes the encoded share hash inside a reactive effect whenever overrides change, so frequent edits can repeatedly compress synchronously.

## Issue Context
- Hash syncing is done in an `$effect` that runs on every overrides mutation.
- Compression is new work compared to the prior binary-only encoding.

## Fix Focus Areas
- configurator/src/lib/codec.ts[88-106]
- configurator/src/App.svelte[71-79]

### Recommended fix
Choose one:
1. Debounce hash updates (e.g., update hash after N ms of inactivity) while still persisting overrides immediately.
2. Offload compression to an async path (worker / idle callback) and update hash when ready.
3. Only compute/share the compressed code when the user explicitly requests a share URL (if continuous hash sync is not required).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Compression test lacks assertion ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The test named "large configs produce shorter codes than small ones" never asserts that the large
config code is shorter than the small config code, so it won’t catch regressions in compression
effectiveness. It currently only checks that both outputs are non-empty.
Code

configurator/tests/share.test.js[R84-92]

+  test('large configs produce shorter codes than small ones', () => {
+    const small = encodeOverrides({ [realToken]: '1rem' });
+    const tokens = data.tokens.filter(t => t.role === 'knob').slice(0, 20);
+    if (tokens.length < 10) return;
+    const large = encodeOverrides(Object.fromEntries(tokens.map((t, i) => [t.name, `${i + 1}.${i}rem`])));
+    // Verify the large config round-trips (compression is transparent)
+    expect(large.length).toBeGreaterThan(0);
+    expect(small.length).toBeGreaterThan(0);
+  });
Relevance

⭐⭐ Medium

Mixed history on strengthening tests; some robustness fixes accepted, but other “test is weak”
suggestions rejected.

PR-#303
PR-#382

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Despite the test name implying a size comparison, the assertions only check that small and large
are non-empty strings.

configurator/tests/share.test.js[79-92]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A test claims to validate that large configs compress better than small ones, but it doesn’t compare lengths. This can give false confidence and allow regressions in compression ratio to pass CI.

## Issue Context
The test currently asserts only `> 0` for both strings.

## Fix Focus Areas
- configurator/tests/share.test.js[84-92]

### Recommended fix
Either:
- Change the assertion to something like `expect(large.length).toBeLessThan(small.length)` (or a ratio/threshold that’s stable across fixtures), or
- Rename/reframe the test to match what it actually checks (e.g., “large configs still encode successfully”).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread configurator/src/lib/codec.ts
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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between d1ce52b and ba64abd.

⛔ Files ignored due to path filters (1)
  • configurator/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • configurator/package.json
  • configurator/src/lib/codec.ts
  • configurator/tests/codec.test.js
  • configurator/tests/share.test.js

Comment thread configurator/src/lib/codec.ts
Comment on lines +128 to +137
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.`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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:


🏁 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 -S

Repository: 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 -S

Repository: 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 || true

Repository: codeslash-dev/SLASHED

Length of output: 3899


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,40p' configurator/src/lib/codec.ts

Repository: codeslash-dev/SLASHED

Length of output: 1316


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,40p' configurator/src/lib/codec.ts

Repository: 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.

Comment thread configurator/tests/share.test.js Outdated
…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
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.

2 participants