@sun-asterisk/sunlint 1.3.26 → 1.3.28

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 (69) hide show
  1. package/config/rules/enhanced-rules-registry.json +101 -17
  2. package/config/rules/rules-registry-generated.json +22 -22
  3. package/origin-rules/security-en.md +351 -338
  4. package/package.json +1 -1
  5. package/rules/common/C003_no_vague_abbreviations/analyzer.js +73 -21
  6. package/rules/common/C017_constructor_logic/symbol-based-analyzer.js +206 -2
  7. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +553 -58
  8. package/rules/common/C029_catch_block_logging/analyzer.js +47 -12
  9. package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +35 -15
  10. package/rules/common/C041_no_sensitive_hardcode/symbol-based-analyzer.js +9 -5
  11. package/rules/security/S003_open_redirect_protection/README.md +371 -0
  12. package/rules/security/S003_open_redirect_protection/analyzer.js +135 -0
  13. package/rules/security/S003_open_redirect_protection/config.json +58 -0
  14. package/rules/security/S003_open_redirect_protection/symbol-based-analyzer.js +884 -0
  15. package/rules/security/S004_sensitive_data_logging/analyzer.js +135 -0
  16. package/rules/security/S004_sensitive_data_logging/config.json +62 -0
  17. package/rules/security/S004_sensitive_data_logging/symbol-based-analyzer.js +592 -0
  18. package/rules/security/S005_no_origin_auth/analyzer.js +97 -148
  19. package/rules/security/S005_no_origin_auth/config.json +28 -67
  20. package/rules/security/S005_no_origin_auth/symbol-based-analyzer.js +708 -0
  21. package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +170 -31
  22. package/rules/security/S010_no_insecure_encryption/analyzer.js +8 -2
  23. package/rules/security/S012_hardcoded_secrets/analyzer.js +149 -0
  24. package/rules/security/S012_hardcoded_secrets/config.json +75 -0
  25. package/rules/security/S012_hardcoded_secrets/symbol-based-analyzer.js +1204 -0
  26. package/rules/security/S013_tls_enforcement/symbol-based-analyzer.js +87 -0
  27. package/rules/security/S017_use_parameterized_queries/analyzer.js +11 -78
  28. package/rules/security/S017_use_parameterized_queries/symbol-based-analyzer.js +1146 -1
  29. package/rules/security/S019_smtp_injection_protection/analyzer.js +120 -0
  30. package/rules/security/S019_smtp_injection_protection/config.json +35 -0
  31. package/rules/security/S019_smtp_injection_protection/symbol-based-analyzer.js +687 -0
  32. package/rules/security/S020_no_eval_dynamic_code/analyzer.js +55 -130
  33. package/rules/security/S020_no_eval_dynamic_code/symbol-based-analyzer.js +4 -19
  34. package/rules/security/S022_escape_output_context/README.md +254 -0
  35. package/rules/security/S022_escape_output_context/analyzer.js +510 -0
  36. package/rules/security/S022_escape_output_context/config.json +229 -0
  37. package/rules/security/S023_no_json_injection/analyzer.js +15 -0
  38. package/rules/security/S023_no_json_injection/ast-analyzer.js +18 -3
  39. package/rules/security/S023_no_json_injection/config.json +133 -0
  40. package/rules/security/S024_xpath_xxe_protection/regex-based-analyzer.js +41 -0
  41. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +67 -8
  42. package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +29 -6
  43. package/rules/security/S029_csrf_protection/config.json +127 -0
  44. package/rules/security/S030_directory_browsing_protection/regex-based-analyzer.js +160 -28
  45. package/rules/security/S030_directory_browsing_protection/symbol-based-analyzer.js +81 -19
  46. package/rules/security/S031_secure_session_cookies/analyzer.js +20 -2
  47. package/rules/security/S031_secure_session_cookies/regex-based-analyzer.js +100 -0
  48. package/rules/security/S031_secure_session_cookies/symbol-based-analyzer.js +8 -1
  49. package/rules/security/S032_httponly_session_cookies/analyzer.js +2 -2
  50. package/rules/security/S032_httponly_session_cookies/regex-based-analyzer.js +115 -0
  51. package/rules/security/S032_httponly_session_cookies/symbol-based-analyzer.js +39 -10
  52. package/rules/security/S036_lfi_rfi_protection/analyzer.js +224 -0
  53. package/rules/security/S036_lfi_rfi_protection/config.json +20 -0
  54. package/rules/security/S040_session_fixation_protection/analyzer.js +153 -0
  55. package/rules/security/S040_session_fixation_protection/config.json +20 -0
  56. package/rules/security/S042_require_re_authentication_for_long_lived/README.md +83 -0
  57. package/rules/security/S042_require_re_authentication_for_long_lived/analyzer.js +153 -0
  58. package/rules/security/S042_require_re_authentication_for_long_lived/config.json +41 -0
  59. package/rules/security/S042_require_re_authentication_for_long_lived/symbol-based-analyzer.js +1139 -0
  60. package/rules/security/S043_password_changes_invalidate_all_sessions/README.md +107 -0
  61. package/rules/security/S043_password_changes_invalidate_all_sessions/analyzer.js +153 -0
  62. package/rules/security/S043_password_changes_invalidate_all_sessions/config.json +41 -0
  63. package/rules/security/S043_password_changes_invalidate_all_sessions/symbol-based-analyzer.js +541 -0
  64. package/docs/COMMAND-EXAMPLES.md +0 -390
  65. package/docs/FILE_LIMITS_COMPLETION_REPORT.md +0 -151
  66. package/docs/FOLDER_STRUCTURE.md +0 -59
  67. package/docs/SIMPLIFIED_USAGE_GUIDE.md +0 -208
  68. package/rules/security/S017_use_parameterized_queries/regex-based-analyzer.js +0 -541
  69. package/rules/security/S020_no_eval_dynamic_code/regex-based-analyzer.js +0 -307
