{"record":{"id":"e3c6ccfdda809dc5","repo":"jdx/mise","slug":"repository-is-outside-the-approved-relay-scope","errorCode":null,"errorMessage":"repository is outside the approved relay scope","messagePattern":"repository is outside the approved relay scope","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/github_relay.rs","lineNumber":206,"sourceCode":"        {\n            bail!(\"invalid relay path\");\n        }\n    }\n    Ok(())\n}\n\n#[cfg(any(unix, test))]\nfn authorize(scope: &Scope, method: &str, path: &str, query: Option<&str>) -> Result<Target> {\n    validate_path(path)?;\n    let p: Vec<_> = path.split('/').collect();\n    let (owner, repo) = match p.as_slice() {\n        [\"api\", \"repos\", owner, repo, ..] => (*owner, *repo),\n        [\"git\" | \"web\", owner, repo, ..] => (*owner, repo.strip_suffix(\".git\").unwrap_or(repo)),\n        _ => bail!(\"unsupported GitHub operation\"),\n    };\n    let name = repository(&format!(\"{owner}/{repo}\"))?;\n    if !scope.permits(&name) {\n        bail!(\"repository is outside the approved relay scope\");\n    }\n    let git = p[0] == \"git\";\n    let allowed = match p.as_slice() {\n        [\"git\", _, _, \"info\", \"refs\"] => {\n            method == \"GET\" && query == Some(\"service=git-upload-pack\")\n        }\n        [\"git\", _, _, \"git-upload-pack\"] => method == \"POST\" && query.is_none(),\n        [\"api\", \"repos\", _, _] => method == \"GET\" || method == \"HEAD\",\n        [\"api\", \"repos\", _, _, \"git\", kind, ..] => {\n            matches!(*kind, \"refs\" | \"matching-refs\") && matches!(method, \"GET\" | \"HEAD\")\n        }\n        [\"api\", \"repos\", _, _, kind, ..] => {\n            matches!(\n                *kind,\n                \"contents\" | \"releases\" | \"tags\" | \"branches\" | \"tarball\" | \"zipball\"\n            ) && matches!(method, \"GET\" | \"HEAD\")\n        }\n        [\"web\", _, _, \"releases\", \"download\", _, ..] => matches!(method, \"GET\" | \"HEAD\"),","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/github_relay.rs#L188-L224","documentation":"After extracting OWNER/REPO from the relayed path, `authorize` checks the parsed repository name against the relay `Scope`'s approved repository list (`scope.permits`). Requesting a repository that was not approved when the relay scope was created fails with this error, enforcing that the relay only ever talks to allow-listed repos.","triggerScenarios":"Forwarding a request whose owner/repo differs from the scope — e.g. scope created for `octocat/hello-world` but the URL points at `octocat/other-repo`, or a dependency that redirects/fetches from a different org/repo; note names are lowercased before comparison.","commonSituations":"Tools following redirects or release asset links to other repos; a lockfile pinning a fork or renamed repository; passing `owner/repo` with different casing is fine (lowercased), but a different repo name or a typo is not.","solutions":["Add the repository to the approved scope when creating the relay (e.g. include it in `from_flags`).","Correct the URL/owner/repo typo so it matches an already-approved repository.","Update the dependency or lockfile reference that points at the unapproved repo."],"exampleFix":"// before\nlet scope = relay::from_flags(\"octocat/hello-world\")?;\nrelay::forward(&scope, \"GET\", \"/api/repos/octocat/other-repo\", None)?;\n// after\nlet scope = relay::from_flags(\"octocat/hello-world,octocat/other-repo\")?;\nrelay::forward(&scope, \"GET\", \"/api/repos/octocat/other-repo\", None)?;","handlingStrategy":"validation","validationCode":"fn repo_in_scope(scope_repo: &str, requested: &str) -> bool {\n    requested.to_ascii_lowercase() == scope_repo.to_ascii_lowercase()\n}\n// or: keep the scope's repo list and check membership before forwarding\nassert!(repo_in_scope(\"octocat/hello-world\", \"OctoCat/Hello-World\"));","typeGuard":null,"tryCatchPattern":"match relay::forward(&scope, \"GET\", path, None) {\n    Err(e) if e.to_string().contains(\"approved relay scope\") => eprintln!(\"repo not in scope: {path}\"),\n    Err(e) => return Err(e),\n    Ok(resp) => resp,\n}","preventionTips":["Create the relay scope with every repo your toolchain will fetch (including forks).","Extract owner/repo from URLs before forwarding and check them against the scope list.","Watch for redirects or lockfiles silently pointing at a different org/repo."],"tags":["github","relay","authorization","scope"],"backgroundTag":"insufficient-permissions","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}