/** * Forbids spreading the entire `credentials` object into a cache-key hash input * inside `cache.ts`. * * Bad: * crc32(JSON.stringify({ ...params, ...credentials })) * * Good (smartsheet/google-drive/freshservice pattern): * if (!credentials.unitoCredentialId) { * throw new HttpErrors.UnprocessableEntityError('...'); * } * hashValue({ ...params, userIdentifier: credentials.unitoCredentialId }); * * `unitoCredentialId` is Unito's internal record ID for the credential row — a * non-secret UUID. Hashing it never puts secret material in memory. Do NOT * fall back to `accessToken`. * * Why: * 1. Cache thrash on credential rotation. Access tokens rotate on every OAuth * refresh (and refresh tokens themselves rotate on use for some providers); * spreading the full credential makes EVERY field a cache-key input, so a * single-field rotation invalidates the whole cache. * 2. Credential surface area. The full credential transits memory as a JSON * string before hashing. Any error in this path can surface in stack * traces or memory dumps. * 3. Wrong unit of identity. Cache should be keyed on the stable identifier * of the credential (`unitoCredentialId`), not the credential material. * * Scope: * Only fires in files whose path ends in `/src/cache.ts`. `credentials.ts` * has legitimate `...credentials` spread (enrichment patterns) and is never * affected. */ import type { Rule } from 'eslint'; declare const rule: Rule.RuleModule; export default rule;