@sun-asterisk/sunlint 1.2.2 → 1.3.1
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 +107 -1
- package/CONTRIBUTING.md +1654 -66
- package/README.md +19 -6
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- package/config/engines/engines-enhanced.json +86 -0
- package/config/engines/semantic-config.json +114 -0
- package/config/eslint-rule-mapping.json +50 -38
- 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 +23 -4
- package/config/rules/S027-categories.json +122 -0
- package/config/rules/enhanced-rules-registry.json +2564 -0
- package/config/rules/rules-registry-generated.json +785 -837
- package/config/rules/rules-registry.json +13 -1
- package/core/adapters/sunlint-rule-adapter.js +25 -30
- package/core/analysis-orchestrator.js +42 -2
- package/core/categories.js +52 -0
- package/core/category-constants.js +39 -0
- package/core/cli-action-handler.js +53 -32
- package/core/cli-program.js +11 -3
- package/core/config-manager.js +111 -0
- package/core/config-merger.js +88 -0
- package/core/constants/categories.js +168 -0
- package/core/constants/defaults.js +165 -0
- package/core/constants/engines.js +185 -0
- package/core/constants/index.js +30 -0
- package/core/constants/rules.js +215 -0
- package/core/enhanced-rules-registry.js +3 -3
- package/core/file-targeting-service.js +128 -7
- package/core/interfaces/rule-plugin.interface.js +207 -0
- package/core/plugin-manager.js +448 -0
- package/core/rule-selection-service.js +42 -15
- package/core/semantic-engine.js +658 -0
- package/core/semantic-rule-base.js +433 -0
- package/core/unified-rule-registry.js +484 -0
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/CONSTANTS-ARCHITECTURE.md +288 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/core/base-engine.js +249 -0
- package/engines/engine-factory.js +275 -0
- package/engines/eslint-engine.js +171 -19
- package/engines/heuristic-engine.js +569 -78
- package/integrations/eslint/plugin/index.js +26 -28
- package/origin-rules/common-en.md +8 -8
- package/package.json +10 -6
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +1 -1
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C029_catch_block_logging/analyzer.js +17 -5
- 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 +230 -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/C047_no_duplicate_retry_logic/c047-semantic-rule.js +278 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-analyzer-enhanced.js +968 -0
- package/rules/common/C047_no_duplicate_retry_logic/symbol-config.json +71 -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 +8 -0
- 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/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/scripts/category-manager.js +150 -0
- package/scripts/generate-rules-registry.js +88 -0
- package/scripts/migrate-rule-registry.js +157 -0
- package/scripts/prepare-release.sh +1 -1
- package/scripts/validate-system.js +48 -0
- package/.sunlint.json +0 -35
- package/config/README.md +0 -88
- package/config/engines/eslint-rule-mapping.json +0 -74
- package/config/schemas/sunlint-schema.json +0 -0
- package/config/testing/test-s005-working.ts +0 -22
- package/core/multi-rule-runner.js +0 -0
- 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/engines/tree-sitter-parser.js +0 -0
- package/engines/universal-ast-engine.js +0 -0
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- package/rules/common/C029_catch_block_logging/analyzer-backup.js +0 -426
- package/rules/common/C029_catch_block_logging/analyzer-fixed.js +0 -130
- package/rules/common/C029_catch_block_logging/analyzer-multi-tech.js +0 -487
- package/rules/common/C029_catch_block_logging/analyzer-simple.js +0 -110
- package/rules/common/C029_catch_block_logging/ast-analyzer-backup.js +0 -441
- package/rules/common/C029_catch_block_logging/ast-analyzer-new.js +0 -127
- package/rules/common/C029_catch_block_logging/ast-analyzer.js +0 -133
- package/rules/common/C029_catch_block_logging/cfg-analyzer.js +0 -408
- package/rules/common/C029_catch_block_logging/dataflow-analyzer.js +0 -454
- package/rules/common/C029_catch_block_logging/multi-language-ast-engine.js +0 -700
- package/rules/common/C029_catch_block_logging/pattern-learning-analyzer.js +0 -568
- package/rules/common/C029_catch_block_logging/semantic-analyzer.js +0 -459
|
@@ -22,7 +22,6 @@ const c043 = require("./rules/common/c043-no-console-or-print.js");
|
|
|
22
22
|
const c047 = require("./rules/common/c047-no-duplicate-retry-logic.js");
|
|
23
23
|
const c072 = require("./rules/common/c072-one-assert-per-test.js");
|
|
24
24
|
const c075 = require("./rules/common/c075-explicit-function-return-types.js");
|
|
25
|
-
const c076 = require("./rules/common/c076-single-behavior-per-test.js");
|
|
26
25
|
|
|
27
26
|
// 📘 TypeScript Rules (T-series)
|
|
28
27
|
const t002 = require("./rules/typescript/t002-interface-prefix-i.js");
|
|
@@ -85,33 +84,32 @@ const s058 = require("./rules/security/s058-no-ssrf.js");
|
|
|
85
84
|
|
|
86
85
|
module.exports = {
|
|
87
86
|
rules: {
|
|
88
|
-
"c002": c002,
|
|
89
|
-
"c003": c003,
|
|
90
|
-
"c006": c006,
|
|
91
|
-
"c010": c010,
|
|
92
|
-
"c013": c013,
|
|
93
|
-
"c014": c014,
|
|
94
|
-
"c017": c017,
|
|
95
|
-
"c018": c018,
|
|
96
|
-
"c030": c030,
|
|
97
|
-
"c035": c035,
|
|
98
|
-
"c023": c023,
|
|
99
|
-
"c029": c029,
|
|
100
|
-
"c041": c041,
|
|
101
|
-
"c042": c042,
|
|
102
|
-
"c043": c043,
|
|
103
|
-
"c047": c047,
|
|
104
|
-
"c072": c072,
|
|
105
|
-
"c075": c075,
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"t021": t021,
|
|
87
|
+
"c002-no-duplicate-code": c002,
|
|
88
|
+
"c003-no-vague-abbreviations": c003,
|
|
89
|
+
"c006-function-name-verb-noun": c006,
|
|
90
|
+
"c010-limit-block-nesting": c010,
|
|
91
|
+
"c013-no-dead-code": c013,
|
|
92
|
+
"c014-abstract-dependency-preferred": c014,
|
|
93
|
+
"c017-limit-constructor-logic": c017,
|
|
94
|
+
"c018-no-generic-throw": c018,
|
|
95
|
+
"c030-use-custom-error-classes": c030,
|
|
96
|
+
"c035-no-empty-catch": c035,
|
|
97
|
+
"c023-no-duplicate-variable-name-in-scope": c023,
|
|
98
|
+
"c029-catch-block-logging": c029,
|
|
99
|
+
"c041-no-config-inline": c041,
|
|
100
|
+
"c042-boolean-name-prefix": c042,
|
|
101
|
+
"c043-no-console-or-print": c043,
|
|
102
|
+
"c047-no-duplicate-retry-logic": c047,
|
|
103
|
+
"c072-one-assert-per-test": c072,
|
|
104
|
+
"c075-explicit-function-return-types": c075,
|
|
105
|
+
"t002-interface-prefix-i": t002,
|
|
106
|
+
"t003-ts-ignore-reason": t003,
|
|
107
|
+
"t004-no-empty-type": t004,
|
|
108
|
+
"t007-no-fn-in-constructor": t007,
|
|
109
|
+
"t010-no-nested-union-tuple": t010,
|
|
110
|
+
"t019-no-this-assign": t019,
|
|
111
|
+
"t020-no-default-multi-export": t020,
|
|
112
|
+
"t021-limit-nested-generics": t021,
|
|
115
113
|
// Security rules
|
|
116
114
|
"typescript_s001": s001,
|
|
117
115
|
"typescript_s002": s002,
|
|
@@ -675,7 +675,7 @@
|
|
|
675
675
|
|
|
676
676
|
- **Objective**: Protect sensitive application data, avoid security risks, and comply with security standards. Exposing sensitive information can lead to serious security and privacy issues.
|
|
677
677
|
|
|
678
|
-
- **Details
|
|
678
|
+
- **Details**:
|
|
679
679
|
- Use environment variables or separate config files to store secrets
|
|
680
680
|
- Add secret files to `.gitignore` to prevent committing them
|
|
681
681
|
- Use secret management tools such as Vault or AWS Secrets Manager
|
|
@@ -692,7 +692,7 @@
|
|
|
692
692
|
|
|
693
693
|
- **Objective**: Ensure clarity and readability by making boolean variables self-explanatory. This naming convention improves code maintainability and documentation.
|
|
694
694
|
|
|
695
|
-
- **Details
|
|
695
|
+
- **Details**:
|
|
696
696
|
- Use `is` for state attributes (e.g., `isActive`, `isEnabled`)
|
|
697
697
|
- Use `has` for ownership (e.g., `hasPermission`, `hasChildren`)
|
|
698
698
|
- Use `should` for decision flags (e.g., `shouldUpdate`, `shouldRetry`)
|
|
@@ -709,7 +709,7 @@
|
|
|
709
709
|
|
|
710
710
|
- **Objective**: Ensure logging is done in a controlled and effective manner in production. Using `print` or `console.log` can lead to performance issues, security risks, and log management difficulties.
|
|
711
711
|
|
|
712
|
-
- **Details
|
|
712
|
+
- **Details**:
|
|
713
713
|
- Use a dedicated logging framework instead of `print` or `console.log`
|
|
714
714
|
- Set appropriate log levels for each environment (debug, info, warn, error)
|
|
715
715
|
- Ensure logs contain useful metadata like timestamp, level, and context
|
|
@@ -726,7 +726,7 @@
|
|
|
726
726
|
|
|
727
727
|
- **Objective**: Leverage well-tested, optimized, and community-maintained libraries to reduce bugs and improve development efficiency.
|
|
728
728
|
|
|
729
|
-
- **Details
|
|
729
|
+
- **Details**:
|
|
730
730
|
- Prefer using standard language libraries
|
|
731
731
|
- Use trusted and popular community libraries
|
|
732
732
|
- Evaluate library compatibility and performance
|
|
@@ -743,7 +743,7 @@
|
|
|
743
743
|
|
|
744
744
|
- **Objective**: Ensure APIs return appropriate HTTP status codes so clients can handle errors effectively. HTTP 500 should be reserved for unexpected system errors.
|
|
745
745
|
|
|
746
|
-
- **Details
|
|
746
|
+
- **Details**:
|
|
747
747
|
- Use specific HTTP status codes based on error type:
|
|
748
748
|
- 400 for validation errors
|
|
749
749
|
- 401 for authentication failures
|
|
@@ -763,7 +763,7 @@
|
|
|
763
763
|
|
|
764
764
|
- **Objective**: Keep code readable, maintainable, and efficient by avoiding the use of overly complex regular expressions in business-critical logic.
|
|
765
765
|
|
|
766
|
-
- **Details
|
|
766
|
+
- **Details**:
|
|
767
767
|
- Move complex regex into constants or helper functions
|
|
768
768
|
- Prefer string manipulation libraries over complex regex
|
|
769
769
|
- Break down complex regex into simpler processing steps
|
|
@@ -781,7 +781,7 @@
|
|
|
781
781
|
|
|
782
782
|
- **Objective**: Centralize retry logic to improve consistency, maintainability, and observability of error handling and retry mechanisms.
|
|
783
783
|
|
|
784
|
-
- **Details
|
|
784
|
+
- **Details**:
|
|
785
785
|
- Create a dedicated utility class or service for retry logic
|
|
786
786
|
- Centralize retry policy configuration (retry count, delay, backoff)
|
|
787
787
|
- Use decorator pattern or AOP to apply retry logic
|
|
@@ -799,7 +799,7 @@
|
|
|
799
799
|
|
|
800
800
|
- **Objective**: Maintain a clear layered architecture, ensuring logic and data flow are well-structured and maintainable.
|
|
801
801
|
|
|
802
|
-
- **Details
|
|
802
|
+
- **Details**:
|
|
803
803
|
- Controllers should only call Services, not Repositories directly
|
|
804
804
|
- Services should only call Repositories, not Controllers
|
|
805
805
|
- Repositories should only handle data access, not call Services
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sunlint",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -40,17 +40,18 @@
|
|
|
40
40
|
"demo:file-targeting": "./demo-file-targeting.sh",
|
|
41
41
|
"lint": "node cli.js --config=.sunlint.json --input=.",
|
|
42
42
|
"lint:eslint-integration": "node cli.js --all --eslint-integration --input=.",
|
|
43
|
-
"build": "npm run copy-rules && echo 'Build completed with rules copy'",
|
|
43
|
+
"build": "npm run copy-rules && npm run generate-registry && echo 'Build completed with rules copy and registry generation'",
|
|
44
44
|
"copy-rules": "node scripts/copy-rules.js",
|
|
45
|
+
"generate-registry": "node scripts/generate-rules-registry.js",
|
|
45
46
|
"clean": "rm -rf coverage/ *.log reports/ *.tgz",
|
|
46
47
|
"postpack": "echo '📦 Package created successfully! Size: ' && ls -lh *.tgz | awk '{print $5}'",
|
|
47
48
|
"start": "node cli.js --help",
|
|
48
49
|
"version": "node cli.js --version",
|
|
49
|
-
"pack": "npm run copy-rules && npm pack",
|
|
50
|
+
"pack": "npm run copy-rules && npm run generate-registry && npm pack",
|
|
50
51
|
"publish:github": "npm publish --registry=https://npm.pkg.github.com",
|
|
51
52
|
"publish:npmjs": "npm publish --registry=https://registry.npmjs.org",
|
|
52
53
|
"publish:test": "npm publish --dry-run --registry=https://registry.npmjs.org",
|
|
53
|
-
"prepublishOnly": "npm run clean && npm run copy-rules"
|
|
54
|
+
"prepublishOnly": "npm run clean && npm run copy-rules && npm run generate-registry"
|
|
54
55
|
},
|
|
55
56
|
"keywords": [
|
|
56
57
|
"linting",
|
|
@@ -99,7 +100,8 @@
|
|
|
99
100
|
"espree": "^10.3.0",
|
|
100
101
|
"minimatch": "^10.0.3",
|
|
101
102
|
"node-fetch": "^3.3.2",
|
|
102
|
-
"table": "^6.8.2"
|
|
103
|
+
"table": "^6.8.2",
|
|
104
|
+
"ts-morph": "^26.0.0"
|
|
103
105
|
},
|
|
104
106
|
"peerDependencies": {
|
|
105
107
|
"typescript": ">=4.0.0"
|
|
@@ -126,8 +128,10 @@
|
|
|
126
128
|
},
|
|
127
129
|
"devDependencies": {
|
|
128
130
|
"@types/node": "^22.10.1",
|
|
131
|
+
"eslint-plugin-import": "^2.32.0",
|
|
129
132
|
"glob": "^11.0.3",
|
|
130
|
-
"jest": "^29.7.0"
|
|
133
|
+
"jest": "^29.7.0",
|
|
134
|
+
"typescript": "^5.8.3"
|
|
131
135
|
},
|
|
132
136
|
"bugs": {
|
|
133
137
|
"url": "https://github.com/sun-asterisk/engineer-excellence/issues"
|
|
@@ -295,7 +295,7 @@ class C003NoVagueAbbreviations {
|
|
|
295
295
|
const violation = this.checkVariableName(variableName, content, lineNumber, match.index);
|
|
296
296
|
if (violation) {
|
|
297
297
|
violations.push({
|
|
298
|
-
|
|
298
|
+
ruleId: 'C003',
|
|
299
299
|
severity: 'warning',
|
|
300
300
|
message: violation.message,
|
|
301
301
|
line: lineNumber,
|
|
@@ -51,6 +51,13 @@ class C017Analyzer {
|
|
|
51
51
|
if (this.isConstructorStart(trimmedLine)) {
|
|
52
52
|
const constructorInfo = this.extractConstructorInfo(lines, index);
|
|
53
53
|
|
|
54
|
+
// Debug logging to understand boundary detection
|
|
55
|
+
if (config?.verbose) {
|
|
56
|
+
console.log(`[DEBUG] Constructor found at line ${lineNumber}`);
|
|
57
|
+
console.log(`[DEBUG] Constructor body lines: ${constructorInfo.body.length}`);
|
|
58
|
+
console.log(`[DEBUG] Constructor content:`, constructorInfo.body.map((l, i) => `${lineNumber + i}: ${l}`));
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
const complexLogic = this.findComplexLogic(constructorInfo.body);
|
|
55
62
|
|
|
56
63
|
complexLogic.forEach(logic => {
|
|
@@ -81,34 +88,94 @@ class C017Analyzer {
|
|
|
81
88
|
const constructorLines = [];
|
|
82
89
|
let braceDepth = 0;
|
|
83
90
|
let foundConstructorBrace = false;
|
|
91
|
+
let inConstructor = false;
|
|
92
|
+
let parenthesesDepth = 0;
|
|
93
|
+
let inCallback = false;
|
|
84
94
|
|
|
85
95
|
for (let i = startIndex; i < lines.length; i++) {
|
|
86
96
|
const line = lines[i];
|
|
87
|
-
|
|
97
|
+
const trimmedLine = line.trim();
|
|
88
98
|
|
|
89
99
|
// Check if this is the constructor line with opening brace
|
|
90
|
-
if (this.isConstructorStart(
|
|
91
|
-
|
|
100
|
+
if (this.isConstructorStart(trimmedLine)) {
|
|
101
|
+
inConstructor = true;
|
|
102
|
+
constructorLines.push(line);
|
|
103
|
+
|
|
104
|
+
// Special case: empty constructor () {}
|
|
105
|
+
if (trimmedLine.includes(') {}')) {
|
|
106
|
+
foundConstructorBrace = true;
|
|
107
|
+
braceDepth = 0; // Already closed
|
|
108
|
+
break; // Stop immediately for empty constructor
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Count braces and parentheses in the constructor line itself
|
|
92
112
|
for (const char of line) {
|
|
93
113
|
if (char === '{') {
|
|
94
114
|
foundConstructorBrace = true;
|
|
95
115
|
braceDepth = 1; // Start counting from 1 for the constructor block
|
|
116
|
+
} else if (char === '(') {
|
|
117
|
+
parenthesesDepth++;
|
|
118
|
+
} else if (char === ')') {
|
|
119
|
+
parenthesesDepth--;
|
|
96
120
|
}
|
|
97
121
|
}
|
|
98
|
-
} else if (foundConstructorBrace) {
|
|
99
|
-
|
|
122
|
+
} else if (inConstructor && foundConstructorBrace) {
|
|
123
|
+
constructorLines.push(line);
|
|
124
|
+
|
|
125
|
+
// Track callback functions and nested structures
|
|
100
126
|
for (const char of line) {
|
|
101
|
-
if (char === '
|
|
127
|
+
if (char === '(') {
|
|
128
|
+
parenthesesDepth++;
|
|
129
|
+
// Detect callback patterns: .use(, .then(, .catch(, etc.
|
|
130
|
+
if (line.includes('.use(') || line.includes('.then(') ||
|
|
131
|
+
line.includes('.catch(') || line.includes('.finally(') ||
|
|
132
|
+
line.includes('=>')) {
|
|
133
|
+
inCallback = true;
|
|
134
|
+
}
|
|
135
|
+
} else if (char === ')') {
|
|
136
|
+
parenthesesDepth--;
|
|
137
|
+
if (parenthesesDepth <= 0) {
|
|
138
|
+
inCallback = false;
|
|
139
|
+
}
|
|
140
|
+
} else if (char === '{') {
|
|
102
141
|
braceDepth++;
|
|
103
142
|
} else if (char === '}') {
|
|
104
143
|
braceDepth--;
|
|
105
144
|
}
|
|
106
145
|
}
|
|
107
146
|
|
|
108
|
-
// If we've closed all braces, we're done
|
|
109
|
-
if (braceDepth === 0) {
|
|
147
|
+
// If we've closed all braces and not in callback, we're done
|
|
148
|
+
if (braceDepth === 0 && !inCallback) {
|
|
110
149
|
break;
|
|
111
150
|
}
|
|
151
|
+
} else if (inConstructor) {
|
|
152
|
+
// Haven't found the opening brace yet, keep looking
|
|
153
|
+
constructorLines.push(line);
|
|
154
|
+
|
|
155
|
+
// Check for empty constructor pattern: ) {}
|
|
156
|
+
if (trimmedLine.includes(') {}')) {
|
|
157
|
+
foundConstructorBrace = true;
|
|
158
|
+
braceDepth = 0; // Already closed
|
|
159
|
+
break; // Stop immediately
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (const char of line) {
|
|
163
|
+
if (char === '{') {
|
|
164
|
+
foundConstructorBrace = true;
|
|
165
|
+
braceDepth = 1;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (const char of line) {
|
|
173
|
+
if (char === '{') {
|
|
174
|
+
foundConstructorBrace = true;
|
|
175
|
+
braceDepth = 1;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
112
179
|
}
|
|
113
180
|
}
|
|
114
181
|
|
|
@@ -120,6 +187,14 @@ class C017Analyzer {
|
|
|
120
187
|
|
|
121
188
|
findComplexLogic(constructorBody) {
|
|
122
189
|
const complexLogic = [];
|
|
190
|
+
let inCallbackFunction = false;
|
|
191
|
+
let callbackDepth = 0;
|
|
192
|
+
|
|
193
|
+
// Check if this is an empty constructor first
|
|
194
|
+
// TEMPORARILY DISABLED for debugging
|
|
195
|
+
// if (this.isEmptyConstructor(constructorBody)) {
|
|
196
|
+
// return complexLogic; // Return empty array - no violations for empty constructors
|
|
197
|
+
// }
|
|
123
198
|
|
|
124
199
|
constructorBody.forEach((line, index) => {
|
|
125
200
|
const trimmedLine = line.trim();
|
|
@@ -135,7 +210,27 @@ class C017Analyzer {
|
|
|
135
210
|
return;
|
|
136
211
|
}
|
|
137
212
|
|
|
138
|
-
//
|
|
213
|
+
// Track callback functions to avoid flagging their content
|
|
214
|
+
if (this.isCallbackStart(trimmedLine)) {
|
|
215
|
+
inCallbackFunction = true;
|
|
216
|
+
callbackDepth = 1;
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (inCallbackFunction) {
|
|
221
|
+
// Count braces to track callback boundaries
|
|
222
|
+
const openBraces = (trimmedLine.match(/\{/g) || []).length;
|
|
223
|
+
const closeBraces = (trimmedLine.match(/\}/g) || []).length;
|
|
224
|
+
callbackDepth += openBraces - closeBraces;
|
|
225
|
+
|
|
226
|
+
if (callbackDepth <= 0) {
|
|
227
|
+
inCallbackFunction = false;
|
|
228
|
+
callbackDepth = 0;
|
|
229
|
+
}
|
|
230
|
+
return; // Skip analysis inside callbacks
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Analyze line for complex logic patterns only if not in callback
|
|
139
234
|
const logicType = this.analyzeLineComplexity(trimmedLine);
|
|
140
235
|
|
|
141
236
|
if (logicType) {
|
|
@@ -153,6 +248,55 @@ class C017Analyzer {
|
|
|
153
248
|
return complexLogic;
|
|
154
249
|
}
|
|
155
250
|
|
|
251
|
+
isEmptyConstructor(constructorBody) {
|
|
252
|
+
// Check if constructor body contains only dependency injection parameters and closing brace
|
|
253
|
+
const meaningfulLines = constructorBody.filter(line => {
|
|
254
|
+
const trimmed = line.trim();
|
|
255
|
+
|
|
256
|
+
// Skip empty lines, comments, and constructor declaration
|
|
257
|
+
if (!trimmed ||
|
|
258
|
+
trimmed.startsWith('//') ||
|
|
259
|
+
trimmed.startsWith('/*') ||
|
|
260
|
+
trimmed.startsWith('*') ||
|
|
261
|
+
this.isConstructorStart(trimmed) ||
|
|
262
|
+
trimmed === '{' ||
|
|
263
|
+
trimmed === '}' ||
|
|
264
|
+
trimmed === ') {}') {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Skip constructor parameter declarations (NestJS style)
|
|
269
|
+
if (trimmed.startsWith('@') || // Decorators like @InjectRepository
|
|
270
|
+
trimmed.match(/^\s*(private|public|protected)\s+readonly\s+\w+/) || // DI parameters
|
|
271
|
+
trimmed.match(/^\s*(private|public|protected)\s+\w+/) || // Other parameters
|
|
272
|
+
trimmed.endsWith(',') || // Parameter continuation
|
|
273
|
+
trimmed.endsWith(') {')) { // Constructor parameter closing
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// This is a meaningful line (logic, assignments, calls, etc.)
|
|
278
|
+
return true;
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
return meaningfulLines.length === 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
isCallbackStart(line) {
|
|
285
|
+
// Patterns that indicate callback function starts
|
|
286
|
+
const callbackPatterns = [
|
|
287
|
+
/\.(use|then|catch|finally|map|filter|forEach|reduce)\s*\(/,
|
|
288
|
+
/=>\s*\{/,
|
|
289
|
+
/function\s*\(/,
|
|
290
|
+
/\(\s*\w+\s*\)\s*=>/,
|
|
291
|
+
/interceptors\.(request|response)\.use\(/,
|
|
292
|
+
/\.addEventListener\(/,
|
|
293
|
+
/setTimeout\(/,
|
|
294
|
+
/setInterval\(/
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
return callbackPatterns.some(pattern => pattern.test(line));
|
|
298
|
+
}
|
|
299
|
+
|
|
156
300
|
analyzeLineComplexity(line) {
|
|
157
301
|
// Simple property assignments are OK
|
|
158
302
|
if (this.isSimpleAssignment(line)) {
|
|
@@ -179,6 +323,16 @@ class C017Analyzer {
|
|
|
179
323
|
return null;
|
|
180
324
|
}
|
|
181
325
|
|
|
326
|
+
// Configuration setup is OK
|
|
327
|
+
if (this.isConfigurationSetup(line)) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Interceptor setup is OK (axios, etc.)
|
|
332
|
+
if (this.isInterceptorSetup(line)) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
|
|
182
336
|
// Detect complex logic patterns
|
|
183
337
|
const complexPatterns = [
|
|
184
338
|
{
|
|
@@ -209,7 +363,7 @@ class C017Analyzer {
|
|
|
209
363
|
pattern: /\w+\s*\(\s*[^)]*\)\s*[;{]/,
|
|
210
364
|
type: 'method_call',
|
|
211
365
|
description: 'complex method calls',
|
|
212
|
-
confidence: 0.
|
|
366
|
+
confidence: 0.4 // Reduced confidence to minimize false positives
|
|
213
367
|
},
|
|
214
368
|
{
|
|
215
369
|
pattern: /new\s+\w+\s*\([^)]*\)\s*\./,
|
|
@@ -238,16 +392,75 @@ class C017Analyzer {
|
|
|
238
392
|
}
|
|
239
393
|
|
|
240
394
|
isSimpleAssignment(line) {
|
|
241
|
-
// Patterns for simple assignments
|
|
395
|
+
// Patterns for simple assignments - expanded to catch more legitimate patterns
|
|
242
396
|
const simplePatterns = [
|
|
243
397
|
/^this\.\w+\s*=\s*[^;]+;?$/, // this.property = value;
|
|
244
398
|
/^this\.\w+\s*=\s*\w+;?$/, // this.property = parameter;
|
|
245
399
|
/^this\.\w+\s*=\s*(null|undefined|true|false|\d+|'[^']*'|"[^"]*");?$/, // this.property = literal;
|
|
400
|
+
/^this\.\w+\s*=\s*this\.\w+\s*\.\s*get\s*\(/, // this.property = this.configService.get(
|
|
401
|
+
/^this\.\w+\s*=\s*new\s+\w+\s*\(/, // this.property = new SomeClass(
|
|
402
|
+
/^this\.\w+\s*=\s*\w+\s*\.\s*\w+\s*\(/, // this.property = service.method(
|
|
403
|
+
// Enhanced patterns for configuration initialization
|
|
404
|
+
/^this\.\w+\s*=\s*new\s+\w+Client\s*\(/, // this.s3 = new S3Client(
|
|
405
|
+
/^this\.\w+\s*=\s*new\s+\w+\s*\(\s*\{/, // this.prop = new Class({ config })
|
|
406
|
+
/^this\.\w+\s*=\s*\w+\s*\.\s*create\s*\(/, // this.prop = Factory.create(
|
|
407
|
+
/^this\.\w+\s*=\s*\w+\s*\.\s*getInstance\s*\(/, // this.prop = Service.getInstance(
|
|
246
408
|
];
|
|
247
409
|
|
|
248
410
|
return simplePatterns.some(pattern => pattern.test(line));
|
|
249
411
|
}
|
|
250
412
|
|
|
413
|
+
isConfigurationSetup(line) {
|
|
414
|
+
// Check if this is configuration/options object setup
|
|
415
|
+
const configPatterns = [
|
|
416
|
+
/^\s*\w+:\s*/, // Property definition in object literal
|
|
417
|
+
/level:\s*/, // Log level setting
|
|
418
|
+
/timestamp:\s*/, // Timestamp configuration
|
|
419
|
+
/formatters:\s*/, // Formatter configuration
|
|
420
|
+
/mixin:\s*/, // Mixin configuration
|
|
421
|
+
/transport:\s*/, // Transport configuration
|
|
422
|
+
/options:\s*/, // Options configuration
|
|
423
|
+
/target:\s*/, // Target configuration
|
|
424
|
+
/baseURL:\s*/, // Axios baseURL
|
|
425
|
+
/timeout:\s*/, // Axios timeout
|
|
426
|
+
/region:\s*/, // AWS region configuration
|
|
427
|
+
/credentials:\s*/, // AWS credentials
|
|
428
|
+
/endpoint:\s*/, // API endpoint
|
|
429
|
+
/apiVersion:\s*/, // API version
|
|
430
|
+
/maxRetries:\s*/, // Retry configuration
|
|
431
|
+
/headers:\s*/, // HTTP headers
|
|
432
|
+
/defaultHeaders:\s*/, // Default headers
|
|
433
|
+
/interceptors:\s*/, // Request/response interceptors
|
|
434
|
+
/transformRequest:\s*/, // Request transformation
|
|
435
|
+
/transformResponse:\s*/, // Response transformation
|
|
436
|
+
/validateStatus:\s*/, // Status validation
|
|
437
|
+
/responseType:\s*/, // Response type
|
|
438
|
+
/maxContentLength:\s*/, // Content length limit
|
|
439
|
+
/ssl:\s*/, // SSL configuration
|
|
440
|
+
/auth:\s*/, // Authentication
|
|
441
|
+
/withCredentials:\s*/, // CORS credentials
|
|
442
|
+
/maxRedirects:\s*/, // Max redirects
|
|
443
|
+
];
|
|
444
|
+
|
|
445
|
+
return configPatterns.some(pattern => pattern.test(line));
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
isInterceptorSetup(line) {
|
|
449
|
+
// Check if this is axios interceptor setup (should be allowed in constructor)
|
|
450
|
+
const interceptorPatterns = [
|
|
451
|
+
/interceptors\.(request|response)\.use/,
|
|
452
|
+
/\.use\s*\(/,
|
|
453
|
+
/\.then\s*\(/,
|
|
454
|
+
/\.catch\s*\(/,
|
|
455
|
+
/\.finally\s*\(/,
|
|
456
|
+
/=>\s*\{?/,
|
|
457
|
+
/=>\s*[^{]/, // Arrow function without braces
|
|
458
|
+
/function\s*\(/
|
|
459
|
+
];
|
|
460
|
+
|
|
461
|
+
return interceptorPatterns.some(pattern => pattern.test(line));
|
|
462
|
+
}
|
|
463
|
+
|
|
251
464
|
isSuperCall(line) {
|
|
252
465
|
return line.includes('super(') || line.startsWith('super.');
|
|
253
466
|
}
|
|
@@ -279,7 +492,7 @@ class C017Analyzer {
|
|
|
279
492
|
}
|
|
280
493
|
|
|
281
494
|
isAllowedMethodCall(line) {
|
|
282
|
-
// Method calls that are typically OK in constructors
|
|
495
|
+
// Method calls that are typically OK in constructors - expanded list
|
|
283
496
|
const allowedMethods = [
|
|
284
497
|
'makeObservable',
|
|
285
498
|
'makeAutoObservable',
|
|
@@ -289,22 +502,46 @@ class C017Analyzer {
|
|
|
289
502
|
'Object.assign',
|
|
290
503
|
'Object.defineProperty',
|
|
291
504
|
'console.log',
|
|
292
|
-
'console.warn'
|
|
505
|
+
'console.warn',
|
|
506
|
+
// Common initialization patterns
|
|
507
|
+
'createLogger',
|
|
508
|
+
'initializeLogger',
|
|
509
|
+
'setupConfiguration',
|
|
510
|
+
'initializeService',
|
|
511
|
+
'configure',
|
|
512
|
+
'init',
|
|
513
|
+
'setup',
|
|
514
|
+
// Common service/config patterns
|
|
515
|
+
'get',
|
|
516
|
+
'getService',
|
|
517
|
+
'getInstance',
|
|
518
|
+
// Dependency injection patterns
|
|
519
|
+
'inject',
|
|
520
|
+
'resolve',
|
|
521
|
+
// Axios/HTTP setup patterns
|
|
522
|
+
'interceptors',
|
|
523
|
+
'use',
|
|
524
|
+
'defaults',
|
|
525
|
+
'timeout',
|
|
526
|
+
'baseURL',
|
|
527
|
+
'headers'
|
|
293
528
|
];
|
|
294
529
|
|
|
530
|
+
const lowerLine = line.toLowerCase();
|
|
295
531
|
return allowedMethods.some(method =>
|
|
296
|
-
|
|
532
|
+
lowerLine.includes(method.toLowerCase())
|
|
297
533
|
);
|
|
298
534
|
}
|
|
299
535
|
|
|
300
536
|
hasComplexExpression(line) {
|
|
301
537
|
// Check for complex expressions (multiple operators, complex calculations)
|
|
538
|
+
// Made more restrictive to reduce false positives
|
|
302
539
|
const complexityIndicators = [
|
|
303
540
|
/[+\-*/]\s*[+\-*/]/, // Multiple arithmetic operators
|
|
304
541
|
/\?\s*.*\s*:/, // Ternary operators
|
|
305
|
-
|
|
306
|
-
/\[[^\]]*\]/,
|
|
307
|
-
/\.[^.]*\./
|
|
542
|
+
/&&.*&&|\|\|.*\|\|/, // Multiple logical operators (more restrictive)
|
|
543
|
+
/\[[^\]]*\].*\[[^\]]*\]/, // Multiple array accesses
|
|
544
|
+
/\.[^.]*\.[^.]*\./ // Triple+ chained property access (more restrictive)
|
|
308
545
|
];
|
|
309
546
|
|
|
310
547
|
return complexityIndicators.some(pattern => pattern.test(line));
|