Copied description from karlhorky/eslint-tricks#2
Examples of incorrect code for this rule:
// ❌ No sessionToken in first parameter
export const selectUserById = cache(async (id: User['id']) => {
const [user] = await sql<User[]>`
SELECT
id,
name
FROM
users
`;
return user;
});
// ❌ No Unauthenticated suffix
export const selectAnimals = cache(async () => {
const animals = await sql<Animal[]>`
SELECT
*
FROM
animals
ORDER BY
id
`;
return animals;
});
Examples of correct code for this rule:
// ✅ sessionToken in first parameter and used in query
export const selectUserById = cache(async (sessionToken: Session['token'], id: User['id']) => {
const [user] = await sql<User[]>`
SELECT
id,
name
FROM
users
INNER JOIN sessions ON (
sessions.token = ${sessionToken}
AND sessions.expiry_timestamp > now()
)
`;
return user;
});
// ✅ Unauthenticated suffix
export const selectAnimalsUnauthenticated = cache(async () => {
const animals = await sql<Animal[]>`
SELECT
*
FROM
animals
ORDER BY
id
`;
return animals;
});
ESLint-level validation to make sure that exported database query functions either use sessionToken or are marked ...Unauthenticated (also supports wrapping in React cache())
Version with no-restricted-syntax:
overrides: [
{
files: ['packages/database/queries/*.ts'],
rules: {
'no-restricted-syntax': [
...noRestrictedSyntaxOptions,
// Enforce unambiguous exported database function patterns
// (require either accepting a session token ("sessionToken")
// as the first parameter or having a name ending with
// "Unauthenticated")
{
selector:
"ExportNamedDeclaration > FunctionDeclaration[id.name!=/Unauthenticated$/][params.0.name!='sessionToken'], ExportNamedDeclaration > VariableDeclaration[declarations.0.init.callee.name='cache'][declarations.0.id.name!=/Unauthenticated$/][declarations.0.init.arguments.0.params.0.name!='sessionToken']",
message: `Ambiguous authentication of exported database query function - either pass \`sessionToken\` as the first parameter or name the function ending with \`Unauthenticated\`:
function getUser(sessionToken: string, userId: number)
const getUser = cache(async (sessionToken: string, userId: number) =>
function getArticleCategoriesUnauthenticated()
const getArticleCategoriesUnauthenticated = cache(async () =>
`,
},
// Enforce usage of session token ("sessionToken"
// first parameter) within database functions
{
selector:
"ExportNamedDeclaration > FunctionDeclaration[params.0.name='sessionToken'] > BlockStatement:not(:has([type='Identifier'][name='sessionToken'])), ExportNamedDeclaration > VariableDeclaration[declarations.0.init.callee.name='cache'][declarations.0.init.arguments.0.params.0.name='sessionToken'] > BlockStatement:not(:has([type='Identifier'][name='sessionToken']))",
message:
'Unused `sessionToken` parameter in database query function - use `sessionToken` in database queries to implement authentication and authorization',
},
],
},
},
],
Examples in VS Code:
More examples:
MVP
First version done in:
Copied description from karlhorky/eslint-tricks#2
Examples of incorrect code for this rule:
Examples of correct code for this rule:
ESLint-level validation to make sure that exported database query functions either use
sessionTokenor are marked...Unauthenticated(also supports wrapping in Reactcache())Version with
no-restricted-syntax:Examples in VS Code:
More examples:
MVP
First version done in: