chore: stop the migrate job printing the Neon endpoint, and write down why - #4
Merged
Merged
Conversation
…n why
The Migrate workflow's first real run (read-only `current` against dev) worked,
and leaked. `alembic current --verbose` emits a header:
Current revision(s) for ***ep-flat-...eu-central-1.aws.neon.tech/neondb?***:
Alembic obscures the password and GitHub masked the secret, but the Neon
endpoint hostname, region and database name reached a PUBLIC Actions log —
breaking the rule stated at the top of that very file, via a flag rather than
an `echo`. Credentials were not exposed and the run has been deleted; the
residual risk was reconnaissance only, since Neon still requires credentials
and the free tier has no IP allowlist to bypass.
Both `current` steps now run bare. `alembic current` prints `0002 (head)`,
which is all the audit trail needs. `alembic history --verbose` keeps its flag:
it reads the migration files and never opens a connection, so it has no URL to
print. Also verified nothing else on that path logs one — `sqlalchemy.engine`
is pinned to WARNING in alembic.ini and migrations/env.py never prints it.
CLAUDE.md gains the two traps that made this workflow inert for a day:
`workflow_dispatch` only registers from the DEFAULT branch, and `environment`
selects the database while the REF selects the migrations. Plus the rule that
main's copy must stay byte-identical to dev's, because the merge base predates
the file and any difference is an add/add conflict at the first promotion.
It also gains the end-to-end security verification pass Kilian asked for: a
dated, tiered checklist to run once the product is feature-complete and before
it is shown to anyone. Tier 1 is what CI already proves and must NOT be
re-tested by hand; tier 2 is what only the real deployment can show (routing,
docs-off, CORS, cookie attributes, IDOR with two real accounts, demo writes,
rate limits, headers, bundle secrets); tier 3 is the infra that lives outside
this repo, where no test in CI can ever notice a control going missing — the
WAF rule above all.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Aug 14, 2026
This branch was successfully deployed
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.
Follow-up to #3, from actually running the thing.
The workflow works
First real dispatch — read-only
current,--ref dev,environment=dev— resolved thedevenvironment's secrets, connected to the dev Neon branch and reportedRev: 0002 (head), withhistory,upgradeandseedall correctly skipped. So PR #2's migration is now confirmed applied to dev by the workflow, not by a laptop.And it leaked
alembic current --verboseprints a header before the revision:Alembic obscures the password and GitHub masked the secret, but the Neon endpoint hostname, region and database name reached a public Actions log — breaking the rule written at the top of that very file, via a flag rather than an
echo. My error: I specified--verbose.Impact: reconnaissance only. No credential was exposed, Neon still requires them, and there is no IP allowlist to bypass on the free tier. The run has been deleted, so the hostname is out of the public history.
The fix
Both
currentsteps run bare.alembic currentprints0002 (head), which is the whole point of the audit trail.alembic history --verbosekeeps its flag — it reads the migration files and never opens a connection, so there is no URL for it to print.Also audited the rest of the path rather than assuming:
sqlalchemy.engineis pinned toWARNINGinalembic.ini, andmigrations/env.pynever prints a URL.--verbosewas the only source.CLAUDE.md — the two traps that made this workflow inert for a day
workflow_dispatchonly registers from the DEFAULT branch. Ondevalone it was completely invisible — a 404 fromgh, and not in the Actions UI either.environmentchooses the DATABASE; the REF chooses the MIGRATIONS. Independent inputs, and mixing them up is how production gets a revision nobody reviewed.Plus the constraint that falls out of #3:
main's copy must stay byte-identical todev's, because the merge base predates the file and any difference is an add/add conflict at the first promotion. Hence the companion one-file PR againstmain— and hence notes going inCLAUDE.mdrather than in a comment that would need duplicating.The security verification pass (Kilian's request)
A dated, tiered checklist, deliberately structured so it cannot become theatre:
frame-ancestorsbreaks the federated mount, and no non-VITE_value in the built bundle./api/auth/*above all, plusframework: null, preview SSO, Dependabot alerts, 2FA, and whether Neon's CU-hours match the model.It also says which items to script and which to hand to Kilian, and to write the outcome down with a date so the next pass verifies rather than re-verifies.
Not in this PR
The 6 open Dependabot alerts (5 starlette, 1 pytest). I checked each against our code: no
request.form(), noStaticFiles, noHTTPEndpoint, and nothing inserver/readsrequest.url.path,url.hostnameor the Host header —enforce_authusesrequest.scope["route"].path, the matched route template. None is exploitable here, including the one advertised as bypassing path-based security checks. Fixing them anyway means FastAPI0.128.8 → 0.141.1and starlette0.52.1 → 1.6.0(FastAPI 0.128.8 pinsstarlette<1.0.0), which is a 0.x→1.x major jump deserving its own PR and its own review.Risk
Docs and one CI-only file. No app code, no dependency change, no version bump.