@sun-asterisk/sunlint
Version:
☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards
95 lines (87 loc) • 2.8 kB
JavaScript
/**
* ESLint Rule: S050 - Session Token Weak Entropy or Algorithm
* Rule ID: custom/s050
* Description: Ensure session tokens use secure algorithms and have at least 64-bit entropy.
*/
;
const WEAK_HASH_ALGOS = new Set(["md5", "sha1"]);
module.exports = {
meta: {
type: "problem",
docs: {
description:
"Avoid using weak hash algorithms or low entropy sources for session tokens. Use crypto.randomBytes(16), HMAC, or SHA-256.",
recommended: false,
},
messages: {
weakHash:
"Avoid using weak hash algorithm '{{algo}}' for session tokens. Use SHA-256, HMAC, or AES instead.",
lowEntropy:
"Session token should be generated with at least 64-bit entropy. Avoid using '{{func}}'.",
smallRandomBytes:
"Session token generated with crypto.randomBytes({{bytes}}) has insufficient entropy. Use at least 8 bytes (64-bit).",
},
schema: [],
},
create(context) {
return {
CallExpression(node) {
const callee = node.callee;
// Case 1: crypto.createHash("md5") or ("sha1")
if (
callee.type === "MemberExpression" &&
callee.object.name === "crypto" &&
callee.property.name === "createHash"
) {
const [arg] = node.arguments;
if (
arg &&
arg.type === "Literal" &&
typeof arg.value === "string"
) {
const algo = arg.value.toLowerCase();
if (WEAK_HASH_ALGOS.has(algo)) {
context.report({
node: arg,
messageId: "weakHash",
data: { algo },
});
}
}
}
// Case 2: crypto.randomBytes(n), n < 8
if (
callee.type === "MemberExpression" &&
callee.object.name === "crypto" &&
callee.property.name === "randomBytes"
) {
const [arg] = node.arguments;
if (
arg &&
arg.type === "Literal" &&
typeof arg.value === "number" &&
arg.value < 8
) {
context.report({
node: arg,
messageId: "smallRandomBytes",
data: { bytes: arg.value },
});
}
}
// Case 3: Math.random() or Date.now() used
if (
callee.type === "MemberExpression" &&
((callee.object.name === "Math" && callee.property.name === "random") ||
(callee.object.name === "Date" && callee.property.name === "now"))
) {
context.report({
node: callee,
messageId: "lowEntropy",
data: { func: `${callee.object.name}.${callee.property.name}` },
});
}
},
};
},
};