fix(security): stop hardcoding a personal tailnet FQDN in the host allowlist - #6
Merged
Merged
Conversation
…lowlist
_LOCAL_HOSTS carried "minglongs-mac-mini.tailab5be0.ts.net" as a literal, and it
ships in the published 0.2.5 wheel on PyPI (verified by unzipping the artifact
from the index). Two problems, and the second is the reason this is worth fixing
before a security-framed launch:
1. It publishes the maintainer's machine name and tailnet ID to everyone who
installs the package.
2. It puts a hardcoded exception inside the DNS-rebinding guard. The guard's
whole claim is "requests whose Host is a non-local DNS name are refused" —
and the shipped source shows one specific non-local DNS name that is not.
Anyone reading app.py while evaluating the kill-switch story finds it.
For every other user the entry was also dead weight: an allowlisted hostname
they do not control and cannot use.
Replaced with AUM_TRUSTED_HOSTS, a comma-separated opt-in read at import:
_LOCAL_HOSTS = {"localhost", "127.0.0.1", "::1"} | _configured_trusted_hosts()
Empty by default, so out of the box only loopback names are accepted. This also
turns a maintainer-specific hack into the general feature the README already
described — anyone fronting the app with a reverse proxy or tailnet serve needs
exactly this, and previously had no way to get it.
Verified:
- 74 tests pass
- default: _LOCAL_HOSTS == {127.0.0.1, ::1, localhost}; the tailnet FQDN is
refused; localhost still allowed
- with AUM_TRUSTED_HOSTS set: that FQDN allowed, an unrelated host still refused
- rebuilt wheel greps clean for tailab5be0 / minglongs / oclaw / founder-agent-os;
the only remaining "ts.net" is the generic README placeholder
Rides the unreleased 0.2.6, so one release fixes both this and the shipped
config leak from #5.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015swHyENHGowhH1cwMXxiE2
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.
Found while fact-checking the launch kit's drafted HN answers against the real code. This is a second personal-infrastructure leak in the published package, independent of #5, and it lands in a worse place.
The problem
app.pycarried this as a literal, and it ships in the live 0.2.5 wheel on PyPI — I unzipped the artifact from the index to confirm:Two problems:
Hostis a non-local DNS name are refused" — and the shipped source shows exactly one non-local DNS name that isn't. The launch kit tells you to lead the HN first comment with "CSRF + DNS-rebinding guards"; anyone who opensapp.pyafter reading that finds a hardcoded exception for one specific person's machine. On a launch whose entire hook is adversarial-caller discipline, that's the comment you least want.For every other user the entry was also dead weight — an allowlisted hostname they don't control and can't use.
The fix
AUM_TRUSTED_HOSTSis a comma-separated opt-in, empty by default. Out of the box only loopback names are accepted — strictly tighter than today. It also turns a maintainer-specific hack into the general feature the README already described: anyone fronting this with a reverse proxy ortailscale serveneeds exactly this and previously had no way to get it.Your launchd job doesn't set the variable, so after upgrading,
https://minglongs-mac-mini.tailab5be0.ts.net:8448will start getting refused by the rebinding guard. Add it to theEnvironmentVariablesdict in~/Library/LaunchAgents/ai.agent-usage-manager.plist:then
launchctl kickstart -k gui/501/ai.agent-usage-manager. I deliberately didn't touch the plist — it's your running service.Verification
74 passed_LOCAL_HOSTS == {127.0.0.1, ::1, localhost}; the tailnet FQDN is refused;localhoststill allowedAUM_TRUSTED_HOSTSset: that FQDN allowed, an unrelated host still refusedtailab5be0,minglongs,oclaw,founder-agent-os; the only remainingts.netis the generic README placeholderSequencing
Rides the unreleased 0.2.6, so one release closes both this and the shipped-config leak from #5. PyPI still serves 0.2.5 with both problems — this should land before you cut the tag.
Draft, because it changes a security guard's default behaviour and costs you one plist edit.