fix(sync): pull only a clone of the listed repository, over the listed URL #140

Merged
james.maes merged 2 commits from fix/139-verify-origin into main 2026-11-10 18:55:23 +00:00
2 Commits
Author SHA1 Message Date
james.maesandClaude Fable 5.2 510d3e0561 fix(sync): refuse URL rewrites from the folder, keep the user's remotes safe
Follow-up to the independent review of this branch.

- A url.<base>.insteadOf entry in the folder's own configuration redirected
  the fetch of the listed URL to another repository. It is now refused
  before anything is fetched, and so is the folder's core.sshCommand when
  the fetch is not HTTPS. The user's global configuration still applies.
- The temporary remote is marked as gclo's; a remote of that name the user
  created is never removed, and cleanup can no longer mask the fetch's own
  error. A marked leftover is removed on any later pull. The temporary
  fetch follows origin's tag option.
- The mismatch message gives the git remote set-url command for a
  repository that was renamed or moved.
- RemoteUrl: redaction rebuilds the URL from scheme, host, port and path,
  so no shape leaves user information, query or fragment behind; a
  non-default web port distinguishes two servers; an unreadable host is
  nobody's instead of an exception; IPv6 hosts keep their brackets.
- The WSL script compares the stored origin URL, not the rewritten one.
- The credential callback is tested directly. The threat model and README
  no longer claim more than the code does about redirects.

Refs #139

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-10 12:54:51 -06:00
james.maesandClaude Fable 5.2 d5da49b2a2 fix(sync): pull only a clone of the listed repository, over the listed URL
A folder with a repository's name used to be pulled through whatever
origin it had. A same-named clone of a fork, of another owner's
repository or of another host was reported Updated although the listed
repository was never synced, and the git transport was whatever that
folder's configuration said.

- IGitClient.FetchAndPullAsync takes the clone URL the repository was
  listed with. A folder whose origin names a different repository fails
  its row before any request; the message names both, without any
  credentials stored in the origin.
- The fetch always uses the listed URL. An origin that names the same
  repository in another spelling (SSH, plain HTTP, embedded credentials)
  is fetched through a remote that exists only for that call; origin is
  never rewritten, and tags and pruning behave as before.
- The git credential callback answers only HTTPS requests for the listed
  URL's host and port. The default-branch retarget gets a provider of its
  own instead of sharing the fetch's replay counter.
- WslCloner validates the organization and repository as single names,
  requires an HTTPS URL passed after "--", scopes the credential helper
  to the URL's host, and pulls an existing destination only when its
  origin is the listed URL. The view model passes the organization the
  table was loaded for.
- The repository lister refuses a name that is not one plain name before
  making a request.

New internal RemoteUrl holds the two decisions (same repository, may
receive credentials) and is covered by table tests. Threat model: T14
added, T4, T8 and T9 updated.

Closes #139

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-10 12:29:09 -06:00