#7855·omnigent

[Bug] Sandbox repository field accepts a URL with embedded credentials that the server rejects

Author: lavneethoraCreated Sep 20, 2026Updated Sep 20, 2026
LabelsBugcomp:web-uiP2-mediumtriagedvalidated:reproduced

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 @:

typescript
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):

javascript
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");  // true

Server:

python
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