Fail closed on separator-only AllowedHosts in standalone MCP host security - #1372
Conversation
There was a problem hiding this comment.
Code Review
This pull request improves the security of the standalone MCP host configuration in Program.cs by ensuring that empty or separator-only values (such as ";", ";;", or " ; ") in AllowedHosts are treated as invalid and fail closed to the loopback allowlist. This prevents HostFilteringMiddleware from bypassing host filtering. Corresponding unit tests in McpHttpTransportApiKeyTests.cs have been added and updated to verify this behavior. I have no feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Adversarial self-review — no findingsReviewed the diff against the failure conditions in #1367 at head CRITICAL / HIGH / MEDIUM / LOW: none. Verification of the three adversarial questions:
Also checked: the method is idempotent (re-running on Out of scope (noted, not changed): the any-host token set ( Bot comments: the Copilot reviewer returned "unable to review … quota limit" (no findings); no other comments present. Test evidence: |
Consolidated adversarial review findings (two independent reviews)Two independent adversarial reviews (security lens + test lens) of this PR were adjudicated by the batch coordinator. Four findings survive; all will be fixed in one batched push per the zero-skip policy. Bot status: Gemini Code Assist reviewed with "no feedback to provide"; Copilot was quota-blocked. No unaddressed bot threads. HIGHF2 — The call site is untested and there is no end-to-end proof (test lens, CONFIRMED). MEDIUMF1 — Port-suffixed any-host wildcards bypass the guard but disable HostFilteringMiddleware (security lens, CONFIRMED). The guard tests raw split tokens with exact ordinal match ( F3 — New comment overclaims (test lens). The comment says all separator-only inputs mean "allow every host", but the middleware splits with LOWF4 — Preservation theory pins byte-identical raw strings without stating intent (test lens, adjudicated partial-accept). Raw preservation IS the deliberate contract (valid operator configs are not normalized) — behavior stays, but the theory gets a short comment documenting that intent so a future normalization change is a conscious contract change, not an accident. Fixes, verification evidence, and a finding → commit map will follow in this thread. |
Fix evidence — all four findings addressed (zero-skip)All findings from the consolidated adversarial review above are fixed and pushed. Head:
Test evidence (Release,
Bot threads: Gemini Code Assist reviewed with "no feedback to provide"; Copilot was quota-blocked. No inline threads existed at the time of the original comment. ADDENDUM — Codex round 2 (head
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6dfccf3cbd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fa5c857de7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Closes #1367
Defect
Program.ApplyStandaloneMcpHostSecurity(backend/src/Taskdeck.Api/Program.cs) rewroteAllowedHoststo the loopback allowlist only when the raw value was blank or contained anany-host token (
*,0.0.0.0,[::]). A separator-only value like";",";;", or" ; "slipped through both guards:
string.IsNullOrWhiteSpace(";")isfalse.RemoveEmptyEntries | TrimEntriesyields an empty array, so.Any(any-host)isfalse.ASP.NET Core
HostFilteringMiddlewarethen parses";"as zero configured hosts and falls backto allowing all hosts — the exact fail-open this method exists to prevent.
Fix
Compute the parsed host set once and treat "parses to zero non-empty hosts" as the failure
condition. Fail closed to
StandaloneMcpLoopbackAllowedHostswhenever the post-split host set isempty (this subsumes the old blank check) or contains an any-host token. Explicit exact
allowlists are unchanged.
Tests
Extended the existing #1364 host-security tests in
backend/tests/Taskdeck.Api.Tests/McpHttpTransportApiKeyTests.cs:StandaloneMcpHostSecurity_ReplacesPermissiveAllowedHostsgains regression cases";",";;"," ; "— each proven to be rewritten to the loopback allowlist.StandaloneMcpHostSecurity_PreservesExplicitAllowedHostsbecomes a theory adding"mcp.example.com"(legitimate explicit allowlist preserved) and"good; ;"(a real host mixedwith separator noise is preserved, not failed closed).
Verification
dotnet build backend/Taskdeck.sln -c Release -m:1— 0 errors.dotnet test ... --filter "FullyQualifiedName~McpHttpTransportApiKeyTests"— 43 passed, 0failed (15 host-security cases, including the 5 new ones, all green).
No EF/model changes; no docs gates touched.