Skip to content

fix(browser): save from gzip, deflate and cached responses within a bound - #7361

Merged
morluto merged 3 commits into
morluto:mainfrom
earakely-scale:fix/saved-compressed-responses
Oct 27, 2026
Merged

morluto merged 3 commits into
morluto:mainfrom
earakely-scale:fix/saved-compressed-responses

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Since #7267, a scenario step that saves values from a response refuses any response whose Content-Encoding is not absent or identity, and any whose received size is unknown or zero, as from the browser's cache. Grafana answers POST /api/folders gzipped, so a UI step that saves a new folder's uid fails, and recordings that create Grafana folders through the UI cannot carry its id to later steps. #7162, which #7267 builds on, read such responses within a bound; that bound did not carry over (noted when closing #7162).

This PR bounds a saved response's decoded size before reading it: its received size, or the declared Content-Length of one served from cache, times 1032 for each gzip, x-gzip or deflate coding, the most DEFLATE (zlib) expands. A response whose bound fits the 12,000,000-byte limit is read; one whose bound passes it is refused before the body is read, as are other codings (br, zstd) and unknown or zero sizes. The same reader protects marked response content in network evidence, so a marked gzip body within the bound is now read and redacted rather than withheld.

Problem and expected behavior

  • A UI step saving from a gzip or deflate JSON answer should save when the received size bounds the decoded text within the limit.
  • A cached answer with a declared length should be bounded by that length.
  • A response no bound covers, or one that could decode past the limit, should stay unread and refused, as now.

Change and scope

  • src/browser/BrowserScenarioResponseValues.ts: decodedBodyBound, used by readBoundedResponseBody for UI saves and for marked network content.
  • docs/browser-scenario-contract.md: the UI-step reading rule.
  • Tests in src/browser/PlaywrightScenarioRequest.test.ts and src/browser/network/PlaywrightScenarioNetwork.test.ts.

Contract and boundary impact

  • Semantic owner and earliest changed stage: the browser scenario's saved-response reader.
  • Contribution scope fit and linked maintainer decision: follows the comparison of feat(browser): save values in browser scenarios and send them in later steps #7162 against feat(browser): reuse saved response values across scenario steps #7267.
  • CLI and MCP/tool-catalog contract: unchanged; more responses can be saved from.
  • Provider, bridge, target-format, or platform compatibility: unchanged.
  • Evidence, artifact, provenance, or reconstruction contract: marked gzip or deflate response content within the bound is retained redacted instead of withheld.
  • Target selection, authority, upstream behavior, and cleanup details: not applicable.
  • Generated metadata, package, or installation impact: none.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Real Chrome, a loopback fixture whose create answer is gzip-compressed JSON: a scenario clicks Save with save_response on POST /api/items, then opens /items/{saved:item_1}. On main the save step fails with "the decoded response size cannot be bounded before reading it; this response body was not read."; on this branch every step completes and the destination opens with the saved id (/items/item-43).
  • Grafana: both saved-values recordings that created folders through the UI saved each folder's uid from POST /api/folders, whose answers were gzip-encoded.
  • Tests added or updated: UI saves from gzip, deflate and x-gzip answers, from a cached answer by its declared length, and from a cached gzip answer; a gzip answer whose bound passes the limit is refused unread; br, gzip, zstd, zero and unknown sizes stay refused unread; marked gzip network content within the bound is read and redacted; br and unknown cache sizes stay withheld.
  • Base reproduction: the seven new behaviour tests fail on main 83bbad2b.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run -s check:fast, format:check, scan:todos, docs:check, verify:test-discovery, mcp-catalog:check and
    jscpd: passed, merged with main 27ac1b67.
  • npx knip: staticFileUrlPath and ObjcClassImportBindingCoverage only, which main reports too.
  • npx vitest run --project domain --project services: 11,538 passed, 6 skipped. --project adapters: 7,661 passed,
    19 skipped, 1 failed ("stops Inspector target authorization after the current stat settles"), which fails the same
    way on main in this environment.
  • npm run -s test:focused -- the environment and analysis-view CLI and MCP boundaries: 7 files, 79 passed.
  • The real-Chrome run above, against main 83bbad2b and this branch.
  • CI run 40434949397: the failing tests, providerBinding.test.ts (bundle-root identity for artifact inventory),
    artifactInspectionCoverageMcp.test.ts (both FIFO cases) and ghidraJavaDoctor.test.ts (empty PATH component),
    are outside the browser. The first three fail the same way on main 27ac1b67 locally, and main's CI run 40436425782
    at bfdde7ee fails the bundle-root test too; the Ghidra doctor test passes locally on both. Other shards show no
    failing test.
  • Greptile: 5/5, no findings. Codex could not review this head (repository usage limit).

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: Google Chrome on macOS with a loopback fixture.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the body is read only when its decoded size is bounded within the existing limit before reading; marked content keeps the existing redaction.

Maintainer validation

Merged main at ef11f77 into 4660989. build:cached, check:fast, and focused browser response, provider binding, artifact MCP coverage, and Ghidra doctor tests passed (167 tests). GitHub CI is rerunning. No new real-browser run was performed for this merge.

Author readiness

Human owner: @earakely-scale

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

🤖 Generated with Claude Code

earakely-scale and others added 2 commits October 26, 2026 18:44
…ound

A saved response's decoded size is bounded by its received size, or the
declared length of one served from cache, times 1032 for each gzip or deflate
coding, the most DEFLATE expands. Grafana answers folder creates gzipped, so a
UI step saving a new folder's uid was refused. Other codings and unknown or
zero sizes stay refused before the body is read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issue was established.

Summary

This PR lets browser steps save values from gzip, deflate, and cached responses when their calculated decoded-size bound fits the existing limit.

  • Browser scenarios save values from bounded compressed and cached responses.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Saved response or marked network content] --> B[Get received size or declared length]
  B --> C[Calculate decoded-size bound]
  C --> D{Known bound within limit?}
  D -->|No| E[Refuse without reading body]
  D -->|Yes| F[Read body]
  F --> G{Actual size within limit?}
  G -->|No| H[Reject body]
  G -->|Yes| I[Save values or redact marked content]
Loading

Reviews (2) · Last reviewed commit: "merge: update main for browser response ..." · Reviewed by Greptile

@earakely-scale
earakely-scale marked this pull request as ready for review October 27, 2026 02:36
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-27T02:36:44.569975Z
We couldn’t complete this review. Comment @codex review to try again.
2bb245d Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@morluto
morluto merged commit 5fcf0d2 into morluto:main Oct 27, 2026
43 checks passed
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