farion1231/cc-switch · error · anyhow::Error

INVALID_REPO_REF

INVALID_REPO_REF

Error message

{"code":"INVALID_REPO_REF","context":{"owner":"{owner}","name":"{name}"},"suggestion":"checkRepoUrl"}

What it means

Structured error (code INVALID_REPO_REF, suggestion checkRepoUrl) from validate_repo_ref: the GitHub owner or repo name failed the charset check (is_valid_github_owner / is_valid_github_repo_name — ASCII alphanumerics plus hyphen, bounded length, non-empty). These values get formatted straight into https://github.com/{owner}/{name}/archive/refs/heads/{branch}.zip, so any URL-significant character (/ . % \\ etc.) could rewrite where the request lands.

Source

Thrown at src-tauri/src/services/skill.rs:2912

        }
        branch.split('/').all(|segment| {
            !segment.is_empty()
                && !segment.starts_with('.')
                && !segment.ends_with('.')
                && !segment.ends_with(".lock")
        })
    }

    /// 校验一组仓库坐标,用于任何会被拼进 github.com URL 的地方。
    ///
    /// 动机:`download_repo` 把 owner/name/branch 直接 format 进
    /// `https://github.com/{owner}/{name}/archive/refs/heads/{branch}.zip`,而 URL
    /// 解析会消解点段——branch 写成 `../../../releases/download/v1/evil` 时,落点变成
    /// 该仓库的 **release asset**,即攻击者可上传的任意字节。归档内容一旦可控,
    /// 解压路径校验就成了唯一防线,所以这一层必须堵死。
    pub(crate) fn validate_repo_ref(owner: &str, name: &str, branch: &str) -> Result<()> {
        if !Self::is_valid_github_owner(owner) || !Self::is_valid_github_repo_name(name) {
            return Err(anyhow!(format_skill_error(
                "INVALID_REPO_REF",
                &[("owner", owner), ("name", name)],
                Some("checkRepoUrl"),
            )));
        }
        if !Self::is_valid_git_branch(branch) {
            return Err(anyhow!(format_skill_error(
                "INVALID_REPO_REF",
                &[("owner", owner), ("name", name), ("branch", branch)],
                Some("checkRepoUrl"),
            )));
        }
        Ok(())
    }

    /// 出口断言:URL 拼好后再确认它确实指向预期的 github.com 路径。
    ///
    /// 这是纵深防御——即便上面的字符集校验将来漏了某种变形(百分号编码、新的

View on GitHub (pinned to a2e22f3302)

Solutions

  1. Enter the bare GitHub login (no @, no slashes) and bare repo name
  2. Strip protocol, host, trailing path and .git from a pasted URL before submitting
  3. If the input is from a deeplink, treat this error as an attack attempt and log/reject rather than retry

Example fix

// before
let repo = SkillRepo { owner: url.clone(), name, branch }; // full URL pasted as owner

// after — extract owner/name from a pasted URL first
let trimmed = url.trim_start_matches("https://github.com/").trim_end_matches(".git");
let (owner, name) = trimmed.split_once('/').ok_or("invalid repo url")?;
SkillService::validate_repo_ref(owner, name, branch)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust — validate coordinates before constructing a SkillRepo
fn parse_repo_input(owner: &str, name: &str) -> Result<()> {
    let ok = |s: &str| !s.is_empty() && s.len() <= 100
        && s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-');
    if !ok(owner) || !ok(name) { return Err(anyhow!("invalid owner/name")); }
    Ok(())
}

Type guard

export function isBareGithubSlug(owner: string, name: string): boolean {
  const ok = (s: string) => /^[A-Za-z0-9-]{1,100}$/.test(s);
  return ok(owner) && ok(name);
}

Try / catch

match SkillService::validate_repo_ref(&owner, &name, &branch) {
    Err(e) if e.to_string().contains("INVALID_REPO_REF") => {
        // show 'check the repository URL' hint; do not retry with the same input
    }
    other => other,
}

Prevention

When it happens

Trigger: Adding/discovering a repo with owner "@user", "org/team", "user..name", percent-encoded fragments, unicode, or a name containing '/' — usually a malformed paste ('github.com/user/repo/' with trailing slug), or a crafted deeplink that supplies attacker-chosen coordinates.

Common situations: Users paste full URLs or SSH remotes (git@github.com:user/repo.git) into a field expecting a bare owner/name; deeplinks (deplink.html) carrying hostile repo params; typos.

Related errors


AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16). Data as JSON: /api/errors/31f15991828154cb. Report an issue: GitHub.