@sun-asterisk/sunlint 1.3.0 → 1.3.2
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 +115 -1
- package/CONTRIBUTING.md +249 -605
- package/README.md +3 -4
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- package/config/large-project.json +143 -0
- package/config/presets/all.json +0 -1
- package/config/release.json +70 -0
- package/config/rule-analysis-strategies.js +38 -3
- package/config/rules/enhanced-rules-registry.json +474 -1179
- package/config/rules/rules-registry-generated.json +3 -3
- package/core/cli-action-handler.js +24 -30
- package/core/cli-program.js +11 -3
- package/core/config-merger.js +29 -2
- package/core/enhanced-rules-registry.js +3 -2
- package/core/semantic-engine.js +129 -19
- package/core/semantic-rule-base.js +4 -2
- package/core/unified-rule-registry.js +1 -1
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/heuristic-engine.js +135 -16
- package/integrations/eslint/plugin/index.js +0 -2
- package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
- package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
- package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
- package/origin-rules/common-en.md +19 -15
- package/package.json +1 -1
- package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
- package/rules/common/C006_function_naming/analyzer.js +29 -3
- package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
- package/rules/common/C010_limit_block_nesting/config.json +64 -0
- package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
- package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
- package/rules/common/C013_no_dead_code/analyzer.js +75 -177
- package/rules/common/C013_no_dead_code/config.json +61 -0
- package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
- package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
- package/rules/common/C014_dependency_injection/analyzer.js +48 -313
- package/rules/common/C014_dependency_injection/config.json +26 -0
- package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
- package/rules/common/C018_no_throw_generic_error/config.json +50 -0
- package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
- package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
- package/rules/common/C019_log_level_usage/analyzer.js +110 -317
- package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
- package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
- package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
- package/rules/common/C023_no_duplicate_variable/config.json +50 -0
- package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
- package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
- package/rules/common/C033_separate_service_repository/README.md +78 -0
- package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
- package/rules/common/C033_separate_service_repository/config.json +50 -0
- package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
- package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
- package/rules/common/C035_error_logging_context/analyzer.js +232 -0
- package/rules/common/C035_error_logging_context/config.json +54 -0
- package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
- package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
- package/rules/common/C040_centralized_validation/analyzer.js +165 -0
- package/rules/common/C040_centralized_validation/config.json +46 -0
- package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
- package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
- package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
- package/rules/common/C076_explicit_function_types/README.md +30 -0
- package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
- package/rules/common/C076_explicit_function_types/config.json +15 -0
- package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
- package/rules/index.js +6 -1
- package/rules/parser/rule-parser.js +13 -2
- package/rules/security/S005_no_origin_auth/README.md +226 -0
- package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
- package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
- package/rules/security/S005_no_origin_auth/config.json +85 -0
- package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
- package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
- package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
- package/rules/security/S007_no_plaintext_otp/README.md +198 -0
- package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
- package/rules/security/S007_no_plaintext_otp/config.json +79 -0
- package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
- package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
- package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
- package/rules/security/S009_no_insecure_encryption/README.md +158 -0
- package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
- package/rules/security/S009_no_insecure_encryption/config.json +55 -0
- package/rules/security/S010_no_insecure_encryption/README.md +224 -0
- package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
- package/rules/security/S010_no_insecure_encryption/config.json +48 -0
- package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
- package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
- package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
- package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
- package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
- package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
- package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
- package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
- package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
- package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
- package/rules/security/S055_content_type_validation/README.md +176 -0
- package/rules/security/S055_content_type_validation/analyzer.js +312 -0
- package/rules/security/S055_content_type_validation/config.json +48 -0
- package/rules/utils/rule-helpers.js +140 -1
- package/scripts/consolidate-config.js +116 -0
- package/scripts/prepare-release.sh +1 -1
- package/config/rules/rules-registry.json +0 -765
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
- package/docs/FUTURE_PACKAGES.md +0 -83
- package/docs/HEURISTIC_VS_AI.md +0 -113
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
- package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
- package/docs/RELEASE_GUIDE.md +0 -230
- package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# S005: No Origin Header Authentication
|
|
2
|
+
|
|
3
|
+
## Tổng quan
|
|
4
|
+
|
|
5
|
+
Rule S005 phát hiện việc sử dụng Origin header cho authentication hoặc access control. Origin header có thể bị giả mạo (spoofed) và không an toàn để sử dụng làm cơ chế xác thực.
|
|
6
|
+
|
|
7
|
+
## Mô tả
|
|
8
|
+
|
|
9
|
+
Origin header trong HTTP request chỉ ra domain nào đã gửi request. Tuy nhiên, header này có thể dễ dàng bị giả mạo bởi attacker và không nên được sử dụng làm cơ chế authentication hoặc authorization.
|
|
10
|
+
|
|
11
|
+
## Tại sao quan trọng?
|
|
12
|
+
|
|
13
|
+
- **Bảo mật**: Origin header có thể bị spoofed, dẫn đến bypass authentication
|
|
14
|
+
- **Reliability**: Không phải tất cả browser đều gửi Origin header
|
|
15
|
+
- **Standards**: Không tuân thủ best practices về web security
|
|
16
|
+
|
|
17
|
+
## Các pattern được phát hiện
|
|
18
|
+
|
|
19
|
+
### ❌ Không hợp lệ
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
// 1. Sử dụng Origin header trực tiếp cho authentication
|
|
23
|
+
function authenticate(req, res, next) {
|
|
24
|
+
const origin = req.headers.origin;
|
|
25
|
+
if (origin === 'https://trusted.com') {
|
|
26
|
+
req.authenticated = true;
|
|
27
|
+
next();
|
|
28
|
+
} else {
|
|
29
|
+
res.status(401).json({ error: 'Unauthorized' });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2. Middleware sử dụng Origin cho access control
|
|
34
|
+
app.use('/api', (req, res, next) => {
|
|
35
|
+
if (req.get('origin') === 'https://admin.example.com') {
|
|
36
|
+
req.isAdmin = true;
|
|
37
|
+
}
|
|
38
|
+
next();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// 3. Conditional authentication dựa trên Origin
|
|
42
|
+
function checkAccess(req, res) {
|
|
43
|
+
if (req.headers.origin && req.headers.origin.includes('admin')) {
|
|
44
|
+
return res.json({ access: 'granted', token: generateToken() });
|
|
45
|
+
}
|
|
46
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 4. CORS configuration mixed với authentication
|
|
50
|
+
app.use(cors({
|
|
51
|
+
origin: function(origin, callback) {
|
|
52
|
+
if (adminOrigins.includes(origin)) {
|
|
53
|
+
callback(null, { credentials: true, authenticated: true });
|
|
54
|
+
} else {
|
|
55
|
+
callback(null, false);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}));
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### ✅ Hợp lệ
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
// 1. JWT token authentication
|
|
65
|
+
function authenticate(req, res, next) {
|
|
66
|
+
const token = req.headers.authorization;
|
|
67
|
+
jwt.verify(token, secret, (err, decoded) => {
|
|
68
|
+
if (err) return res.status(401).json({ error: 'Invalid token' });
|
|
69
|
+
req.user = decoded;
|
|
70
|
+
next();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. Origin header chỉ để logging
|
|
75
|
+
function logRequest(req) {
|
|
76
|
+
console.log('Request from origin:', req.headers.origin);
|
|
77
|
+
logger.info(`Origin: ${req.get('origin')}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 3. CORS configuration đúng (không mixing với auth)
|
|
81
|
+
app.use(cors({
|
|
82
|
+
origin: ['https://example.com', 'https://app.example.com'],
|
|
83
|
+
credentials: true
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
// 4. Origin cho CORS preflight handling
|
|
87
|
+
function handlePreflight(req, res) {
|
|
88
|
+
const origin = req.headers.origin;
|
|
89
|
+
if (allowedOrigins.includes(origin)) {
|
|
90
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
91
|
+
}
|
|
92
|
+
res.end();
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Các loại vi phạm
|
|
97
|
+
|
|
98
|
+
| Type | Severity | Mô tả |
|
|
99
|
+
|------|----------|--------|
|
|
100
|
+
| `origin_header_auth` | error | Trực tiếp sử dụng req.headers.origin cho authentication |
|
|
101
|
+
| `origin_header_method` | error | Sử dụng req.get('origin') cho authentication |
|
|
102
|
+
| `conditional_origin_auth` | error | Conditional logic dựa trên Origin cho authentication |
|
|
103
|
+
| `middleware_origin_auth` | error | Middleware authentication dựa trên Origin |
|
|
104
|
+
| `cors_origin_auth` | warning | CORS configuration mixing với authentication |
|
|
105
|
+
| `express_origin_auth` | error | Express routes sử dụng Origin cho authentication |
|
|
106
|
+
|
|
107
|
+
## Cách khắc phục
|
|
108
|
+
|
|
109
|
+
### 1. Sử dụng JWT Tokens
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
// Thay vì
|
|
113
|
+
if (req.headers.origin === 'trusted.com') {
|
|
114
|
+
req.authenticated = true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Sử dụng
|
|
118
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
119
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
120
|
+
req.user = decoded;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 2. Session-based Authentication
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
// Thay vì
|
|
127
|
+
if (req.get('origin') === 'admin.com') {
|
|
128
|
+
req.isAdmin = true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Sử dụng
|
|
132
|
+
if (req.session && req.session.user && req.session.user.role === 'admin') {
|
|
133
|
+
req.isAdmin = true;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 3. API Key Authentication
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
// Thay vì
|
|
141
|
+
const origin = req.headers.origin;
|
|
142
|
+
if (trustedOrigins.includes(origin)) {
|
|
143
|
+
next();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Sử dụng
|
|
147
|
+
const apiKey = req.headers['x-api-key'];
|
|
148
|
+
if (validateApiKey(apiKey)) {
|
|
149
|
+
next();
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 4. Proper CORS Configuration
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
// Đúng: CORS không làm authentication
|
|
157
|
+
app.use(cors({
|
|
158
|
+
origin: function(origin, callback) {
|
|
159
|
+
if (!origin || allowedOrigins.includes(origin)) {
|
|
160
|
+
callback(null, true);
|
|
161
|
+
} else {
|
|
162
|
+
callback(new Error('Not allowed by CORS'));
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
credentials: true
|
|
166
|
+
}));
|
|
167
|
+
|
|
168
|
+
// Authentication riêng biệt
|
|
169
|
+
app.use('/api', authenticateToken);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Configuration
|
|
173
|
+
|
|
174
|
+
Rule có thể được cấu hình trong `config.json`:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"checkAuthContext": true,
|
|
179
|
+
"checkMiddleware": true,
|
|
180
|
+
"checkConditionals": true,
|
|
181
|
+
"checkCORSMixing": true,
|
|
182
|
+
"contextDepth": 3,
|
|
183
|
+
"ignoreComments": true
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Technology Stack
|
|
188
|
+
|
|
189
|
+
- **AST Analysis**: Babel parser với TypeScript/JavaScript support
|
|
190
|
+
- **Fallback**: Regex patterns cho edge cases
|
|
191
|
+
- **Accuracy**: 95% với AST, 85% với regex
|
|
192
|
+
- **Languages**: TypeScript, JavaScript
|
|
193
|
+
|
|
194
|
+
## Testing
|
|
195
|
+
|
|
196
|
+
Rule được test với:
|
|
197
|
+
- Valid code patterns (không có violations)
|
|
198
|
+
- Invalid authentication patterns (có violations)
|
|
199
|
+
- Edge cases và syntax errors
|
|
200
|
+
- Context detection scenarios
|
|
201
|
+
|
|
202
|
+
## Best Practices
|
|
203
|
+
|
|
204
|
+
1. **Luôn sử dụng proper authentication mechanisms**:
|
|
205
|
+
- JWT tokens
|
|
206
|
+
- Session-based auth
|
|
207
|
+
- API keys
|
|
208
|
+
- OAuth 2.0
|
|
209
|
+
|
|
210
|
+
2. **Tách biệt CORS và Authentication**:
|
|
211
|
+
- CORS chỉ để control resource sharing
|
|
212
|
+
- Authentication để verify identity
|
|
213
|
+
|
|
214
|
+
3. **Origin header chỉ dùng cho**:
|
|
215
|
+
- Logging và monitoring
|
|
216
|
+
- CORS preflight handling
|
|
217
|
+
- Analytics (không sensitive)
|
|
218
|
+
|
|
219
|
+
4. **Không bao giờ trust Origin header cho security decisions**
|
|
220
|
+
|
|
221
|
+
## Tài liệu tham khảo
|
|
222
|
+
|
|
223
|
+
- [OWASP: CORS Origin Header Scrutiny](https://owasp.org/www-community/vulnerabilities/CORS_OriginHeaderScrutiny)
|
|
224
|
+
- [MDN: Origin Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin)
|
|
225
|
+
- [Auth0: JWT Best Practices](https://auth0.com/docs/secure/tokens/json-web-tokens)
|
|
226
|
+
- [OWASP: Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)
|
|
@@ -0,0 +1,184 @@
|
|
|
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
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const S005ASTAnalyzer = require('./ast-analyzer');
|
|
8
|
+
|
|
9
|
+
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
|
+
}
|
|
16
|
+
|
|
17
|
+
async analyze(files, language, options = {}) {
|
|
18
|
+
const violations = [];
|
|
19
|
+
|
|
20
|
+
if (options.verbose) {
|
|
21
|
+
console.log(`🔍 Running S005 analysis on ${files.length} files...`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Use AST analysis as primary method
|
|
25
|
+
const astViolations = await this.astAnalyzer.analyze(files, language, options);
|
|
26
|
+
violations.push(...astViolations);
|
|
27
|
+
|
|
28
|
+
// Add regex-based patterns for edge cases AST might miss
|
|
29
|
+
for (const filePath of files) {
|
|
30
|
+
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
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (options.verbose && violations.length > 0) {
|
|
51
|
+
console.log(`📊 S005 found ${violations.length} violations`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return violations;
|
|
55
|
+
}
|
|
56
|
+
|
|
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
|
+
}
|
|
82
|
+
|
|
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
|
+
}
|
|
96
|
+
|
|
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
|
+
}
|
|
110
|
+
|
|
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
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
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'
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
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'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return violations;
|
|
155
|
+
}
|
|
156
|
+
|
|
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'
|
|
171
|
+
];
|
|
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
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = S005Analyzer;
|