Skip to content

fix(server): admit the reached IP on a wildcard bind; isolate the origin tests from the environment - #121

Merged
hyperpolymath merged 1 commit into
mainfrom
fix/wildcard-origin-and-test-isolation
Oct 15, 2026
Merged

hyperpolymath merged 1 commit into
mainfrom
fix/wildcard-origin-and-test-isolation

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

What this changes

Both of CodeRabbit's functional-correctness findings from #120, accepted and fixed.

1. Wildcard-bind writes (Major). On a wildcard bind (start(host="0.0.0.0")) the origin allow-list excludes the wildcard address and the Host check runs on loopback binds only, so a UI served at e.g. http://192.168.1.10:8080 received 403 on every write: browsers send Origin on same-origin POSTs, and the served address was not in _allowed_origins(). The guard now admits, on a wildcard bind only, an origin that names the request's own IP: an http:// origin whose host is an IP literal (IPv4 or bracketed IPv6, never a name), on the bound port, whose host-and-port equals the Host header. A browser sends such an Origin only for a page it loaded from exactly that address, so the request is same-origin by construction. A DNS-rebinding page arrives with the attacker's domain as both Host and Origin; a name is not an IP literal, so it is refused exactly as before. Hostnames (e.g. http://lab-box:8080) and proxied ports still have to be listed in JULIA_METAMANIFOLD_ALLOWED_ORIGINS.

2. Test isolation (Minor). The baseline origin assertions in test_server_security.jl assumed JULIA_METAMANIFOLD_ALLOWED_ORIGINS was unset; the suite failed for a developer running it with the variable set (a supported deployment setting). It is now cleared before the testset and restored in the finally.

Tests

A new "Wildcard bind" testset covers: the served IP admitted (GET with CORS echo, preflight 204, a POST that reaches the route and fails there with 400, not 403), an IPv6 literal admitted, other addresses/ports/schemes refused, the DNS-rebinding shape (domain matching its own Host) refused, and a listed hostname working only with the variable set.

Verified locally, both with JULIA_METAMANIFOLD_ALLOWED_ORIGINS unset (86/86 in this file; 2636 passed, 0 failed in the full unit suite) and set to http://localhost:3000, http://192.168.1.10:8080 (still 86/86; the same run on main fails 4 tests).

How to run the tests

julia --project=. test/runtests.jl test_server_security.jl

Versions tested against

Julia 1.13.1 (local; CI installs the pinned 1.12.5), R 4.6.1 (conda-forge; lockfile pins 4.5.0). No frontend change.

Notes

  • The commit is unsigned — the signing key's passphrase was not available in the session that produced it.
  • Rejected the alternative CodeRabbit suggested (admitting a matching request Host generally): a rebinding page matches its own Host too, so only the IP-literal form is safe without new configuration.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe vibe@mistral.ai

…gin tests from the environment

Both CodeRabbit functional-correctness findings from PR #120, accepted.

Wildcard bind: the origin allow-list excludes wildcard addresses and the
Host check runs on loopback binds only, so a UI served from a wildcard
bind (start(host="0.0.0.0")) received 403 on every write: the browser
sends Origin on same-origin POSTs, and the served address
(http://192.168.1.10:8080) was not in _allowed_origins(). The operator's
only recourse was to list the machine's address in
JULIA_METAMANIFOLD_ALLOWED_ORIGINS, which the README documents for split
deployments but which makes the plain wildcard-served UI unusable for
writes.

The guard now admits, on a wildcard bind, an origin that names the
request's own IP: an http origin whose host is an IP literal (IPv4 or
bracketed IPv6, never a name), on the bound port, whose host-and-port
equals the Host header. A browser sends such an Origin only for a page
it loaded from exactly that address, so the request is same-origin by
construction; a DNS-rebinding page arrives with the attacker's domain as
both Host and Origin, and a name is not an IP literal, so it is refused
as before. Hostnames and proxied ports must still be listed in
JULIA_METAMANIFOLD_ALLOWED_ORIGINS.

Test isolation: the baseline origin assertions in
test_server_security.jl assumed JULIA_METAMANIFOLD_ALLOWED_ORIGINS was
unset; the suite failed for a developer running it with the variable set
(a supported deployment setting). It is now cleared before the testset
and restored in the finally.

Tests: a new wildcard-bind testset covers the admitted IP (GET with CORS
echo, preflight 204, a POST that reaches the route), the admitted IPv6
literal, other addresses, ports and schemes refused, the rebinding
shape refused, and a listed hostname required without the variable.
Verified locally both with the variable unset (86/86 in the file,
2636 passed in the full suite) and set to
"http://localhost:3000, http://192.168.1.10:8080" (still 86/86; the
same run on main fails 4 tests).

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
@coderabbitai

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 3 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: c1223cc1-5148-41c5-b359-81086e0b72d8
📥 Commits

Reviewing files that changed from the base of the PR and between afc76f2 and 525c280.

📒 Files selected for processing: 2
  • src/server/server.jl
  • test/unit/test_server_security.jl
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath merged commit b672663 into main Oct 15, 2026
10 checks passed
@hyperpolymath

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Pull request is closed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant