mirror of
https://github.com/usestrix/strix.git
synced 2026-10-13 03:57:59 +00:00
resolve(proxy): recompute Content-Length when repeat_request replaces 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 #603
This commit is contained in:
parent
f342808d2b
commit
f4d2aea432
2 changed files with 68 additions and 0 deletions
|
|
@ -261,6 +261,12 @@ def apply_modifications(
|
|||
headers.update(modifications["headers"])
|
||||
if "body" in modifications:
|
||||
body = modifications["body"]
|
||||
explicit_content_length = "Content-Length" in {
|
||||
k.title() for k in modifications.get("headers", {})
|
||||
}
|
||||
if not explicit_content_length:
|
||||
for key in [k for k in headers if k.title() == "Content-Length"]:
|
||||
del headers[key]
|
||||
if "cookies" in modifications:
|
||||
cookies: dict[str, str] = {}
|
||||
if headers.get("Cookie"):
|
||||
|
|
|
|||
62
tests/test_caido_api.py
Normal file
62
tests/test_caido_api.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
"""Tests for request modification in the Caido proxy integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from strix.tools.proxy.caido_api import apply_modifications, build_raw_request
|
||||
|
||||
|
||||
def _components(body: str = "old", headers: dict[str, str] | None = None) -> dict:
|
||||
return {
|
||||
"method": "POST",
|
||||
"url_path": "/submit",
|
||||
"headers": headers or {"Content-Length": str(len(body))},
|
||||
"body": body,
|
||||
}
|
||||
|
||||
|
||||
def test_apply_modifications_drops_stale_content_length_on_body_replace() -> None:
|
||||
components = _components(body="old")
|
||||
result = apply_modifications(
|
||||
components,
|
||||
{"body": "a much longer replacement body than the original"},
|
||||
"http://example.com/submit",
|
||||
)
|
||||
assert "Content-Length" not in {k.title() for k in result["headers"]}
|
||||
|
||||
|
||||
def test_apply_modifications_keeps_explicit_content_length_on_body_replace() -> None:
|
||||
components = _components(body="old")
|
||||
result = apply_modifications(
|
||||
components,
|
||||
{"body": "new-body", "headers": {"Content-Length": "8"}},
|
||||
"http://example.com/submit",
|
||||
)
|
||||
assert result["headers"]["Content-Length"] == "8"
|
||||
|
||||
|
||||
def test_apply_modifications_without_body_change_keeps_headers() -> None:
|
||||
components = _components(body="old")
|
||||
result = apply_modifications(
|
||||
components,
|
||||
{"headers": {"X-Test": "1"}},
|
||||
"http://example.com/submit",
|
||||
)
|
||||
assert result["headers"]["Content-Length"] == "3"
|
||||
|
||||
|
||||
def test_build_raw_request_recomputes_content_length_after_replacement() -> None:
|
||||
components = _components(body="old")
|
||||
new_body = "a much longer replacement body than the original"
|
||||
modified = apply_modifications(
|
||||
components,
|
||||
{"body": new_body},
|
||||
"http://example.com/submit",
|
||||
)
|
||||
_, raw = build_raw_request(
|
||||
method=modified["method"],
|
||||
url=modified["url"],
|
||||
headers=modified["headers"],
|
||||
body=modified["body"],
|
||||
)
|
||||
head = raw.decode("utf-8").split("\r\n\r\n", 1)[0]
|
||||
assert f"Content-Length: {len(new_body.encode('utf-8'))}" in head
|
||||
Loading…
Add table
Reference in a new issue