{"record":{"id":"890cdecdaf09f449","repo":"Hmbown/CodeWhale","slug":"remote-url-must-not-start-with","errorCode":null,"errorMessage":"remote url must not start with '-'","messagePattern":"remote url must not start with '-'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/cloud_dispatch.rs","lineNumber":411,"sourceCode":"/// Whether a git remote is safe to clone, show, or hand to a sandbox.\n///\n/// Rejects leading-dash injection (`--upload-pack=…`), embedded userinfo,\n/// and network remotes that do not classify as a supported forge. Local\n/// path remotes (offline fixtures) are allowed when they do not start\n/// with `-` and carry no userinfo.\npub fn safe_git_remote_url(raw: &str) -> bool {\n    validate_git_remote_url(raw).is_ok()\n}\n\n/// Classify and validate `job.remote_url` before any `git clone` or\n/// sandbox clone. Returns the trimmed URL on success.\npub fn validate_git_remote_url(raw: &str) -> Result<String> {\n    let url = raw.trim();\n    if url.is_empty() || url.len() > MAX_REMOTE_BYTES {\n        bail!(\"remote url is empty or oversized\");\n    }\n    if url.starts_with('-') {\n        bail!(\"remote url must not start with '-'\");\n    }\n    if url.chars().any(char::is_control) {\n        bail!(\"remote url contains control characters\");\n    }\n    if remote_has_userinfo(url) {\n        bail!(\"remote url must not embed userinfo\");\n    }\n    if looks_like_network_git_url(url) && classify_url(url).is_none() {\n        bail!(\"remote url is not a supported forge\");\n    }\n    Ok(url.to_string())\n}\n\n/// Display form of a remote: userinfo is never printed.\npub fn redact_remote_url(raw: &str) -> String {\n    redact_url_userinfo(raw)\n}\n","sourceCodeStart":393,"sourceCodeEnd":429,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/cloud_dispatch.rs#L393-L429","documentation":"validate_git_remote_url rejects URLs beginning with '-' so the value can never be parsed as an option flag by git or a shell. This is an option-injection guard: a URL like `-oProxyCommand=...` passed to `git clone <url>` could otherwise execute attacker-controlled code.","triggerScenarios":"Calling validate_git_remote_url / safe_git_remote_url / clone_repository with a raw string whose first non-whitespace character is '-', e.g. a malicious repo URL field, or a value where the actual URL was accidentally cut off and a flag remains.","commonSituations":"Security tests or fuzzed inputs starting with '-'; a truncated paste where only the tail of a command like `git clone -o ...` was captured; config values written by scripts that forgot the scheme.","solutions":["Provide a full URL with a scheme (https:// or git@host:...) so it cannot start with '-'.","Trim and validate user-supplied remote values at the boundary before storing or cloning.","If you need to pass flags to git, use the dedicated option parameters, never the URL slot.","Audit any code path that composes `git clone <value>` from raw user input."],"exampleFix":"// before\nlet url = \"-oProxyCommand=evil\";\nvalidate_git_remote_url(url)?;\n// after\nlet url = \"https://github.com/org/repo.git\";\nvalidate_git_remote_url(url)?;","handlingStrategy":"validation","validationCode":"fn url_safe_start(raw: &str) -> bool {\n    !raw.trim().starts_with('-')\n}","typeGuard":null,"tryCatchPattern":"match validate_git_remote_url(raw) {\n    Err(e) if e.to_string().contains(\"must not start with '-')\") => {\n        eprintln!(\"remote URL looks like a flag; provide a full https:// or git@ URL\");\n    }\n    other => { /* ... */ }\n}","preventionTips":["Require a scheme (https://) or scp-like (git@host:) prefix in user-supplied remotes.","Never pass unvalidated user text into a position git parses as an argument.","Keep this check server/library-side; do not rely on the UI alone.","Fuzz inputs starting with '-' in tests for clone flows."],"tags":["security","git","validation","option-injection"],"backgroundTag":"invalid-url-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}