mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 04:12:17 +03:00
fix(oauth): fall back to public Code Suggestions on any GitLab Duo direct_access 403 (#12958) (#13758)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
This commit is contained in:
committed by
GitHub
parent
8f6205e36a
commit
5ff85c6db6
@@ -0,0 +1 @@
|
||||
- **fix(providers):** GitLab Duo Retest and chat requests now fall back to the public Code Suggestions endpoint for ANY `direct_access` 403 (not only the "direct connections are disabled" tenant-config message), and surface the real upstream error body instead of a generic "Access denied" when both endpoints reject the token (#12958) — thanks @Rahulsharma0810
|
||||
@@ -599,12 +599,17 @@ export class GitlabExecutor extends BaseExecutor {
|
||||
};
|
||||
}
|
||||
|
||||
if (response.status === 403 && !isGitLabDirectAccessDisabled(response.status, bodyText)) {
|
||||
return {
|
||||
target: null,
|
||||
credentials,
|
||||
errorResponse: toOpenAIError(403, "GitLab Duo direct access scope is unavailable"),
|
||||
};
|
||||
// #12958: any direct_access 403 (not only GitLab's exact "direct connections
|
||||
// are disabled" tenant-config message) is recoverable via the public
|
||||
// completions fallback — mirrors the 401 branch above and the connection-test
|
||||
// path's shouldFallbackToPublicCodeSuggestions() contract.
|
||||
if (response.status === 403 && input.log) {
|
||||
input.log.warn(
|
||||
"GITLAB-DUO",
|
||||
isGitLabDirectAccessDisabled(response.status, bodyText)
|
||||
? "direct_access exchange rejected (403, direct connections disabled); falling back to public completions endpoint"
|
||||
: `direct_access exchange rejected (403); falling back to public completions endpoint. Body: ${bodyText.slice(0, 500)}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -239,6 +239,16 @@ function isTokenExpired(connection: any) {
|
||||
return expiresAt <= Date.now() + buffer;
|
||||
}
|
||||
|
||||
// #12958: GitLab's own `direct_access` 403 JSON body (e.g. `{"error":"insufficient_scope"}`)
|
||||
// is safe operator-facing diagnostic text — it is not a stack trace and does not echo the
|
||||
// token — but is capped and stripped of control characters defensively before it reaches
|
||||
// the stored/surfaced error message, per docs/security/ERROR_SANITIZATION.md.
|
||||
function sanitizeUpstreamBodyText(bodyText: string): string {
|
||||
const collapsed = bodyText.replace(/[\r\n\t | ||||