@sun-asterisk/sunlint 1.0.7 → 1.1.3
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/.sunlint.json +35 -0
- package/CHANGELOG.md +30 -3
- package/CONTRIBUTING.md +235 -0
- package/PROJECT_STRUCTURE.md +60 -0
- package/README.md +73 -52
- package/cli.js +1 -0
- package/config/README.md +88 -0
- package/config/defaults/ai-rules-context.json +231 -0
- package/config/engines/engines.json +49 -0
- package/config/engines/eslint-rule-mapping.json +74 -0
- package/config/eslint-rule-mapping.json +126 -0
- package/config/integrations/eslint/base.config.js +125 -0
- package/config/integrations/eslint/simple.config.js +24 -0
- package/config/presets/strict.json +0 -1
- package/config/rule-analysis-strategies.js +74 -0
- package/config/{rules-registry.json → rules/rules-registry.json} +22 -0
- package/core/analysis-orchestrator.js +383 -591
- package/core/ast-modules/README.md +103 -0
- package/core/ast-modules/base-parser.js +90 -0
- package/core/ast-modules/index.js +97 -0
- package/core/ast-modules/package.json +37 -0
- package/core/ast-modules/parsers/eslint-js-parser.js +147 -0
- package/core/ast-modules/parsers/eslint-ts-parser.js +106 -0
- package/core/ast-modules/parsers/javascript-parser.js +187 -0
- package/core/ast-modules/parsers/typescript-parser.js +187 -0
- package/core/cli-action-handler.js +271 -255
- package/core/cli-program.js +18 -4
- package/core/config-manager.js +9 -3
- package/core/config-merger.js +40 -1
- package/core/config-validator.js +2 -2
- package/core/enhanced-rules-registry.js +331 -0
- package/core/file-targeting-service.js +92 -23
- package/core/interfaces/analysis-engine.interface.js +100 -0
- package/core/multi-rule-runner.js +0 -221
- package/core/output-service.js +1 -1
- package/core/rule-mapping-service.js +1 -1
- package/core/rule-selection-service.js +10 -2
- package/docs/AI.md +163 -0
- package/docs/ARCHITECTURE.md +78 -0
- package/docs/CI-CD-GUIDE.md +315 -0
- package/docs/COMMAND-EXAMPLES.md +256 -0
- package/docs/CONFIGURATION.md +414 -0
- package/docs/DEBUG.md +86 -0
- package/docs/DEPLOYMENT-STRATEGIES.md +270 -0
- package/docs/DISTRIBUTION.md +153 -0
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +392 -0
- package/docs/ESLINT_INTEGRATION.md +238 -0
- package/docs/FOLDER_STRUCTURE.md +59 -0
- package/docs/HEURISTIC_VS_AI.md +113 -0
- package/docs/README.md +32 -0
- package/docs/RELEASE_GUIDE.md +230 -0
- package/engines/eslint-engine.js +601 -0
- package/engines/heuristic-engine.js +860 -0
- package/engines/openai-engine.js +374 -0
- package/engines/tree-sitter-parser.js +0 -0
- package/engines/universal-ast-engine.js +0 -0
- package/integrations/eslint/README.md +99 -0
- package/integrations/eslint/configs/.eslintrc.js +98 -0
- package/integrations/eslint/configs/eslint.config.js +133 -0
- package/integrations/eslint/configs/eslint.config.simple.js +24 -0
- package/integrations/eslint/package.json +23 -0
- package/integrations/eslint/plugin/index.js +164 -0
- package/integrations/eslint/plugin/package.json +13 -0
- package/integrations/eslint/plugin/rules/common/c002-no-duplicate-code.js +204 -0
- package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +246 -0
- package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +216 -0
- package/integrations/eslint/plugin/rules/common/c010-limit-block-nesting.js +90 -0
- package/integrations/eslint/plugin/rules/common/c013-no-dead-code.js +78 -0
- package/integrations/eslint/plugin/rules/common/c014-abstract-dependency-preferred.js +38 -0
- package/integrations/eslint/plugin/rules/common/c017-limit-constructor-logic.js +146 -0
- package/integrations/eslint/plugin/rules/common/c018-no-generic-throw.js +335 -0
- package/integrations/eslint/plugin/rules/common/c023-no-duplicate-variable-name-in-scope.js +142 -0
- package/integrations/eslint/plugin/rules/common/c029-catch-block-logging.js +115 -0
- package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +294 -0
- package/integrations/eslint/plugin/rules/common/c035-no-empty-catch.js +162 -0
- package/integrations/eslint/plugin/rules/common/c041-no-config-inline.js +122 -0
- package/integrations/eslint/plugin/rules/common/c042-boolean-name-prefix.js +406 -0
- package/integrations/eslint/plugin/rules/common/c043-no-console-or-print.js +300 -0
- package/integrations/eslint/plugin/rules/common/c047-no-duplicate-retry-logic.js +239 -0
- package/integrations/eslint/plugin/rules/common/c072-one-assert-per-test.js +184 -0
- package/integrations/eslint/plugin/rules/common/c075-explicit-function-return-types.js +168 -0
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +254 -0
- package/integrations/eslint/plugin/rules/security/s001-fail-securely.js +381 -0
- package/integrations/eslint/plugin/rules/security/s002-idor-check.js +945 -0
- package/integrations/eslint/plugin/rules/security/s003-no-unvalidated-redirect.js +86 -0
- package/integrations/eslint/plugin/rules/security/s007-no-plaintext-otp.js +74 -0
- package/integrations/eslint/plugin/rules/security/s013-verify-tls-connection.js +47 -0
- package/integrations/eslint/plugin/rules/security/s047-secure-random-passwords.js +108 -0
- package/integrations/eslint/plugin/rules/security/s055-verification-rest-check-the-incoming-content-type.js +143 -0
- package/integrations/eslint/plugin/rules/typescript/t002-interface-prefix-i.js +42 -0
- package/integrations/eslint/plugin/rules/typescript/t003-ts-ignore-reason.js +48 -0
- package/integrations/eslint/plugin/rules/typescript/t004-no-empty-type.js +95 -0
- package/integrations/eslint/plugin/rules/typescript/t007-no-fn-in-constructor.js +52 -0
- package/integrations/eslint/plugin/rules/typescript/t010-no-nested-union-tuple.js +48 -0
- package/integrations/eslint/plugin/rules/typescript/t019-no-this-assign.js +81 -0
- package/integrations/eslint/plugin/rules/typescript/t020-no-default-multi-export.js +127 -0
- package/integrations/eslint/plugin/rules/typescript/t021-limit-nested-generics.js +150 -0
- package/integrations/eslint/test-c041-rule.js +87 -0
- package/integrations/eslint/tsconfig.json +27 -0
- package/package.json +29 -16
- package/rules/README.md +252 -0
- package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
- package/rules/common/C002_no_duplicate_code/config.json +23 -0
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
- package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
- package/rules/{C006_function_naming → common/C006_function_naming}/analyzer.js +13 -2
- package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
- package/rules/common/C013_no_dead_code/analyzer.js +206 -0
- package/rules/common/C014_dependency_injection/analyzer.js +338 -0
- package/rules/common/C017_constructor_logic/analyzer.js +314 -0
- package/rules/{C019_log_level_usage → common/C019_log_level_usage}/analyzer.js +5 -2
- package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/analyzer.js +49 -15
- package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
- package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
- package/rules/common/C043_no_console_or_print/analyzer.js +304 -0
- package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +351 -0
- package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
- package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
- package/rules/docs/C002_no_duplicate_code.md +57 -0
- package/rules/index.js +149 -0
- package/rules/migration/converter.js +385 -0
- package/rules/migration/mapping.json +164 -0
- package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
- package/rules/security/S026_json_schema_validation/config.json +27 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +263 -0
- package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
- package/rules/security/S029_csrf_protection/analyzer.js +264 -0
- package/rules/tests/C002_no_duplicate_code.test.js +50 -0
- package/rules/universal/C010/generic.js +0 -0
- package/rules/universal/C010/tree-sitter-analyzer.js +0 -0
- package/rules/utils/ast-utils.js +191 -0
- package/rules/utils/base-analyzer.js +98 -0
- package/rules/utils/pattern-matchers.js +239 -0
- package/rules/utils/rule-helpers.js +264 -0
- package/rules/utils/severity-constants.js +93 -0
- package/scripts/build-release.sh +117 -0
- package/scripts/ci-report.js +179 -0
- package/scripts/install.sh +196 -0
- package/scripts/manual-release.sh +338 -0
- package/scripts/merge-reports.js +424 -0
- package/scripts/pre-release-test.sh +175 -0
- package/scripts/prepare-release.sh +202 -0
- package/scripts/setup-github-registry.sh +42 -0
- package/scripts/test-scripts/README.md +22 -0
- package/scripts/test-scripts/test-c041-comparison.js +114 -0
- package/scripts/test-scripts/test-c041-eslint.js +67 -0
- package/scripts/test-scripts/test-eslint-rules.js +146 -0
- package/scripts/test-scripts/test-real-world.js +44 -0
- package/scripts/test-scripts/test-rules-on-real-projects.js +86 -0
- package/scripts/trigger-release.sh +285 -0
- package/scripts/validate-rule-structure.js +148 -0
- package/scripts/verify-install.sh +82 -0
- package/config/sunlint-schema.json +0 -159
- package/config/typescript/custom-rules.js +0 -9
- package/config/typescript/package-lock.json +0 -1585
- package/config/typescript/package.json +0 -13
- package/config/typescript/security-rules/index.js +0 -90
- package/config/typescript/tsconfig.json +0 -29
- package/core/ai-analyzer.js +0 -169
- package/core/eslint-engine-service.js +0 -312
- package/core/eslint-instance-manager.js +0 -104
- package/core/eslint-integration-service.js +0 -363
- package/core/sunlint-engine-service.js +0 -23
- package/core/typescript-analyzer.js +0 -262
- package/core/typescript-engine.js +0 -313
- /package/config/{default.json → defaults/default.json} +0 -0
- /package/config/{typescript/eslint.config.js → integrations/eslint/typescript.config.js} +0 -0
- /package/config/{typescript/custom-rules-new.js → schemas/sunlint-schema.json} +0 -0
- /package/config/{typescript → testing}/test-s005-working.ts +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s005-no-origin-auth.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s006-activation-recovery-secret-not-plaintext.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s008-crypto-agility.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s009-no-insecure-crypto.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s010-no-insecure-random-in-sensitive-context.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s011-no-insecure-uuid.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s012-hardcode-secret.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s014-insecure-tls-version.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s015-insecure-tls-certificate.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s016-sensitive-query-parameter.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s017-no-sql-injection.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s018-positive-input-validation.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s019-no-raw-user-input-in-email.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s020-no-eval-dynamic-execution.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s022-output-encoding.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s023-no-json-injection.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s025-server-side-input-validation.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s026-json-schema-validation.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s027-no-hardcoded-secrets.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s029-require-csrf-protection.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s030-no-directory-browsing.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s033-require-samesite-cookie.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s034-require-host-cookie-prefix.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s035-cookie-specific-path.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s036-no-unsafe-file-include.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s037-require-anti-cache-headers.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s038-no-version-disclosure.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s039-no-session-token-in-url.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s041-require-session-invalidate-on-logout.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s042-require-periodic-reauthentication.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s043-terminate-sessions-on-password-change.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s044-require-full-session-for-sensitive-operations.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s045-anti-automation-controls.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s046-secure-notification-on-auth-change.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s048-password-credential-recovery.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s050-session-token-weak-hash.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s052-secure-random-authentication-code.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s054-verification-default-account.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s057-utc-logging.js +0 -0
- /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s058-no-ssrf.js +0 -0
- /package/rules/{C006_function_naming → common/C006_function_naming}/config.json +0 -0
- /package/rules/{C019_log_level_usage → common/C019_log_level_usage}/config.json +0 -0
- /package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/config.json +0 -0
- /package/rules/{C031_validation_separation → common/C031_validation_separation}/analyzer.js +0 -0
- /package/rules/{C031_validation_separation/README.md → docs/C031_validation_separation.md} +0 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Analysis Engine Plugin
|
|
3
|
+
* Following Rule C005: Single responsibility - AI-powered analysis
|
|
4
|
+
* Following Rule C014: Dependency injection - implements interface
|
|
5
|
+
* Following Rule C015: Use domain language - clear AI analysis terms
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const AnalysisEngineInterface = require('../core/interfaces/analysis-engine.interface');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
|
|
11
|
+
class OpenAIEngine extends AnalysisEngineInterface {
|
|
12
|
+
constructor() {
|
|
13
|
+
super('openai', '1.0', ['typescript', 'javascript', 'dart', 'swift', 'kotlin', 'all']);
|
|
14
|
+
|
|
15
|
+
this.apiKey = null;
|
|
16
|
+
this.model = 'gpt-4o-mini';
|
|
17
|
+
this.provider = 'openai';
|
|
18
|
+
this.aiRulesContext = {};
|
|
19
|
+
this.supportedRulesList = [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Initialize OpenAI engine with configuration
|
|
24
|
+
* Following Rule C006: Verb-noun naming
|
|
25
|
+
* @param {Object} config - Engine configuration
|
|
26
|
+
*/
|
|
27
|
+
async initialize(config) {
|
|
28
|
+
try {
|
|
29
|
+
// Set up API configuration
|
|
30
|
+
this.apiKey = process.env.OPENAI_API_KEY || config.apiKey;
|
|
31
|
+
this.model = config.model || 'gpt-4o-mini';
|
|
32
|
+
this.provider = config.provider || 'openai';
|
|
33
|
+
|
|
34
|
+
if (!this.apiKey) {
|
|
35
|
+
throw new Error('OpenAI API key not configured. Set OPENAI_API_KEY environment variable.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Load AI rules context
|
|
39
|
+
this.loadAIRulesContext();
|
|
40
|
+
|
|
41
|
+
// Test connection
|
|
42
|
+
await this.testConnection();
|
|
43
|
+
|
|
44
|
+
this.initialized = true;
|
|
45
|
+
console.log(`🤖 OpenAI engine initialized with model ${this.model}`);
|
|
46
|
+
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('Failed to initialize OpenAI engine:', error.message);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Load AI rules context configuration
|
|
55
|
+
* Following Rule C006: Verb-noun naming
|
|
56
|
+
*/
|
|
57
|
+
loadAIRulesContext() {
|
|
58
|
+
try {
|
|
59
|
+
const contextPath = require('path').resolve(__dirname, '../config/defaults/ai-rules-context.json');
|
|
60
|
+
if (fs.existsSync(contextPath)) {
|
|
61
|
+
const contextData = require(contextPath);
|
|
62
|
+
this.aiRulesContext = contextData.rules || {};
|
|
63
|
+
this.supportedRulesList = Object.keys(this.aiRulesContext);
|
|
64
|
+
console.log(`📋 Loaded AI context for ${this.supportedRulesList.length} rules`);
|
|
65
|
+
} else {
|
|
66
|
+
console.warn('⚠️ AI rules context not found, using basic support');
|
|
67
|
+
// Fallback to basic rules that we know work
|
|
68
|
+
this.supportedRulesList = ['C006', 'C019', 'C029', 'C031'];
|
|
69
|
+
this.aiRulesContext = this.createBasicRulesContext();
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.warn('⚠️ Failed to load AI rules context:', error.message);
|
|
73
|
+
this.supportedRulesList = ['C019']; // Minimum fallback
|
|
74
|
+
this.aiRulesContext = this.createFallbackContext();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create basic rules context for common rules
|
|
80
|
+
* Following Rule C006: Verb-noun naming
|
|
81
|
+
* @returns {Object} Basic rules context
|
|
82
|
+
*/
|
|
83
|
+
createBasicRulesContext() {
|
|
84
|
+
return {
|
|
85
|
+
'C006': {
|
|
86
|
+
name: 'Function Naming Convention',
|
|
87
|
+
context: 'Analyze function naming patterns for verb-noun convention',
|
|
88
|
+
focus_areas: ['function declarations', 'method names', 'arrow functions'],
|
|
89
|
+
severity: 'warning'
|
|
90
|
+
},
|
|
91
|
+
'C019': {
|
|
92
|
+
name: 'Log Level Usage',
|
|
93
|
+
context: 'Analyze logging patterns for appropriate error levels',
|
|
94
|
+
focus_areas: ['console.error', 'logger.error', 'log levels'],
|
|
95
|
+
severity: 'warning'
|
|
96
|
+
},
|
|
97
|
+
'C029': {
|
|
98
|
+
name: 'Catch Block Error Logging',
|
|
99
|
+
context: 'Analyze error handling in catch blocks',
|
|
100
|
+
focus_areas: ['try-catch blocks', 'error logging', 'exception handling'],
|
|
101
|
+
severity: 'error'
|
|
102
|
+
},
|
|
103
|
+
'C031': {
|
|
104
|
+
name: 'Validation Logic Separation',
|
|
105
|
+
context: 'Analyze separation of validation logic from business logic',
|
|
106
|
+
focus_areas: ['validation functions', 'business logic', 'separation of concerns'],
|
|
107
|
+
severity: 'error'
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Create fallback context for minimal support
|
|
114
|
+
* Following Rule C006: Verb-noun naming
|
|
115
|
+
* @returns {Object} Fallback context
|
|
116
|
+
*/
|
|
117
|
+
createFallbackContext() {
|
|
118
|
+
return {
|
|
119
|
+
'C019': {
|
|
120
|
+
name: 'Log Level Usage',
|
|
121
|
+
context: 'Analyze logging patterns for appropriate error levels',
|
|
122
|
+
focus_areas: ['console.error', 'logger.error', 'log levels'],
|
|
123
|
+
severity: 'warning'
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Analyze files using OpenAI
|
|
130
|
+
* Following Rule C006: Verb-noun naming
|
|
131
|
+
* @param {string[]} files - Files to analyze
|
|
132
|
+
* @param {Object[]} rules - Rules to apply
|
|
133
|
+
* @param {Object} options - Analysis options
|
|
134
|
+
* @returns {Promise<Object>} Analysis results
|
|
135
|
+
*/
|
|
136
|
+
async analyze(files, rules, options) {
|
|
137
|
+
if (!this.initialized) {
|
|
138
|
+
throw new Error('OpenAI engine not initialized');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const results = {
|
|
142
|
+
results: [],
|
|
143
|
+
filesAnalyzed: files.length,
|
|
144
|
+
engine: 'openai',
|
|
145
|
+
metadata: {
|
|
146
|
+
model: this.model,
|
|
147
|
+
rulesAnalyzed: rules.map(r => r.id)
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
for (const filePath of files) {
|
|
152
|
+
try {
|
|
153
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
154
|
+
const fileViolations = [];
|
|
155
|
+
|
|
156
|
+
// Analyze each rule for this file
|
|
157
|
+
for (const rule of rules) {
|
|
158
|
+
if (!this.isRuleSupported(rule.id)) {
|
|
159
|
+
console.warn(`⚠️ Rule ${rule.id} not supported by OpenAI engine, skipping...`);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const ruleViolations = await this.analyzeRuleForFile(filePath, fileContent, rule);
|
|
164
|
+
fileViolations.push(...ruleViolations);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (fileViolations.length > 0) {
|
|
168
|
+
results.results.push({
|
|
169
|
+
file: filePath,
|
|
170
|
+
violations: fileViolations
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error(`❌ Failed to analyze ${filePath}:`, error.message);
|
|
176
|
+
// Continue with other files
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return results;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Analyze a specific rule for a file using AI
|
|
185
|
+
* Following Rule C006: Verb-noun naming
|
|
186
|
+
* @param {string} filePath - File path
|
|
187
|
+
* @param {string} content - File content
|
|
188
|
+
* @param {Object} rule - Rule to analyze
|
|
189
|
+
* @returns {Promise<Object[]>} Rule violations
|
|
190
|
+
*/
|
|
191
|
+
async analyzeRuleForFile(filePath, content, rule) {
|
|
192
|
+
const ruleContext = this.aiRulesContext[rule.id];
|
|
193
|
+
if (!ruleContext) {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const prompt = this.buildDynamicPrompt(content, rule, ruleContext);
|
|
199
|
+
const aiResponse = await this.callOpenAI(prompt);
|
|
200
|
+
return this.parseAIResponse(aiResponse, filePath, rule.id);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(`❌ AI analysis failed for rule ${rule.id}:`, error.message);
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Build dynamic prompt based on rule context
|
|
209
|
+
* Following Rule C006: Verb-noun naming
|
|
210
|
+
* @param {string} content - File content
|
|
211
|
+
* @param {Object} rule - Rule object
|
|
212
|
+
* @param {Object} ruleContext - Rule-specific context
|
|
213
|
+
* @returns {string} Generated prompt
|
|
214
|
+
*/
|
|
215
|
+
buildDynamicPrompt(content, rule, ruleContext) {
|
|
216
|
+
return `You are a code quality expert analyzing code for ${ruleContext.name}.
|
|
217
|
+
|
|
218
|
+
RULE: ${rule.id} - ${rule.name || ruleContext.name}
|
|
219
|
+
CONTEXT: ${ruleContext.context}
|
|
220
|
+
DESCRIPTION: ${rule.description || ruleContext.name}
|
|
221
|
+
|
|
222
|
+
FOCUS AREAS: ${ruleContext.focus_areas.join(', ')}
|
|
223
|
+
|
|
224
|
+
ANALYZE THIS CODE:
|
|
225
|
+
\`\`\`
|
|
226
|
+
${content}
|
|
227
|
+
\`\`\`
|
|
228
|
+
|
|
229
|
+
SPECIFIC INSTRUCTIONS:
|
|
230
|
+
- Only analyze for the specified rule and focus areas
|
|
231
|
+
- Be precise about line numbers and column positions
|
|
232
|
+
- Provide actionable suggestions for fixes
|
|
233
|
+
- Do not analyze other code quality issues outside the focus areas
|
|
234
|
+
|
|
235
|
+
RESPOND WITH JSON FORMAT:
|
|
236
|
+
{
|
|
237
|
+
"violations": [
|
|
238
|
+
{
|
|
239
|
+
"line": <line_number>,
|
|
240
|
+
"column": <column_number>,
|
|
241
|
+
"message": "<specific_violation_description>",
|
|
242
|
+
"severity": "${ruleContext.severity}",
|
|
243
|
+
"code": "<code_snippet>",
|
|
244
|
+
"suggestion": "<how_to_fix>"
|
|
245
|
+
}
|
|
246
|
+
],
|
|
247
|
+
"summary": "<overall_assessment>"
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
Be precise and focused only on the rule: ${rule.id}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Call OpenAI API
|
|
255
|
+
* Following Rule C006: Verb-noun naming
|
|
256
|
+
* @param {string} prompt - Prompt to send
|
|
257
|
+
* @returns {Promise<string>} AI response
|
|
258
|
+
*/
|
|
259
|
+
async callOpenAI(prompt) {
|
|
260
|
+
const fetch = (await import('node-fetch')).default;
|
|
261
|
+
|
|
262
|
+
const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
263
|
+
method: 'POST',
|
|
264
|
+
headers: {
|
|
265
|
+
'Content-Type': 'application/json',
|
|
266
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
267
|
+
},
|
|
268
|
+
body: JSON.stringify({
|
|
269
|
+
model: this.model,
|
|
270
|
+
messages: [
|
|
271
|
+
{
|
|
272
|
+
role: 'system',
|
|
273
|
+
content: 'You are an expert code analyzer. Provide precise, actionable feedback focused only on the specified rule.'
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
role: 'user',
|
|
277
|
+
content: prompt
|
|
278
|
+
}
|
|
279
|
+
],
|
|
280
|
+
temperature: 0.1,
|
|
281
|
+
max_tokens: 2000
|
|
282
|
+
})
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (!response.ok) {
|
|
286
|
+
throw new Error(`OpenAI API error: ${response.status} ${response.statusText}`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const data = await response.json();
|
|
290
|
+
return data.choices[0].message.content;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Parse AI response into violation format
|
|
295
|
+
* Following Rule C006: Verb-noun naming
|
|
296
|
+
* @param {string} aiResponse - AI response text
|
|
297
|
+
* @param {string} filePath - File path
|
|
298
|
+
* @param {string} ruleId - Rule ID
|
|
299
|
+
* @returns {Object[]} Parsed violations
|
|
300
|
+
*/
|
|
301
|
+
parseAIResponse(aiResponse, filePath, ruleId) {
|
|
302
|
+
try {
|
|
303
|
+
// Extract JSON from AI response
|
|
304
|
+
const jsonMatch = aiResponse.match(/\{[\s\S]*\}/);
|
|
305
|
+
if (!jsonMatch) {
|
|
306
|
+
console.warn('⚠️ No JSON found in AI response');
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
311
|
+
|
|
312
|
+
if (!parsed.violations || !Array.isArray(parsed.violations)) {
|
|
313
|
+
console.warn('⚠️ Invalid violations format in AI response');
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return parsed.violations.map(violation => ({
|
|
318
|
+
line: violation.line || 1,
|
|
319
|
+
column: violation.column || 1,
|
|
320
|
+
message: violation.message || 'AI detected violation',
|
|
321
|
+
severity: violation.severity || 'warning',
|
|
322
|
+
ruleId: ruleId, // ✅ Dynamic rule ID instead of hardcoded
|
|
323
|
+
code: violation.code || '',
|
|
324
|
+
suggestion: violation.suggestion || '',
|
|
325
|
+
file: filePath,
|
|
326
|
+
source: 'ai'
|
|
327
|
+
}));
|
|
328
|
+
|
|
329
|
+
} catch (error) {
|
|
330
|
+
console.error('Failed to parse AI response:', error.message);
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get supported rules
|
|
337
|
+
* Following Rule C006: Verb-noun naming
|
|
338
|
+
* @returns {string[]} Supported rule IDs
|
|
339
|
+
*/
|
|
340
|
+
getSupportedRules() {
|
|
341
|
+
return this.supportedRulesList;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Test connection to OpenAI API
|
|
346
|
+
* Following Rule C006: Verb-noun naming
|
|
347
|
+
* @returns {Promise<Object>} Connection test result
|
|
348
|
+
*/
|
|
349
|
+
async testConnection() {
|
|
350
|
+
if (!this.apiKey) {
|
|
351
|
+
throw new Error('API key not configured');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
const testPrompt = 'Respond with: {"status": "ok"}';
|
|
356
|
+
await this.callOpenAI(testPrompt);
|
|
357
|
+
return { success: true, provider: this.provider, model: this.model };
|
|
358
|
+
} catch (error) {
|
|
359
|
+
throw new Error(`Connection test failed: ${error.message}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Cleanup OpenAI engine resources
|
|
365
|
+
* Following Rule C006: Verb-noun naming
|
|
366
|
+
*/
|
|
367
|
+
async cleanup() {
|
|
368
|
+
// OpenAI doesn't require specific cleanup
|
|
369
|
+
await super.cleanup();
|
|
370
|
+
console.log('🤖 OpenAI engine cleanup completed');
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
module.exports = OpenAIEngine;
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# ESLint Integration
|
|
2
|
+
|
|
3
|
+
This folder contains SunLint's ESLint plugin integration, organized by rule categories.
|
|
4
|
+
|
|
5
|
+
## 📁 Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
integrations/eslint/
|
|
9
|
+
├── package.json # ESLint integration dependencies
|
|
10
|
+
├── tsconfig.json # TypeScript configuration
|
|
11
|
+
├── plugin/ # ESLint Plugin
|
|
12
|
+
│ ├── index.js # Plugin entry point
|
|
13
|
+
│ ├── package.json # Plugin package configuration
|
|
14
|
+
│ └── rules/ # Organized rules by category
|
|
15
|
+
│ ├── coding/ # C-series: 22 coding standard rules
|
|
16
|
+
│ ├── security/ # S-series: 49 security rules
|
|
17
|
+
│ └── typescript/ # T-series: 10 TypeScript rules
|
|
18
|
+
├── configs/ # ESLint Configuration Presets
|
|
19
|
+
│ ├── .eslintrc.js # Legacy ESLint config
|
|
20
|
+
│ ├── eslint.config.js # Modern ESLint config
|
|
21
|
+
│ └── eslint.config.simple.js # Simple config variant
|
|
22
|
+
└── tools/ # ESLint Integration Tools
|
|
23
|
+
└── cli.js # ESLint integration CLI
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 🚀 Usage
|
|
27
|
+
|
|
28
|
+
### ESLint Plugin Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Install the plugin
|
|
32
|
+
npm install --save-dev ./integrations/eslint/plugin
|
|
33
|
+
|
|
34
|
+
# Use in your ESLint config
|
|
35
|
+
{
|
|
36
|
+
"plugins": ["@sun-asterisk/sunlint"],
|
|
37
|
+
"rules": {
|
|
38
|
+
"@sun-asterisk/sunlint/c006-function-name-verb-noun": "error",
|
|
39
|
+
"@sun-asterisk/sunlint/s001-fail-securely": "error"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Direct CLI Usage
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Use ESLint integration CLI
|
|
48
|
+
node ../../cli.js --input=src
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 📊 Rule Categories
|
|
52
|
+
|
|
53
|
+
### 🔹 Coding Rules (22 rules)
|
|
54
|
+
Quality and best practices rules with `c-` prefix:
|
|
55
|
+
- `c002-no-duplicate-code.js` - Detect code duplication
|
|
56
|
+
- `c006-function-name-verb-noun.js` - Function naming conventions
|
|
57
|
+
- `c010-limit-block-nesting.js` - Control nesting depth
|
|
58
|
+
- *...19 more coding rules*
|
|
59
|
+
|
|
60
|
+
### 🔒 Security Rules (49 rules)
|
|
61
|
+
Security and vulnerability rules with `s-` prefix:
|
|
62
|
+
- `s001-fail-securely.js` - Secure failure handling
|
|
63
|
+
- `s003-no-unvalidated-redirect.js` - Prevent open redirects
|
|
64
|
+
- `s012-hardcode-secret.js` - Detect hardcoded secrets
|
|
65
|
+
- *...46 more security rules*
|
|
66
|
+
|
|
67
|
+
### 📘 TypeScript Rules (10 rules)
|
|
68
|
+
TypeScript-specific rules with `t-` prefix:
|
|
69
|
+
- `t002-interface-prefix-i.js` - Interface naming conventions
|
|
70
|
+
- `t003-ts-ignore-reason.js` - Require reasons for @ts-ignore
|
|
71
|
+
- `t004-interface-public-only.js` - Interface member visibility
|
|
72
|
+
- *...7 more TypeScript rules*
|
|
73
|
+
|
|
74
|
+
## 🔄 Integration with Main SunLint
|
|
75
|
+
|
|
76
|
+
This ESLint integration works alongside the main SunLint heuristic engine:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Combined analysis: SunLint heuristic + ESLint rules
|
|
80
|
+
sunlint --all --eslint-integration --input=src
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The ESLint engine is loaded automatically when `--eslint-integration` flag is used or when ESLint config is detected in the project.
|
|
84
|
+
|
|
85
|
+
## 📦 Distribution
|
|
86
|
+
|
|
87
|
+
The plugin can be packaged and distributed independently:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
cd integrations/eslint/plugin
|
|
91
|
+
npm pack
|
|
92
|
+
# Creates: sun-asterisk-sunlint-eslint-plugin-x.x.x.tgz
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
**🏗️ Architecture**: Modular, organized by rule categories
|
|
98
|
+
**🎯 Purpose**: Clean ESLint integration with 81 total rules
|
|
99
|
+
**🚀 Status**: Production ready, well-organized structure
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// ESLint Legacy Configuration for SunLint (ESLint v8.x compatibility)
|
|
2
|
+
// Following Rule C005: Single responsibility - ESLint configuration
|
|
3
|
+
module.exports = {
|
|
4
|
+
env: {
|
|
5
|
+
es2022: true,
|
|
6
|
+
node: true,
|
|
7
|
+
browser: true
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
parser: '@typescript-eslint/parser',
|
|
11
|
+
|
|
12
|
+
parserOptions: {
|
|
13
|
+
ecmaVersion: 2022,
|
|
14
|
+
sourceType: 'module',
|
|
15
|
+
project: '../tsconfig.json'
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
plugins: [
|
|
19
|
+
'custom',
|
|
20
|
+
'@typescript-eslint'
|
|
21
|
+
],
|
|
22
|
+
|
|
23
|
+
globals: {
|
|
24
|
+
console: 'readonly',
|
|
25
|
+
process: 'readonly',
|
|
26
|
+
eval: 'readonly'
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
rules: {
|
|
30
|
+
// Rule C019: No console.error for non-critical errors
|
|
31
|
+
'no-console': ['warn', { allow: ['warn', 'log'] }],
|
|
32
|
+
|
|
33
|
+
// Code quality rules
|
|
34
|
+
'no-unused-vars': 'warn',
|
|
35
|
+
'prefer-const': 'warn',
|
|
36
|
+
'no-var': 'error',
|
|
37
|
+
'no-undef': 'warn',
|
|
38
|
+
|
|
39
|
+
// Rule C005: Single responsibility principle
|
|
40
|
+
'max-lines-per-function': ['warn', { max: 50 }],
|
|
41
|
+
'complexity': ['warn', { max: 10 }],
|
|
42
|
+
|
|
43
|
+
// Rule C014: Avoid direct new instantiation patterns
|
|
44
|
+
'no-new': 'warn',
|
|
45
|
+
|
|
46
|
+
// Security and best practices
|
|
47
|
+
'no-eval': 'error',
|
|
48
|
+
'no-implied-eval': 'error',
|
|
49
|
+
'no-new-func': 'error',
|
|
50
|
+
'eqeqeq': 'error',
|
|
51
|
+
'curly': 'error',
|
|
52
|
+
|
|
53
|
+
// Security rules from custom plugin (dynamic loading based on category)
|
|
54
|
+
'custom/typescript_s003': 'off', // Will be enabled dynamically
|
|
55
|
+
'custom/typescript_s005': 'off', // Will be enabled dynamically
|
|
56
|
+
'custom/typescript_s006': 'off',
|
|
57
|
+
'custom/typescript_s008': 'off',
|
|
58
|
+
'custom/typescript_s009': 'off',
|
|
59
|
+
'custom/typescript_s010': 'off',
|
|
60
|
+
'custom/typescript_s011': 'off',
|
|
61
|
+
'custom/typescript_s012': 'off',
|
|
62
|
+
'custom/typescript_s014': 'off',
|
|
63
|
+
'custom/typescript_s015': 'off',
|
|
64
|
+
'custom/typescript_s016': 'off',
|
|
65
|
+
'custom/typescript_s017': 'off',
|
|
66
|
+
'custom/typescript_s018': 'off',
|
|
67
|
+
'custom/typescript_s019': 'off',
|
|
68
|
+
'custom/typescript_s020': 'off',
|
|
69
|
+
'custom/typescript_s022': 'off',
|
|
70
|
+
'custom/typescript_s023': 'off',
|
|
71
|
+
'custom/typescript_s025': 'off',
|
|
72
|
+
'custom/typescript_s026': 'off',
|
|
73
|
+
'custom/typescript_s027': 'off',
|
|
74
|
+
'custom/typescript_s029': 'off',
|
|
75
|
+
'custom/typescript_s030': 'off',
|
|
76
|
+
'custom/typescript_s033': 'off',
|
|
77
|
+
'custom/typescript_s034': 'off',
|
|
78
|
+
'custom/typescript_s035': 'off',
|
|
79
|
+
'custom/typescript_s036': 'off',
|
|
80
|
+
'custom/typescript_s037': 'off',
|
|
81
|
+
'custom/typescript_s038': 'off',
|
|
82
|
+
'custom/typescript_s039': 'off',
|
|
83
|
+
'custom/typescript_s041': 'off',
|
|
84
|
+
'custom/typescript_s042': 'off',
|
|
85
|
+
'custom/typescript_s043': 'off',
|
|
86
|
+
'custom/typescript_s044': 'off',
|
|
87
|
+
'custom/typescript_s045': 'off',
|
|
88
|
+
'custom/typescript_s046': 'off',
|
|
89
|
+
'custom/typescript_s047': 'off',
|
|
90
|
+
'custom/typescript_s048': 'off',
|
|
91
|
+
'custom/typescript_s050': 'off',
|
|
92
|
+
'custom/typescript_s052': 'off',
|
|
93
|
+
'custom/typescript_s054': 'off',
|
|
94
|
+
'custom/typescript_s055': 'off',
|
|
95
|
+
'custom/typescript_s057': 'off',
|
|
96
|
+
'custom/typescript_s058': 'off'
|
|
97
|
+
}
|
|
98
|
+
};
|