@sun-asterisk/sunlint 1.3.16 → 1.3.17

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/config/rule-analysis-strategies.js +3 -3
  2. package/config/rules/enhanced-rules-registry.json +40 -20
  3. package/core/cli-action-handler.js +2 -2
  4. package/core/config-merger.js +28 -6
  5. package/core/constants/defaults.js +1 -1
  6. package/core/file-targeting-service.js +72 -4
  7. package/core/output-service.js +21 -4
  8. package/engines/heuristic-engine.js +5 -0
  9. package/package.json +1 -1
  10. package/rules/common/C002_no_duplicate_code/README.md +115 -0
  11. package/rules/common/C002_no_duplicate_code/analyzer.js +615 -219
  12. package/rules/common/C002_no_duplicate_code/test-cases/api-handlers.ts +64 -0
  13. package/rules/common/C002_no_duplicate_code/test-cases/data-processor.ts +46 -0
  14. package/rules/common/C002_no_duplicate_code/test-cases/good-example.tsx +40 -0
  15. package/rules/common/C002_no_duplicate_code/test-cases/product-service.ts +57 -0
  16. package/rules/common/C002_no_duplicate_code/test-cases/user-service.ts +49 -0
  17. package/rules/common/C008/analyzer.js +40 -0
  18. package/rules/common/C008/config.json +20 -0
  19. package/rules/common/C008/ts-morph-analyzer.js +1067 -0
  20. package/rules/common/C018_no_throw_generic_error/analyzer.js +1 -1
  21. package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +27 -3
  22. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +504 -162
  23. package/rules/common/C029_catch_block_logging/analyzer.js +499 -89
  24. package/rules/common/C033_separate_service_repository/README.md +131 -20
  25. package/rules/common/C033_separate_service_repository/analyzer.js +1 -1
  26. package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +417 -274
  27. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +144 -254
  28. package/rules/common/C041_no_sensitive_hardcode/config.json +50 -0
  29. package/rules/common/C041_no_sensitive_hardcode/symbol-based-analyzer.js +575 -0
  30. package/rules/common/C067_no_hardcoded_config/analyzer.js +17 -16
  31. package/rules/common/C067_no_hardcoded_config/symbol-based-analyzer.js +3477 -659
  32. package/rules/docs/C002_no_duplicate_code.md +276 -11
  33. package/rules/index.js +5 -1
  34. package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +266 -88
  35. package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +805 -0
  36. package/rules/security/S010_no_insecure_encryption/README.md +78 -0
  37. package/rules/security/S010_no_insecure_encryption/analyzer.js +463 -398
  38. package/rules/security/S013_tls_enforcement/README.md +51 -0
  39. package/rules/security/S013_tls_enforcement/analyzer.js +99 -0
  40. package/rules/security/S013_tls_enforcement/config.json +41 -0
  41. package/rules/security/S013_tls_enforcement/symbol-based-analyzer.js +339 -0
  42. package/rules/security/S014_tls_version_enforcement/README.md +354 -0
  43. package/rules/security/S014_tls_version_enforcement/analyzer.js +118 -0
  44. package/rules/security/S014_tls_version_enforcement/config.json +56 -0
  45. package/rules/security/S014_tls_version_enforcement/symbol-based-analyzer.js +194 -0
  46. package/rules/security/S055_content_type_validation/analyzer.js +121 -279
  47. package/rules/security/S055_content_type_validation/symbol-based-analyzer.js +346 -0
  48. package/rules/tests/C002_no_duplicate_code.test.js +111 -22
  49. package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +0 -755
  50. package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +0 -296
