{"record":{"id":"8ca7c72f53894ceb","repo":"jdx/mise","slug":"unsupported-query-parameter","errorCode":null,"errorMessage":"unsupported query parameter","messagePattern":"unsupported query parameter","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/github_relay.rs","lineNumber":234,"sourceCode":"            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\"),\n        [\"web\", _, _, \"archive\", _, ..] => matches!(method, \"GET\" | \"HEAD\"),\n        _ => false,\n    };\n    if !allowed {\n        bail!(\"GitHub relay permits read-only repository operations only\");\n    }\n    if !git && let Some(query) = query {\n        for (key, _) in url::form_urlencoded::parse(query.as_bytes()) {\n            if !matches!(key.as_ref(), \"ref\" | \"page\" | \"per_page\") {\n                bail!(\"unsupported query parameter\");\n            }\n        }\n    }\n    let host = if p[0] == \"api\" {\n        \"api.github.com\"\n    } else {\n        \"github.com\"\n    };\n    let suffix = path.split_once('/').expect(\"validated path\").1;\n    let mut url = format!(\"https://{host}/{suffix}\");\n    let archive_repo = match p.as_slice() {\n        [\"api\", \"repos\", _, _, \"tarball\" | \"zipball\", ..] => Some(name.clone()),\n        [\"web\", _, _, \"archive\", rest @ ..] => {\n            let reference = rest.join(\"/\");\n            let (kind, reference) = if let Some(reference) = reference.strip_suffix(\".tar.gz\") {\n                (\"tarball\", reference)\n            } else if let Some(reference) = reference.strip_suffix(\".zip\") {\n                (\"zipball\", reference)","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/github_relay.rs#L216-L252","documentation":"For non-git relayed requests, `authorize` parses the query string and permits only the parameters `ref`, `page`, and `per_page`. Any other query key (sort, client_id, token, callback, etc.) is rejected with this error, keeping the forwarded URL deterministic and preventing credential leakage or cache-busting tricks.","triggerScenarios":"Calling `forward`/`operation` on an api/web path with a query like `?sort=stars`, `?access_token=...`, `?client_id=`, `?archive_format=zipball`, or an empty-but-present unknown key; git paths are exempt.","commonSituations":"Clients appending OAuth or analytics params automatically; pagination helpers adding `order`/`sort`; copying an API URL from browser devtools that includes extra params; adding `archive_format` to a tarball URL.","solutions":["Strip all query parameters except `ref`, `page`, and `per_page` before forwarding.","Move needed values into the path (e.g. put the ref in the tarball path) instead of the query string.","Authenticate via the relay's own credential handling — never pass `access_token` as a query parameter."],"exampleFix":"// before\nrelay::forward(&scope, \"GET\", \"/api/repos/o/r/commits\", Some(\"ref=main&sort=dated\"))?;\n// after\nrelay::forward(&scope, \"GET\", \"/api/repos/o/r/commits\", Some(\"ref=main\"))?;","handlingStrategy":"validation","validationCode":"fn relay_safe_query(q: Option<&str>) -> bool {\n    q.map(|q| url::form_urlencoded::parse(q.as_bytes())\n        .all(|(k, _)| matches!(k.as_ref(), \"ref\" | \"page\" | \"per_page\")))\n        .unwrap_or(true)\n}","typeGuard":null,"tryCatchPattern":"match relay::forward(&scope, \"GET\", path, Some(query)) {\n    Err(e) if e.to_string().contains(\"query parameter\") => eprintln!(\"strip unsupported query params: {query}\"),\n    Err(e) => return Err(e),\n    Ok(resp) => resp,\n}","preventionTips":["Build query strings explicitly instead of forwarding whole URLs captured elsewhere.","Disable client features that auto-append params (OAuth, analytics, sort/order).","Pass auth via the relay, never via `access_token` query parameters."],"tags":["github","relay","query","validation"],"backgroundTag":"invalid-query-parameter","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"}