@sun-asterisk/sunlint 1.3.6 → 1.3.7
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/CHANGELOG.md +38 -1
- package/config/rules/enhanced-rules-registry.json +42 -10
- package/core/analysis-orchestrator.js +9 -5
- package/core/performance-optimizer.js +8 -2
- package/package.json +1 -1
- package/rules/common/C073_validate_required_config_on_startup/README.md +110 -0
- package/rules/common/C073_validate_required_config_on_startup/analyzer.js +770 -0
- package/rules/common/C073_validate_required_config_on_startup/config.json +46 -0
- package/rules/common/C073_validate_required_config_on_startup/symbol-based-analyzer.js +370 -0
- package/rules/security/S057_utc_logging/README.md +152 -0
- package/rules/security/S057_utc_logging/analyzer.js +457 -0
- package/rules/security/S057_utc_logging/config.json +105 -0
- package/rules/security/S058_no_ssrf/README.md +180 -0
- package/rules/security/S058_no_ssrf/analyzer.js +403 -0
- package/rules/security/S058_no_ssrf/config.json +125 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class S057UtcLoggingAnalyzer {
|
|
5
|
+
constructor(ruleId = 'S057', verbose = false) {
|
|
6
|
+
this.ruleId = ruleId;
|
|
7
|
+
this.verbose = verbose;
|
|
8
|
+
this.loadConfig();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
loadConfig() {
|
|
12
|
+
try {
|
|
13
|
+
const configPath = path.join(__dirname, 'config.json');
|
|
14
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
15
|
+
const config = JSON.parse(configContent);
|
|
16
|
+
|
|
17
|
+
this.disallowedDatePatterns = config.options.disallowedDatePatterns || [];
|
|
18
|
+
this.allowedUtcPatterns = config.options.allowedUtcPatterns || [];
|
|
19
|
+
this.logFrameworks = config.options.logFrameworks || [];
|
|
20
|
+
this.logStatements = config.options.logStatements || [];
|
|
21
|
+
this.configChecks = config.options.configChecks || [];
|
|
22
|
+
this.policy = config.options.policy || {};
|
|
23
|
+
this.thresholds = config.options.thresholds || {};
|
|
24
|
+
this.exemptions = config.options.exemptions || {};
|
|
25
|
+
|
|
26
|
+
if (this.verbose) {
|
|
27
|
+
console.log(`[DEBUG] S057: Loaded config with ${this.disallowedDatePatterns.length} disallowed patterns, ${this.allowedUtcPatterns.length} allowed patterns`);
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.warn(`[S057] Failed to load config: ${error.message}`);
|
|
31
|
+
this.disallowedDatePatterns = [];
|
|
32
|
+
this.allowedUtcPatterns = [];
|
|
33
|
+
this.logFrameworks = [];
|
|
34
|
+
this.logStatements = [];
|
|
35
|
+
this.configChecks = [];
|
|
36
|
+
this.policy = {};
|
|
37
|
+
this.thresholds = {};
|
|
38
|
+
this.exemptions = {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
analyze(files, options = {}) {
|
|
43
|
+
this.verbose = options.verbose || false;
|
|
44
|
+
const violations = [];
|
|
45
|
+
|
|
46
|
+
if (!Array.isArray(files)) {
|
|
47
|
+
files = [files];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const filePath of files) {
|
|
51
|
+
if (this.verbose) {
|
|
52
|
+
console.log(`[DEBUG] 🎯 S057: Analyzing ${filePath.split('/').pop()}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
57
|
+
const fileExtension = path.extname(filePath);
|
|
58
|
+
const fileViolations = this.analyzeFile(filePath, content, fileExtension);
|
|
59
|
+
violations.push(...fileViolations);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.warn(`[S057] Error analyzing ${filePath}: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (this.verbose) {
|
|
66
|
+
console.log(`[DEBUG] 🎯 S057: Found ${violations.length} UTC logging violations`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return violations;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
analyzeFile(filePath, content, fileExtension) {
|
|
73
|
+
const language = this.detectLanguage(fileExtension);
|
|
74
|
+
if (!language) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check if file is exempted (test files)
|
|
79
|
+
if (this.isExemptedFile(filePath)) {
|
|
80
|
+
if (this.verbose) {
|
|
81
|
+
console.log(`[DEBUG] 🔍 S057: Skipping exempted file: ${filePath.split('/').pop()}`);
|
|
82
|
+
}
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return this.analyzeWithHeuristic(filePath, content, language);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
detectLanguage(fileExtension) {
|
|
90
|
+
const extensions = {
|
|
91
|
+
'.ts': 'typescript',
|
|
92
|
+
'.tsx': 'typescript',
|
|
93
|
+
'.js': 'javascript',
|
|
94
|
+
'.jsx': 'javascript',
|
|
95
|
+
'.mjs': 'javascript'
|
|
96
|
+
};
|
|
97
|
+
return extensions[fileExtension] || null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
isExemptedFile(filePath) {
|
|
101
|
+
if (!this.exemptions.allowedPatterns) return false;
|
|
102
|
+
|
|
103
|
+
for (const pattern of this.exemptions.allowedPatterns) {
|
|
104
|
+
const regex = new RegExp(pattern, 'i');
|
|
105
|
+
if (regex.test(filePath)) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
analyzeWithHeuristic(filePath, content, language) {
|
|
113
|
+
const violations = [];
|
|
114
|
+
const lines = content.split('\n');
|
|
115
|
+
|
|
116
|
+
// Find logging statements with date/time usage
|
|
117
|
+
const logWithTimeStatements = this.detectLogWithTimeStatements(content, lines);
|
|
118
|
+
|
|
119
|
+
if (this.verbose) {
|
|
120
|
+
console.log(`[DEBUG] 🔍 S057: Found ${logWithTimeStatements.length} log statements with time usage`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const logStatement of logWithTimeStatements) {
|
|
124
|
+
// Check if using disallowed date patterns
|
|
125
|
+
const disallowedPattern = this.checkDisallowedDatePattern(logStatement);
|
|
126
|
+
if (disallowedPattern) {
|
|
127
|
+
violations.push({
|
|
128
|
+
ruleId: this.ruleId,
|
|
129
|
+
message: `Non-UTC timestamp in log: '${disallowedPattern.pattern}' should use UTC format like toISOString()`,
|
|
130
|
+
severity: 'warning',
|
|
131
|
+
line: logStatement.line,
|
|
132
|
+
column: logStatement.column,
|
|
133
|
+
filePath: filePath,
|
|
134
|
+
details: {
|
|
135
|
+
violationType: 'non_utc_timestamp',
|
|
136
|
+
detectedPattern: disallowedPattern.pattern,
|
|
137
|
+
suggestion: this.getSuggestion(disallowedPattern.pattern),
|
|
138
|
+
logStatement: logStatement.fullStatement
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check if NOT using allowed UTC patterns when dealing with time
|
|
144
|
+
const hasTimeReference = this.hasTimeReference(logStatement.fullStatement);
|
|
145
|
+
const hasAllowedUtcPattern = this.hasAllowedUtcPattern(logStatement.fullStatement);
|
|
146
|
+
|
|
147
|
+
if (hasTimeReference && !hasAllowedUtcPattern && !disallowedPattern) {
|
|
148
|
+
violations.push({
|
|
149
|
+
ruleId: this.ruleId,
|
|
150
|
+
message: `Log statement with time should use UTC format (toISOString(), moment.utc(), etc.)`,
|
|
151
|
+
severity: 'warning',
|
|
152
|
+
line: logStatement.line,
|
|
153
|
+
column: logStatement.column,
|
|
154
|
+
filePath: filePath,
|
|
155
|
+
details: {
|
|
156
|
+
violationType: 'missing_utc_format',
|
|
157
|
+
suggestion: 'Use new Date().toISOString() or moment.utc().format() for consistent UTC timestamps',
|
|
158
|
+
logStatement: logStatement.fullStatement
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check logging framework configuration
|
|
165
|
+
const configViolations = this.checkLoggingConfig(filePath, content, lines);
|
|
166
|
+
violations.push(...configViolations);
|
|
167
|
+
|
|
168
|
+
return violations;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
detectLogWithTimeStatements(content, lines) {
|
|
172
|
+
const statements = [];
|
|
173
|
+
|
|
174
|
+
for (let i = 0; i < lines.length; i++) {
|
|
175
|
+
const line = lines[i].trim();
|
|
176
|
+
const lineNumber = i + 1;
|
|
177
|
+
|
|
178
|
+
// Skip comments
|
|
179
|
+
if (line.startsWith('//') || line.startsWith('/*') || line.startsWith('*')) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check for log statements
|
|
184
|
+
for (const logPattern of this.logStatements) {
|
|
185
|
+
const regex = new RegExp(logPattern, 'gi');
|
|
186
|
+
let match;
|
|
187
|
+
|
|
188
|
+
while ((match = regex.exec(line)) !== null) {
|
|
189
|
+
const columnPosition = match.index + 1;
|
|
190
|
+
|
|
191
|
+
// Check if this log statement contains time-related code
|
|
192
|
+
if (this.containsTimeReference(line)) {
|
|
193
|
+
statements.push({
|
|
194
|
+
line: lineNumber,
|
|
195
|
+
column: columnPosition,
|
|
196
|
+
fullStatement: line,
|
|
197
|
+
logPattern: logPattern
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (this.verbose) {
|
|
205
|
+
console.log(`[DEBUG] 🔍 S057: Found ${statements.length} log statements with time references`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return statements;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
containsTimeReference(statement) {
|
|
212
|
+
const timeKeywords = [
|
|
213
|
+
'new Date',
|
|
214
|
+
'Date\\.',
|
|
215
|
+
'moment',
|
|
216
|
+
'dayjs',
|
|
217
|
+
'DateTime',
|
|
218
|
+
'LocalDateTime',
|
|
219
|
+
'Instant',
|
|
220
|
+
'ZonedDateTime',
|
|
221
|
+
'Calendar',
|
|
222
|
+
'timestamp',
|
|
223
|
+
'time',
|
|
224
|
+
'date'
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
for (const keyword of timeKeywords) {
|
|
228
|
+
const regex = new RegExp(keyword, 'i');
|
|
229
|
+
if (regex.test(statement)) {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
checkDisallowedDatePattern(logStatement) {
|
|
238
|
+
for (const pattern of this.disallowedDatePatterns) {
|
|
239
|
+
const regex = new RegExp(pattern, 'gi');
|
|
240
|
+
const match = regex.exec(logStatement.fullStatement);
|
|
241
|
+
if (match) {
|
|
242
|
+
return {
|
|
243
|
+
pattern: match[0],
|
|
244
|
+
regexPattern: pattern
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
hasTimeReference(statement) {
|
|
252
|
+
// More specific time reference check - must be actual time creation/formatting
|
|
253
|
+
const timeCreationPatterns = [
|
|
254
|
+
'new Date\\(',
|
|
255
|
+
'Date\\.',
|
|
256
|
+
'moment\\(',
|
|
257
|
+
'dayjs\\(',
|
|
258
|
+
'DateTime\\.',
|
|
259
|
+
'LocalDateTime\\.',
|
|
260
|
+
'Instant\\.',
|
|
261
|
+
'ZonedDateTime\\.',
|
|
262
|
+
'Calendar\\.',
|
|
263
|
+
'\\.getTime\\(',
|
|
264
|
+
'\\.valueOf\\(',
|
|
265
|
+
'\\.toISOString\\(',
|
|
266
|
+
'\\.toString\\(',
|
|
267
|
+
'\\.toLocale'
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
for (const pattern of timeCreationPatterns) {
|
|
271
|
+
const regex = new RegExp(pattern, 'i');
|
|
272
|
+
if (regex.test(statement)) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
hasAllowedUtcPattern(statement) {
|
|
281
|
+
for (const pattern of this.allowedUtcPatterns) {
|
|
282
|
+
const regex = new RegExp(pattern, 'i');
|
|
283
|
+
if (regex.test(statement)) {
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Additional safe patterns not in config
|
|
289
|
+
const additionalSafePatterns = [
|
|
290
|
+
'winston\\.format\\.timezone\\(["\']UTC["\']\\)',
|
|
291
|
+
'timezone\\(["\']UTC["\']\\)',
|
|
292
|
+
'Date\\.now\\(\\)',
|
|
293
|
+
'epoch',
|
|
294
|
+
'unix'
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
for (const pattern of additionalSafePatterns) {
|
|
298
|
+
const regex = new RegExp(pattern, 'i');
|
|
299
|
+
if (regex.test(statement)) {
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
checkLoggingConfig(filePath, content, lines) {
|
|
308
|
+
const violations = [];
|
|
309
|
+
|
|
310
|
+
// Check for winston/pino/bunyan configuration
|
|
311
|
+
const configPatterns = [
|
|
312
|
+
'winston\\.createLogger',
|
|
313
|
+
'new winston\\.Logger',
|
|
314
|
+
'pino\\(',
|
|
315
|
+
'bunyan\\.createLogger'
|
|
316
|
+
];
|
|
317
|
+
|
|
318
|
+
for (let i = 0; i < lines.length; i++) {
|
|
319
|
+
const line = lines[i].trim();
|
|
320
|
+
const lineNumber = i + 1;
|
|
321
|
+
|
|
322
|
+
for (const configPattern of configPatterns) {
|
|
323
|
+
const regex = new RegExp(configPattern, 'i');
|
|
324
|
+
if (regex.test(line)) {
|
|
325
|
+
// Check if UTC configuration is present
|
|
326
|
+
const hasUtcConfig = this.checkUtcConfigAdvanced(content, i, lines, line);
|
|
327
|
+
if (!hasUtcConfig) {
|
|
328
|
+
violations.push({
|
|
329
|
+
ruleId: this.ruleId,
|
|
330
|
+
message: `Logging framework configuration should specify UTC timezone`,
|
|
331
|
+
severity: 'warning',
|
|
332
|
+
line: lineNumber,
|
|
333
|
+
column: 1,
|
|
334
|
+
filePath: filePath,
|
|
335
|
+
details: {
|
|
336
|
+
violationType: 'missing_utc_config',
|
|
337
|
+
configType: configPattern,
|
|
338
|
+
suggestion: 'Add timezone: "UTC" or similar UTC configuration to logger setup'
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return violations;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
checkUtcConfig(content, startLine, lines) {
|
|
350
|
+
// Look for UTC configuration in the next 15 lines and previous 5 lines
|
|
351
|
+
const searchStartLine = Math.max(0, startLine - 5);
|
|
352
|
+
const endLine = Math.min(startLine + 15, lines.length);
|
|
353
|
+
|
|
354
|
+
for (let i = searchStartLine; i < endLine; i++) {
|
|
355
|
+
const line = lines[i];
|
|
356
|
+
|
|
357
|
+
for (const configCheck of this.configChecks) {
|
|
358
|
+
const regex = new RegExp(configCheck, 'i');
|
|
359
|
+
if (regex.test(line)) {
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Additional UTC indicators
|
|
365
|
+
const utcIndicators = [
|
|
366
|
+
/['"]Z['"]/, // 'Z' or "Z" timezone indicator
|
|
367
|
+
/\+00:00/, // +00:00 timezone offset
|
|
368
|
+
/\.isoTime/, // pino.stdTimeFunctions.isoTime
|
|
369
|
+
/stdTimeFunctions\.isoTime/, // pino standard ISO time functions
|
|
370
|
+
/formatters.*timestamp.*isoTime/i // pino timestamp formatter
|
|
371
|
+
];
|
|
372
|
+
|
|
373
|
+
for (const indicator of utcIndicators) {
|
|
374
|
+
if (indicator.test(line)) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
checkUtcConfigAdvanced(content, startLine, lines, currentLine) {
|
|
384
|
+
// First, use the basic check
|
|
385
|
+
if (this.checkUtcConfig(content, startLine, lines)) {
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Advanced: Check if pino() is called with a config variable
|
|
390
|
+
const pinoCallMatch = currentLine.match(/pino\(\s*(\w+)\s*\)/);
|
|
391
|
+
if (pinoCallMatch) {
|
|
392
|
+
const configVarName = pinoCallMatch[1];
|
|
393
|
+
|
|
394
|
+
// Look for the config variable definition in the entire file
|
|
395
|
+
for (let i = 0; i < lines.length; i++) {
|
|
396
|
+
const line = lines[i];
|
|
397
|
+
|
|
398
|
+
// Check if this line defines the config variable or calls a function that creates it
|
|
399
|
+
if (line.includes(`${configVarName} =`) || line.includes(`const ${configVarName}`) ||
|
|
400
|
+
line.includes(`let ${configVarName}`) || line.includes(`var ${configVarName}`)) {
|
|
401
|
+
|
|
402
|
+
// Check the function call that creates the config
|
|
403
|
+
const functionCallMatch = line.match(/=\s*(\w+)\s*\(/);
|
|
404
|
+
if (functionCallMatch) {
|
|
405
|
+
const functionName = functionCallMatch[1];
|
|
406
|
+
|
|
407
|
+
// Look for the function definition and check for UTC config in it
|
|
408
|
+
for (let j = 0; j < lines.length; j++) {
|
|
409
|
+
const funcLine = lines[j];
|
|
410
|
+
if (funcLine.includes(`const ${functionName}`) || funcLine.includes(`function ${functionName}`)) {
|
|
411
|
+
// Check next 50 lines for UTC configuration
|
|
412
|
+
for (let k = j; k < Math.min(j + 50, lines.length); k++) {
|
|
413
|
+
const checkLine = lines[k];
|
|
414
|
+
|
|
415
|
+
// Check for pino stdTimeFunctions.isoTime
|
|
416
|
+
if (/timestamp.*pino\.stdTimeFunctions\.isoTime|stdTimeFunctions\.isoTime/.test(checkLine)) {
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Check for other UTC indicators
|
|
421
|
+
for (const configCheck of this.configChecks) {
|
|
422
|
+
const regex = new RegExp(configCheck, 'i');
|
|
423
|
+
if (regex.test(checkLine)) {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
getSuggestion(disallowedPattern) {
|
|
439
|
+
const suggestions = {
|
|
440
|
+
'new Date().toString()': 'new Date().toISOString()',
|
|
441
|
+
'new Date().toLocaleString()': 'new Date().toISOString()',
|
|
442
|
+
'new Date().toLocaleDateString()': 'new Date().toISOString().split("T")[0]',
|
|
443
|
+
'new Date().toLocaleTimeString()': 'new Date().toISOString().split("T")[1]',
|
|
444
|
+
'moment().format()': 'moment.utc().format()',
|
|
445
|
+
'moment()': 'moment.utc()',
|
|
446
|
+
'dayjs().format()': 'dayjs.utc().format()',
|
|
447
|
+
'DateTime.now()': 'Instant.now()',
|
|
448
|
+
'LocalDateTime.now()': 'OffsetDateTime.now(ZoneOffset.UTC)',
|
|
449
|
+
'.getTime()': '.toISOString()',
|
|
450
|
+
'.valueOf()': '.toISOString()'
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
return suggestions[disallowedPattern] || 'Use UTC equivalent like toISOString()';
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
module.exports = S057UtcLoggingAnalyzer;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ruleId": "S057",
|
|
3
|
+
"name": "Log with UTC Timestamps",
|
|
4
|
+
"description": "Ensure all logs use synchronized UTC time with ISO 8601/RFC3339 format to avoid timezone discrepancies across systems.",
|
|
5
|
+
"category": "security",
|
|
6
|
+
"severity": "warning",
|
|
7
|
+
"options": {
|
|
8
|
+
"disallowedDatePatterns": [
|
|
9
|
+
"new Date\\(\\)\\.toString\\(",
|
|
10
|
+
"new Date\\(\\)\\.toLocaleString\\(",
|
|
11
|
+
"new Date\\(\\)\\.toLocaleDateString\\(",
|
|
12
|
+
"new Date\\(\\)\\.toLocaleTimeString\\(",
|
|
13
|
+
"DateTime\\.now\\(",
|
|
14
|
+
"LocalDateTime\\.now\\(",
|
|
15
|
+
"Calendar\\.getInstance\\(",
|
|
16
|
+
"ZonedDateTime\\.now\\(\\s*\\)",
|
|
17
|
+
"moment\\(\\)\\.format\\(",
|
|
18
|
+
"moment\\(\\)",
|
|
19
|
+
"dayjs\\(\\)\\.format\\(",
|
|
20
|
+
"date-fns format\\(",
|
|
21
|
+
"\\.getTime\\(\\)",
|
|
22
|
+
"\\.valueOf\\(\\)"
|
|
23
|
+
],
|
|
24
|
+
"allowedUtcPatterns": [
|
|
25
|
+
"toISOString\\(",
|
|
26
|
+
"Instant\\.now\\(",
|
|
27
|
+
"OffsetDateTime\\.now\\(ZoneOffset\\.UTC\\)",
|
|
28
|
+
"ZonedDateTime\\.now\\(ZoneId\\.of\\(\"UTC\"\\)\\)",
|
|
29
|
+
"DateTimeFormatter\\.ISO_INSTANT",
|
|
30
|
+
"DateTimeFormatter\\.RFC_1123_DATE_TIME",
|
|
31
|
+
"moment\\.utc\\(",
|
|
32
|
+
"dayjs\\.utc\\(",
|
|
33
|
+
"new Date\\(\\)\\.toISOString\\(",
|
|
34
|
+
"new Date\\(\\)\\.getUTCFullYear\\(",
|
|
35
|
+
"new Date\\(\\)\\.getUTCMonth\\(",
|
|
36
|
+
"new Date\\(\\)\\.getUTCDate\\(",
|
|
37
|
+
"Date\\.now\\("
|
|
38
|
+
],
|
|
39
|
+
"logFrameworks": [
|
|
40
|
+
"winston",
|
|
41
|
+
"pino",
|
|
42
|
+
"bunyan",
|
|
43
|
+
"log4js",
|
|
44
|
+
"log4j",
|
|
45
|
+
"slf4j",
|
|
46
|
+
"logback",
|
|
47
|
+
"console\\.log",
|
|
48
|
+
"console\\.info",
|
|
49
|
+
"console\\.warn",
|
|
50
|
+
"console\\.error",
|
|
51
|
+
"logger\\.",
|
|
52
|
+
"log\\."
|
|
53
|
+
],
|
|
54
|
+
"logStatements": [
|
|
55
|
+
"console\\.(?:log|info|warn|error|debug)",
|
|
56
|
+
"logger\\.(?:log|info|warn|error|debug|trace)",
|
|
57
|
+
"log\\.(?:log|info|warn|error|debug|trace)",
|
|
58
|
+
"winston\\.",
|
|
59
|
+
"pino\\.",
|
|
60
|
+
"bunyan\\."
|
|
61
|
+
],
|
|
62
|
+
"requiredConfig": {
|
|
63
|
+
"timezone": "UTC",
|
|
64
|
+
"format": ["ISO8601", "RFC3339", "ISO_INSTANT"],
|
|
65
|
+
"ntpSync": true
|
|
66
|
+
},
|
|
67
|
+
"configChecks": [
|
|
68
|
+
"timezone.*UTC",
|
|
69
|
+
"tz.*UTC",
|
|
70
|
+
"timeZone.*UTC",
|
|
71
|
+
"utc.*true",
|
|
72
|
+
"ISO8601",
|
|
73
|
+
"RFC3339",
|
|
74
|
+
"ISO_INSTANT",
|
|
75
|
+
"'Z'",
|
|
76
|
+
"\"Z\"",
|
|
77
|
+
"\\+00:00",
|
|
78
|
+
"Z'$",
|
|
79
|
+
"\\.l'Z'",
|
|
80
|
+
"HH:mm:ss'Z'",
|
|
81
|
+
"timestamp.*isoTime"
|
|
82
|
+
],
|
|
83
|
+
"policy": {
|
|
84
|
+
"requireUtcFormat": true,
|
|
85
|
+
"requireNtpSync": false,
|
|
86
|
+
"blockLocalTime": true,
|
|
87
|
+
"enforceIsoFormat": true
|
|
88
|
+
},
|
|
89
|
+
"thresholds": {
|
|
90
|
+
"maxNonUtcLogs": 0,
|
|
91
|
+
"maxInconsistentFormats": 1
|
|
92
|
+
},
|
|
93
|
+
"exemptions": {
|
|
94
|
+
"allowedInTests": false,
|
|
95
|
+
"allowedInDev": false,
|
|
96
|
+
"allowedPatterns": [
|
|
97
|
+
"\\.test\\.(?:js|ts)$",
|
|
98
|
+
"\\.spec\\.(?:js|ts)$",
|
|
99
|
+
"/test/.*\\.(?:js|ts)$",
|
|
100
|
+
"/tests/.*\\.(?:js|ts)$",
|
|
101
|
+
"/__tests__/.*\\.(?:js|ts)$"
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|