CVE-2026-70474
Summary
Three OAuth2 credential endpoints look up credentials by id alone with no workspaceId filter. Two of these endpoints (callback, refresh) are whitelisted from all authentication. This allows:
1. Cross-workspace credential access — Any authenticated user can initiate OAuth2 flows against credentials belonging to other workspaces.
2. Unauthenticated token injection — An unauthenticated attacker can forge OAuth2 callbacks to overwrite tokens in any credential.
3. Unauthenticated token refresh — An unauthenticated attacker can refresh tokens for any credential.
Root Cause
Vulnerable code: no workspace scoping
All three OAuth2 handlers query the Credential table by id only:
packages/server/src/routes/oauth2/index.ts:80-82 (authorize)
const credential = await credentialRepository.findOneBy({
id: credentialId
// Missing: workspaceId filter
})
packages/server/src/routes/oauth2/index.ts:183-185 (callback)
const credential = await credentialRepository.findOneBy({
id: state as string
// Missing: workspaceId filter
})
packages/server/src/routes/oauth2/index.ts:314-316 (refresh)
const credential = await credentialRepository.findOneBy({
id: credentialId
// Missing: workspaceId filter
})
Correct pattern (same codebase)
The standard credential service correctly enforces workspace isolation:
packages/server/src/services/credentials/index.ts:130-132
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
id: credentialId,
workspaceId: workspaceId // <-- Workspace scoping present
})
Authentication bypass via whitelist
packages/server/src/utils/constants.ts:40-41
export const WHITELIST_URLS = [
// ...
'/api/v1/oauth2-credential/callback', // line 40
'/api/v1/oauth2-credential/refresh', // line 41
// ...
]
packages/server/src/index.ts:223-225 — prefix-matched whitelist skips all auth:
const isWhitelisted = whitelistURLs.some((url) => req.path.startsWith(url))
if (isWhitelisted) {
next() // No JWT verification, no API key check
}
⚡ Watch CVE-2026-70474
Get an email if CVE-2026-70474 is added to CISA KEV, gains public exploit code, or a new advisory cites it — max one per day, one-click unsubscribe.
Advisory coverage (2)
External references
Embed the live status
— this badge updates automatically when the KEV or exploit status changes. How to embed it →
[](https://www.csirts.com/cve/CVE-2026-70474)