How code-review-graph Implements HTTP Transport Security and Origin Guard Protection
The code-review-graph repository implements a pure-ASGI origin guard that validates HTTP Host and Origin headers against loopback bindings, rejecting cross-origin requests with a 403 Forbidden response before they reach the FastMCP application.
The code-review-graph project provides an optional HTTP transport layer for its Model Context Protocol (MCP) graph server. When the server binds to localhost interfaces, it activates a strict origin guard to mitigate cross-origin request forgery (CSRF) and DNS rebinding attacks. This security mechanism is implemented entirely within ASGI middleware, ensuring that untrusted browser origins cannot invoke the MCP endpoints.
Loopback-Only Activation Strategy
The origin guard applies a loopback-only activation policy, ensuring that security checks only enforce same-origin policies when the server binds to local interfaces.
Safe Host Constants
The implementation defines LOOPBACK_HOSTS as a frozen set of hostnames that are considered safe for local binding. This constant is defined at lines 39–40 in code_review_graph/http_origin_guard.py and includes standard loopback identifiers such as localhost, 127.0.0.1, and ::1.
Loopback Verification
The helper function is_loopback_host() determines whether a given host resolves to a loopback IP address. According to the source code at lines 45–53, this function uses Python's ipaddress.ip_address(...).is_loopback method to verify that the resolved IP is within the loopback range, preventing attackers from using DNS rebinding to bypass restrictions.
Strict Header Parsing
Before validation occurs, the guard normalizes incoming request headers to eliminate ambiguity and reject malformed input.
Host Header Normalization
The split_host_port() function parses the Host header into a normalized hostname and optional port. As implemented at lines 68–84 of http_origin_guard.py, this function rejects malformed authorities containing spaces or forbidden characters (/, \, ?, #, @), ensuring that only valid HTTP authorities reach the validation logic.
Port Validation
The _normalize_port() helper ensures that extracted port values are valid integers within the TCP port range. Located at lines 58–66, this function validates that ports are numeric and within acceptable bounds before comparing them against the server's configured listening port.
Origin Header Validation
The guard enforces strict scheme and authority matching on the browser-controlled Origin header.
Allowed Schemes
The constant _ALLOWED_ORIGIN_SCHEMES restricts valid origins to http and https protocols only (lines 41–42). This prevents custom protocol schemes from bypassing security checks.
Authority Matching
When an Origin header is present, the middleware validates that its authority (host and port) exactly matches the server's loopback binding. The guard rejects any request where the origin authority differs from the configured loopback host and port, effectively blocking cross-origin requests from foreign domains.
ASGI Middleware Architecture
The LoopbackOriginGuard class implements the ASGI middleware protocol, wrapping the FastMCP application to intercept requests at the transport layer.
Middleware Implementation
LoopbackOriginGuard is a callable ASGI middleware that inspects scope["headers"] before passing control to the underlying application. If the guard detects a foreign Host or Origin header that violates the loopback policy, it immediately returns a 403 Forbidden response without invoking the FastMCP handler.
Integration with FastMCP
The factory function build_http_middleware(host, port) constructs a properly configured guard instance for the FastMCP HTTP server. According to the source at lines 1155–1156, this function wires the guard into the ASGI application stack, enabling automatic protection when the server starts with loopback-specific arguments.
# Build the FastMCP HTTP app with the origin guard enabled (default loopback bind)
from fastmcp import FastMCP
from code_review_graph.http_origin_guard import build_http_middleware
mcp = FastMCP("my-graph")
app = mcp.http_app(middleware=build_http_middleware("127.0.0.1", 5555))
# Manually invoke the guard in custom ASGI setups
from code_review_graph.http_origin_guard import LoopbackOriginGuard
guard = LoopbackOriginGuard("127.0.0.1", 5555)
async def asgi_app(scope, receive, send):
# … your ASGI endpoint …
pass
app_with_guard = guard(asgi_app) # Returns 403 on foreign Origin/Host
Security Testing Coverage
The end-to-end test suite in tests/test_http_origin_guard.py validates the guard's behavior against various attack scenarios.
Foreign Origin Rejection
Tests at lines 56–61 verify that requests containing a malicious Origin header (e.g., http://evil.example) receive a 403 Forbidden response, confirming that the guard blocks basic cross-origin attacks.
Same-Origin Acceptance
Lines 62–70 demonstrate that requests from the same origin, localhost variants, or requests without an Origin header (typical for non-browser MCP clients) succeed and reach the application handler.
DNS Rebinding Protection
The test suite at lines 72–78 specifically targets DNS rebinding attacks, verifying that requests using a legitimate loopback IP but a forged Host header are rejected. This prevents attackers from using DNS rebinding to exploit the localhost binding.
Summary
- Loopback-only enforcement: The
LoopbackOriginGuardactivates only when binding to loopback addresses, usingLOOPBACK_HOSTSandis_loopback_host()to verify safe bindings. - Strict header validation:
split_host_port()and_normalize_port()parse and validateHostheaders, rejecting malformed authorities before processing. - Scheme restriction: Only
httpandhttpsorigins are permitted via_ALLOWED_ORIGIN_SCHEMES, blocking non-standard protocols. - ASGI middleware layer: The guard integrates as ASGI middleware through
build_http_middleware(), returning 403 Forbidden for violations before FastMCP processing. - Comprehensive testing:
tests/test_http_origin_guard.pyverifies protection against foreign origins, same-origin access, and DNS rebinding attacks.
Frequently Asked Questions
What is the primary purpose of the origin guard in code-review-graph?
The origin guard protects the optional HTTP server mode from cross-origin attacks by validating that requests originate from the same loopback binding or from trusted non-browser clients. It prevents malicious websites from invoking the MCP server when it is exposed locally via the serve --http command.
How does the guard prevent DNS rebinding attacks?
The guard uses is_loopback_host() to resolve hostnames and verify they map to loopback IP addresses (127.0.0.0/8 or ::1) while also validating the Host header against the expected binding. This prevents attackers from using DNS rebinding to point a malicious domain at 127.0.0.1 and bypassing origin checks, as the guard validates both the resolved IP and the declared host authority.
Why does the origin guard only activate on loopback addresses?
The guard specifically checks for loopback bindings because the HTTP server is intended for local development and testing. When binding to public interfaces, the project assumes proper network-level security or reverse proxying. The LOOPBACK_HOSTS constant and is_loopback_host() function ensure the middleware only enforces strict origin checking when the attack surface is limited to the local machine, avoiding false positives in legitimate cross-origin scenarios for public deployments.
Can the origin guard be bypassed by omitting the Origin header?
No, this is by design. The guard distinguishes between browser and non-browser clients: browsers always send Origin for cross-origin requests, while legitimate MCP clients typically omit the header entirely. Requests without an Origin header are allowed (as verified in tests/test_http_origin_guard.py lines 62–70), but requests with a mismatched Origin or Host header are rejected with 403 Forbidden.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →