-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSEC-001-cryptographic-security.rules.ts
More file actions
86 lines (78 loc) · 2.88 KB
/
SEC-001-cryptographic-security.rules.ts
File metadata and controls
86 lines (78 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/// <reference path="../rules.d.ts" />
// Files excluded from security checks (SPARQL 1.1 spec compliance, graph positioning)
const EXCLUDED_PATTERNS = [
"sparql/filters/BuiltInFunctions.ts", // SPARQL RAND(), MD5(), SHA1() — W3C spec-mandated
"sparql/filters/functions/", // Decomposed SPARQL function modules — same W3C spec compliance
"memory/CompactGraphStore.ts", // Random initial positions for graph node layout
];
function isExcluded(filePath: string): boolean {
return EXCLUDED_PATTERNS.some((pattern) => filePath.includes(pattern));
}
export default {
rules: {
"no-math-random": {
description:
"Math.random() is not cryptographically secure — use crypto.randomUUID() or crypto.randomBytes()",
severity: "error",
async check(ctx) {
const hits = await ctx.grepFiles(
/Math\.random\s*\(/,
"packages/*/src/**/*.ts",
);
for (const hit of hits) {
if (isExcluded(hit.file)) continue;
// Allow clearly documented fallbacks (check surrounding 5 lines for suppression comments)
const content = await ctx.readFile(hit.file);
const lines = content.split("\n");
const contextStart = Math.max(0, hit.line - 5);
const contextEnd = Math.min(lines.length, hit.line);
const context = lines.slice(contextStart, contextEnd).join("\n");
if (
context.includes("Last resort fallback") ||
context.includes("not cryptographically secure") ||
context.includes("// nosec") ||
context.includes("SECURITY CONTEXT")
) {
continue;
}
ctx.report.violation({
message:
"Math.random() is not cryptographically secure for ID/token generation",
file: hit.file,
line: hit.line,
fix: "Use crypto.randomUUID() for IDs or crypto.randomBytes() for tokens",
});
}
},
},
"no-weak-hash": {
description:
"MD5 and SHA1 are cryptographically broken — use SHA-256 or SHA-512",
severity: "error",
async check(ctx) {
const patterns = [
{
pattern: /createHash\s*\(\s*['"]md5['"]\s*\)/,
algo: "MD5",
},
{
pattern: /createHash\s*\(\s*['"]sha1['"]\s*\)/,
algo: "SHA1",
},
];
for (const { pattern, algo } of patterns) {
const hits = await ctx.grepFiles(pattern, "packages/*/src/**/*.ts");
for (const hit of hits) {
if (isExcluded(hit.file)) continue;
ctx.report.violation({
message: `${algo} is cryptographically broken`,
file: hit.file,
line: hit.line,
fix: "Use createHash('sha256') or createHash('sha512') instead",
});
}
}
},
},
},
} satisfies RuleSet;