@sun-asterisk/sunlint 1.3.7 โ 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.
- package/CHANGELOG.md +38 -0
- package/config/defaults/default.json +2 -1
- package/config/rule-analysis-strategies.js +20 -0
- package/config/rules/enhanced-rules-registry.json +190 -35
- package/core/file-targeting-service.js +83 -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/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 +436 -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 +287 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
## ๐งช **v1.3.8 - C065 Rule Enhancement & Advanced Context Analysis (October 1, 2025)**
|
|
6
|
+
|
|
7
|
+
**Release Date**: October 1, 2025
|
|
8
|
+
**Type**: Major Enhancement
|
|
9
|
+
**Branch**: `feature.sunlint.heuristic_rule_c065`
|
|
10
|
+
|
|
11
|
+
### โจ **Major Features**
|
|
12
|
+
- **ENHANCED**: C065 "One Behavior per Test" Rule with Advanced Context Analysis
|
|
13
|
+
- **New**: UI Workflow Detection for legitimate testing patterns
|
|
14
|
+
- **New**: UI Interaction Loop Detection (fireEvent iterations)
|
|
15
|
+
- **New**: Smart Control Flow Analysis with UI pattern exclusions
|
|
16
|
+
- **New**: Assertion context grouping for accurate behavior detection
|
|
17
|
+
- **ENHANCED**: File Targeting System with Smart Test Detection
|
|
18
|
+
- **Performance**: 98% file reduction (6873 โ 112 files) with `--include-tests`
|
|
19
|
+
- **Accuracy**: Intelligent project targeting and language filter override
|
|
20
|
+
- **ENHANCED**: Debug Output Management
|
|
21
|
+
- **Clean**: Production-ready output with conditional debug logging
|
|
22
|
+
- **Detailed**: Comprehensive debug info available with `--verbose` flag
|
|
23
|
+
|
|
24
|
+
### ๐ **Critical Bug Fixes**
|
|
25
|
+
- **FIXED**: C065 Rule Registry and Configuration
|
|
26
|
+
- **Issue**: Rule loading from incorrect `rules/quality/` path
|
|
27
|
+
- **Solution**: Updated to correct `rules/common/` path with proper categorization
|
|
28
|
+
- **FIXED**: False Positive Control Flow Detection
|
|
29
|
+
- **Issue**: UI testing loops incorrectly flagged as violations
|
|
30
|
+
- **Solution**: Pattern recognition for legitimate UI element iteration
|
|
31
|
+
- **FIXED**: Test File Language Filtering
|
|
32
|
+
- **Issue**: Test files excluded by language patterns
|
|
33
|
+
- **Solution**: Override exclusions for `--include-tests` flag
|
|
34
|
+
|
|
35
|
+
### ๐ฏ **Rule Accuracy Validation**
|
|
36
|
+
- **UI Loops**: `for (const checkbox of listCheckbox) { fireEvent.click(checkbox); }` โ
No false positive
|
|
37
|
+
- **Button Iteration**: `for (const button of buttons) { fireEvent.click(button); }` โ
No false positive
|
|
38
|
+
- **Business Logic**: Complex control flow with if/else statements โ Properly flagged
|
|
39
|
+
- **Multiple Behaviors**: Tests with multiple mock setups โ Properly flagged
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
5
43
|
## ๏ฟฝ **v1.3.7 - File Count Reporting & Performance Fixes (September 11, 2025)**
|
|
6
44
|
|
|
7
45
|
**Release Date**: September 11, 2025
|
|
@@ -41,6 +41,26 @@ module.exports = {
|
|
|
41
41
|
reason: 'JSON injection detection requires AST context analysis',
|
|
42
42
|
methods: ['ast', 'regex'],
|
|
43
43
|
accuracy: { ast: 95, regex: 60 }
|
|
44
|
+
},
|
|
45
|
+
'S054': {
|
|
46
|
+
reason: 'Default account detection in code, SQL, and config files',
|
|
47
|
+
methods: ['regex', 'ast'],
|
|
48
|
+
accuracy: { regex: 85, ast: 90 }
|
|
49
|
+
},
|
|
50
|
+
'S052': {
|
|
51
|
+
reason: 'OTP entropy analysis requires hybrid approach for RNG detection and context awareness',
|
|
52
|
+
methods: ['regex', 'ast'],
|
|
53
|
+
accuracy: { regex: 80, ast: 90 }
|
|
54
|
+
},
|
|
55
|
+
'S051': {
|
|
56
|
+
reason: 'Password length policy requires multi-signal context detection and cross-file validation',
|
|
57
|
+
methods: ['regex', 'ast'],
|
|
58
|
+
accuracy: { regex: 85, ast: 92 }
|
|
59
|
+
},
|
|
60
|
+
'C065': {
|
|
61
|
+
reason: 'Test behavior analysis requires hybrid heuristic + AST context for multiple assertions and control flow detection',
|
|
62
|
+
methods: ['regex', 'ast'],
|
|
63
|
+
accuracy: { regex: 85, ast: 92 }
|
|
44
64
|
}
|
|
45
65
|
},
|
|
46
66
|
|
|
@@ -736,7 +736,13 @@
|
|
|
736
736
|
"config": "./rules/security/S025_server_side_validation/config.json",
|
|
737
737
|
"version": "1.0.0",
|
|
738
738
|
"status": "stable",
|
|
739
|
-
"tags": [
|
|
739
|
+
"tags": [
|
|
740
|
+
"security",
|
|
741
|
+
"validation",
|
|
742
|
+
"server-side",
|
|
743
|
+
"owasp",
|
|
744
|
+
"input-validation"
|
|
745
|
+
],
|
|
740
746
|
"strategy": {
|
|
741
747
|
"preferred": "ast",
|
|
742
748
|
"fallbacks": ["ast", "regex"],
|
|
@@ -927,40 +933,71 @@
|
|
|
927
933
|
"tags": ["security", "file-inclusion", "path-traversal"]
|
|
928
934
|
},
|
|
929
935
|
"S037": {
|
|
930
|
-
"name": "
|
|
931
|
-
"description": "
|
|
936
|
+
"name": "Configure comprehensive cache headers to prevent sensitive data leakage",
|
|
937
|
+
"description": "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.",
|
|
932
938
|
"category": "security",
|
|
933
939
|
"severity": "warning",
|
|
934
940
|
"languages": ["typescript", "javascript"],
|
|
935
|
-
"analyzer": "
|
|
936
|
-
"
|
|
941
|
+
"analyzer": "./rules/security/S037_cache_headers/analyzer.js",
|
|
942
|
+
"config": "./rules/security/S037_cache_headers/config.json",
|
|
937
943
|
"version": "1.0.0",
|
|
938
|
-
"status": "
|
|
939
|
-
"tags": ["security", "caching", "headers"]
|
|
944
|
+
"status": "experimental",
|
|
945
|
+
"tags": ["security", "caching", "headers", "privacy"],
|
|
946
|
+
"strategy": {
|
|
947
|
+
"preferred": "ast",
|
|
948
|
+
"fallbacks": ["ast", "regex"],
|
|
949
|
+
"accuracy": { "ast": 90, "regex": 75 }
|
|
950
|
+
},
|
|
951
|
+
"engineMappings": {
|
|
952
|
+
"heuristic": ["rules/security/S037_cache_headers/analyzer.js"]
|
|
953
|
+
}
|
|
940
954
|
},
|
|
941
955
|
"S038": {
|
|
942
|
-
"name": "
|
|
943
|
-
"description": "Prevent version information disclosure",
|
|
956
|
+
"name": "Do not expose version information in response headers",
|
|
957
|
+
"description": "Prevent exposure of server version information through response headers (Server, X-Powered-By, X-AspNet-Version, etc.) to reduce information disclosure and potential attack vectors.",
|
|
944
958
|
"category": "security",
|
|
945
959
|
"severity": "warning",
|
|
946
960
|
"languages": ["typescript", "javascript"],
|
|
947
|
-
"analyzer": "
|
|
948
|
-
"
|
|
961
|
+
"analyzer": "./rules/security/S038_no_version_headers/analyzer.js",
|
|
962
|
+
"config": "./rules/security/S038_no_version_headers/config.json",
|
|
949
963
|
"version": "1.0.0",
|
|
950
|
-
"status": "
|
|
951
|
-
"tags": ["security", "information-disclosure", "version"]
|
|
964
|
+
"status": "experimental",
|
|
965
|
+
"tags": ["security", "information-disclosure", "version", "headers"],
|
|
966
|
+
"strategy": {
|
|
967
|
+
"preferred": "ast",
|
|
968
|
+
"fallbacks": ["ast", "regex"],
|
|
969
|
+
"accuracy": { "ast": 90, "regex": 75 }
|
|
970
|
+
},
|
|
971
|
+
"engineMappings": {
|
|
972
|
+
"heuristic": ["rules/security/S038_no_version_headers/analyzer.js"]
|
|
973
|
+
}
|
|
952
974
|
},
|
|
953
975
|
"S039": {
|
|
954
|
-
"name": "
|
|
955
|
-
"description": "
|
|
976
|
+
"name": "Do not pass Session Tokens via URL parameters",
|
|
977
|
+
"description": "Detects when session tokens, authentication tokens, JWT tokens, or other sensitive authentication data are passed as URL parameters instead of secure headers or request body. URL parameters are logged in web server logs, browser history, and can be exposed in referrer headers.",
|
|
956
978
|
"category": "security",
|
|
957
|
-
"severity": "
|
|
979
|
+
"severity": "warning",
|
|
958
980
|
"languages": ["typescript", "javascript"],
|
|
959
|
-
"analyzer": "
|
|
960
|
-
"
|
|
981
|
+
"analyzer": "./rules/security/S039_no_session_tokens_in_url/analyzer.js",
|
|
982
|
+
"config": "./rules/security/S039_no_session_tokens_in_url/config.json",
|
|
961
983
|
"version": "1.0.0",
|
|
962
|
-
"status": "
|
|
963
|
-
"tags": [
|
|
984
|
+
"status": "experimental",
|
|
985
|
+
"tags": [
|
|
986
|
+
"security",
|
|
987
|
+
"session-tokens",
|
|
988
|
+
"url-parameters",
|
|
989
|
+
"authentication"
|
|
990
|
+
],
|
|
991
|
+
"strategy": {
|
|
992
|
+
"preferred": "ast",
|
|
993
|
+
"fallbacks": ["ast", "regex"],
|
|
994
|
+
"accuracy": { "ast": 85, "regex": 70 }
|
|
995
|
+
},
|
|
996
|
+
"engineMappings": {
|
|
997
|
+
"heuristic": [
|
|
998
|
+
"rules/security/S039_no_session_tokens_in_url/analyzer.js"
|
|
999
|
+
]
|
|
1000
|
+
}
|
|
964
1001
|
},
|
|
965
1002
|
"S041": {
|
|
966
1003
|
"name": "Require Session Invalidate on Logout",
|
|
@@ -1058,6 +1095,36 @@
|
|
|
1058
1095
|
"status": "stable",
|
|
1059
1096
|
"tags": ["security", "password", "recovery"]
|
|
1060
1097
|
},
|
|
1098
|
+
"S049": {
|
|
1099
|
+
"name": "Authentication tokens should have short validity periods",
|
|
1100
|
+
"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.",
|
|
1101
|
+
"category": "security",
|
|
1102
|
+
"severity": "error",
|
|
1103
|
+
"languages": ["typescript", "javascript"],
|
|
1104
|
+
"analyzer": "./rules/security/S049_short_validity_tokens/analyzer.js",
|
|
1105
|
+
"config": "./rules/security/S049_short_validity_tokens/config.json",
|
|
1106
|
+
"version": "1.0.0",
|
|
1107
|
+
"status": "stable",
|
|
1108
|
+
"tags": [
|
|
1109
|
+
"security",
|
|
1110
|
+
"authentication",
|
|
1111
|
+
"tokens",
|
|
1112
|
+
"jwt",
|
|
1113
|
+
"session",
|
|
1114
|
+
"owasp"
|
|
1115
|
+
],
|
|
1116
|
+
"strategy": {
|
|
1117
|
+
"preferred": "ast",
|
|
1118
|
+
"fallbacks": ["ast", "regex"],
|
|
1119
|
+
"accuracy": {
|
|
1120
|
+
"ast": 90,
|
|
1121
|
+
"regex": 75
|
|
1122
|
+
}
|
|
1123
|
+
},
|
|
1124
|
+
"engineMappings": {
|
|
1125
|
+
"heuristic": ["rules/security/S049_short_validity_tokens/analyzer.js"]
|
|
1126
|
+
}
|
|
1127
|
+
},
|
|
1061
1128
|
"S050": {
|
|
1062
1129
|
"name": "Session Token Weak Hash",
|
|
1063
1130
|
"description": "Prevent weak hashing for session tokens",
|
|
@@ -1070,29 +1137,71 @@
|
|
|
1070
1137
|
"status": "stable",
|
|
1071
1138
|
"tags": ["security", "session", "hashing"]
|
|
1072
1139
|
},
|
|
1140
|
+
"S051": {
|
|
1141
|
+
"name": "Password length policy enforcement (12-64 chars recommended, reject >128)",
|
|
1142
|
+
"description": "Enforce strong password length policies with multi-signal detection. Prevent weak validators, missing limits, and FE/BE mismatches.",
|
|
1143
|
+
"category": "security",
|
|
1144
|
+
"severity": "error",
|
|
1145
|
+
"languages": ["typescript", "javascript"],
|
|
1146
|
+
"analyzer": "./rules/security/S051_password_length_policy/analyzer.js",
|
|
1147
|
+
"config": "./rules/security/S051_password_length_policy/config.json",
|
|
1148
|
+
"eslintRule": "custom/typescript_s051",
|
|
1149
|
+
"version": "1.0.0",
|
|
1150
|
+
"status": "stable",
|
|
1151
|
+
"tags": ["security", "password", "validation", "length", "policy"],
|
|
1152
|
+
"engineMappings": {
|
|
1153
|
+
"eslint": ["custom/typescript_s051"],
|
|
1154
|
+
"heuristic": ["./rules/security/S051_password_length_policy/analyzer.js"]
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
"C065": {
|
|
1158
|
+
"name": "One Behavior per Test (AAA Pattern)",
|
|
1159
|
+
"description": "Enforce single behavior testing - each test should verify exactly one action/behavior with clear Arrange-Act-Assert structure",
|
|
1160
|
+
"category": "common",
|
|
1161
|
+
"severity": "warning",
|
|
1162
|
+
"languages": ["typescript", "javascript", "java", "csharp", "swift", "kotlin", "python"],
|
|
1163
|
+
"analyzer": "./rules/common/C065_one_behavior_per_test/analyzer.js",
|
|
1164
|
+
"config": "./rules/common/C065_one_behavior_per_test/config.json",
|
|
1165
|
+
"version": "1.0.0",
|
|
1166
|
+
"status": "stable",
|
|
1167
|
+
"tags": ["testing", "aaa", "behavior", "maintainability", "clarity"],
|
|
1168
|
+
"engineMappings": {
|
|
1169
|
+
"heuristic": ["./rules/common/C065_one_behavior_per_test/analyzer.js"]
|
|
1170
|
+
}
|
|
1171
|
+
},
|
|
1073
1172
|
"S052": {
|
|
1074
|
-
"name": "
|
|
1075
|
-
"description": "
|
|
1173
|
+
"name": "OTP must have โฅ20-bit entropy (โฅ6 digits) and use CSPRNG",
|
|
1174
|
+
"description": "Prevent guessable OTP by enforcing CSPRNG and minimal entropy. Ban non-crypto RNG and too-short codes.",
|
|
1076
1175
|
"category": "security",
|
|
1077
1176
|
"severity": "error",
|
|
1078
1177
|
"languages": ["typescript", "javascript"],
|
|
1079
|
-
"analyzer": "
|
|
1178
|
+
"analyzer": "./rules/security/S052_weak_otp_entropy/analyzer.js",
|
|
1179
|
+
"config": "./rules/security/S052_weak_otp_entropy/config.json",
|
|
1080
1180
|
"eslintRule": "custom/typescript_s052",
|
|
1081
1181
|
"version": "1.0.0",
|
|
1082
1182
|
"status": "stable",
|
|
1083
|
-
"tags": ["security", "
|
|
1183
|
+
"tags": ["security", "otp", "entropy", "csprng"],
|
|
1184
|
+
"engines": {
|
|
1185
|
+
"eslint": ["custom/typescript_s052"],
|
|
1186
|
+
"heuristic": ["./rules/security/S052_weak_otp_entropy/analyzer.js"]
|
|
1187
|
+
}
|
|
1084
1188
|
},
|
|
1085
1189
|
"S054": {
|
|
1086
|
-
"name": "
|
|
1087
|
-
"description": "
|
|
1190
|
+
"name": "Disallow Default/Built-in Accounts (admin/root/sa/...)",
|
|
1191
|
+
"description": "Prevent use of default or shared accounts. Enforce per-user identities, initial password change, and disabling well-known built-ins.",
|
|
1088
1192
|
"category": "security",
|
|
1089
1193
|
"severity": "error",
|
|
1090
|
-
"languages": ["typescript", "javascript"],
|
|
1091
|
-
"analyzer": "
|
|
1194
|
+
"languages": ["typescript", "javascript", "sql", "terraform", "yaml", "dockerfile", "all"],
|
|
1195
|
+
"analyzer": "./rules/security/S054_no_default_accounts/analyzer.js",
|
|
1196
|
+
"config": "./rules/security/S054_no_default_accounts/config.json",
|
|
1092
1197
|
"eslintRule": "custom/typescript_s054",
|
|
1093
1198
|
"version": "1.0.0",
|
|
1094
1199
|
"status": "stable",
|
|
1095
|
-
"tags": ["security", "accounts", "default"]
|
|
1200
|
+
"tags": ["security", "accounts", "default", "authentication", "authorization"],
|
|
1201
|
+
"engines": {
|
|
1202
|
+
"eslint": ["custom/typescript_s054"],
|
|
1203
|
+
"heuristic": ["./rules/security/S054_no_default_accounts/analyzer.js"]
|
|
1204
|
+
}
|
|
1096
1205
|
},
|
|
1097
1206
|
"S055": {
|
|
1098
1207
|
"name": "REST Content-Type Verification",
|
|
@@ -1106,6 +1215,31 @@
|
|
|
1106
1215
|
"status": "stable",
|
|
1107
1216
|
"tags": ["security", "rest", "content-type"]
|
|
1108
1217
|
},
|
|
1218
|
+
"S056": {
|
|
1219
|
+
"name": "Protect against Log Injection attacks",
|
|
1220
|
+
"description": "Protect against Log Injection attacks. Log injection occurs when user-controlled data is written to log files without proper sanitization, potentially allowing attackers to manipulate log entries, inject malicious content, or exploit log processing systems.",
|
|
1221
|
+
"category": "security",
|
|
1222
|
+
"severity": "error",
|
|
1223
|
+
"languages": ["typescript", "javascript"],
|
|
1224
|
+
"analyzer": "./rules/security/S056_log_injection_protection/analyzer.js",
|
|
1225
|
+
"config": "./rules/security/S056_log_injection_protection/config.json",
|
|
1226
|
+
"version": "1.0.0",
|
|
1227
|
+
"status": "stable",
|
|
1228
|
+
"tags": ["security", "logging", "injection", "owasp", "crlf"],
|
|
1229
|
+
"strategy": {
|
|
1230
|
+
"preferred": "ast",
|
|
1231
|
+
"fallbacks": ["ast", "regex"],
|
|
1232
|
+
"accuracy": {
|
|
1233
|
+
"ast": 95,
|
|
1234
|
+
"regex": 85
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
"engineMappings": {
|
|
1238
|
+
"heuristic": [
|
|
1239
|
+
"rules/security/S056_log_injection_protection/analyzer.js"
|
|
1240
|
+
]
|
|
1241
|
+
}
|
|
1242
|
+
},
|
|
1109
1243
|
"S057": {
|
|
1110
1244
|
"name": "Log with UTC Timestamps",
|
|
1111
1245
|
"description": "Ensure all logs use synchronized UTC time with ISO 8601/RFC3339 format to avoid timezone discrepancies across systems",
|
|
@@ -1353,7 +1487,13 @@
|
|
|
1353
1487
|
"config": "./rules/common/C067_no_hardcoded_config/config.json",
|
|
1354
1488
|
"version": "1.0.0",
|
|
1355
1489
|
"status": "stable",
|
|
1356
|
-
"tags": [
|
|
1490
|
+
"tags": [
|
|
1491
|
+
"configuration",
|
|
1492
|
+
"hardcode",
|
|
1493
|
+
"environment",
|
|
1494
|
+
"maintainability",
|
|
1495
|
+
"security"
|
|
1496
|
+
],
|
|
1357
1497
|
"strategy": {
|
|
1358
1498
|
"preferred": "ast",
|
|
1359
1499
|
"fallbacks": ["ast"],
|
|
@@ -1375,7 +1515,13 @@
|
|
|
1375
1515
|
"config": "../rules/common/C070_no_real_time_tests/config.json",
|
|
1376
1516
|
"version": "1.0.0",
|
|
1377
1517
|
"status": "stable",
|
|
1378
|
-
"tags": [
|
|
1518
|
+
"tags": [
|
|
1519
|
+
"testing",
|
|
1520
|
+
"flaky-tests",
|
|
1521
|
+
"timing",
|
|
1522
|
+
"fake-timers",
|
|
1523
|
+
"reliability"
|
|
1524
|
+
],
|
|
1379
1525
|
"strategy": {
|
|
1380
1526
|
"preferred": "ast",
|
|
1381
1527
|
"fallbacks": ["regex"],
|
|
@@ -1385,7 +1531,9 @@
|
|
|
1385
1531
|
}
|
|
1386
1532
|
},
|
|
1387
1533
|
"engineMappings": {
|
|
1388
|
-
"heuristic": [
|
|
1534
|
+
"heuristic": [
|
|
1535
|
+
"../rules/common/C070_no_real_time_tests/regex-analyzer.js"
|
|
1536
|
+
]
|
|
1389
1537
|
}
|
|
1390
1538
|
},
|
|
1391
1539
|
"C072": {
|
|
@@ -1419,8 +1567,12 @@
|
|
|
1419
1567
|
"status": "stable",
|
|
1420
1568
|
"tags": ["configuration", "validation", "startup", "fail-fast"],
|
|
1421
1569
|
"engineMappings": {
|
|
1422
|
-
"heuristic": [
|
|
1423
|
-
|
|
1570
|
+
"heuristic": [
|
|
1571
|
+
"rules/common/C073_validate_required_config_on_startup/analyzer.js"
|
|
1572
|
+
],
|
|
1573
|
+
"semantic": [
|
|
1574
|
+
"rules/common/C073_validate_required_config_on_startup/symbol-based-analyzer.js"
|
|
1575
|
+
]
|
|
1424
1576
|
},
|
|
1425
1577
|
"strategy": {
|
|
1426
1578
|
"preferred": "semantic",
|
|
@@ -1837,6 +1989,7 @@
|
|
|
1837
1989
|
"C047",
|
|
1838
1990
|
"C048",
|
|
1839
1991
|
"C052",
|
|
1992
|
+
"C065",
|
|
1840
1993
|
"C072",
|
|
1841
1994
|
"C073",
|
|
1842
1995
|
"C075",
|
|
@@ -1906,9 +2059,11 @@
|
|
|
1906
2059
|
"S047",
|
|
1907
2060
|
"S048",
|
|
1908
2061
|
"S050",
|
|
2062
|
+
"S051",
|
|
1909
2063
|
"S052",
|
|
1910
2064
|
"S054",
|
|
1911
2065
|
"S055",
|
|
2066
|
+
"S056",
|
|
1912
2067
|
"S057",
|
|
1913
2068
|
"S058"
|
|
1914
2069
|
],
|
|
@@ -2003,7 +2158,7 @@
|
|
|
2003
2158
|
"lastUpdated": "2025-08-25",
|
|
2004
2159
|
"totalRules": 98,
|
|
2005
2160
|
"qualityRules": 33,
|
|
2006
|
-
"securityRules":
|
|
2161
|
+
"securityRules": 51,
|
|
2007
2162
|
"stableRules": 45,
|
|
2008
2163
|
"experimentalRules": 1,
|
|
2009
2164
|
"supportedLanguages": 4,
|
|
@@ -16,7 +16,7 @@ class FileTargetingService {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Get target files based on enhanced configuration
|
|
19
|
-
* ENHANCED: Uses metadata for intelligent file targeting
|
|
19
|
+
* ENHANCED: Uses metadata for intelligent file targeting with smart project-level optimization
|
|
20
20
|
*/
|
|
21
21
|
async getTargetFiles(inputPaths, config, cliOptions = {}) {
|
|
22
22
|
try {
|
|
@@ -32,11 +32,14 @@ class FileTargetingService {
|
|
|
32
32
|
|
|
33
33
|
let allFiles = [];
|
|
34
34
|
|
|
35
|
+
// Smart project-level optimization
|
|
36
|
+
const optimizedPaths = this.optimizeProjectPaths(inputPaths, cliOptions);
|
|
37
|
+
|
|
35
38
|
// Use enhanced targeting based on metadata
|
|
36
39
|
if (metadata?.shouldBypassProjectDiscovery) {
|
|
37
|
-
allFiles = await this.collectTargetedFiles(
|
|
40
|
+
allFiles = await this.collectTargetedFiles(optimizedPaths, config, cliOptions);
|
|
38
41
|
} else {
|
|
39
|
-
allFiles = await this.collectProjectFiles(
|
|
42
|
+
allFiles = await this.collectProjectFiles(optimizedPaths, config, cliOptions);
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
// Apply filtering logic
|
|
@@ -150,13 +153,69 @@ class FileTargetingService {
|
|
|
150
153
|
return allFiles;
|
|
151
154
|
}
|
|
152
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Optimize project paths to focus on source and test directories
|
|
158
|
+
* Prevents unnecessary scanning of entire project when smart targeting is possible
|
|
159
|
+
*/
|
|
160
|
+
optimizeProjectPaths(inputPaths, cliOptions = {}) {
|
|
161
|
+
const optimizedPaths = [];
|
|
162
|
+
|
|
163
|
+
for (const inputPath of inputPaths) {
|
|
164
|
+
// If targeting entire project directory, try to find source/test subdirectories
|
|
165
|
+
if (fs.existsSync(inputPath) && fs.statSync(inputPath).isDirectory()) {
|
|
166
|
+
const projectOptimization = this.findProjectSourceDirs(inputPath, cliOptions);
|
|
167
|
+
if (projectOptimization.length > 0) {
|
|
168
|
+
if (cliOptions.verbose) {
|
|
169
|
+
console.log(chalk.blue(`๐ฏ Smart targeting: Found ${projectOptimization.length} source directories in ${path.basename(inputPath)}`));
|
|
170
|
+
}
|
|
171
|
+
optimizedPaths.push(...projectOptimization);
|
|
172
|
+
} else {
|
|
173
|
+
optimizedPaths.push(inputPath);
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
optimizedPaths.push(inputPath);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return optimizedPaths;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Find source directories in project to avoid scanning entire project
|
|
185
|
+
*/
|
|
186
|
+
findProjectSourceDirs(projectPath, cliOptions = {}) {
|
|
187
|
+
const sourceDirs = [];
|
|
188
|
+
const candidateDirs = ['src', 'lib', 'app', 'packages'];
|
|
189
|
+
const testDirs = ['test', 'tests', '__tests__', 'spec', 'specs'];
|
|
190
|
+
|
|
191
|
+
// Always include test directories if --include-tests flag is used
|
|
192
|
+
const dirsToCheck = cliOptions.includeTests ? [...candidateDirs, ...testDirs] : candidateDirs;
|
|
193
|
+
|
|
194
|
+
for (const dir of dirsToCheck) {
|
|
195
|
+
const dirPath = path.join(projectPath, dir);
|
|
196
|
+
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
|
|
197
|
+
sourceDirs.push(dirPath);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return sourceDirs;
|
|
202
|
+
}
|
|
203
|
+
|
|
153
204
|
/**
|
|
154
205
|
* Check if directory should be skipped
|
|
206
|
+
* Enhanced with more comprehensive ignore patterns
|
|
207
|
+
* Enhanced with more comprehensive ignore patterns
|
|
155
208
|
*/
|
|
156
209
|
shouldSkipDirectory(dirName) {
|
|
157
210
|
const skipDirs = [
|
|
158
211
|
'node_modules', '.git', 'dist', 'build', 'coverage',
|
|
159
|
-
'.next', '.nuxt', 'vendor', 'target', 'generated'
|
|
212
|
+
'.next', '.nuxt', 'vendor', 'target', 'generated',
|
|
213
|
+
'.vscode', '.id',
|
|
214
|
+
'.vscode', '.idea', '.husky', '.github', '.yarn',
|
|
215
|
+
'out', 'public', 'static', 'assets', 'tmp', 'temp',
|
|
216
|
+
'cache', '.cache', 'logs', 'logea', '.husky', '.github', '.yarn',
|
|
217
|
+
'out', 'public', 'static', 'assets', 'tmp', 'temp',
|
|
218
|
+
'cache', '.cache', 'logs', 'log'
|
|
160
219
|
];
|
|
161
220
|
return skipDirs.includes(dirName);
|
|
162
221
|
}
|
|
@@ -278,10 +337,27 @@ class FileTargetingService {
|
|
|
278
337
|
if (debug) console.log(`๐ [DEBUG] After include patterns ${langConfig.include}: ${langFiles.length} files`);
|
|
279
338
|
}
|
|
280
339
|
|
|
281
|
-
// Apply language-specific exclude patterns
|
|
340
|
+
// Apply language-specific exclude patterns (but respect --include-tests for test files)
|
|
282
341
|
if (langConfig.exclude && langConfig.exclude.length > 0) {
|
|
283
|
-
|
|
284
|
-
|
|
342
|
+
// Skip test-related exclude patterns if --include-tests is enabled
|
|
343
|
+
let effectiveExcludes = langConfig.exclude;
|
|
344
|
+
if (cliOptions.includeTests === true) {
|
|
345
|
+
// Remove test-related patterns completely
|
|
346
|
+
effectiveExcludes = langConfig.exclude.filter(pattern => {
|
|
347
|
+
const isTestPattern = pattern.includes('.test.') || pattern.includes('.spec.') ||
|
|
348
|
+
pattern.includes('/test/') || pattern.includes('/tests/') ||
|
|
349
|
+
pattern.includes('/__tests__/');
|
|
350
|
+
return !isTestPattern;
|
|
351
|
+
});
|
|
352
|
+
if (debug) console.log(`๐ [DEBUG] --include-tests enabled, filtered excludes: ${effectiveExcludes}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (effectiveExcludes.length > 0) {
|
|
356
|
+
langFiles = this.applyExcludePatterns(langFiles, effectiveExcludes, debug);
|
|
357
|
+
if (debug) console.log(`๐ [DEBUG] After exclude patterns ${effectiveExcludes}: ${langFiles.length} files`);
|
|
358
|
+
} else {
|
|
359
|
+
if (debug) console.log(`๐ [DEBUG] All exclude patterns filtered out by --include-tests`);
|
|
360
|
+
}
|
|
285
361
|
}
|
|
286
362
|
|
|
287
363
|
languageFiles.push(...langFiles);
|
package/package.json
CHANGED