[Bug] Sandbox repository field accepts a URL with embedded credentials that the server rejects
Description
isValidSandboxRepoUrl in web/src/shell/NewChatDialog.tsx accepts a repository URL with embedded credentials, so the Add button enables and the token is sent to the server, which then rejects the create with a 422.
The server treats this as a secret-handling boundary, not a formatting rule (omnigent/server/managed_hosts.py):
Reject embedded credentials (user:token@host) BEFORE any error that echoes the URL: the token would otherwise be persisted verbatim in a session label and the sandbox Pod spec, and leak into the error/logs.It also keeps the URL out of the error message for the same reason. The client is the last place that token can be stopped before it leaves the browser, and it is the one place that does not check for it.
The client regex allows @ inside the host segment, since @ is not excluded from [^\s#/]+, and the scp-style pattern likewise permits a second @:
return /^https:\/\/[^\s#/]+\/[^\s#]+$/.test(t) || /^git@[^\s#:]+:[^\s#]+$/.test(t);| Input | Client | Server |
|---|---|---|
https://github.com/org/repo |
accepted | accepted |
[email protected]:org/repo.git |
accepted | accepted |
https://user:[email protected]/org/repo |
accepted | rejected, embedded credentials |
git@user:[email protected]:org/repo |
accepted | rejected, embedded credentials |
https://github.com// |
accepted | rejected, unusable URL |
The same validator gates the fork dialog (web/src/shell/ForkSessionDialog.tsx), so both paths behave this way.
Proposed fix: exclude @ from the authority in both patterns, mirroring the server's check, so a credential URL is refused inline and never transmitted. The docstring says the client only gates the submit button and the server is the authority, which stays true; this is about the one input where an early refusal keeps a secret out of the request.
Steps to reproduce
The screen itself needs a GitHub connection advertised by the server, so this is easiest to show against the two validators directly.
Client (the exact expression from isValidSandboxRepoUrl):
const isValid = (url) => {
const t = url.trim();
return /^https:\/\/[^\s#/]+\/[^\s#]+$/.test(t) || /^git@[^\s#:]+:[^\s#]+$/.test(t);
};
isValid("https://user:[email protected]/org/repo"); // trueServer:
from omnigent.server.managed_hosts import parse_repo_workspace
parse_repo_workspace("https://user:[email protected]/org/repo")
# ValueError: a repository URL must not embed credentials (user:token@host) ...In the UI, with a managed sandbox host: paste that URL into the repository field on the new-chat screen. The Add button enables and the repo chip appears, and the failure only arrives as a 422 after the create request carries the token to the server.
Expected: the field refuses a URL with embedded credentials inline, as the server does, so the token never leaves the browser.
Version
main @ f8e1e4bf
OS
MacOS
Harness
No response
Harness mode
No response
Platform or device
macOS
Observed impact
None
Authentication type
No response
Source: omnigent-ai/omnigent