@rigour-labs/core 2.19.2 → 2.21.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.
@@ -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 at line ${line}`,
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() at line ${line}`,
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 at line ${line}`,
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 at line ${line}`,
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}' at line ${line}`,
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 at line ${line}`,
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
  }
@@ -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
  }
@@ -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
  }
@@ -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 violations = [];
23
+ const failures = [];
24
24
  for (const [file, content] of contents) {
25
- for (const pattern of patterns) {
26
- if (pattern.test(content)) {
27
- violations.push(file);
28
- break;
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
- if (violations.length > 0) {
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} at line ${vuln.line}`, [vuln.file], `Found: "${vuln.match.slice(0, 60)}..." - Use parameterized queries/sanitization.`, `Security: ${vuln.type.replace('_', ' ').toUpperCase()}`));
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) {
@@ -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
  }>;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/core",
3
- "version": "2.19.2",
3
+ "version": "2.21.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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 at line ${line}`,
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() at line ${line}`,
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 at line ${line}`,
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 at line ${line}`,
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}' at line ${line}`,
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 at line ${line}`,
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
  }
@@ -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 violations: string[] = [];
29
+ const failures: Failure[] = [];
30
30
  for (const [file, content] of contents) {
31
- for (const pattern of patterns) {
32
- if (pattern.test(content)) {
33
- violations.push(file);
34
- break;
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
- if (violations.length > 0) {
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} at line ${vuln.line}`,
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
  }
@@ -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>;