@rigour-labs/core 2.19.2 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/gates/ast-handlers/typescript.js +12 -6
- package/dist/gates/base.d.ts +1 -1
- package/dist/gates/base.js +3 -1
- package/dist/gates/content.js +9 -12
- package/dist/gates/security-patterns.js +1 -1
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.js +2 -0
- package/package.json +1 -1
- package/src/gates/ast-handlers/typescript.ts +12 -6
- package/src/gates/base.ts +3 -1
- package/src/gates/content.ts +17 -18
- package/src/gates/security-patterns.ts +4 -2
- package/src/types/index.ts +2 -0
|
@@ -57,9 +57,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
57
57
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
58
58
|
addFailure({
|
|
59
59
|
id: 'STALENESS_NO_VAR',
|
|
60
|
-
title: `Stale 'var' keyword
|
|
60
|
+
title: `Stale 'var' keyword`,
|
|
61
61
|
details: `Use 'const' or 'let' instead of 'var' in ${relativePath}:${line}`,
|
|
62
62
|
files: [relativePath],
|
|
63
|
+
line,
|
|
63
64
|
hint: `Replace 'var' with 'const' (preferred) or 'let' for modern JavaScript.`
|
|
64
65
|
});
|
|
65
66
|
}
|
|
@@ -70,9 +71,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
70
71
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
71
72
|
addFailure({
|
|
72
73
|
id: 'STALENESS_NO_COMMONJS',
|
|
73
|
-
title: `CommonJS require()
|
|
74
|
+
title: `CommonJS require()`,
|
|
74
75
|
details: `Use ES6 'import' instead of 'require()' in ${relativePath}:${line}`,
|
|
75
76
|
files: [relativePath],
|
|
77
|
+
line,
|
|
76
78
|
hint: `Replace require('module') with import module from 'module'.`
|
|
77
79
|
});
|
|
78
80
|
}
|
|
@@ -85,9 +87,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
85
87
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
86
88
|
addFailure({
|
|
87
89
|
id: 'STALENESS_NO_ARGUMENTS',
|
|
88
|
-
title: `Legacy 'arguments' object
|
|
90
|
+
title: `Legacy 'arguments' object`,
|
|
89
91
|
details: `Use rest parameters (...args) instead of 'arguments' in ${relativePath}:${line}`,
|
|
90
92
|
files: [relativePath],
|
|
93
|
+
line,
|
|
91
94
|
hint: `Replace 'arguments' with rest parameters: function(...args) { }`
|
|
92
95
|
});
|
|
93
96
|
}
|
|
@@ -98,9 +101,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
98
101
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
99
102
|
addFailure({
|
|
100
103
|
id: 'SECURITY_PROTOTYPE_POLLUTION',
|
|
101
|
-
title: `Direct __proto__ access
|
|
104
|
+
title: `Direct __proto__ access`,
|
|
102
105
|
details: `Prototype pollution vulnerability in ${relativePath}:${line}`,
|
|
103
106
|
files: [relativePath],
|
|
107
|
+
line,
|
|
104
108
|
hint: `Use Object.getPrototypeOf() or Object.setPrototypeOf() instead of __proto__.`
|
|
105
109
|
});
|
|
106
110
|
}
|
|
@@ -111,9 +115,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
111
115
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
112
116
|
addFailure({
|
|
113
117
|
id: 'SECURITY_PROTOTYPE_POLLUTION',
|
|
114
|
-
title: `Unsafe bracket notation access to '${accessKey}'
|
|
118
|
+
title: `Unsafe bracket notation access to '${accessKey}'`,
|
|
115
119
|
details: `Potential prototype pollution via bracket notation in ${relativePath}:${line}`,
|
|
116
120
|
files: [relativePath],
|
|
121
|
+
line,
|
|
117
122
|
hint: `Block access to '${accessKey}' property when handling user input. Use allowlist for object keys.`
|
|
118
123
|
});
|
|
119
124
|
}
|
|
@@ -130,9 +135,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
130
135
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
131
136
|
addFailure({
|
|
132
137
|
id: 'SECURITY_PROTOTYPE_POLLUTION_MERGE',
|
|
133
|
-
title: `Object.assign() merge pattern
|
|
138
|
+
title: `Object.assign() merge pattern`,
|
|
134
139
|
details: `Object.assign({}, ...) can propagate prototype pollution in ${relativePath}:${line}`,
|
|
135
140
|
files: [relativePath],
|
|
141
|
+
line,
|
|
136
142
|
hint: `Validate and sanitize source objects before merging. Block __proto__ and constructor keys.`
|
|
137
143
|
});
|
|
138
144
|
}
|
package/dist/gates/base.d.ts
CHANGED
|
@@ -11,5 +11,5 @@ export declare abstract class Gate {
|
|
|
11
11
|
readonly title: string;
|
|
12
12
|
constructor(id: string, title: string);
|
|
13
13
|
abstract run(context: GateContext): Promise<Failure[]>;
|
|
14
|
-
protected createFailure(details: string, files?: string[], hint?: string, title?: string): Failure;
|
|
14
|
+
protected createFailure(details: string, files?: string[], hint?: string, title?: string, line?: number, endLine?: number): Failure;
|
|
15
15
|
}
|
package/dist/gates/base.js
CHANGED
|
@@ -5,12 +5,14 @@ export class Gate {
|
|
|
5
5
|
this.id = id;
|
|
6
6
|
this.title = title;
|
|
7
7
|
}
|
|
8
|
-
createFailure(details, files, hint, title) {
|
|
8
|
+
createFailure(details, files, hint, title, line, endLine) {
|
|
9
9
|
return {
|
|
10
10
|
id: this.id,
|
|
11
11
|
title: title || this.title,
|
|
12
12
|
details,
|
|
13
13
|
files,
|
|
14
|
+
line,
|
|
15
|
+
endLine,
|
|
14
16
|
hint,
|
|
15
17
|
};
|
|
16
18
|
}
|
package/dist/gates/content.js
CHANGED
|
@@ -20,20 +20,17 @@ export class ContentGate extends Gate {
|
|
|
20
20
|
patterns: context.patterns
|
|
21
21
|
});
|
|
22
22
|
const contents = await FileScanner.readFiles(context.cwd, files);
|
|
23
|
-
const
|
|
23
|
+
const failures = [];
|
|
24
24
|
for (const [file, content] of contents) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
const lines = content.split('\n');
|
|
26
|
+
lines.forEach((line, index) => {
|
|
27
|
+
for (const pattern of patterns) {
|
|
28
|
+
if (pattern.test(line)) {
|
|
29
|
+
failures.push(this.createFailure(`Forbidden placeholder '${pattern.source}' found`, [file], 'Remove forbidden comments. address the root cause or create a tracked issue.', undefined, index + 1, index + 1));
|
|
30
|
+
}
|
|
29
31
|
}
|
|
30
|
-
}
|
|
32
|
+
});
|
|
31
33
|
}
|
|
32
|
-
|
|
33
|
-
return [
|
|
34
|
-
this.createFailure('Forbidden placeholders found in the following files:', violations, 'Remove all TODO and FIXME comments. Use the "Done is Done" mentality—address the root cause or create a tracked issue.'),
|
|
35
|
-
];
|
|
36
|
-
}
|
|
37
|
-
return [];
|
|
34
|
+
return failures;
|
|
38
35
|
}
|
|
39
36
|
}
|
|
@@ -186,7 +186,7 @@ export class SecurityPatternsGate extends Gate {
|
|
|
186
186
|
const blockThreshold = this.severityOrder[this.config.block_on_severity ?? 'high'];
|
|
187
187
|
for (const vuln of filteredVulns) {
|
|
188
188
|
if (this.severityOrder[vuln.severity] <= blockThreshold) {
|
|
189
|
-
failures.push(this.createFailure(`[${vuln.cwe}] ${vuln.description}
|
|
189
|
+
failures.push(this.createFailure(`[${vuln.cwe}] ${vuln.description}`, [vuln.file], `Found: "${vuln.match.slice(0, 60)}..." - Use parameterized queries/sanitization.`, `Security: ${vuln.type.replace('_', ' ').toUpperCase()}`, vuln.line, vuln.line));
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
if (filteredVulns.length > 0 && failures.length === 0) {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1080,18 +1080,24 @@ export declare const FailureSchema: z.ZodObject<{
|
|
|
1080
1080
|
title: z.ZodString;
|
|
1081
1081
|
details: z.ZodString;
|
|
1082
1082
|
files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1083
|
+
line: z.ZodOptional<z.ZodNumber>;
|
|
1084
|
+
endLine: z.ZodOptional<z.ZodNumber>;
|
|
1083
1085
|
hint: z.ZodOptional<z.ZodString>;
|
|
1084
1086
|
}, "strip", z.ZodTypeAny, {
|
|
1085
1087
|
id: string;
|
|
1086
1088
|
title: string;
|
|
1087
1089
|
details: string;
|
|
1088
1090
|
files?: string[] | undefined;
|
|
1091
|
+
line?: number | undefined;
|
|
1092
|
+
endLine?: number | undefined;
|
|
1089
1093
|
hint?: string | undefined;
|
|
1090
1094
|
}, {
|
|
1091
1095
|
id: string;
|
|
1092
1096
|
title: string;
|
|
1093
1097
|
details: string;
|
|
1094
1098
|
files?: string[] | undefined;
|
|
1099
|
+
line?: number | undefined;
|
|
1100
|
+
endLine?: number | undefined;
|
|
1095
1101
|
hint?: string | undefined;
|
|
1096
1102
|
}>;
|
|
1097
1103
|
export type Failure = z.infer<typeof FailureSchema>;
|
|
@@ -1103,18 +1109,24 @@ export declare const ReportSchema: z.ZodObject<{
|
|
|
1103
1109
|
title: z.ZodString;
|
|
1104
1110
|
details: z.ZodString;
|
|
1105
1111
|
files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1112
|
+
line: z.ZodOptional<z.ZodNumber>;
|
|
1113
|
+
endLine: z.ZodOptional<z.ZodNumber>;
|
|
1106
1114
|
hint: z.ZodOptional<z.ZodString>;
|
|
1107
1115
|
}, "strip", z.ZodTypeAny, {
|
|
1108
1116
|
id: string;
|
|
1109
1117
|
title: string;
|
|
1110
1118
|
details: string;
|
|
1111
1119
|
files?: string[] | undefined;
|
|
1120
|
+
line?: number | undefined;
|
|
1121
|
+
endLine?: number | undefined;
|
|
1112
1122
|
hint?: string | undefined;
|
|
1113
1123
|
}, {
|
|
1114
1124
|
id: string;
|
|
1115
1125
|
title: string;
|
|
1116
1126
|
details: string;
|
|
1117
1127
|
files?: string[] | undefined;
|
|
1128
|
+
line?: number | undefined;
|
|
1129
|
+
endLine?: number | undefined;
|
|
1118
1130
|
hint?: string | undefined;
|
|
1119
1131
|
}>, "many">;
|
|
1120
1132
|
stats: z.ZodObject<{
|
|
@@ -1139,6 +1151,8 @@ export declare const ReportSchema: z.ZodObject<{
|
|
|
1139
1151
|
title: string;
|
|
1140
1152
|
details: string;
|
|
1141
1153
|
files?: string[] | undefined;
|
|
1154
|
+
line?: number | undefined;
|
|
1155
|
+
endLine?: number | undefined;
|
|
1142
1156
|
hint?: string | undefined;
|
|
1143
1157
|
}[];
|
|
1144
1158
|
}, {
|
|
@@ -1153,6 +1167,8 @@ export declare const ReportSchema: z.ZodObject<{
|
|
|
1153
1167
|
title: string;
|
|
1154
1168
|
details: string;
|
|
1155
1169
|
files?: string[] | undefined;
|
|
1170
|
+
line?: number | undefined;
|
|
1171
|
+
endLine?: number | undefined;
|
|
1156
1172
|
hint?: string | undefined;
|
|
1157
1173
|
}[];
|
|
1158
1174
|
}>;
|
package/dist/types/index.js
CHANGED
|
@@ -127,6 +127,8 @@ export const FailureSchema = z.object({
|
|
|
127
127
|
title: z.string(),
|
|
128
128
|
details: z.string(),
|
|
129
129
|
files: z.array(z.string()).optional(),
|
|
130
|
+
line: z.number().optional(),
|
|
131
|
+
endLine: z.number().optional(),
|
|
130
132
|
hint: z.string().optional(),
|
|
131
133
|
});
|
|
132
134
|
export const ReportSchema = z.object({
|
package/package.json
CHANGED
|
@@ -65,9 +65,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
65
65
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
66
66
|
addFailure({
|
|
67
67
|
id: 'STALENESS_NO_VAR',
|
|
68
|
-
title: `Stale 'var' keyword
|
|
68
|
+
title: `Stale 'var' keyword`,
|
|
69
69
|
details: `Use 'const' or 'let' instead of 'var' in ${relativePath}:${line}`,
|
|
70
70
|
files: [relativePath],
|
|
71
|
+
line,
|
|
71
72
|
hint: `Replace 'var' with 'const' (preferred) or 'let' for modern JavaScript.`
|
|
72
73
|
});
|
|
73
74
|
}
|
|
@@ -79,9 +80,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
79
80
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
80
81
|
addFailure({
|
|
81
82
|
id: 'STALENESS_NO_COMMONJS',
|
|
82
|
-
title: `CommonJS require()
|
|
83
|
+
title: `CommonJS require()`,
|
|
83
84
|
details: `Use ES6 'import' instead of 'require()' in ${relativePath}:${line}`,
|
|
84
85
|
files: [relativePath],
|
|
86
|
+
line,
|
|
85
87
|
hint: `Replace require('module') with import module from 'module'.`
|
|
86
88
|
});
|
|
87
89
|
}
|
|
@@ -95,9 +97,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
95
97
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
96
98
|
addFailure({
|
|
97
99
|
id: 'STALENESS_NO_ARGUMENTS',
|
|
98
|
-
title: `Legacy 'arguments' object
|
|
100
|
+
title: `Legacy 'arguments' object`,
|
|
99
101
|
details: `Use rest parameters (...args) instead of 'arguments' in ${relativePath}:${line}`,
|
|
100
102
|
files: [relativePath],
|
|
103
|
+
line,
|
|
101
104
|
hint: `Replace 'arguments' with rest parameters: function(...args) { }`
|
|
102
105
|
});
|
|
103
106
|
}
|
|
@@ -110,9 +113,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
110
113
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
111
114
|
addFailure({
|
|
112
115
|
id: 'SECURITY_PROTOTYPE_POLLUTION',
|
|
113
|
-
title: `Direct __proto__ access
|
|
116
|
+
title: `Direct __proto__ access`,
|
|
114
117
|
details: `Prototype pollution vulnerability in ${relativePath}:${line}`,
|
|
115
118
|
files: [relativePath],
|
|
119
|
+
line,
|
|
116
120
|
hint: `Use Object.getPrototypeOf() or Object.setPrototypeOf() instead of __proto__.`
|
|
117
121
|
});
|
|
118
122
|
}
|
|
@@ -124,9 +128,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
124
128
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
125
129
|
addFailure({
|
|
126
130
|
id: 'SECURITY_PROTOTYPE_POLLUTION',
|
|
127
|
-
title: `Unsafe bracket notation access to '${accessKey}'
|
|
131
|
+
title: `Unsafe bracket notation access to '${accessKey}'`,
|
|
128
132
|
details: `Potential prototype pollution via bracket notation in ${relativePath}:${line}`,
|
|
129
133
|
files: [relativePath],
|
|
134
|
+
line,
|
|
130
135
|
hint: `Block access to '${accessKey}' property when handling user input. Use allowlist for object keys.`
|
|
131
136
|
});
|
|
132
137
|
}
|
|
@@ -144,9 +149,10 @@ export class TypeScriptHandler extends ASTHandler {
|
|
|
144
149
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
145
150
|
addFailure({
|
|
146
151
|
id: 'SECURITY_PROTOTYPE_POLLUTION_MERGE',
|
|
147
|
-
title: `Object.assign() merge pattern
|
|
152
|
+
title: `Object.assign() merge pattern`,
|
|
148
153
|
details: `Object.assign({}, ...) can propagate prototype pollution in ${relativePath}:${line}`,
|
|
149
154
|
files: [relativePath],
|
|
155
|
+
line,
|
|
150
156
|
hint: `Validate and sanitize source objects before merging. Block __proto__ and constructor keys.`
|
|
151
157
|
});
|
|
152
158
|
}
|
package/src/gates/base.ts
CHANGED
|
@@ -13,12 +13,14 @@ export abstract class Gate {
|
|
|
13
13
|
|
|
14
14
|
abstract run(context: GateContext): Promise<Failure[]>;
|
|
15
15
|
|
|
16
|
-
protected createFailure(details: string, files?: string[], hint?: string, title?: string): Failure {
|
|
16
|
+
protected createFailure(details: string, files?: string[], hint?: string, title?: string, line?: number, endLine?: number): Failure {
|
|
17
17
|
return {
|
|
18
18
|
id: this.id,
|
|
19
19
|
title: title || this.title,
|
|
20
20
|
details,
|
|
21
21
|
files,
|
|
22
|
+
line,
|
|
23
|
+
endLine,
|
|
22
24
|
hint,
|
|
23
25
|
};
|
|
24
26
|
}
|
package/src/gates/content.ts
CHANGED
|
@@ -13,7 +13,7 @@ export class ContentGate extends Gate {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
async run(context: GateContext): Promise<Failure[]> {
|
|
16
|
-
const patterns = [];
|
|
16
|
+
const patterns: RegExp[] = [];
|
|
17
17
|
if (this.config.forbidTodos) patterns.push(/TODO/i);
|
|
18
18
|
if (this.config.forbidFixme) patterns.push(/FIXME/i);
|
|
19
19
|
|
|
@@ -26,26 +26,25 @@ export class ContentGate extends Gate {
|
|
|
26
26
|
});
|
|
27
27
|
const contents = await FileScanner.readFiles(context.cwd, files);
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
const failures: Failure[] = [];
|
|
30
30
|
for (const [file, content] of contents) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
const lines = content.split('\n');
|
|
32
|
+
lines.forEach((line, index) => {
|
|
33
|
+
for (const pattern of patterns) {
|
|
34
|
+
if (pattern.test(line)) {
|
|
35
|
+
failures.push(this.createFailure(
|
|
36
|
+
`Forbidden placeholder '${pattern.source}' found`,
|
|
37
|
+
[file],
|
|
38
|
+
'Remove forbidden comments. address the root cause or create a tracked issue.',
|
|
39
|
+
undefined,
|
|
40
|
+
index + 1,
|
|
41
|
+
index + 1
|
|
42
|
+
));
|
|
43
|
+
}
|
|
35
44
|
}
|
|
36
|
-
}
|
|
45
|
+
});
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
return [
|
|
41
|
-
this.createFailure(
|
|
42
|
-
'Forbidden placeholders found in the following files:',
|
|
43
|
-
violations,
|
|
44
|
-
'Remove all TODO and FIXME comments. Use the "Done is Done" mentality—address the root cause or create a tracked issue.'
|
|
45
|
-
),
|
|
46
|
-
];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return [];
|
|
48
|
+
return failures;
|
|
50
49
|
}
|
|
51
50
|
}
|
|
@@ -231,10 +231,12 @@ export class SecurityPatternsGate extends Gate {
|
|
|
231
231
|
for (const vuln of filteredVulns) {
|
|
232
232
|
if (this.severityOrder[vuln.severity] <= blockThreshold) {
|
|
233
233
|
failures.push(this.createFailure(
|
|
234
|
-
`[${vuln.cwe}] ${vuln.description}
|
|
234
|
+
`[${vuln.cwe}] ${vuln.description}`,
|
|
235
235
|
[vuln.file],
|
|
236
236
|
`Found: "${vuln.match.slice(0, 60)}..." - Use parameterized queries/sanitization.`,
|
|
237
|
-
`Security: ${vuln.type.replace('_', ' ').toUpperCase()}
|
|
237
|
+
`Security: ${vuln.type.replace('_', ' ').toUpperCase()}`,
|
|
238
|
+
vuln.line,
|
|
239
|
+
vuln.line
|
|
238
240
|
));
|
|
239
241
|
}
|
|
240
242
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -141,6 +141,8 @@ export const FailureSchema = z.object({
|
|
|
141
141
|
title: z.string(),
|
|
142
142
|
details: z.string(),
|
|
143
143
|
files: z.array(z.string()).optional(),
|
|
144
|
+
line: z.number().optional(),
|
|
145
|
+
endLine: z.number().optional(),
|
|
144
146
|
hint: z.string().optional(),
|
|
145
147
|
});
|
|
146
148
|
export type Failure = z.infer<typeof FailureSchema>;
|