You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 4367cc2
Browse filesBrowse the repository at this point in the historyBrowse files
Keep pasted pack tokens out of import errors, helpers and config
An operator can import an agent pack from a pasted git URL that carries
a token in its userinfo: x-access-token:<token>@, oauth2:<token>@, or
the token alone as the user. Several places kept it or passed it on.
A failed clone's PackImportException named the pasted URL verbatim, and
that message reaches the API error body, the UI and the mediator's
error log. git hides a password in its own output, but when a token is
pasted as the user alone it asks for a password and names that user
(decoded by git 2.33, re-encoded by 2.50), so git's stderr carried it
as well. The host allowlist echoed a URL it could not parse, and named
the scheme it refused; a URL pasted without https:// parses the token
before its colon as that scheme.
A token pasted as the user alone carries no password, so the clone did
not run as a tokened command: git handed the token to the operator's
credential helpers when it asked them for the missing password, and
wrote it to their trace2 targets.
The clone's .git/config held the tokened origin from before the
transfer until after the import walked the checkout, under the
worker's temp dir with default permissions, and until the janitor's
sweep when a worker died mid-import.
The failure now names the URL without its userinfo, through the same
SanitizeUrl the launch-base resolver uses. git's stderr loses the
userinfo of every http(s) URL in it, then the part that carries the
credential (the password, or the user when none is given) as bare text
in each spelling git echoes it; a user named beside a password, an
account and never echoed outside a URL, is left so git's reason stays
readable. Neither allowlist refusal echoes the input.
The clone runs as a tokened command whenever the URL carries any
credential. TokenedGitCommand.IsTokened is unchanged: a stored
https://user@mirror workspace URL may authenticate through an operator
helper. The clone directory is owner-only before git runs, and once
cloned, origin is rewritten through the workspace provider's own
fail-closed strip, so a clone that can shed it neither way is refused
and deleted. An ssh URL's git@ names an account and is left as written.
Not changed here:
- A successful import stores the pasted URL, token included, as
pack.url, and the pack list and detail queries return it to every
team member, Viewers included. Sync and the add-from-sync flow
re-clone from that URL, so storing or returning it without the token
needs the credential kept server-side (an encrypted reference, a
migration, and an add that resolves the pack instead of its URL).
- During the clone the token is still in git's argv and in
.git/config, now readable by the worker's own uid only. Passing it
through the environment as an http.extraHeader would close that, for
the workspace provider's clones too.
- For a token pasted as the user alone, git with no helper left to ask
falls through to a core.askPass the operator's config may set.
Verified against git 2.33.0 and 2.50.1.
/// <summary>Operators tune how long an orphaned pack clone lingers before the janitor reclaims it (a TimeSpan, e.g. "00:30:00"); default 1h. Pinned by a test (Rule 8). MUST exceed the maximum possible import duration so the age-based sweep never deletes a live clone.</summary>
/// <summary>The clone as the runner gets it: <see cref="BuildCloneArgs"/> in <paramref name="dir"/>, with the network. A pasted URL carrying a token clones as a <see cref="TokenedGitCommand"/>, so no credential helper stores it and no trace2 target records it.</summary>
/// <summary><paramref name="secret"/> in each spelling git may echo it: as written, decoded (git 2.33 names a user decoded) and re-encoded (later git re-encodes it).</summary>
/// <summary>An http(s) URL's userinfo in free text: what follows the scheme up to an '@', with no '/', '?', '#', whitespace or quote between.</summary>
/// <summary>The URL origin keeps and an error names: without its userinfo (<see cref="RemoteTipResolver.SanitizeUrl"/>) when that carries a <see cref="PastedSecret"/>, otherwise as written.</summary>
/// <summary>True when <paramref name="url"/> is a well-formed absolute https URL whose host is on <paramref name="hosts"/>; else false with an actionable <paramref name="reason"/>. Pure + internal so it's unit-pinned.</summary>
/// <summary><paramref name="spec"/> as a tokened command when the remote it can reach is tokened — <see cref="CredentialHelperReset"/> ahead of its arguments, <see cref="TraceOff"/> over its environment — otherwise unchanged.</summary>
/// <summary><paramref name="spec"/> as a tokened command (<see cref="AsTokened"/>) when the remote it can reach is tokened, otherwise unchanged.</summary>
0 commit comments