Why AI tools produce this: AI coding tools generate this anti-pattern by default, it appears in a large share of AI-written auth code.
Why this matters
URLs leak into server access logs, reverse-proxy logs, browser history, and the Referer header (sent to every third-party CDN, analytics, and ad script on the destination page), so any of password, client_secret, api_key, apikey, or secret in the query is a leaked credential.
Send credentials in the POST request body or in the Authorization header. Never in the URL.
// ruleid: auth.flow.credentials-in-urlexport function login(pw: string) { return fetch('https://api.example.com/login?password=' + pw);}// ruleid: auth.flow.credentials-in-urlexport function callApi(secret: string) { return fetch(`https://api.example.com/data?secret=${secret}`);}// ruleid: auth.flow.credentials-in-urlexport function buildAuthUrl(clientSecret: string) { const params = new URLSearchParams(); params.append('client_secret', clientSecret); return `https://auth.example.com/token?${params.toString()}`;}// ruleid: auth.flow.credentials-in-urlexport const apiKeyUrl = 'https://api.example.com/search?q=cats&api_key=ABC123';
SAFE
safe.ts
// ok: auth.flow.credentials-in-url -- OAuth authorization code in callback URL is normalexport function handleCallback(authCode: string) { return `https://app.example.com/callback?code=${authCode}`;}// ok: auth.flow.credentials-in-url -- CSRF state parameter is not a secret credentialexport function buildState(state: string) { return `https://auth.example.com/authorize?response_type=code&state=${state}`;}// ok: auth.flow.credentials-in-url -- password sent in POST body, not the URLexport function login(password: string) { return fetch('https://api.example.com/login', { method: 'POST', body: JSON.stringify({ password }), });}// ok: auth.flow.credentials-in-url -- credential in Authorization header, not URLexport function callApi(accessToken: string) { return fetch('https://api.example.com/data', { headers: { Authorization: `Bearer ${accessToken}` }, });}// ok: auth.flow.credentials-in-url -- email reset link uses a non-secret reset_token nameexport const resetLink = 'https://app.example.com/reset?reset_token=xyz';// ok: auth.flow.credentials-in-url -- passing access_token as a query param to a// provider's userinfo/API endpoint is a legitimate (sometimes mandated)// server-side OAuth pattern, so it is deliberately not flagged.export function fetchUserInfo(url: URL, accessToken: string) { url.searchParams.set('access_token', accessToken); return fetch(url);}
Suppressing this rule
If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.