@sun-asterisk/sunlint 1.3.7 → 1.3.9
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 +63 -0
- package/config/defaults/default.json +2 -1
- package/config/rule-analysis-strategies.js +20 -0
- package/config/rules/enhanced-rules-registry.json +247 -53
- package/core/file-targeting-service.js +98 -7
- package/package.json +1 -1
- package/rules/common/C065_one_behavior_per_test/analyzer.js +851 -0
- package/rules/common/C065_one_behavior_per_test/config.json +95 -0
- package/rules/security/S020_no_eval_dynamic_code/README.md +136 -0
- package/rules/security/S020_no_eval_dynamic_code/analyzer.js +263 -0
- package/rules/security/S020_no_eval_dynamic_code/config.json +54 -0
- package/rules/security/S020_no_eval_dynamic_code/regex-based-analyzer.js +307 -0
- package/rules/security/S020_no_eval_dynamic_code/symbol-based-analyzer.js +280 -0
- package/rules/security/S024_xpath_xxe_protection/symbol-based-analyzer.js +3 -3
- package/rules/security/S025_server_side_validation/symbol-based-analyzer.js +3 -4
- package/rules/security/S030_directory_browsing_protection/README.md +128 -0
- package/rules/security/S030_directory_browsing_protection/analyzer.js +264 -0
- package/rules/security/S030_directory_browsing_protection/config.json +63 -0
- package/rules/security/S030_directory_browsing_protection/regex-based-analyzer.js +483 -0
- package/rules/security/S030_directory_browsing_protection/symbol-based-analyzer.js +539 -0
- package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +8 -9
- package/rules/security/S037_cache_headers/README.md +128 -0
- package/rules/security/S037_cache_headers/analyzer.js +263 -0
- package/rules/security/S037_cache_headers/config.json +50 -0
- package/rules/security/S037_cache_headers/regex-based-analyzer.js +463 -0
- package/rules/security/S037_cache_headers/symbol-based-analyzer.js +546 -0
- package/rules/security/S038_no_version_headers/README.md +234 -0
- package/rules/security/S038_no_version_headers/analyzer.js +262 -0
- package/rules/security/S038_no_version_headers/config.json +49 -0
- package/rules/security/S038_no_version_headers/regex-based-analyzer.js +339 -0
- package/rules/security/S038_no_version_headers/symbol-based-analyzer.js +375 -0
- package/rules/security/S039_no_session_tokens_in_url/README.md +198 -0
- package/rules/security/S039_no_session_tokens_in_url/analyzer.js +262 -0
- package/rules/security/S039_no_session_tokens_in_url/config.json +92 -0
- package/rules/security/S039_no_session_tokens_in_url/regex-based-analyzer.js +337 -0
- package/rules/security/S039_no_session_tokens_in_url/symbol-based-analyzer.js +443 -0
- package/rules/security/S049_short_validity_tokens/analyzer.js +175 -0
- package/rules/security/S049_short_validity_tokens/config.json +124 -0
- package/rules/security/S049_short_validity_tokens/regex-based-analyzer.js +295 -0
- package/rules/security/S049_short_validity_tokens/symbol-based-analyzer.js +389 -0
- package/rules/security/S051_password_length_policy/analyzer.js +410 -0
- package/rules/security/S051_password_length_policy/config.json +83 -0
- package/rules/security/S052_weak_otp_entropy/analyzer.js +403 -0
- package/rules/security/S052_weak_otp_entropy/config.json +57 -0
- package/rules/security/S054_no_default_accounts/README.md +129 -0
- package/rules/security/S054_no_default_accounts/analyzer.js +792 -0
- package/rules/security/S054_no_default_accounts/config.json +101 -0
- package/rules/security/S056_log_injection_protection/analyzer.js +242 -0
- package/rules/security/S056_log_injection_protection/config.json +148 -0
- package/rules/security/S056_log_injection_protection/regex-based-analyzer.js +120 -0
- package/rules/security/S056_log_injection_protection/symbol-based-analyzer.js +246 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"rule": {
|
|
3
|
+
"id": "S049",
|
|
4
|
+
"name": "Authentication tokens should have short validity periods",
|
|
5
|
+
"description": "Authentication tokens (JWT, session tokens, etc.) should have appropriately short validity periods to minimize the risk of token compromise. Long-lived tokens increase the attack surface and potential impact of token theft.",
|
|
6
|
+
"category": "security",
|
|
7
|
+
"severity": "error",
|
|
8
|
+
"languages": ["typescript", "javascript"],
|
|
9
|
+
"frameworks": ["express", "nestjs", "node"],
|
|
10
|
+
"version": "1.0.0",
|
|
11
|
+
"status": "stable",
|
|
12
|
+
"tags": ["security", "authentication", "tokens", "jwt", "session", "owasp"],
|
|
13
|
+
"references": [
|
|
14
|
+
"https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/04-Authentication_Testing/01-Testing_for_Credentials_Transported_over_an_Encrypted_Channel",
|
|
15
|
+
"https://owasp.org/www-project-application-security-verification-standard/",
|
|
16
|
+
"https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html",
|
|
17
|
+
"https://tools.ietf.org/html/rfc7519#section-4.1.4"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"configuration": {
|
|
21
|
+
"enableJWTValidityCheck": true,
|
|
22
|
+
"enableSessionTokenCheck": true,
|
|
23
|
+
"enableOAuthTokenCheck": true,
|
|
24
|
+
"maxValidityPeriods": {
|
|
25
|
+
"accessToken": 3600,
|
|
26
|
+
"refreshToken": 86400,
|
|
27
|
+
"sessionToken": 1800,
|
|
28
|
+
"idToken": 3600,
|
|
29
|
+
"authToken": 3600
|
|
30
|
+
},
|
|
31
|
+
"jwtProperties": [
|
|
32
|
+
"exp",
|
|
33
|
+
"expiresIn",
|
|
34
|
+
"expiry",
|
|
35
|
+
"expires",
|
|
36
|
+
"ttl",
|
|
37
|
+
"maxAge",
|
|
38
|
+
"lifetime"
|
|
39
|
+
],
|
|
40
|
+
"tokenMethods": [
|
|
41
|
+
"sign",
|
|
42
|
+
"create",
|
|
43
|
+
"generate",
|
|
44
|
+
"issue",
|
|
45
|
+
"encode"
|
|
46
|
+
],
|
|
47
|
+
"jwtLibraries": [
|
|
48
|
+
"jsonwebtoken",
|
|
49
|
+
"jose",
|
|
50
|
+
"@nestjs/jwt",
|
|
51
|
+
"jwt-simple",
|
|
52
|
+
"node-jsonwebtoken",
|
|
53
|
+
"passport-jwt"
|
|
54
|
+
],
|
|
55
|
+
"sessionMethods": [
|
|
56
|
+
"session",
|
|
57
|
+
"cookie",
|
|
58
|
+
"maxAge",
|
|
59
|
+
"expires"
|
|
60
|
+
],
|
|
61
|
+
"sessionLibraries": [
|
|
62
|
+
"express-session",
|
|
63
|
+
"connect-session",
|
|
64
|
+
"cookie-session",
|
|
65
|
+
"client-sessions"
|
|
66
|
+
],
|
|
67
|
+
"oauthProperties": [
|
|
68
|
+
"access_token_lifetime",
|
|
69
|
+
"refresh_token_lifetime",
|
|
70
|
+
"token_lifetime",
|
|
71
|
+
"expires_in"
|
|
72
|
+
],
|
|
73
|
+
"timeUnits": {
|
|
74
|
+
"seconds": 1,
|
|
75
|
+
"minutes": 60,
|
|
76
|
+
"hours": 3600,
|
|
77
|
+
"days": 86400,
|
|
78
|
+
"weeks": 604800,
|
|
79
|
+
"months": 2592000,
|
|
80
|
+
"years": 31536000
|
|
81
|
+
},
|
|
82
|
+
"dangerousPatterns": [
|
|
83
|
+
"no-expire",
|
|
84
|
+
"never-expire",
|
|
85
|
+
"permanent",
|
|
86
|
+
"forever",
|
|
87
|
+
"infinite",
|
|
88
|
+
"unlimited"
|
|
89
|
+
],
|
|
90
|
+
"exemptedScenarios": [
|
|
91
|
+
"test",
|
|
92
|
+
"testing",
|
|
93
|
+
"mock",
|
|
94
|
+
"development",
|
|
95
|
+
"dev"
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
"examples": {
|
|
99
|
+
"violations": [
|
|
100
|
+
{
|
|
101
|
+
"description": "JWT token with excessive expiration time",
|
|
102
|
+
"code": "jwt.sign(payload, secret, { expiresIn: '30d' })"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"description": "Session with very long max age",
|
|
106
|
+
"code": "session({ maxAge: 30 * 24 * 60 * 60 * 1000 })"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"description": "Token with no expiration",
|
|
110
|
+
"code": "jwt.sign(payload, secret)"
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
"clean": [
|
|
114
|
+
{
|
|
115
|
+
"description": "JWT token with appropriate expiration",
|
|
116
|
+
"code": "jwt.sign(payload, secret, { expiresIn: '1h' })"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"description": "Session with reasonable max age",
|
|
120
|
+
"code": "session({ maxAge: 30 * 60 * 1000 })"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S049 Regex-based Analyzer - Authentication tokens should have short validity periods
|
|
3
|
+
* Fallback analyzer using regex patterns when AST parsing is not available
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
|
|
9
|
+
class S049RegexBasedAnalyzer {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.ruleId = "S049";
|
|
12
|
+
|
|
13
|
+
// Load configuration
|
|
14
|
+
const configPath = path.join(__dirname, 'config.json');
|
|
15
|
+
this.config = JSON.parse(fs.readFileSync(configPath, 'utf8')).configuration;
|
|
16
|
+
|
|
17
|
+
this.initializePatterns();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async initialize(semanticEngine) {
|
|
21
|
+
// Regex analyzer doesn't need semantic engine
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Initialize regex patterns for detection
|
|
26
|
+
*/
|
|
27
|
+
initializePatterns() {
|
|
28
|
+
// JWT token creation patterns
|
|
29
|
+
this.jwtPatterns = [
|
|
30
|
+
// jwt.sign(payload, secret, { expiresIn: 'long-time' }) - handles multiline
|
|
31
|
+
/jwt\.sign\s*\([^)]*,\s*[^)]*,\s*\{[^}]*?expiresIn\s*:\s*['"]\s*([^'"]+)\s*['"][^}]*?\}/gs,
|
|
32
|
+
|
|
33
|
+
// jwt.sign(payload, secret, { exp: timestamp })
|
|
34
|
+
/jwt\.sign\s*\([^)]*,\s*[^)]*,\s*\{[^}]*?exp\s*:\s*(\d+)[^}]*?\}/gs,
|
|
35
|
+
|
|
36
|
+
// Token creation without expiration - matches jwt.sign with exactly 2 parameters
|
|
37
|
+
/jwt\.sign\s*\(\s*[^,)]+\s*,\s*[^,)]+\s*\)/g,
|
|
38
|
+
|
|
39
|
+
// Other JWT libraries
|
|
40
|
+
/(?:jsonwebtoken|jose|@nestjs\/jwt).*\.(?:sign|create|generate)\s*\([^)]*?\)/g
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Session configuration patterns
|
|
44
|
+
this.sessionPatterns = [
|
|
45
|
+
// session({ maxAge: long-time }) - handles multiline
|
|
46
|
+
/session\s*\(\s*\{[^}]*?maxAge\s*:\s*(\d+)[^}]*?\}/gs,
|
|
47
|
+
|
|
48
|
+
// cookie({ maxAge: long-time })
|
|
49
|
+
/cookie\s*\(\s*\{[^}]*?maxAge\s*:\s*(\d+)[^}]*?\}/gs,
|
|
50
|
+
|
|
51
|
+
// express-session configuration
|
|
52
|
+
/require\s*\(\s*['"]\s*express-session\s*['"]\s*\)\s*\(\s*\{[^}]*?maxAge\s*:\s*(\d+)[^}]*?\}/gs
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// OAuth token patterns
|
|
56
|
+
this.oauthPatterns = [
|
|
57
|
+
// OAuth configuration with lifetime
|
|
58
|
+
/(?:access_token_lifetime|refresh_token_lifetime|token_lifetime|expires_in)\s*[:=]\s*(\d+)/g
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// Dangerous patterns (never expire, etc.)
|
|
62
|
+
this.dangerousPatterns = this.config.dangerousPatterns.map(pattern =>
|
|
63
|
+
new RegExp(pattern, 'gi')
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Analyze file for authentication token validity issues
|
|
69
|
+
*/
|
|
70
|
+
async analyze(filePath, language = "typescript", options = {}) {
|
|
71
|
+
try {
|
|
72
|
+
const sourceCode = fs.readFileSync(filePath, "utf8");
|
|
73
|
+
const lines = sourceCode.split('\n');
|
|
74
|
+
const violations = [];
|
|
75
|
+
|
|
76
|
+
// Check for JWT token issues
|
|
77
|
+
this.checkJWTPatterns(sourceCode, lines, filePath, violations);
|
|
78
|
+
|
|
79
|
+
// Check for session configuration issues
|
|
80
|
+
this.checkSessionPatterns(sourceCode, lines, filePath, violations);
|
|
81
|
+
|
|
82
|
+
// Check for OAuth token issues
|
|
83
|
+
this.checkOAuthPatterns(sourceCode, lines, filePath, violations);
|
|
84
|
+
|
|
85
|
+
// Check for dangerous patterns
|
|
86
|
+
this.checkDangerousPatterns(sourceCode, lines, filePath, violations);
|
|
87
|
+
|
|
88
|
+
return violations;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error(`🔧 [S049] Error in regex-based analysis:`, error);
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check JWT patterns for violations
|
|
97
|
+
*/
|
|
98
|
+
checkJWTPatterns(sourceCode, lines, filePath, violations) {
|
|
99
|
+
for (const pattern of this.jwtPatterns) {
|
|
100
|
+
let match;
|
|
101
|
+
const globalPattern = new RegExp(pattern.source, 'g');
|
|
102
|
+
|
|
103
|
+
while ((match = globalPattern.exec(sourceCode)) !== null) {
|
|
104
|
+
const lineInfo = this.getLineInfo(sourceCode, match.index);
|
|
105
|
+
|
|
106
|
+
if (this.isInExemptedContext(lines[lineInfo.line - 1])) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check if this is a token creation without expiration
|
|
111
|
+
if (!match[1]) {
|
|
112
|
+
this.addViolation(violations, filePath, lineInfo, lines[lineInfo.line - 1],
|
|
113
|
+
'JWT token created without expiration time',
|
|
114
|
+
'missing-expiration');
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check if expiration time is too long
|
|
119
|
+
const expirationValue = match[1];
|
|
120
|
+
const seconds = this.parseTimeValue(expirationValue);
|
|
121
|
+
|
|
122
|
+
if (seconds > this.config.maxValidityPeriods.accessToken) {
|
|
123
|
+
this.addViolation(violations, filePath, lineInfo, lines[lineInfo.line - 1],
|
|
124
|
+
`JWT token expiration time (${expirationValue}) exceeds recommended maximum of ${this.config.maxValidityPeriods.accessToken} seconds`,
|
|
125
|
+
'long-expiration');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Check session patterns for violations
|
|
133
|
+
*/
|
|
134
|
+
checkSessionPatterns(sourceCode, lines, filePath, violations) {
|
|
135
|
+
for (const pattern of this.sessionPatterns) {
|
|
136
|
+
let match;
|
|
137
|
+
const globalPattern = new RegExp(pattern.source, 'g');
|
|
138
|
+
|
|
139
|
+
while ((match = globalPattern.exec(sourceCode)) !== null) {
|
|
140
|
+
const lineInfo = this.getLineInfo(sourceCode, match.index);
|
|
141
|
+
|
|
142
|
+
if (this.isInExemptedContext(lines[lineInfo.line - 1])) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const maxAgeValue = match[1];
|
|
147
|
+
if (maxAgeValue) {
|
|
148
|
+
// Session values are often in milliseconds
|
|
149
|
+
const seconds = Math.floor(parseInt(maxAgeValue) / 1000);
|
|
150
|
+
|
|
151
|
+
if (seconds > this.config.maxValidityPeriods.sessionToken) {
|
|
152
|
+
this.addViolation(violations, filePath, lineInfo, lines[lineInfo.line - 1],
|
|
153
|
+
`Session maxAge (${maxAgeValue}ms) exceeds recommended maximum of ${this.config.maxValidityPeriods.sessionToken} seconds`,
|
|
154
|
+
'long-session');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check OAuth patterns for violations
|
|
163
|
+
*/
|
|
164
|
+
checkOAuthPatterns(sourceCode, lines, filePath, violations) {
|
|
165
|
+
for (const pattern of this.oauthPatterns) {
|
|
166
|
+
let match;
|
|
167
|
+
const globalPattern = new RegExp(pattern.source, 'g');
|
|
168
|
+
|
|
169
|
+
while ((match = globalPattern.exec(sourceCode)) !== null) {
|
|
170
|
+
const lineInfo = this.getLineInfo(sourceCode, match.index);
|
|
171
|
+
|
|
172
|
+
if (this.isInExemptedContext(lines[lineInfo.line - 1])) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const tokenValue = match[1];
|
|
177
|
+
if (tokenValue) {
|
|
178
|
+
const seconds = parseInt(tokenValue);
|
|
179
|
+
|
|
180
|
+
if (seconds > this.config.maxValidityPeriods.accessToken) {
|
|
181
|
+
this.addViolation(violations, filePath, lineInfo, lines[lineInfo.line - 1],
|
|
182
|
+
`OAuth token lifetime (${tokenValue}s) exceeds recommended maximum of ${this.config.maxValidityPeriods.accessToken} seconds`,
|
|
183
|
+
'long-oauth-token');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Check for dangerous patterns (never expire, etc.)
|
|
192
|
+
*/
|
|
193
|
+
checkDangerousPatterns(sourceCode, lines, filePath, violations) {
|
|
194
|
+
for (const pattern of this.dangerousPatterns) {
|
|
195
|
+
let match;
|
|
196
|
+
|
|
197
|
+
while ((match = pattern.exec(sourceCode)) !== null) {
|
|
198
|
+
const lineInfo = this.getLineInfo(sourceCode, match.index);
|
|
199
|
+
|
|
200
|
+
if (this.isInExemptedContext(lines[lineInfo.line - 1])) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
this.addViolation(violations, filePath, lineInfo, lines[lineInfo.line - 1],
|
|
205
|
+
`Dangerous token configuration detected: "${match[0]}" suggests tokens that never expire`,
|
|
206
|
+
'dangerous-pattern');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Check if line is in exempted context (test, mock, etc.)
|
|
213
|
+
*/
|
|
214
|
+
isInExemptedContext(line) {
|
|
215
|
+
if (!line) return false;
|
|
216
|
+
|
|
217
|
+
const lowerLine = line.toLowerCase();
|
|
218
|
+
return this.config.exemptedScenarios.some(scenario =>
|
|
219
|
+
lowerLine.includes(scenario)
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Parse time value to seconds
|
|
225
|
+
*/
|
|
226
|
+
parseTimeValue(value) {
|
|
227
|
+
if (typeof value === 'number') {
|
|
228
|
+
return value;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (typeof value === 'string') {
|
|
232
|
+
// Handle string formats like '1h', '30d', '2w'
|
|
233
|
+
const match = value.match(/^(\d+)\s*([a-zA-Z]+)?$/);
|
|
234
|
+
if (match) {
|
|
235
|
+
const num = parseInt(match[1]);
|
|
236
|
+
const unit = match[2]?.toLowerCase() || 's';
|
|
237
|
+
|
|
238
|
+
// Map common units
|
|
239
|
+
const multiplier = this.config.timeUnits[unit] ||
|
|
240
|
+
this.config.timeUnits[unit + 's'] || 1;
|
|
241
|
+
|
|
242
|
+
return num * multiplier;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Handle pure numbers as strings
|
|
246
|
+
const numValue = parseInt(value);
|
|
247
|
+
if (!isNaN(numValue)) {
|
|
248
|
+
return numValue;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get line information from source position
|
|
257
|
+
*/
|
|
258
|
+
getLineInfo(sourceCode, index) {
|
|
259
|
+
const beforeMatch = sourceCode.substring(0, index);
|
|
260
|
+
const line = beforeMatch.split('\n').length;
|
|
261
|
+
const lineStart = beforeMatch.lastIndexOf('\n') + 1;
|
|
262
|
+
const column = index - lineStart;
|
|
263
|
+
|
|
264
|
+
return { line, column };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Add violation to results
|
|
269
|
+
*/
|
|
270
|
+
addViolation(violations, filePath, lineInfo, sourceLine, message, subType) {
|
|
271
|
+
violations.push({
|
|
272
|
+
ruleId: this.ruleId,
|
|
273
|
+
message,
|
|
274
|
+
severity: "error",
|
|
275
|
+
line: lineInfo.line,
|
|
276
|
+
column: lineInfo.column,
|
|
277
|
+
endLine: lineInfo.line,
|
|
278
|
+
endColumn: lineInfo.column + (sourceLine?.length || 0),
|
|
279
|
+
source: sourceLine || "",
|
|
280
|
+
filePath,
|
|
281
|
+
type: "regex-based",
|
|
282
|
+
subType,
|
|
283
|
+
context: {
|
|
284
|
+
matchType: "pattern-based",
|
|
285
|
+
pattern: subType
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
cleanup() {
|
|
291
|
+
// Cleanup resources if needed
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
module.exports = S049RegexBasedAnalyzer;
|