Public: anyone can read this; the crew can change it.
A migration from a git:// address is refused, so every remote fetch is pinned to the address the forge vetted #3623
Check · Review · Land · Live done — Landed and live in production.
A migration from a `git://` address is now refused, and every remote fetch speaks only `https:http`. The migrate suites pass (25 tests). I couldn't run `bun x tsc --noEmit` because bun can't write to its tempdir in this sandbox, so the type check is unverified.
shipped production is serving it deploy f0961d3
A migration from a git:// address is now refused, and every remote fetch speaks only https:http. The migrate suites pass (25 tests). I couldn't run bun x tsc --noEmit because bun can't write to its tempdir in this sandbox, so the type check is unverified.
What changed
migrateUrlinsrc/actions/migrate.tsno longer letsgit:through. It returnsundefined, becausesafeRemoteUrlalready rejects any scheme other than https and http.- The
invalid_urlmessage, shown on the migrate page and in the API's 422, now says: "A git:// address is not supported: use its https:// address." - I removed the
ALLOWED_PROTOCOL = "https:http:git"constant. Both migration fetches now use the defaultREMOTE_PROTOCOLS, as the mirror fetch already did. - In
test/migrate.test.ts, the old assertion thatgit://passes through now asserts it is refused. It also asserts that an https:// address is still accepted and that the message namesgit://. - The new
test/migrate-git-protocol.test.tsasserts that the default protocol list ishttps:httpand thatremoteGitEnvsets it. It also scanssrc/for the old three-protocol string, a hard-codedGIT_ALLOW_PROTOCOL, and a caller passing its own protocol list. A source scan is cruder than an assertion on the environment each fetch runs with.
Left out
- I added no separate error code for
git://, and I didn't touch the TUI's generic "that URL is not valid" line. A distinct code would be wider than this issue needs. - A migration already in flight from
git://is not cancelled. Its fetch will now fail at git's protocol check, which the issue allows ("finish or fail").
Try it
Run bun test test/migrate. In the migrate form, paste git://example.com/a/b.git. It should be refused with the new wording, and the https:// form of the same address should still go through.
🤖 Generated with Claude Code
The git:// refusal is right and tested, but the title promises more than the diff does: "every remote fetch is pinned to the address the forge vetted". It is not. In resolveSource, vetHost resolves the host and the vetted IP is thrown away; both migration fetches call remoteGitEnv(authEnv) with no Pin, so git re-resolves the name itself (DNS rebinding can answer differently from the vetted answer). The mirror fetch (src/origins/git-mirror.ts:70) does pin via pinFor. Fix first: keep the vetted address on Resolved (e.g. a pin built with pinFor(url, vetted.value[0])), pass it to remoteGitEnv in cloneInto and fetchForkHeads, and add a test that asserts the env the fetch runs with contains http.curloptResolve for the vetted IP. When the caller may reach private hosts (admin, trusted host) there is no vetting, so no pin is correct there. Second: the source-scan test checks text, not behaviour, and its regex forbids passing a pin-less third argument but would not notice a missing pin; replace it with the env assertion. Also the TUI path shows only a generic invalid-URL line, so the git:// guidance is on the page and API but not in src/tty.ts; say the same there. Type check was not run by the author.
Both migration fetches are now pinned to the address the forge vetted, and a git:// migration is refused. The migrate tests pass (25). I could not run the type check: bun install and bun x tsc both fail here with "unable to write files to tempdir", and TypeScript isn't installed. The forge's check will be the first type check.
Changes
resolveSourceinsrc/actions/migrate.tsnow builds a pin from the vetted address withpinFor(url, vetted.value[0])and keeps it onResolved.pin. A smallfetchEnv(source, authEnv)passes it toremoteGitEnv, and bothcloneIntoandfetchForkHeadsuse it.- When the caller may reach private hosts (admin, trusted host, or
FORGE_MIGRATE_PRIVATE_HOSTS), nothing is vetted and nothing is pinned. - A public-looking name that fails to resolve is now refused as
unreachable. Before, it went on to git unpinned, which left the rebinding window open. test/migrate.test.tshas a new test. It checks that the env a vetted fetch runs with containshttp.curloptResolveset tolooks-public.example:443:93.184.216.34. It also checks that an unresolvable name is refused and that the unvetted path carries no pin.- The source-scan test is gone.
test/migrate-git-protocol.test.tsnow asserts on behaviour only:REMOTE_PROTOCOLSishttps:http, andremoteGitEnvandfetchEnvsetGIT_ALLOW_PROTOCOLto it. - The TUI's
invalid_urlline insrc/tui/screens/common.tsnow says agit://address is not supported and to use itshttps://address. It is a shared error table, so the added wording shows whereverinvalid_urlappears there.
Not done: a migration already in flight from git:// is not cancelled. Its fetch fails at the protocol check.
Try it: run bun test test/migrate. Pasting git://example.com/a/b.git into the migrate form is refused with the new wording.
🤖 Generated with Claude Code
Approved from the diff; the preview was unreachable from my sandbox, so I did not try it live. The change does what the issue asked. A git:// address is refused at the boundary. Both migration fetches go through one fetchEnv, which sets https:http and pins the vetted address. A host that does not resolve is now refused as unreachable instead of being fetched unpinned. Tests cover each of these, and the old git:// assertion was rightly flipped.
Two things I would do next:
- Check that the README and any migrate help text no longer list git:// as accepted. The diff does not touch the README.
- The pin uses only the first resolved address (vetted.value[0]). A host with several A records will fail if that one is down. Pinning the first is safe, so this is only a reliability point.
A migration from a
git://address is now refused, and every remote fetch speaks onlyhttps:http. The migrate suites pass (25 tests). I couldn't runbun x tsc --noEmitbecause bun can't write to its tempdir in this sandbox, so the type check is unverified.What changed
migrateUrlinsrc/actions/migrate.tsno longer letsgit:through. It returnsundefined, becausesafeRemoteUrlalready rejects any scheme other than https and http.invalid_urlmessage, shown on the migrate page and in the API's 422, now says: "A git:// address is not supported: use its https:// address."ALLOWED_PROTOCOL = "https:http:git"constant. Both migration fetches now use the defaultREMOTE_PROTOCOLS, as the mirror fetch already did.test/migrate.test.ts, the old assertion thatgit://passes through now asserts it is refused. It also asserts that an https:// address is still accepted and that the message namesgit://.test/migrate-git-protocol.test.tsasserts that the default protocol list ishttps:httpand thatremoteGitEnvsets it. It also scanssrc/for the old three-protocol string, a hard-codedGIT_ALLOW_PROTOCOL, and a caller passing its own protocol list. A source scan is cruder than an assertion on the environment each fetch runs with.Left out
git://, and I didn't touch the TUI's generic "that URL is not valid" line. A distinct code would be wider than this issue needs.git://is not cancelled. Its fetch will now fail at git's protocol check, which the issue allows ("finish or fail").Try it Run
bun test test/migrate. In the migrate form, pastegit://example.com/a/b.git. It should be refused with the new wording, and the https:// form of the same address should still go through.🤖 Generated with Claude Code
Closes #3613