@sun-asterisk/sunlint 1.3.6 → 1.3.8

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +76 -1
  2. package/config/defaults/default.json +2 -1
  3. package/config/rule-analysis-strategies.js +20 -0
  4. package/config/rules/enhanced-rules-registry.json +230 -43
  5. package/core/analysis-orchestrator.js +9 -5
  6. package/core/file-targeting-service.js +83 -7
  7. package/core/performance-optimizer.js +8 -2
  8. package/package.json +1 -1
  9. package/rules/common/C065_one_behavior_per_test/analyzer.js +851 -0
  10. package/rules/common/C065_one_behavior_per_test/config.json +95 -0
  11. package/rules/common/C073_validate_required_config_on_startup/README.md +110 -0
  12. package/rules/common/C073_validate_required_config_on_startup/analyzer.js +770 -0
  13. package/rules/common/C073_validate_required_config_on_startup/config.json +46 -0
  14. package/rules/common/C073_validate_required_config_on_startup/symbol-based-analyzer.js +370 -0
  15. package/rules/security/S037_cache_headers/README.md +128 -0
  16. package/rules/security/S037_cache_headers/analyzer.js +263 -0
  17. package/rules/security/S037_cache_headers/config.json +50 -0
  18. package/rules/security/S037_cache_headers/regex-based-analyzer.js +463 -0
  19. package/rules/security/S037_cache_headers/symbol-based-analyzer.js +546 -0
  20. package/rules/security/S038_no_version_headers/README.md +234 -0
  21. package/rules/security/S038_no_version_headers/analyzer.js +262 -0
  22. package/rules/security/S038_no_version_headers/config.json +49 -0
  23. package/rules/security/S038_no_version_headers/regex-based-analyzer.js +339 -0
  24. package/rules/security/S038_no_version_headers/symbol-based-analyzer.js +375 -0
  25. package/rules/security/S039_no_session_tokens_in_url/README.md +198 -0
  26. package/rules/security/S039_no_session_tokens_in_url/analyzer.js +262 -0
  27. package/rules/security/S039_no_session_tokens_in_url/config.json +92 -0
  28. package/rules/security/S039_no_session_tokens_in_url/regex-based-analyzer.js +337 -0
  29. package/rules/security/S039_no_session_tokens_in_url/symbol-based-analyzer.js +436 -0
  30. package/rules/security/S049_short_validity_tokens/analyzer.js +175 -0
  31. package/rules/security/S049_short_validity_tokens/config.json +124 -0
  32. package/rules/security/S049_short_validity_tokens/regex-based-analyzer.js +295 -0
  33. package/rules/security/S049_short_validity_tokens/symbol-based-analyzer.js +389 -0
  34. package/rules/security/S051_password_length_policy/analyzer.js +410 -0
  35. package/rules/security/S051_password_length_policy/config.json +83 -0
  36. package/rules/security/S052_weak_otp_entropy/analyzer.js +403 -0
  37. package/rules/security/S052_weak_otp_entropy/config.json +57 -0
  38. package/rules/security/S054_no_default_accounts/README.md +129 -0
  39. package/rules/security/S054_no_default_accounts/analyzer.js +792 -0
  40. package/rules/security/S054_no_default_accounts/config.json +101 -0
  41. package/rules/security/S056_log_injection_protection/analyzer.js +242 -0
  42. package/rules/security/S056_log_injection_protection/config.json +148 -0
  43. package/rules/security/S056_log_injection_protection/regex-based-analyzer.js +120 -0
  44. package/rules/security/S056_log_injection_protection/symbol-based-analyzer.js +287 -0
  45. package/rules/security/S057_utc_logging/README.md +152 -0
  46. package/rules/security/S057_utc_logging/analyzer.js +457 -0
  47. package/rules/security/S057_utc_logging/config.json +105 -0
  48. package/rules/security/S058_no_ssrf/README.md +180 -0
  49. package/rules/security/S058_no_ssrf/analyzer.js +403 -0
  50. package/rules/security/S058_no_ssrf/config.json +125 -0
