Skip to content

Recompute Content-Length when repeat_request replaces body#614

Open
Hardikrepo wants to merge 2 commits into
usestrix:mainfrom
Hardikrepo:fix/proxy-stale-content-length
Open

Recompute Content-Length when repeat_request replaces body#614
Hardikrepo wants to merge 2 commits into
usestrix:mainfrom
Hardikrepo:fix/proxy-stale-content-length

Conversation

@Hardikrepo

Copy link
Copy Markdown

Summary

  • apply_modifications() in strix/tools/proxy/caido_api.py replaced the request body via modifications={"body": ...} but left the original Content-Length header untouched.
  • build_raw_request() only computes Content-Length when the header is absent, so the stale, smaller value from the original request was sent alongside the new, larger body.
  • A target server reading exactly Content-Length bytes truncates the replayed body (or stalls), silently dropping the modified/injected payload — defeating parameter-tampering / injection replays, which is the core use case of repeat_request, and producing misleading negative results.

Fix

When modifications["body"] is present, drop any existing Content-Length header from the carried-over headers (matched case-insensitively), unless the caller explicitly supplies Content-Length in that same call's modifications["headers"]. This lets build_raw_request's existing "fill in if absent" logic recompute the correct length from the new body.

Test plan

  • Added tests/test_caido_api.py: stale header dropped on body replacement, explicit caller-supplied Content-Length respected, headers untouched when body isn't modified, and an end-to-end check that build_raw_request emits the recomputed length in the raw HTTP request.
  • Verified RED on unmodified code (reproduces the exact stale Content-Length: 3 scenario from the bug report), then GREEN after the fix.
  • Full test suite (uv run pytest tests/) has no new failures.
  • ruff check and mypy clean on changed files.

Resolves #603

… body

Problem:
apply_modifications() let modifications={"body": ...} replace the request
body but left the original Content-Length header untouched.
build_raw_request() only fills in Content-Length when the header is
absent, so the stale, smaller value from the original request was sent
on the wire alongside the new, larger body.

Impact:
A server reading exactly Content-Length bytes truncates the replayed
body (or stalls waiting for more), silently discarding the
modified/injected payload. This defeats parameter-tampering and
injection replays, which is the core use case of repeat_request, and
produces misleading negative results during testing.

Resolution:
When modifications["body"] is present, drop any existing Content-Length
header from the carried-over headers (matched case-insensitively),
unless the caller explicitly supplies Content-Length in this same call's
modifications["headers"]. This lets build_raw_request's existing
"fill in if absent" logic recompute the correct length from the new
body.

Testing:
Added tests/test_caido_api.py covering: stale header dropped on body
replacement, explicit caller-supplied Content-Length respected, headers
left untouched when body isn't modified, and an end-to-end check that
build_raw_request emits the recomputed length in the raw HTTP request.
Verified RED (failing on pre-fix code, reproducing the exact stale
Content-Length: 3 scenario from the bug report) before GREEN.

Resolves usestrix#603
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug in apply_modifications where replacing the request body via modifications["body"] left the original Content-Length header intact, causing build_raw_request to send a stale (typically smaller) length alongside the new body — silently truncating replayed payloads on the target server.

  • Core fix: when modifications["body"] is present and the caller hasn't explicitly supplied a new Content-Length in modifications["headers"], the old header is stripped (case-insensitively) so build_raw_request's existing fill-in logic recomputes the correct byte length.
  • Tests: four new unit tests cover the stale-header-drop, explicit-header-preservation, no-body-change, and end-to-end recomputation paths — all asserting against real build_raw_request output.

Confidence Score: 4/5

Safe to merge; the fix correctly resolves the stale Content-Length bug across all the meaningful call patterns.

The logic in apply_modifications is correct for all documented scenarios. One pre-existing gap becomes slightly more visible: build_raw_request's if body and … guard means an empty-string body replacement now produces a request with no Content-Length at all rather than a wrong one, which is usually fine but can trip strict servers. The change itself is small and well-tested.

The build_raw_request guard at line 160 of strix/tools/proxy/caido_api.py is worth revisiting for the empty-body edge case, though it is pre-existing and not introduced by this PR.

Important Files Changed

Filename Overview
strix/tools/proxy/caido_api.py Adds Content-Length removal in apply_modifications when body is replaced; logic correctly handles case-insensitive header names and preserves explicit caller-supplied Content-Length. One minor edge case: empty-string body replacement drops the stale header but build_raw_request won't add Content-Length: 0.
tests/test_caido_api.py New test file covering the four key scenarios: stale header dropped, explicit header preserved, no-body-change leaves headers alone, and end-to-end Content-Length recomputation. Tests are well-structured and assertions are accurate.

Comments Outside Diff (1)

  1. strix/tools/proxy/caido_api.py, line 160-161 (link)

    P2 When modifications["body"] is "" (empty string), the stale Content-Length is correctly dropped here, but build_raw_request will not emit Content-Length: 0 because its guard is if body and ... — a falsy empty string skips the injection entirely. Sending a POST with no Content-Length on an empty body is acceptable in HTTP/1.1, but some strict servers or WAFs expect Content-Length: 0. Changing the guard to check for body is not None would cover this case consistently.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: strix/tools/proxy/caido_api.py
    Line: 160-161
    
    Comment:
    When `modifications["body"]` is `""` (empty string), the stale Content-Length is correctly dropped here, but `build_raw_request` will not emit `Content-Length: 0` because its guard is `if body and ...` — a falsy empty string skips the injection entirely. Sending a POST with no `Content-Length` on an empty body is acceptable in HTTP/1.1, but some strict servers or WAFs expect `Content-Length: 0`. Changing the guard to check for `body is not None` would cover this case consistently.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
strix/tools/proxy/caido_api.py:160-161
When `modifications["body"]` is `""` (empty string), the stale Content-Length is correctly dropped here, but `build_raw_request` will not emit `Content-Length: 0` because its guard is `if body and ...` — a falsy empty string skips the injection entirely. Sending a POST with no `Content-Length` on an empty body is acceptable in HTTP/1.1, but some strict servers or WAFs expect `Content-Length: 0`. Changing the guard to check for `body is not None` would cover this case consistently.

```suggestion
    if body is not None and "Content-Length" not in {k.title() for k in final_headers}:
        final_headers["Content-Length"] = str(len(body.encode("utf-8")))
```

Reviews (1): Last reviewed commit: "resolve(proxy): recompute Content-Length..." | Re-trigger Greptile

build_raw_request's Content-Length guard was "if body and ...", so a
falsy empty-string body (a valid replacement via repeat_request's
modifications["body"]) skipped Content-Length injection entirely.
Sending no Content-Length is valid HTTP/1.1, but some strict servers or
WAFs expect an explicit Content-Length: 0, so silently omitting it can
still cause inconsistent replay behavior.

Change the guard to "body is not None" so an empty body still gets
Content-Length: 0 while a None body (not applicable here, but kept for
clarity) is still skipped.

Adds test_build_raw_request_sets_content_length_zero_for_empty_body,
verified RED against the prior "if body and ..." guard before GREEN.

Addresses review feedback from usestrix#614.
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.

Proxy repeat_request sends stale Content-Length when the body is replaced

1 participant