Files
dockhand-upstream/src/lib/server/registry-catalog.ts
T

41 lines
2.2 KiB
TypeScript

/**
* Pure catalog-failure classification (#873). No docker/db imports, so it's
* unit-testable without pulling better-sqlite3 into the test process.
*
* GitLab, Harbor, and other registries refuse /v2/_catalog to non-admins by returning
* 401 with `error="insufficient_scope"` in WWW-Authenticate - the token is VALID, the
* operation just isn't permitted. That is NOT an auth failure. A real bad/expired token
* instead returns `error="invalid_token"`. Verified empirically on gitlab.com and a
* self-hosted GitLab (admin gets catalog 200; non-admin and gitlab.com get
* insufficient_scope; a bad token gets invalid_token).
*/
/**
* @param status HTTP status of the catalog response
* @param wwwAuthenticate the response's WWW-Authenticate header (may be null)
* @param authState 'authed' (we obtained and sent a bearer token), 'rejected' (registry
* HAS stored credentials but the token exchange failed - bad/expired creds), or 'anon'
* (no credentials configured)
* @returns 'not_supported' (registry won't list - point user to search) or 'auth_failed'
*/
export function classifyCatalogFailure(
status: number,
wwwAuthenticate: string | null,
authState: 'authed' | 'rejected' | 'anon'
): 'not_supported' | 'auth_failed' {
const err = (wwwAuthenticate || '').match(/error="([^"]+)"/i)?.[1]?.toLowerCase();
// An explicit error code is authoritative regardless of how we authenticated.
if (err === 'insufficient_scope') return 'not_supported';
if (err === 'invalid_token') return 'auth_failed';
// Credentials were configured but the token exchange failed -> real auth problem.
if (authState === 'rejected') return 'auth_failed';
// We authenticated fine but still can't list -> the token lacks catalog permission
// (GitLab/Harbor restrict it to admins). Anonymous 401 is the normal login challenge.
if ((status === 401 || status === 403) && authState === 'authed') return 'not_supported';
return 'auth_failed';
}
/** User-facing message for a registry that won't allow catalog listing (#873). */
export const CATALOG_NOT_SUPPORTED_MSG =
'This registry does not allow listing all images (GitLab and Harbor restrict it to admins). Search for an image by its full path instead.';