@@ -0,0 +1,263 @@
1
+ /**
2
+ * S037 Main Analyzer - Configure comprehensive cache headers to prevent sensitive data leakage
3
+ * Primary: Symbol-based analysis (when available)
4
+ * Fallback: Regex-based for all other cases
5
+ * Command: node cli.js --rule=S037 --input=examples/rule-test-fixtures/rules/S037_cache_headers --engine=heuristic
6
+ */
7
+
8
+ const S037SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
9
+ const S037RegexBasedAnalyzer = require("./regex-based-analyzer.js");
10
+
11
+ class S037Analyzer {
12
+ constructor(options = {}) {
13
+ if (process.env.SUNLINT_DEBUG) {
14
+ console.log(`🔧 [S037] Constructor called with options:`, !!options);
15
+ console.log(
16
+ `🔧 [S037] Options type:`,
17
+ typeof options,
18
+ Object.keys(options || {})
19
+ );
20
+ }
21
+
22
+ this.ruleId = "S037";
23
+ this.ruleName =
24
+ "Configure comprehensive cache headers to prevent sensitive data leakage";
25
+ this.description =
26
+ "Configure comprehensive cache headers (Cache-Control: no-store, no-cache, must-revalidate, Pragma: no-cache, Expires: 0) for sensitive responses to avoid caching sensitive data in browsers or intermediaries.";
27
+ this.semanticEngine = options.semanticEngine || null;
28
+ this.verbose = options.verbose || false;
29
+
30
+ this.config = {
31
+ useSymbolBased: true,
32
+ fallbackToRegex: true,
33
+ regexBasedOnly: false,
34
+ prioritizeSymbolic: true, // Prefer symbol-based when available
35
+ fallbackToSymbol: true, // Allow symbol analysis even without semantic engine
36
+ };
37
+
38
+ try {
39
+ this.symbolAnalyzer = new S037SymbolBasedAnalyzer(this.semanticEngine);
40
+ if (process.env.SUNLINT_DEBUG)
41
+ console.log(`🔧 [S037] Symbol analyzer created successfully`);
42
+ } catch (error) {
43
+ console.error(`🔧 [S037] Error creating symbol analyzer:`, error);
44
+ }
45
+
46
+ try {
47
+ this.regexAnalyzer = new S037RegexBasedAnalyzer(this.semanticEngine);
48
+ if (process.env.SUNLINT_DEBUG)
49
+ console.log(`🔧 [S037] Regex analyzer created successfully`);
50
+ } catch (error) {
51
+ console.error(`🔧 [S037] Error creating regex analyzer:`, error);
52
+ }
53
+ }
54
+
55
+ async initialize(semanticEngine) {
56
+ this.semanticEngine = semanticEngine;
57
+ if (process.env.SUNLINT_DEBUG)
58
+ console.log(`🔧 [S037] Main analyzer initializing...`);
59
+
60
+ if (this.symbolAnalyzer)
61
+ await this.symbolAnalyzer.initialize?.(semanticEngine);
62
+ if (this.regexAnalyzer)
63
+ await this.regexAnalyzer.initialize?.(semanticEngine);
64
+ if (this.regexAnalyzer) this.regexAnalyzer.cleanup?.();
65
+
66
+ if (process.env.SUNLINT_DEBUG)
67
+ console.log(`🔧 [S037] Main analyzer initialized successfully`);
68
+ }
69
+
70
+ analyzeSingle(filePath, options = {}) {
71
+ if (process.env.SUNLINT_DEBUG)
72
+ console.log(`🔍 [S037] analyzeSingle() called for: ${filePath}`);
73
+ return this.analyze([filePath], "typescript", options);
74
+ }
75
+
76
+ async analyze(files, language, options = {}) {
77
+ if (process.env.SUNLINT_DEBUG) {
78
+ console.log(
79
+ `🔧 [S037] analyze() method called with ${files.length} files, language: ${language}`
80
+ );
81
+ }
82
+
83
+ const violations = [];
84
+ for (const filePath of files) {
85
+ try {
86
+ if (process.env.SUNLINT_DEBUG)
87
+ console.log(`🔧 [S037] Processing file: ${filePath}`);
88
+ const fileViolations = await this.analyzeFile(filePath, options);
89
+ violations.push(...fileViolations);
90
+ if (process.env.SUNLINT_DEBUG)
91
+ console.log(
92
+ `🔧 [S037] File ${filePath}: Found ${fileViolations.length} violations`
93
+ );
94
+ } catch (error) {
95
+ console.warn(
96
+ `⚠ [S037] Analysis failed for ${filePath}:`,
97
+ error.message
98
+ );
99
+ }
100
+ }
101
+
102
+ if (process.env.SUNLINT_DEBUG)
103
+ console.log(`🔧 [S037] Total violations found: ${violations.length}`);
104
+ return violations;
105
+ }
106
+
107
+ async analyzeFile(filePath, options = {}) {
108
+ if (process.env.SUNLINT_DEBUG)
109
+ console.log(`🔍 [S037] analyzeFile() called for: ${filePath}`);
110
+ const violationMap = new Map();
111
+
112
+ // Try symbol-based analysis first when semantic engine is available OR when explicitly enabled
113
+ if (process.env.SUNLINT_DEBUG) {
114
+ console.log(
115
+ `🔧 [S037] Symbol check: useSymbolBased=${
116
+ this.config.useSymbolBased
117
+ }, semanticEngine=${!!this.semanticEngine}, project=${!!this
118
+ .semanticEngine?.project}, initialized=${!!this.semanticEngine
119
+ ?.initialized}`
120
+ );
121
+ }
122
+
123
+ const canUseSymbol =
124
+ this.config.useSymbolBased &&
125
+ ((this.semanticEngine?.project && this.semanticEngine?.initialized) ||
126
+ (!this.semanticEngine && this.config.fallbackToSymbol !== false));
127
+
128
+ if (canUseSymbol) {
129
+ try {
130
+ if (process.env.SUNLINT_DEBUG)
131
+ console.log(`🔧 [S037] Trying symbol-based analysis...`);
132
+
133
+ let sourceFile = null;
134
+ if (this.semanticEngine?.project) {
135
+ sourceFile = this.semanticEngine.project.getSourceFile(filePath);
136
+ if (process.env.SUNLINT_DEBUG) {
137
+ console.log(
138
+ `🔧 [S037] Checked existing semantic engine project: sourceFile=${!!sourceFile}`
139
+ );
140
+ }
141
+ }
142
+
143
+ if (!sourceFile) {
144
+ // Create a minimal ts-morph project for this analysis
145
+ if (process.env.SUNLINT_DEBUG)
146
+ console.log(
147
+ `🔧 [S037] Creating temporary ts-morph project for: ${filePath}`
148
+ );
149
+ try {
150
+ const fs = require("fs");
151
+ const path = require("path");
152
+ const { Project } = require("ts-morph");
153
+
154
+ // Check if file exists and read content
155
+ if (!fs.existsSync(filePath)) {
156
+ throw new Error(`File not found: ${filePath}`);
157
+ }
158
+
159
+ const fileContent = fs.readFileSync(filePath, "utf8");
160
+ const fileName = path.basename(filePath);
161
+
162
+ const tempProject = new Project({
163
+ useInMemoryFileSystem: true,
164
+ compilerOptions: {
165
+ allowJs: true,
166
+ allowSyntheticDefaultImports: true,
167
+ },
168
+ });
169
+
170
+ // Add file content to in-memory project
171
+ sourceFile = tempProject.createSourceFile(fileName, fileContent);
172
+ if (process.env.SUNLINT_DEBUG)
173
+ console.log(
174
+ `🔧 [S037] Temporary project created successfully with file: ${fileName}`
175
+ );
176
+ } catch (projectError) {
177
+ if (process.env.SUNLINT_DEBUG)
178
+ console.log(
179
+ `🔧 [S037] Failed to create temporary project:`,
180
+ projectError.message
181
+ );
182
+ throw projectError;
183
+ }
184
+ }
185
+
186
+ if (sourceFile) {
187
+ const symbolViolations = await this.symbolAnalyzer.analyze(
188
+ sourceFile,
189
+ filePath
190
+ );
191
+ symbolViolations.forEach((v) => {
192
+ const key = `${v.line}:${v.column}:${v.message}`;
193
+ if (!violationMap.has(key)) violationMap.set(key, v);
194
+ });
195
+ if (process.env.SUNLINT_DEBUG)
196
+ console.log(
197
+ `🔧 [S037] Symbol analysis completed: ${symbolViolations.length} violations`
198
+ );
199
+
200
+ // If symbol-based found violations or prioritizeSymbolic is true, skip regex
201
+ if (this.config.prioritizeSymbolic && symbolViolations.length >= 0) {
202
+ const finalViolations = Array.from(violationMap.values()).map(
203
+ (v) => ({
204
+ ...v,
205
+ filePath,
206
+ file: filePath,
207
+ })
208
+ );
209
+ if (process.env.SUNLINT_DEBUG)
210
+ console.log(
211
+ `🔧 [S037] Symbol-based analysis prioritized: ${finalViolations.length} violations`
212
+ );
213
+ return finalViolations;
214
+ }
215
+ } else {
216
+ if (process.env.SUNLINT_DEBUG)
217
+ console.log(
218
+ `🔧 [S037] No source file found, skipping symbol analysis`
219
+ );
220
+ }
221
+ } catch (error) {
222
+ console.warn(`⚠ [S037] Symbol analysis failed:`, error.message);
223
+ }
224
+ }
225
+
226
+ // Fallback to regex-based analysis
227
+ if (this.config.fallbackToRegex || this.config.regexBasedOnly) {
228
+ try {
229
+ if (process.env.SUNLINT_DEBUG)
230
+ console.log(`🔧 [S037] Trying regex-based analysis...`);
231
+ const regexViolations = await this.regexAnalyzer.analyze(filePath);
232
+ regexViolations.forEach((v) => {
233
+ const key = `${v.line}:${v.column}:${v.message}`;
234
+ if (!violationMap.has(key)) violationMap.set(key, v);
235
+ });
236
+ if (process.env.SUNLINT_DEBUG)
237
+ console.log(
238
+ `🔧 [S037] Regex analysis completed: ${regexViolations.length} violations`
239
+ );
240
+ } catch (error) {
241
+ console.warn(`⚠ [S037] Regex analysis failed:`, error.message);
242
+ }
243
+ }
244
+
245
+ const finalViolations = Array.from(violationMap.values()).map((v) => ({
246
+ ...v,
247
+ filePath,
248
+ file: filePath,
249
+ }));
250
+ if (process.env.SUNLINT_DEBUG)
251
+ console.log(
252
+ `🔧 [S037] File analysis completed: ${finalViolations.length} unique violations`
253
+ );
254
+ return finalViolations;
255
+ }
256
+
257
+ cleanup() {
258
+ if (this.symbolAnalyzer?.cleanup) this.symbolAnalyzer.cleanup();
259
+ if (this.regexAnalyzer?.cleanup) this.regexAnalyzer.cleanup();
260
+ }
261
+ }
262
+
263
+ module.exports = S037Analyzer;
@@ -0,0 +1,50 @@
1
+ {
2
+ "id": "S037",
3
+ "name": "Configure comprehensive cache headers to prevent sensitive data leakage",
4
+ "category": "security",
5
+ "description": "S037 - Configure comprehensive cache headers (Cache-Control: no-store, no-cache, must-revalidate, Pragma: no-cache, Expires: 0) for sensitive responses to avoid caching sensitive data in browsers or intermediaries.",
6
+ "severity": "warning",
7
+ "enabled": true,
8
+ "semantic": {
9
+ "enabled": true,
10
+ "priority": "medium",
11
+ "fallback": "heuristic"
12
+ },
13
+ "patterns": {
14
+ "include": ["**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"],
15
+ "exclude": [
16
+ "**/*.test.js",
17
+ "**/*.test.ts",
18
+ "**/*.spec.js",
19
+ "**/*.spec.ts",
20
+ "**/node_modules/**",
21
+ "**/dist/**",
22
+ "**/build/**"
23
+ ]
24
+ },
25
+ "analysis": {
26
+ "approach": "symbol-based-primary",
27
+ "fallback": "regex-based",
28
+ "depth": 1,
29
+ "timeout": 4000
30
+ },
31
+ "validation": {
32
+ "headerSetters": ["setHeader", "set", "header"],
33
+ "required": {
34
+ "Cache-Control": ["no-store", "no-cache", "must-revalidate"],
35
+ "Pragma": ["no-cache"],
36
+ "Expires": ["0"]
37
+ },
38
+ "sensitiveIndicators": [
39
+ "session",
40
+ "auth",
41
+ "token",
42
+ "jwt",
43
+ "csrf",
44
+ "user",
45
+ "profile",
46
+ "payment",
47
+ "account"
48
+ ]
49
+ }
50
+ }