@@ -0,0 +1,354 @@
1
+ # S014 - Enforce TLS 1.2 or 1.3 Only
2
+
3
+ ## 📋 Overview
4
+
5
+ **Rule ID:** S014
6
+ **Category:** Security
7
+ **Severity:** Error
8
+ **Enabled:** Yes
9
+
10
+ Ensures that only secure TLS versions (1.2 or 1.3) are used in HTTPS server configurations and client connections. Detects usage of insecure SSL/TLS versions (SSLv2, SSLv3, TLS 1.0, TLS 1.1) which have known vulnerabilities.
11
+
12
+ ---
13
+
14
+ ## 🎯 Detection Scope
15
+
16
+ ### Insecure TLS/SSL Versions ❌
17
+
18
+ | Version | Status | Reason |
19
+ | ----------- | ----------- | ----------------------------------------------- |
20
+ | **SSLv2** | ❌ Insecure | Deprecated (RFC 6176), multiple vulnerabilities |
21
+ | **SSLv3** | ❌ Insecure | POODLE attack (CVE-2014-3566) |
22
+ | **TLS 1.0** | ❌ Insecure | BEAST attack, deprecated by PCI DSS |
23
+ | **TLS 1.1** | ❌ Insecure | Weak ciphers, deprecated by major browsers |
24
+
25
+ ### Secure TLS Versions ✅
26
+
27
+ | Version | Status | Recommendation |
28
+ | ----------- | --------- | --------------------------- |
29
+ | **TLS 1.2** | ✅ Secure | Minimum recommended version |
30
+ | **TLS 1.3** | ✅ Secure | Latest, most secure version |
31
+
32
+ ---
33
+
34
+ ## 🔍 What This Rule Detects
35
+
36
+ ### 1. HTTPS Server Configuration
37
+
38
+ ```typescript
39
+ // ❌ BAD: Using TLS 1.0
40
+ https.createServer(
41
+ {
42
+ minVersion: "TLSv1", // Insecure!
43
+ cert: cert,
44
+ key: key,
45
+ },
46
+ app
47
+ );
48
+
49
+ // ✅ GOOD: Using TLS 1.2+
50
+ https.createServer(
51
+ {
52
+ minVersion: "TLSv1.2", // Secure
53
+ cert: cert,
54
+ key: key,
55
+ },
56
+ app
57
+ );
58
+ ```
59
+
60
+ ### 2. Secure Protocol Settings
61
+
62
+ ```typescript
63
+ // ❌ BAD: Using SSLv3
64
+ const options = {
65
+ secureProtocol: "SSLv3_method", // Insecure!
66
+ };
67
+
68
+ // ✅ GOOD: Using TLS 1.2
69
+ const options = {
70
+ secureProtocol: "TLSv1_2_method", // Secure
71
+ };
72
+ ```
73
+
74
+ ### 3. Client Configuration
75
+
76
+ ```typescript
77
+ // ❌ BAD: axios with TLS 1.0
78
+ const agent = new https.Agent({
79
+ minVersion: "TLSv1", // Insecure!
80
+ });
81
+
82
+ // ✅ GOOD: axios with TLS 1.2+
83
+ const agent = new https.Agent({
84
+ minVersion: "TLSv1.2", // Secure
85
+ });
86
+ ```
87
+
88
+ ### 4. Framework-Specific Configuration
89
+
90
+ #### Express.js
91
+
92
+ ```typescript
93
+ // ❌ BAD
94
+ const server = https.createServer(
95
+ {
96
+ secureProtocol: "TLSv1_method", // Insecure!
97
+ },
98
+ app
99
+ );
100
+
101
+ // ✅ GOOD
102
+ const server = https.createServer(
103
+ {
104
+ minVersion: "TLSv1.2", // Secure
105
+ },
106
+ app
107
+ );
108
+ ```
109
+
110
+ #### Next.js (next.config.js)
111
+
112
+ ```javascript
113
+ // ❌ BAD
114
+ module.exports = {
115
+ server: {
116
+ https: {
117
+ minVersion: "TLSv1.0", // Insecure!
118
+ },
119
+ },
120
+ };
121
+
122
+ // ✅ GOOD
123
+ module.exports = {
124
+ server: {
125
+ https: {
126
+ minVersion: "TLSv1.2", // Secure
127
+ },
128
+ },
129
+ };
130
+ ```
131
+
132
+ #### NestJS
133
+
134
+ ```typescript
135
+ // ❌ BAD
136
+ const httpsOptions = {
137
+ secureProtocol: "SSLv3_method", // Insecure!
138
+ };
139
+
140
+ // ✅ GOOD
141
+ const httpsOptions = {
142
+ minVersion: "TLSv1.2", // Secure
143
+ };
144
+ ```
145
+
146
+ ---
147
+
148
+ ## 🚨 Security Impact
149
+
150
+ ### Critical Vulnerabilities
151
+
152
+ | Attack | Affected Versions | CVE |
153
+ | -------------- | ----------------- | ------------- |
154
+ | **POODLE** | SSLv3 | CVE-2014-3566 |
155
+ | **BEAST** | TLS 1.0 | CVE-2011-3389 |
156
+ | **CRIME** | TLS 1.0/1.1 | CVE-2012-4929 |
157
+ | **BREACH** | TLS 1.0/1.1 | CVE-2013-3587 |
158
+ | **Heartbleed** | TLS 1.0/1.1 | CVE-2014-0160 |
159
+
160
+ ### Compliance Requirements
161
+
162
+ - ✅ **PCI DSS 3.2+:** Requires TLS 1.2+ (TLS 1.0/1.1 banned)
163
+ - ✅ **NIST SP 800-52:** Recommends TLS 1.2+ only
164
+ - ✅ **HIPAA:** Requires strong encryption (TLS 1.2+)
165
+ - ✅ **GDPR:** Requires appropriate security measures
166
+
167
+ ---
168
+
169
+ ## 📝 Examples
170
+
171
+ ### Server Configuration
172
+
173
+ #### ❌ Violations
174
+
175
+ ```typescript
176
+ // Violation 1: SSLv3
177
+ const server = https.createServer(
178
+ {
179
+ secureProtocol: "SSLv3_method",
180
+ },
181
+ app
182
+ );
183
+
184
+ // Violation 2: TLS 1.0
185
+ const options = {
186
+ minVersion: "TLSv1",
187
+ };
188
+
189
+ // Violation 3: TLS 1.1
190
+ https.createServer(
191
+ {
192
+ maxVersion: "TLSv1.1",
193
+ },
194
+ app
195
+ );
196
+
197
+ // Violation 4: String version
198
+ const config = {
199
+ protocol: "TLSv1.0",
200
+ };
201
+ ```
202
+
203
+ #### ✅ Compliant Code
204
+
205
+ ```typescript
206
+ // Good: TLS 1.2
207
+ const server = https.createServer(
208
+ {
209
+ minVersion: "TLSv1.2",
210
+ maxVersion: "TLSv1.3",
211
+ },
212
+ app
213
+ );
214
+
215
+ // Good: TLS 1.3
216
+ const options = {
217
+ secureProtocol: "TLSv1_3_method",
218
+ };
219
+
220
+ // Good: Modern defaults
221
+ https.createServer(
222
+ {
223
+ minVersion: "TLSv1.2",
224
+ },
225
+ app
226
+ );
227
+ ```
228
+
229
+ ### Client Configuration
230
+
231
+ #### ❌ Violations
232
+
233
+ ```typescript
234
+ // Violation: axios with TLS 1.0
235
+ const httpsAgent = new https.Agent({
236
+ minVersion: "TLSv1",
237
+ });
238
+
239
+ axios.get("https://api.example.com", {
240
+ httpsAgent,
241
+ });
242
+
243
+ // Violation: fetch with insecure agent
244
+ const agent = new https.Agent({
245
+ secureProtocol: "TLSv1_method",
246
+ });
247
+ ```
248
+
249
+ #### ✅ Compliant Code
250
+
251
+ ```typescript
252
+ // Good: axios with TLS 1.2+
253
+ const httpsAgent = new https.Agent({
254
+ minVersion: "TLSv1.2",
255
+ });
256
+
257
+ axios.get("https://api.example.com", {
258
+ httpsAgent,
259
+ });
260
+
261
+ // Good: Modern TLS
262
+ const agent = new https.Agent({
263
+ minVersion: "TLSv1.2",
264
+ maxVersion: "TLSv1.3",
265
+ });
266
+ ```
267
+
268
+ ---
269
+
270
+ ## 🔧 How to Fix
271
+
272
+ ### 1. Update Server Configuration
273
+
274
+ ```typescript
275
+ // Before
276
+ const server = https.createServer(
277
+ {
278
+ minVersion: "TLSv1", // ❌
279
+ },
280
+ app
281
+ );
282
+
283
+ // After
284
+ const server = https.createServer(
285
+ {
286
+ minVersion: "TLSv1.2", // ✅
287
+ },
288
+ app
289
+ );
290
+ ```
291
+
292
+ ### 2. Update Client Agents
293
+
294
+ ```typescript
295
+ // Before
296
+ const agent = new https.Agent({
297
+ secureProtocol: "TLSv1_method", // ❌
298
+ });
299
+
300
+ // After
301
+ const agent = new https.Agent({
302
+ minVersion: "TLSv1.2", // ✅
303
+ });
304
+ ```
305
+
306
+ ### 3. Framework Configuration
307
+
308
+ ```typescript
309
+ // Before
310
+ const httpsOptions = {
311
+ secureProtocol: "SSLv3_method", // ❌
312
+ };
313
+
314
+ // After
315
+ const httpsOptions = {
316
+ minVersion: "TLSv1.2", // ✅
317
+ maxVersion: "TLSv1.3",
318
+ };
319
+ ```
320
+
321
+ ---
322
+
323
+ ## ⚙️ Configuration
324
+
325
+ ### Pattern Detection
326
+
327
+ - `minVersion: 'TLSv1'` - Detects minimum version settings
328
+ - `maxVersion: 'TLSv1.1'` - Detects maximum version restrictions
329
+ - `secureProtocol: 'SSLv3_method'` - Detects protocol methods
330
+ - `protocol: 'TLSv1.0'` - Detects string protocols
331
+
332
+ ### Supported Frameworks
333
+
334
+ - Express.js
335
+ - Next.js
336
+ - NestJS
337
+ - Nuxt.js
338
+ - Fastify
339
+ - Koa
340
+
341
+ ---
342
+
343
+ ## 📚 References
344
+
345
+ - [RFC 8996 - Deprecating TLS 1.0 and 1.1](https://datatracker.ietf.org/doc/html/rfc8996)
346
+ - [PCI DSS Requirements](https://www.pcisecuritystandards.org/)
347
+ - [NIST SP 800-52 Rev. 2](https://csrc.nist.gov/publications/detail/sp/800-52/rev-2/final)
348
+ - [OWASP Transport Layer Protection Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html)
349
+
350
+ ---
351
+
352
+ **Created:** October 10, 2025
353
+ **Version:** 1.0.0
354
+ **Status:** Active
@@ -0,0 +1,118 @@
1
+ /**
2
+ * S014 - Enforce TLS 1.2 or 1.3 Only
3
+ *
4
+ * Main analyzer using symbol-based analysis to detect insecure TLS/SSL version usage.
5
+ */
6
+ // Command: node cli.js --rule=S014 --input=examples/rule-test-fixtures/rules/S014_tls_version_enforcement --engine=heuristic
7
+
8
+ const S014SymbolBasedAnalyzer = require("./symbol-based-analyzer");
9
+
10
+ class S014Analyzer {
11
+ constructor(options = {}) {
12
+ this.ruleId = "S014";
13
+ this.semanticEngine = options.semanticEngine || null;
14
+ this.verbose = options.verbose || false;
15
+
16
+ try {
17
+ this.symbolAnalyzer = new S014SymbolBasedAnalyzer(this.semanticEngine);
18
+ } catch (e) {
19
+ console.warn(`⚠ [S014] Failed to create symbol analyzer: ${e.message}`);
20
+ }
21
+ }
22
+
23
+ async initialize(semanticEngine) {
24
+ this.semanticEngine = semanticEngine;
25
+ if (this.symbolAnalyzer && this.symbolAnalyzer.initialize) {
26
+ await this.symbolAnalyzer.initialize(semanticEngine);
27
+ }
28
+ }
29
+
30
+ analyzeSingle(filePath, options = {}) {
31
+ return this.analyze([filePath], "typescript", options);
32
+ }
33
+
34
+ async analyze(files, language, options = {}) {
35
+ const violations = [];
36
+ for (const filePath of files) {
37
+ try {
38
+ const vs = await this.analyzeFile(filePath, options);
39
+ violations.push(...vs);
40
+ } catch (e) {
41
+ console.warn(`⚠ [S014] Error analyzing ${filePath}: ${e.message}`);
42
+ }
43
+ }
44
+ return violations;
45
+ }
46
+
47
+ async analyzeFile(filePath, options = {}) {
48
+ const violationMap = new Map();
49
+
50
+ if (!this.symbolAnalyzer) {
51
+ return [];
52
+ }
53
+
54
+ try {
55
+ let sourceFile = null;
56
+ if (this.semanticEngine?.project) {
57
+ sourceFile = this.semanticEngine.project.getSourceFile(filePath);
58
+ }
59
+
60
+ if (!sourceFile) {
61
+ // Create temporary ts-morph source file
62
+ const fs = require("fs");
63
+ const path = require("path");
64
+ const { Project } = require("ts-morph");
65
+ if (!fs.existsSync(filePath)) {
66
+ throw new Error(`File not found: ${filePath}`);
67
+ }
68
+ const content = fs.readFileSync(filePath, "utf8");
69
+ const tmp = new Project({
70
+ useInMemoryFileSystem: true,
71
+ compilerOptions: { allowJs: true },
72
+ });
73
+ sourceFile = tmp.createSourceFile(path.basename(filePath), content);
74
+ }
75
+
76
+ if (sourceFile) {
77
+ const symbolViolations = await this.symbolAnalyzer.analyze(
78
+ sourceFile,
79
+ filePath
80
+ );
81
+ symbolViolations.forEach((v) => {
82
+ // Use only line number as key to deduplicate multiple detections on same line
83
+ const key = `${v.line}`;
84
+ if (!violationMap.has(key)) {
85
+ violationMap.set(key, v);
86
+ } else {
87
+ // Keep the more specific message if available
88
+ const existing = violationMap.get(key);
89
+ if (v.message.length > existing.message.length) {
90
+ violationMap.set(key, v);
91
+ }
92
+ }
93
+ });
94
+ }
95
+ } catch (err) {
96
+ if (this.verbose) {
97
+ console.warn(
98
+ `⚠ [S014] Symbol analysis failed for ${filePath}:`,
99
+ err.message
100
+ );
101
+ }
102
+ }
103
+
104
+ return Array.from(violationMap.values()).map((v) => ({
105
+ ...v,
106
+ filePath,
107
+ file: filePath,
108
+ }));
109
+ }
110
+
111
+ async cleanup() {
112
+ if (this.symbolAnalyzer) {
113
+ await this.symbolAnalyzer.cleanup?.();
114
+ }
115
+ }
116
+ }
117
+
118
+ module.exports = S014Analyzer;
@@ -0,0 +1,56 @@
1
+ {
2
+ "id": "S014",
3
+ "name": "Enforce TLS 1.2 or 1.3 only",
4
+ "category": "security",
5
+ "description": "S014 - Ensure only TLS 1.2 or TLS 1.3 protocols are used. Detects usage of insecure TLS/SSL versions (SSL v2/v3, TLS 1.0, TLS 1.1) in HTTPS server configurations, client requests, and framework settings.",
6
+ "severity": "error",
7
+ "enabled": true,
8
+ "semantic": {
9
+ "enabled": true,
10
+ "priority": "high",
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
+ "insecureVersions": [
33
+ "SSLv2",
34
+ "SSLv3",
35
+ "TLSv1",
36
+ "TLSv1.0",
37
+ "TLSv1_method",
38
+ "TLSv1.1",
39
+ "TLSv1_1_method"
40
+ ],
41
+ "secureVersions": [
42
+ "TLSv1.2",
43
+ "TLSv1_2_method",
44
+ "TLSv1.3",
45
+ "TLSv1_3_method"
46
+ ],
47
+ "configKeys": [
48
+ "minVersion",
49
+ "maxVersion",
50
+ "secureProtocol",
51
+ "secureOptions",
52
+ "protocol"
53
+ ],
54
+ "frameworks": ["express", "nextjs", "nuxtjs", "nestjs", "fastify", "koa"]
55
+ }
56
+ }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * S014 Symbol-Based Analyzer - Enforce TLS 1.2 or 1.3 only
3
+ *
4
+ * Uses AST analysis via ts-morph to detect insecure TLS/SSL versions
5
+ * in server configurations, client agents, and framework settings.
6
+ */
7
+
8
+ class S014SymbolBasedAnalyzer {
9
+ constructor(semanticEngine) {
10
+ this.ruleId = "S014";
11
+ this.semanticEngine = semanticEngine;
12
+
13
+ // Insecure TLS/SSL versions to detect
14
+ this.insecureVersions = [
15
+ /SSLv2/i,
16
+ /SSLv3/i,
17
+ /TLSv1(?!\.2|\.3|_2|_3)/i, // TLS 1.0 (but not 1.2 or 1.3)
18
+ /TLSv1\.0/i,
19
+ /TLSv1_method/i,
20
+ /TLSv1\.1/i,
21
+ /TLSv1_1_method/i,
22
+ ];
23
+
24
+ // Configuration keys that specify TLS version
25
+ this.versionKeys = [
26
+ "minVersion",
27
+ "maxVersion",
28
+ "secureProtocol",
29
+ "protocol",
30
+ "tlsVersion",
31
+ "sslVersion",
32
+ ];
33
+ }
34
+
35
+ async initialize() {}
36
+
37
+ analyze(sourceFile, filePath) {
38
+ const violations = [];
39
+
40
+ try {
41
+ const { SyntaxKind } = require("ts-morph");
42
+
43
+ // Check all object literal expressions for TLS version configs
44
+ const objLits = sourceFile.getDescendantsOfKind(
45
+ SyntaxKind.ObjectLiteralExpression
46
+ );
47
+
48
+ for (const obj of objLits) {
49
+ try {
50
+ const properties = obj.getProperties();
51
+
52
+ for (const prop of properties) {
53
+ if (prop.getKind() !== SyntaxKind.PropertyAssignment) continue;
54
+
55
+ const propAssignment = prop;
56
+ const propName = propAssignment.getName();
57
+
58
+ // Check if this is a TLS version configuration key
59
+ if (this.versionKeys.includes(propName)) {
60
+ const initializer = propAssignment.getInitializer();
61
+ if (!initializer) continue;
62
+
63
+ let value = "";
64
+ const kind = initializer.getKind();
65
+
66
+ if (kind === SyntaxKind.StringLiteral) {
67
+ value = initializer.getLiteralValue();
68
+ } else if (kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
69
+ value = initializer.getLiteralText().slice(1, -1); // Remove backticks
70
+ } else if (kind === SyntaxKind.TemplateExpression) {
71
+ // Template literal with expressions: `TLSv${version}`
72
+ // We can't know the runtime value, but we can detect potential issues
73
+ const templateText = initializer.getText();
74
+ // Check if template contains TLS/SSL patterns that might result in insecure versions
75
+ if (/TLSv|SSLv/i.test(templateText)) {
76
+ // This is potentially insecure - warn about dynamic TLS version
77
+ violations.push({
78
+ ruleId: this.ruleId,
79
+ message: `Dynamic TLS/SSL version detected in '${propName}' - ensure it uses TLS 1.2 or 1.3 only`,
80
+ severity: "error",
81
+ line: propAssignment.getStartLineNumber(),
82
+ column:
83
+ propAssignment.getStartLinePos() -
84
+ propAssignment.getStartLinePos(true) +
85
+ 1,
86
+ filePath: filePath,
87
+ });
88
+ continue; // Skip further checking for this property
89
+ }
90
+ value = templateText;
91
+ } else if (kind === SyntaxKind.Identifier) {
92
+ // Handle variable reference
93
+ value = initializer.getText();
94
+ } else {
95
+ value = initializer.getText().replace(/['"]/g, "");
96
+ }
97
+
98
+ // Check if value contains insecure version
99
+ for (const insecurePattern of this.insecureVersions) {
100
+ if (insecurePattern.test(value)) {
101
+ violations.push({
102
+ ruleId: this.ruleId,
103
+ message: `Insecure TLS/SSL version '${value}' detected in '${propName}' - use TLS 1.2 or 1.3 only`,
104
+ severity: "error",
105
+ line: propAssignment.getStartLineNumber(),
106
+ column:
107
+ propAssignment.getStartLinePos() -
108
+ propAssignment.getStartLinePos(true) +
109
+ 1,
110
+ filePath: filePath,
111
+ });
112
+ break;
113
+ }
114
+ }
115
+ }
116
+ }
117
+ } catch (e) {
118
+ // Skip problematic objects
119
+ }
120
+ }
121
+
122
+ // Check standalone variable declarations (only for simple string assignments)
123
+ // This catches cases like: const tlsVersion = "TLSv1"
124
+ const varDecls = sourceFile.getDescendantsOfKind(
125
+ SyntaxKind.VariableDeclaration
126
+ );
127
+
128
+ for (const varDecl of varDecls) {
129
+ try {
130
+ const init = varDecl.getInitializer();
131
+ if (!init) continue;
132
+
133
+ const kind = init.getKind();
134
+
135
+ // Only check simple string literals or template literals (not objects/calls)
136
+ // This prevents duplicate detection with ObjectLiteral properties
137
+ if (
138
+ kind !== SyntaxKind.StringLiteral &&
139
+ kind !== SyntaxKind.NoSubstitutionTemplateLiteral &&
140
+ kind !== SyntaxKind.TemplateExpression
141
+ ) {
142
+ continue; // Skip complex expressions
143
+ }
144
+
145
+ let value = "";
146
+ if (kind === SyntaxKind.StringLiteral) {
147
+ value = init.getLiteralValue();
148
+ } else if (kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
149
+ value = init.getLiteralText().slice(1, -1);
150
+ } else if (kind === SyntaxKind.TemplateExpression) {
151
+ // For template expressions, get the static parts
152
+ value = init.getText();
153
+ } else {
154
+ value = init.getText().replace(/['"]/g, "");
155
+ }
156
+
157
+ // Check for insecure versions in variable values
158
+ for (const insecurePattern of this.insecureVersions) {
159
+ if (insecurePattern.test(value)) {
160
+ violations.push({
161
+ ruleId: this.ruleId,
162
+ message: `Insecure TLS/SSL version '${value}' assigned to variable - use TLS 1.2 or 1.3 only`,
163
+ severity: "error",
164
+ line: varDecl.getStartLineNumber(),
165
+ column:
166
+ varDecl.getStartLinePos() - varDecl.getStartLinePos(true) + 1,
167
+ filePath: filePath,
168
+ });
169
+ break;
170
+ }
171
+ }
172
+ } catch (e) {
173
+ // Skip problematic variables
174
+ }
175
+ }
176
+
177
+ // Note: CallExpression checking is not needed because getDescendantsOfKind
178
+ // for ObjectLiteralExpression already captures all objects including those
179
+ // passed as arguments to function calls (e.g., https.createServer({...}))
180
+ // This prevents duplicate violations.
181
+ } catch (err) {
182
+ console.warn(
183
+ `⚠ [S014] Symbol analysis failed for ${filePath}:`,
184
+ err.message
185
+ );
186
+ }
187
+
188
+ return violations;
189
+ }
190
+
191
+ cleanup() {}
192
+ }
193
+
194
+ module.exports = S014SymbolBasedAnalyzer;