fix(proxy): refuse method-override headers; add GCP observability curl example - #15
Merged
Merged
Conversation
Proxy tools landed in #8 without an example under examples/. This adds one for Cloud Trace, Monitoring (MQL, PromQL, dashboards) and Logging, the REST APIs gcloud doesn't fully cover. The routes pin every path to one project and allow read verbs only. The monitoring route also denies notificationChannels and uptimeCheckConfigs: those objects can hold third-party credentials, and the redactor only knows the declared secrets. The header comment says the SA's IAM roles are the real boundary. Two reasons: - Google front ends honor X-HTTP-Method-Override on GET and POST, so a method rule doesn't stop a write on that path. Verified against logging.googleapis.com: GET entries:list with the override set to POST returns 403 (routed) instead of 404. - entries:list names its projects in the body, which the proxy doesn't inspect.
Route rules match the method on the request line, but Google front ends route by X-HTTP-Method-Override when it is present, on GET as well as POST. So `deny = ["DELETE /**"]` did not stop `POST /x` + `X-HTTP-Method-Override: DELETE`, and a `GET /v3/**` allow rule admitted writes. Verified against logging.googleapis.com: GET /v2/entries:list returns 404 without the header and 403 (routed to the POST method, auth required) with `X-HTTP-Method-Override: POST`. vet_request now answers 403 when X-HTTP-Method-Override, X-HTTP-Method or X-Method-Override is present, whatever its value. The request is refused, not stripped: stripping would forward a different request than the tool sent, and a clear 403 tells the agent to use -X. Frameworks that take `_method` from a POST's form body or query string (Laravel, Symfony, Rails) are out of reach, because the proxy doesn't parse bodies. SECURITY.md lists that as a residual risk.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Proxy tools (#8) have no example under
examples/. While checking the route rules for one, I found that Google APIs route byX-HTTP-Method-Override. That header let a request get past the proxy's method rules. This PR fixes that bypass and adds the example.1. Fix: refuse method-override headers (
src/proxy/server.rs)Route rules match the method on the request line. Google front ends use
X-HTTP-Method-Overrideinstead when it is present, on GET and POST. Checked againstlogging.googleapis.com:GET /v2/entries:listGET /v2/entries:list+X-HTTP-Method-Override: POSTPOST /v2/projects/x/logs/yPOST /v2/projects/x/logs/y+X-HTTP-Method-Override: DELETElogs.deleteSo the README's own example,
deny = ["DELETE /**"], did not stopPOST+X-HTTP-Method-Override: DELETE. AGET /…/**allow rule also let writes through.vet_requestnow returns 403 whenX-HTTP-Method-Override,X-HTTP-MethodorX-Method-Overrideis present, whatever its value. The proxy refuses the request instead of stripping the header. Stripping would forward a different request than the tool sent, while a clear 403 tells the agent to use-X.Not covered: frameworks (Laravel, Symfony, Rails) that read
_methodfrom a POST's form body or query string. The proxy doesn't parse bodies. SECURITY.md lists this under residual risks.Docs: the SECURITY.md refusal table and residual risks, a SKILL.md rule for agents, the README rules section, and the flow diagram in
docs/proxy-tools-design.md.2. Example:
examples/gcp-observability-curl.tomlThis file sets up
curlas a proxy tool for Cloud Trace, Cloud Monitoring (v3, MQL, PromQL, dashboards) and Cloud Logging. gcloud covers these REST APIs only partly. The README's proxy-tools section links to it.my-project, and only read verbs are allowed. The one exception isPOST /v2/entries:list, which has no project in its path.denyis a carve-out. The monitoring route deniesnotificationChannels/**anduptimeCheckConfigs/**inside the broadGET /v3/projects/my-project/**. Channel labels and uptime-check headers can carry third-party credentials. Redaction only knows the declared secrets.entries:listnames its projects in the body, and redaction doesn't cover undeclared credentials.Verification
cargo fmt --check,cargo clippy --all-targets -D warnings: clean.cargo test --lib: 480 passed. This includes the newmethod_override_headers_are_refused, which covers every header in any letter case, on GET and POST, with an allow-everything route.cargo test --test proxy_e2e_integration: 6 passed.examples/*.toml. All load.find_route/permits: allowed reads, refused writes, the deny carve-outs, another project, and an unrouted host. All matched the intended decision.🤖 Generated with Claude Code