Commit 322b09f
Add Origin header validation middleware
ToolHive's proxy layer had no Origin-header validation, and the legacy
HTTP+SSE transport sent `Access-Control-Allow-Origin: *`, leaving both
modes open to DNS-rebinding attacks from browser clients. MCP 2025-11-25
§"Security Warning" requires servers to validate Origin on all
connections and respond 403 when the value is invalid.
This change introduces a dedicated middleware at
pkg/transport/middleware/origin/ that rejects requests whose Origin
header is present and not in an operator-configured allowlist. It is
wired centrally in runner.Run for both the factory-based chain
(thv run / thv-proxyrunner) and inline in the `thv proxy` chain.
Wiring is done in runner.Run rather than in the builder because that is
the only point where the effective Host/Port/AllowedOrigins are fully
resolved: the CLI builder (WithMiddlewareFromFlags) defers port
resolution to validateConfig, so an earlier hook would see port 0 and
silently disable loopback-default protection for `thv run`. The
middleware is prepended so Origin validation runs first in the chain.
Behavior:
- New --allowed-origins flag on `thv run` and `thv proxy` accepts a
repeatable exact-match list. When empty and the bind host is
loopback, a default loopback-only allowlist is derived automatically
(http://localhost:PORT + 127.0.0.1 + [::1]). When empty and the
bind is non-loopback, the middleware is skipped and a warning is
logged — the bind-opt-in hardening lands in a follow-up.
- Matching parses each value with net/url and compares scheme://host
[:port] with scheme and host lowercased per RFC 6454 §4. Values
carrying userinfo (RFC 6454 §6) or that fail to parse never match,
closing an Origin-smuggling vector. Requests with multiple Origin
headers are rejected outright.
- 403 responses carry a JSON-RPC error body (id: null, code -32600,
message "Origin not allowed").
- `Access-Control-Allow-Origin: *` removed from the httpsse SSE
handler; the wildcard would have neutered any Origin enforcement
via preflight response inheritance.
Operator CRDs (MCPServer/MCPRemoteProxy/VirtualMCPServer) do not yet
expose an allowedOrigins field, and vMCP composes its own middleware
chain that does not reference this package. Both are deferred to
follow-ups; until then operator/vMCP non-loopback deployments log the
WARN above rather than enforcing.
Closes audit row 5 (Origin validation absent).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Juan Antonio Osorio <ozz@stacklok.com>1 parent 6f63ac0 commit 322b09f
15 files changed
Lines changed: 872 additions & 8 deletions
File tree
- cmd/thv/app
- docs
- cli
- server
- pkg
- runner
- transport
- middleware/origin
- proxy/httpsse
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
110 | 111 | | |
111 | 112 | | |
112 | 113 | | |
113 | | - | |
114 | | - | |
115 | | - | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
116 | 118 | | |
117 | 119 | | |
118 | 120 | | |
| |||
133 | 135 | | |
134 | 136 | | |
135 | 137 | | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
136 | 142 | | |
137 | 143 | | |
138 | 144 | | |
| |||
226 | 232 | | |
227 | 233 | | |
228 | 234 | | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
229 | 251 | | |
230 | 252 | | |
231 | 253 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
144 | 150 | | |
145 | 151 | | |
146 | 152 | | |
| |||
160 | 166 | | |
161 | 167 | | |
162 | 168 | | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
163 | 173 | | |
164 | 174 | | |
165 | 175 | | |
| |||
685 | 695 | | |
686 | 696 | | |
687 | 697 | | |
| 698 | + | |
688 | 699 | | |
689 | 700 | | |
690 | 701 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
106 | 114 | | |
107 | 115 | | |
108 | 116 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
331 | 331 | | |
332 | 332 | | |
333 | 333 | | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
334 | 346 | | |
335 | 347 | | |
336 | 348 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| 25 | + | |
24 | 26 | | |
25 | 27 | | |
26 | 28 | | |
| |||
45 | 47 | | |
46 | 48 | | |
47 | 49 | | |
| 50 | + | |
48 | 51 | | |
49 | 52 | | |
50 | 53 | | |
| |||
57 | 60 | | |
58 | 61 | | |
59 | 62 | | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
60 | 69 | | |
61 | 70 | | |
62 | 71 | | |
63 | 72 | | |
64 | 73 | | |
65 | | - | |
66 | | - | |
67 | | - | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
68 | 77 | | |
69 | 78 | | |
70 | 79 | | |
71 | 80 | | |
72 | 81 | | |
73 | 82 | | |
74 | 83 | | |
75 | | - | |
| 84 | + | |
76 | 85 | | |
77 | 86 | | |
78 | 87 | | |
| |||
421 | 430 | | |
422 | 431 | | |
423 | 432 | | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
424 | 472 | | |
425 | 473 | | |
426 | 474 | | |
| |||
0 commit comments