@@ -1,183 +1,132 @@
1
1
  /**
2
- * Hybrid analyzer for S005 - No Origin Header Authentication
3
- * Uses AST analysis with regex fallback for comprehensive coverage
4
- * Detects usage of Origin header for authentication/access control
2
+ * S005 - No Origin Header Authentication
3
+ *
4
+ * Main analyzer using symbol-based analysis to detect use of Origin header
5
+ * for authentication or authorization decisions.
6
+ *
7
+ * Based on:
8
+ * - OWASP A07:2021 - Identification and Authentication Failures
9
+ * - CWE-290: Authentication Bypass by Spoofing
5
10
  */
6
11
 
7
- const S005ASTAnalyzer = require('./ast-analyzer');
12
+ const S005SymbolBasedAnalyzer = require("./symbol-based-analyzer");
8
13
 
9
14
  class S005Analyzer {
10
- constructor() {
11
- this.ruleId = 'S005';
12
- this.ruleName = 'No Origin Header Authentication';
13
- this.description = 'Do not use Origin header for authentication or access control';
14
- this.astAnalyzer = new S005ASTAnalyzer();
15
+ constructor(options = {}) {
16
+ this.ruleId = "S005";
17
+ this.semanticEngine = options.semanticEngine || null;
18
+ this.verbose = options.verbose || false;
19
+
20
+ try {
21
+ this.symbolAnalyzer = new S005SymbolBasedAnalyzer(this.semanticEngine);
22
+ } catch (e) {
23
+ console.warn(`⚠ [S005] Failed to create symbol analyzer: ${e.message}`);
24
+ }
15
25
  }
16
26
 
17
- async analyze(files, language, options = {}) {
18
- const violations = [];
19
-
20
- if (options.verbose) {
21
- console.log(`🔍 Running S005 analysis on ${files.length} files...`);
27
+ async initialize(semanticEngine) {
28
+ this.semanticEngine = semanticEngine;
29
+ if (this.symbolAnalyzer && this.symbolAnalyzer.initialize) {
30
+ await this.symbolAnalyzer.initialize(semanticEngine);
22
31
  }
32
+ }
23
33
 
24
- // Use AST analysis as primary method
25
- const astViolations = await this.astAnalyzer.analyze(files, language, options);
26
- violations.push(...astViolations);
34
+ analyzeSingle(filePath, options = {}) {
35
+ return this.analyze([filePath], "typescript", options);
36
+ }
27
37
 
28
- // Add regex-based patterns for edge cases AST might miss
38
+ async analyze(files, language, options = {}) {
39
+ const violations = [];
29
40
  for (const filePath of files) {
30
41
  try {
31
- const content = require('fs').readFileSync(filePath, 'utf8');
32
- const regexViolations = this.analyzeWithRegexPatterns(content, filePath, options);
33
-
34
- // Filter out duplicates (same line, same type)
35
- const filteredRegexViolations = regexViolations.filter(regexViolation =>
36
- !astViolations.some(astViolation =>
37
- astViolation.line === regexViolation.line &&
38
- astViolation.filePath === regexViolation.filePath
39
- )
40
- );
41
-
42
- violations.push(...filteredRegexViolations);
43
- } catch (error) {
44
- if (options.verbose) {
45
- console.warn(`⚠️ S005 regex analysis failed for ${filePath}: ${error.message}`);
46
- }
42
+ const vs = await this.analyzeFile(filePath, options);
43
+ violations.push(...vs);
44
+ } catch (e) {
45
+ console.warn(`⚠ [S005] Analysis error for ${filePath}: ${e.message}`);
47
46
  }
48
47
  }
49
-
50
- if (options.verbose && violations.length > 0) {
51
- console.log(`📊 S005 found ${violations.length} violations`);
52
- }
53
-
54
48
  return violations;
55
49
  }
56
50
 
57
- analyzeWithRegexPatterns(content, filePath, options = {}) {
58
- const violations = [];
59
- const lines = content.split('\n');
60
-
61
- lines.forEach((line, index) => {
62
- const lineNumber = index + 1;
63
-
64
- // Pattern 1: Direct origin header access for authentication
65
- // req.headers.origin, req.get('origin'), req.header('origin')
66
- const originHeaderPattern = /(?:req\.headers\.origin|req\.get\s*\(\s*['"`]origin['"`]\s*\)|req\.header\s*\(\s*['"`]origin['"`]\s*\)|headers\[['"`]origin['"`]\])/i;
67
- if (originHeaderPattern.test(line)) {
68
- // Check if this line is used for authentication/authorization
69
- const authContextPattern = /(?:auth|login|verify|check|validate|permission|access|allow|deny|secure|token|session)/i;
70
- if (authContextPattern.test(line) || this.isInAuthContext(lines, index)) {
71
- violations.push({
72
- ruleId: this.ruleId,
73
- severity: 'error',
74
- message: 'Origin header should not be used for authentication. Origin can be spoofed and is not secure for access control.',
75
- line: lineNumber,
76
- column: line.search(originHeaderPattern) + 1,
77
- filePath: filePath,
78
- type: 'origin_header_auth'
79
- });
80
- }
81
- }
51
+ async analyzeFile(filePath, options = {}) {
52
+ const violationMap = new Map();
82
53
 
83
- // Pattern 2: Origin-based CORS validation for authentication
84
- const corsOriginAuthPattern = /(?:cors|origin).*(?:auth|login|permission|access|allow|token)/i;
85
- if (corsOriginAuthPattern.test(line) && !line.includes('//') && !line.includes('*')) {
86
- violations.push({
87
- ruleId: this.ruleId,
88
- severity: 'warning',
89
- message: 'CORS origin validation should not replace proper authentication mechanisms.',
90
- line: lineNumber,
91
- column: line.search(corsOriginAuthPattern) + 1,
92
- filePath: filePath,
93
- type: 'cors_origin_auth'
94
- });
95
- }
54
+ if (!this.symbolAnalyzer) {
55
+ return [];
56
+ }
96
57
 
97
- // Pattern 3: Origin header in conditional authentication logic
98
- const conditionalAuthPattern = /if\s*\([^)]*origin[^)]*\)\s*\{[^}]*(?:auth|login|token|permission|access)/i;
99
- if (conditionalAuthPattern.test(line)) {
100
- violations.push({
101
- ruleId: this.ruleId,
102
- severity: 'error',
103
- message: 'Conditional authentication based on Origin header is insecure. Use proper authentication tokens.',
104
- line: lineNumber,
105
- column: line.search(/origin/i) + 1,
106
- filePath: filePath,
107
- type: 'conditional_origin_auth'
108
- });
109
- }
58
+ // Skip test files, build directories, and node_modules
59
+ if (this.shouldSkipFile(filePath)) {
60
+ return [];
61
+ }
110
62
 
111
- // Pattern 4: Origin in middleware authentication
112
- const middlewarePattern = /(middleware|auth|guard).*origin.*(?:next\(\)|return|allow|permit)/i;
113
- if (middlewarePattern.test(line)) {
114
- violations.push({
115
- ruleId: this.ruleId,
116
- severity: 'error',
117
- message: 'Authentication middleware should not rely on Origin header. Use proper authentication mechanisms.',
118
- line: lineNumber,
119
- column: line.search(/origin/i) + 1,
120
- filePath: filePath,
121
- type: 'middleware_origin_auth'
122
- });
63
+ try {
64
+ let sourceFile = null;
65
+ if (this.semanticEngine?.project) {
66
+ sourceFile = this.semanticEngine.project.getSourceFile(filePath);
123
67
  }
124
68
 
125
- // Pattern 5: Origin header whitelisting for access control
126
- const whitelistPattern = /(?:whitelist|allowlist|allowed.*origins?).*(?:auth|access|permission)/i;
127
- if (whitelistPattern.test(line) && /origin/i.test(line)) {
128
- violations.push({
129
- ruleId: this.ruleId,
130
- severity: 'warning',
131
- message: 'Origin whitelisting should complement, not replace, proper authentication and authorization.',
132
- line: lineNumber,
133
- column: line.search(/origin/i) + 1,
134
- filePath: filePath,
135
- type: 'origin_whitelist_auth'
69
+ if (!sourceFile) {
70
+ // Create temporary ts-morph source file
71
+ const fs = require("fs");
72
+ const path = require("path");
73
+ const { Project } = require("ts-morph");
74
+ if (!fs.existsSync(filePath)) {
75
+ throw new Error(`File not found: ${filePath}`);
76
+ }
77
+ const content = fs.readFileSync(filePath, "utf8");
78
+ const tmp = new Project({
79
+ useInMemoryFileSystem: true,
80
+ compilerOptions: { allowJs: true },
136
81
  });
82
+ sourceFile = tmp.createSourceFile(path.basename(filePath), content);
137
83
  }
138
84
 
139
- // Pattern 6: Express.js specific patterns
140
- const expressPattern = /(?:app\.use|router\.).*origin.*(?:auth|protect|secure)/i;
141
- if (expressPattern.test(line)) {
142
- violations.push({
143
- ruleId: this.ruleId,
144
- severity: 'error',
145
- message: 'Express routes should not use Origin header for authentication or authorization.',
146
- line: lineNumber,
147
- column: line.search(/origin/i) + 1,
148
- filePath: filePath,
149
- type: 'express_origin_auth'
85
+ if (sourceFile) {
86
+ const symbolViolations = await this.symbolAnalyzer.analyze(
87
+ sourceFile,
88
+ filePath
89
+ );
90
+ symbolViolations.forEach((v) => {
91
+ const key = `${v.line}:${v.column}:${v.message}`;
92
+ if (!violationMap.has(key)) violationMap.set(key, v);
150
93
  });
151
94
  }
152
- });
95
+ } catch (e) {
96
+ console.warn(`⚠ [S005] Symbol analysis failed: ${e.message}`);
97
+ }
153
98
 
154
- return violations;
99
+ return Array.from(violationMap.values()).map((v) => ({
100
+ ...v,
101
+ filePath,
102
+ file: filePath,
103
+ }));
155
104
  }
156
105
 
157
- /**
158
- * Check if the current line is within an authentication context
159
- * by looking at surrounding lines
160
- */
161
- isInAuthContext(lines, currentIndex) {
162
- const contextRange = 3; // Check 3 lines before and after
163
- const startIndex = Math.max(0, currentIndex - contextRange);
164
- const endIndex = Math.min(lines.length - 1, currentIndex + contextRange);
165
-
166
- const authKeywords = [
167
- 'authenticate', 'authorize', 'login', 'logout', 'auth',
168
- 'permission', 'access', 'token', 'session', 'user',
169
- 'verify', 'validate', 'check', 'guard', 'protect',
170
- 'middleware', 'passport', 'jwt', 'bearer'
106
+ shouldSkipFile(filePath) {
107
+ const skipPatterns = [
108
+ "test/",
109
+ "tests/",
110
+ "__tests__/",
111
+ ".test.",
112
+ ".spec.",
113
+ "node_modules/",
114
+ "build/",
115
+ "dist/",
116
+ ".next/",
117
+ "coverage/",
118
+ "vendor/",
119
+ "mocks/",
120
+ ".mock.",
171
121
  ];
172
-
173
- for (let i = startIndex; i <= endIndex; i++) {
174
- const line = lines[i].toLowerCase();
175
- if (authKeywords.some(keyword => line.includes(keyword))) {
176
- return true;
177
- }
122
+
123
+ return skipPatterns.some((pattern) => filePath.includes(pattern));
124
+ }
125
+
126
+ cleanup() {
127
+ if (this.symbolAnalyzer?.cleanup) {
128
+ this.symbolAnalyzer.cleanup();
178
129
  }
179
-
180
- return false;
181
130
  }
182
131
  }
183
132
 
@@ -1,85 +1,46 @@
1
1
  {
2
2
  "ruleId": "S005",
3
3
  "name": "No Origin Header Authentication",
4
- "description": "Do not use Origin header for authentication or access control",
4
+ "description": "Prevent using Origin header for authentication or authorization decisions",
5
5
  "category": "security",
6
6
  "severity": "error",
7
7
  "languages": ["typescript", "javascript"],
8
- "version": "1.0.0",
9
- "status": "stable",
10
- "tags": ["security", "authentication", "headers", "origin", "access-control"],
11
-
8
+ "tags": ["security", "owasp", "authentication", "authorization", "spoofing", "headers"],
9
+ "enabled": true,
10
+ "fixable": false,
11
+ "engine": "heuristic",
12
+ "metadata": {
13
+ "owaspCategory": "A07:2021 - Identification and Authentication Failures",
14
+ "cweId": "CWE-290",
15
+ "description": "Origin header can be easily spoofed by attackers and should not be used for authentication or authorization decisions. Use verified tokens, sessions, or cryptographic signatures instead.",
16
+ "impact": "High - Authentication bypass, unauthorized access",
17
+ "likelihood": "Medium",
18
+ "remediation": "Use secure authentication methods: JWT tokens, session cookies, API keys with cryptographic signatures. Origin header should only be used for CORS/CSRF protection, not for access control."
19
+ },
12
20
  "patterns": {
13
21
  "vulnerable": [
14
- "req.headers.origin in authentication context",
15
- "req.get('origin') for access control",
16
- "Origin-based conditional authentication",
17
- "CORS origin configuration mixed with auth",
18
- "Express middleware using origin for security"
22
+ "if (req.headers.origin === 'trusted.com') { authenticate() }",
23
+ "const isAuthorized = allowedOrigins.includes(origin)",
24
+ "if (origin.includes('admin')) { grantAccess() }",
25
+ "switch(origin) { case 'internal': allow() }"
19
26
  ],
20
27
  "secure": [
21
- "JWT token authentication",
22
- "Session-based authentication",
23
- "API key authentication",
24
- "OAuth 2.0 flows",
25
- "Proper CORS configuration without auth reliance"
28
+ "Use for CORS: res.setHeader('Access-Control-Allow-Origin', origin)",
29
+ "Use for CSRF: if (allowedOrigins.includes(origin)) { /* CSRF check */ }",
30
+ "Use verified tokens: const user = await verifyJWT(req.headers.authorization)",
31
+ "Use sessions: const user = await getSessionUser(req.session.id)"
26
32
  ]
27
33
  },
28
-
29
- "configuration": {
30
- "checkAuthContext": true,
31
- "checkMiddleware": true,
32
- "checkConditionals": true,
33
- "checkCORSMixing": true,
34
- "contextDepth": 3,
35
- "ignoreComments": true
36
- },
37
-
38
34
  "examples": {
39
35
  "violations": [
40
- {
41
- "code": "if (req.headers.origin === 'trusted.com') { req.authenticated = true; }",
42
- "reason": "Using Origin header for authentication is insecure"
43
- },
44
- {
45
- "code": "const authMiddleware = (req, res, next) => { if (req.get('origin') === 'admin.com') next(); }",
46
- "reason": "Middleware should not rely on Origin header for access control"
47
- }
48
- ],
49
- "valid": [
50
- {
51
- "code": "const token = req.headers.authorization; jwt.verify(token, secret, callback);",
52
- "reason": "Proper JWT token authentication"
53
- },
54
- {
55
- "code": "console.log('Request from:', req.headers.origin);",
56
- "reason": "Using Origin header for logging only, not authentication"
57
- }
58
- ]
59
- },
60
-
61
- "remediation": {
62
- "recommendations": [
63
- "Use JWT tokens for authentication",
64
- "Implement session-based authentication",
65
- "Use API keys for service authentication",
66
- "Implement OAuth 2.0 for third-party authentication",
67
- "Use proper CORS configuration without relying on it for authentication"
36
+ "if (req.headers.origin === 'admin.example.com') { req.user = adminUser; }",
37
+ "const hasAccess = trustedOrigins.includes(req.get('origin'))",
38
+ "if (origin.endsWith('.internal.com')) { bypassAuth() }"
68
39
  ],
69
- "resources": [
70
- "https://owasp.org/www-community/vulnerabilities/CORS_OriginHeaderScrutiny",
71
- "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin",
72
- "https://auth0.com/docs/secure/tokens/json-web-tokens"
40
+ "fixes": [
41
+ "const user = await verifyToken(req.headers.authorization)",
42
+ "const session = await validateSession(req.cookies.sessionId)",
43
+ "const apiKey = await verifyApiKey(req.headers['x-api-key'])"
73
44
  ]
74
- },
75
-
76
- "performance": {
77
- "complexity": "O(n)",
78
- "accuracy": {
79
- "ast": 95,
80
- "regex": 85
81
- },
82
- "falsePositiveRate": "< 5%",
83
- "coverage": "High for TypeScript/JavaScript"
84
45
  }
85
46
  }