Skip to content

fix(security): stop hardcoding a personal tailnet FQDN in the host allowlist - #6

Merged
minglong51 merged 1 commit into
mainfrom
fix/trusted-hosts-not-hardcoded
Aug 22, 2026
Merged

fix(security): stop hardcoding a personal tailnet FQDN in the host allowlist#6
minglong51 merged 1 commit into
mainfrom
fix/trusted-hosts-not-hardcoded

Conversation

@minglong51

Copy link
Copy Markdown
Owner

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.py carried 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:

_LOCAL_HOSTS = {"localhost", "127.0.0.1", "::1", "minglongs-mac-mini.tailab5be0.ts.net"}

Two problems:

  1. It publishes your machine name and tailnet ID to everyone who installs.
  2. It sits inside the DNS-rebinding guard. That guard's stated claim is "requests whose Host is 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 opens app.py after 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

_LOCAL_HOSTS = {"localhost", "127.0.0.1", "::1"} | _configured_trusted_hosts()

AUM_TRUSTED_HOSTS is 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 or tailscale serve needs exactly this and previously had no way to get it.

⚠️ One action required, or your tailnet UI breaks

Your launchd job doesn't set the variable, so after upgrading, https://minglongs-mac-mini.tailab5be0.ts.net:8448 will start getting refused by the rebinding guard. Add it to the EnvironmentVariables dict in ~/Library/LaunchAgents/ai.agent-usage-manager.plist:

<key>AUM_TRUSTED_HOSTS</key>
<string>minglongs-mac-mini.tailab5be0.ts.net</string>

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
  • 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

Sequencing

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.

…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
@minglong51
minglong51 marked this pull request as ready for review August 22, 2026 02:23
@minglong51
minglong51 merged commit d92765c into main Aug 22, 2026
4 checks passed
@minglong51
minglong51 deleted the fix/trusted-hosts-not-hardcoded branch August 22, 2026 23:44
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