codeslick-cli 1.0.0
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/README.md +458 -0
- package/__tests__/cli-reporter.test.ts +86 -0
- package/__tests__/config-loader.test.ts +247 -0
- package/__tests__/local-scanner.test.ts +245 -0
- package/bin/codeslick.cjs +153 -0
- package/dist/packages/cli/src/commands/auth.d.ts +36 -0
- package/dist/packages/cli/src/commands/auth.d.ts.map +1 -0
- package/dist/packages/cli/src/commands/auth.js +226 -0
- package/dist/packages/cli/src/commands/auth.js.map +1 -0
- package/dist/packages/cli/src/commands/config.d.ts +37 -0
- package/dist/packages/cli/src/commands/config.d.ts.map +1 -0
- package/dist/packages/cli/src/commands/config.js +196 -0
- package/dist/packages/cli/src/commands/config.js.map +1 -0
- package/dist/packages/cli/src/commands/init.d.ts +32 -0
- package/dist/packages/cli/src/commands/init.d.ts.map +1 -0
- package/dist/packages/cli/src/commands/init.js +171 -0
- package/dist/packages/cli/src/commands/init.js.map +1 -0
- package/dist/packages/cli/src/commands/scan.d.ts +40 -0
- package/dist/packages/cli/src/commands/scan.d.ts.map +1 -0
- package/dist/packages/cli/src/commands/scan.js +204 -0
- package/dist/packages/cli/src/commands/scan.js.map +1 -0
- package/dist/packages/cli/src/config/config-loader.d.ts +67 -0
- package/dist/packages/cli/src/config/config-loader.d.ts.map +1 -0
- package/dist/packages/cli/src/config/config-loader.js +146 -0
- package/dist/packages/cli/src/config/config-loader.js.map +1 -0
- package/dist/packages/cli/src/reporters/cli-reporter.d.ts +69 -0
- package/dist/packages/cli/src/reporters/cli-reporter.d.ts.map +1 -0
- package/dist/packages/cli/src/reporters/cli-reporter.js +244 -0
- package/dist/packages/cli/src/reporters/cli-reporter.js.map +1 -0
- package/dist/packages/cli/src/scanner/local-scanner.d.ts +92 -0
- package/dist/packages/cli/src/scanner/local-scanner.d.ts.map +1 -0
- package/dist/packages/cli/src/scanner/local-scanner.js +221 -0
- package/dist/packages/cli/src/scanner/local-scanner.js.map +1 -0
- package/dist/src/lib/analyzers/helpers/ai-code-detection-utils.d.ts +88 -0
- package/dist/src/lib/analyzers/helpers/ai-code-detection-utils.d.ts.map +1 -0
- package/dist/src/lib/analyzers/helpers/ai-code-detection-utils.js +371 -0
- package/dist/src/lib/analyzers/helpers/ai-code-detection-utils.js.map +1 -0
- package/dist/src/lib/analyzers/helpers/jsx-helpers.d.ts +63 -0
- package/dist/src/lib/analyzers/helpers/jsx-helpers.d.ts.map +1 -0
- package/dist/src/lib/analyzers/helpers/jsx-helpers.js +95 -0
- package/dist/src/lib/analyzers/helpers/jsx-helpers.js.map +1 -0
- package/dist/src/lib/analyzers/helpers/variable-tracker.d.ts +59 -0
- package/dist/src/lib/analyzers/helpers/variable-tracker.d.ts.map +1 -0
- package/dist/src/lib/analyzers/helpers/variable-tracker.js +231 -0
- package/dist/src/lib/analyzers/helpers/variable-tracker.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/access-control.d.ts +20 -0
- package/dist/src/lib/analyzers/java/security-checks/access-control.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/access-control.js +129 -0
- package/dist/src/lib/analyzers/java/security-checks/access-control.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/ai-generated-code.d.ts +25 -0
- package/dist/src/lib/analyzers/java/security-checks/ai-generated-code.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/ai-generated-code.js +221 -0
- package/dist/src/lib/analyzers/java/security-checks/ai-generated-code.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/code-quality.d.ts +18 -0
- package/dist/src/lib/analyzers/java/security-checks/code-quality.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/code-quality.js +84 -0
- package/dist/src/lib/analyzers/java/security-checks/code-quality.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/crypto-validation.d.ts +18 -0
- package/dist/src/lib/analyzers/java/security-checks/crypto-validation.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/crypto-validation.js +161 -0
- package/dist/src/lib/analyzers/java/security-checks/crypto-validation.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/deserialization-xxe.d.ts +20 -0
- package/dist/src/lib/analyzers/java/security-checks/deserialization-xxe.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/deserialization-xxe.js +163 -0
- package/dist/src/lib/analyzers/java/security-checks/deserialization-xxe.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/enhanced-supply-chain.d.ts +24 -0
- package/dist/src/lib/analyzers/java/security-checks/enhanced-supply-chain.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/enhanced-supply-chain.js +178 -0
- package/dist/src/lib/analyzers/java/security-checks/enhanced-supply-chain.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/exception-handling.d.ts +25 -0
- package/dist/src/lib/analyzers/java/security-checks/exception-handling.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/exception-handling.js +179 -0
- package/dist/src/lib/analyzers/java/security-checks/exception-handling.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/file-operations.d.ts +17 -0
- package/dist/src/lib/analyzers/java/security-checks/file-operations.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/file-operations.js +67 -0
- package/dist/src/lib/analyzers/java/security-checks/file-operations.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/framework-security.d.ts +25 -0
- package/dist/src/lib/analyzers/java/security-checks/framework-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/framework-security.js +396 -0
- package/dist/src/lib/analyzers/java/security-checks/framework-security.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/hardcoded-credentials.d.ts +20 -0
- package/dist/src/lib/analyzers/java/security-checks/hardcoded-credentials.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/hardcoded-credentials.js +123 -0
- package/dist/src/lib/analyzers/java/security-checks/hardcoded-credentials.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/injection-attacks.d.ts +23 -0
- package/dist/src/lib/analyzers/java/security-checks/injection-attacks.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/injection-attacks.js +201 -0
- package/dist/src/lib/analyzers/java/security-checks/injection-attacks.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/insecure-design.d.ts +20 -0
- package/dist/src/lib/analyzers/java/security-checks/insecure-design.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/insecure-design.js +121 -0
- package/dist/src/lib/analyzers/java/security-checks/insecure-design.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/logging-failures.d.ts +20 -0
- package/dist/src/lib/analyzers/java/security-checks/logging-failures.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/logging-failures.js +89 -0
- package/dist/src/lib/analyzers/java/security-checks/logging-failures.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/security-misconfiguration.d.ts +26 -0
- package/dist/src/lib/analyzers/java/security-checks/security-misconfiguration.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/security-misconfiguration.js +309 -0
- package/dist/src/lib/analyzers/java/security-checks/security-misconfiguration.js.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/unsafe-patterns.d.ts +18 -0
- package/dist/src/lib/analyzers/java/security-checks/unsafe-patterns.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/security-checks/unsafe-patterns.js +114 -0
- package/dist/src/lib/analyzers/java/security-checks/unsafe-patterns.js.map +1 -0
- package/dist/src/lib/analyzers/java/utils/createVulnerability.d.ts +58 -0
- package/dist/src/lib/analyzers/java/utils/createVulnerability.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java/utils/createVulnerability.js +71 -0
- package/dist/src/lib/analyzers/java/utils/createVulnerability.js.map +1 -0
- package/dist/src/lib/analyzers/java-analyzer.d.ts +209 -0
- package/dist/src/lib/analyzers/java-analyzer.d.ts.map +1 -0
- package/dist/src/lib/analyzers/java-analyzer.js +1720 -0
- package/dist/src/lib/analyzers/java-analyzer.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/ai-hallucinations.d.ts +27 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/ai-hallucinations.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/ai-hallucinations.js +123 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/ai-hallucinations.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/async-patterns.d.ts +44 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/async-patterns.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/async-patterns.js +224 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/async-patterns.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/code-patterns.d.ts +50 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/code-patterns.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/code-patterns.js +284 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/code-patterns.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/comparison-issues.d.ts +27 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/comparison-issues.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/comparison-issues.js +86 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/comparison-issues.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/reference-errors.d.ts +32 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/reference-errors.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/reference-errors.js +44 -0
- package/dist/src/lib/analyzers/javascript/quality-checks/reference-errors.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/access-control.d.ts +22 -0
- package/dist/src/lib/analyzers/javascript/security-checks/access-control.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/access-control.js +168 -0
- package/dist/src/lib/analyzers/javascript/security-checks/access-control.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.d.ts +25 -0
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.js +232 -0
- package/dist/src/lib/analyzers/javascript/security-checks/ai-generated-code.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/authentication-failures.d.ts +27 -0
- package/dist/src/lib/analyzers/javascript/security-checks/authentication-failures.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/authentication-failures.js +222 -0
- package/dist/src/lib/analyzers/javascript/security-checks/authentication-failures.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/credential-crypto.d.ts +28 -0
- package/dist/src/lib/analyzers/javascript/security-checks/credential-crypto.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/credential-crypto.js +176 -0
- package/dist/src/lib/analyzers/javascript/security-checks/credential-crypto.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/enhanced-supply-chain.d.ts +23 -0
- package/dist/src/lib/analyzers/javascript/security-checks/enhanced-supply-chain.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/enhanced-supply-chain.js +113 -0
- package/dist/src/lib/analyzers/javascript/security-checks/enhanced-supply-chain.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/exception-handling.d.ts +28 -0
- package/dist/src/lib/analyzers/javascript/security-checks/exception-handling.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/exception-handling.js +227 -0
- package/dist/src/lib/analyzers/javascript/security-checks/exception-handling.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/injection-attacks.d.ts +32 -0
- package/dist/src/lib/analyzers/javascript/security-checks/injection-attacks.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/injection-attacks.js +260 -0
- package/dist/src/lib/analyzers/javascript/security-checks/injection-attacks.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/insecure-design.d.ts +26 -0
- package/dist/src/lib/analyzers/javascript/security-checks/insecure-design.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/insecure-design.js +164 -0
- package/dist/src/lib/analyzers/javascript/security-checks/insecure-design.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/security-misconfiguration.d.ts +26 -0
- package/dist/src/lib/analyzers/javascript/security-checks/security-misconfiguration.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/security-misconfiguration.js +775 -0
- package/dist/src/lib/analyzers/javascript/security-checks/security-misconfiguration.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/software-integrity.d.ts +25 -0
- package/dist/src/lib/analyzers/javascript/security-checks/software-integrity.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/software-integrity.js +168 -0
- package/dist/src/lib/analyzers/javascript/security-checks/software-integrity.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/storage-security.d.ts +27 -0
- package/dist/src/lib/analyzers/javascript/security-checks/storage-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/storage-security.js +108 -0
- package/dist/src/lib/analyzers/javascript/security-checks/storage-security.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/xss-dom-security.d.ts +28 -0
- package/dist/src/lib/analyzers/javascript/security-checks/xss-dom-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/security-checks/xss-dom-security.js +143 -0
- package/dist/src/lib/analyzers/javascript/security-checks/xss-dom-security.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/syntax/syntax-helpers.d.ts +53 -0
- package/dist/src/lib/analyzers/javascript/syntax/syntax-helpers.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/syntax/syntax-helpers.js +144 -0
- package/dist/src/lib/analyzers/javascript/syntax/syntax-helpers.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/syntax/typescript-syntax.d.ts +72 -0
- package/dist/src/lib/analyzers/javascript/syntax/typescript-syntax.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/syntax/typescript-syntax.js +314 -0
- package/dist/src/lib/analyzers/javascript/syntax/typescript-syntax.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/utils/createVulnerability.d.ts +58 -0
- package/dist/src/lib/analyzers/javascript/utils/createVulnerability.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/utils/createVulnerability.js +71 -0
- package/dist/src/lib/analyzers/javascript/utils/createVulnerability.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/utils/metrics-calculator.d.ts +36 -0
- package/dist/src/lib/analyzers/javascript/utils/metrics-calculator.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/utils/metrics-calculator.js +70 -0
- package/dist/src/lib/analyzers/javascript/utils/metrics-calculator.js.map +1 -0
- package/dist/src/lib/analyzers/javascript/utils/performance-analyzer.d.ts +29 -0
- package/dist/src/lib/analyzers/javascript/utils/performance-analyzer.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript/utils/performance-analyzer.js +55 -0
- package/dist/src/lib/analyzers/javascript/utils/performance-analyzer.js.map +1 -0
- package/dist/src/lib/analyzers/javascript-analyzer.d.ts +95 -0
- package/dist/src/lib/analyzers/javascript-analyzer.d.ts.map +1 -0
- package/dist/src/lib/analyzers/javascript-analyzer.js +2141 -0
- package/dist/src/lib/analyzers/javascript-analyzer.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/access-control.d.ts +21 -0
- package/dist/src/lib/analyzers/python/security-checks/access-control.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/access-control.js +305 -0
- package/dist/src/lib/analyzers/python/security-checks/access-control.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/ai-generated-code.d.ts +25 -0
- package/dist/src/lib/analyzers/python/security-checks/ai-generated-code.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/ai-generated-code.js +242 -0
- package/dist/src/lib/analyzers/python/security-checks/ai-generated-code.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/authentication-flaws.d.ts +24 -0
- package/dist/src/lib/analyzers/python/security-checks/authentication-flaws.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/authentication-flaws.js +207 -0
- package/dist/src/lib/analyzers/python/security-checks/authentication-flaws.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/code-quality.d.ts +27 -0
- package/dist/src/lib/analyzers/python/security-checks/code-quality.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/code-quality.js +206 -0
- package/dist/src/lib/analyzers/python/security-checks/code-quality.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.d.ts +24 -0
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.js +113 -0
- package/dist/src/lib/analyzers/python/security-checks/credentials-crypto.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/crypto-failures.d.ts +20 -0
- package/dist/src/lib/analyzers/python/security-checks/crypto-failures.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/crypto-failures.js +129 -0
- package/dist/src/lib/analyzers/python/security-checks/crypto-failures.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/data-integrity.d.ts +19 -0
- package/dist/src/lib/analyzers/python/security-checks/data-integrity.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/data-integrity.js +90 -0
- package/dist/src/lib/analyzers/python/security-checks/data-integrity.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/deserialization.d.ts +20 -0
- package/dist/src/lib/analyzers/python/security-checks/deserialization.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/deserialization.js +68 -0
- package/dist/src/lib/analyzers/python/security-checks/deserialization.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/django-security.d.ts +25 -0
- package/dist/src/lib/analyzers/python/security-checks/django-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/django-security.js +180 -0
- package/dist/src/lib/analyzers/python/security-checks/django-security.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/enhanced-supply-chain.d.ts +23 -0
- package/dist/src/lib/analyzers/python/security-checks/enhanced-supply-chain.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/enhanced-supply-chain.js +127 -0
- package/dist/src/lib/analyzers/python/security-checks/enhanced-supply-chain.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/exception-handling.d.ts +23 -0
- package/dist/src/lib/analyzers/python/security-checks/exception-handling.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/exception-handling.js +120 -0
- package/dist/src/lib/analyzers/python/security-checks/exception-handling.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/flask-security.d.ts +24 -0
- package/dist/src/lib/analyzers/python/security-checks/flask-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/flask-security.js +143 -0
- package/dist/src/lib/analyzers/python/security-checks/flask-security.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/injection-attacks.d.ts +28 -0
- package/dist/src/lib/analyzers/python/security-checks/injection-attacks.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/injection-attacks.js +174 -0
- package/dist/src/lib/analyzers/python/security-checks/injection-attacks.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/insecure-design.d.ts +20 -0
- package/dist/src/lib/analyzers/python/security-checks/insecure-design.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/insecure-design.js +160 -0
- package/dist/src/lib/analyzers/python/security-checks/insecure-design.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/logging-failures.d.ts +20 -0
- package/dist/src/lib/analyzers/python/security-checks/logging-failures.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/logging-failures.js +121 -0
- package/dist/src/lib/analyzers/python/security-checks/logging-failures.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/nosql-injection.d.ts +26 -0
- package/dist/src/lib/analyzers/python/security-checks/nosql-injection.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/nosql-injection.js +248 -0
- package/dist/src/lib/analyzers/python/security-checks/nosql-injection.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/security-misconfiguration.d.ts +26 -0
- package/dist/src/lib/analyzers/python/security-checks/security-misconfiguration.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/security-misconfiguration.js +375 -0
- package/dist/src/lib/analyzers/python/security-checks/security-misconfiguration.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/ssrf-detection.d.ts +26 -0
- package/dist/src/lib/analyzers/python/security-checks/ssrf-detection.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/ssrf-detection.js +160 -0
- package/dist/src/lib/analyzers/python/security-checks/ssrf-detection.js.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/web-security.d.ts +23 -0
- package/dist/src/lib/analyzers/python/security-checks/web-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/security-checks/web-security.js +117 -0
- package/dist/src/lib/analyzers/python/security-checks/web-security.js.map +1 -0
- package/dist/src/lib/analyzers/python/utils/createVulnerability.d.ts +58 -0
- package/dist/src/lib/analyzers/python/utils/createVulnerability.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python/utils/createVulnerability.js +71 -0
- package/dist/src/lib/analyzers/python/utils/createVulnerability.js.map +1 -0
- package/dist/src/lib/analyzers/python-analyzer.d.ts +111 -0
- package/dist/src/lib/analyzers/python-analyzer.d.ts.map +1 -0
- package/dist/src/lib/analyzers/python-analyzer.js +1600 -0
- package/dist/src/lib/analyzers/python-analyzer.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/ai-providers.d.ts +14 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/ai-providers.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/ai-providers.js +47 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/ai-providers.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/aws.d.ts +13 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/aws.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/aws.js +36 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/aws.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/cloud-providers.d.ts +15 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/cloud-providers.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/cloud-providers.js +68 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/cloud-providers.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/communication.d.ts +15 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/communication.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/communication.js +68 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/communication.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/generic.d.ts +12 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/generic.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/generic.js +45 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/generic.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/github.d.ts +14 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/github.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/github.js +47 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/github.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/stripe.d.ts +13 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/stripe.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/stripe.js +36 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys/stripe.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys.d.ts +15 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys.js +32 -0
- package/dist/src/lib/analyzers/secrets/patterns/api-keys.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/credentials.d.ts +15 -0
- package/dist/src/lib/analyzers/secrets/patterns/credentials.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/credentials.js +68 -0
- package/dist/src/lib/analyzers/secrets/patterns/credentials.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/private-keys.d.ts +16 -0
- package/dist/src/lib/analyzers/secrets/patterns/private-keys.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/private-keys.js +79 -0
- package/dist/src/lib/analyzers/secrets/patterns/private-keys.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/tokens.d.ts +15 -0
- package/dist/src/lib/analyzers/secrets/patterns/tokens.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/patterns/tokens.js +58 -0
- package/dist/src/lib/analyzers/secrets/patterns/tokens.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/secrets-analyzer.d.ts +88 -0
- package/dist/src/lib/analyzers/secrets/secrets-analyzer.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/secrets-analyzer.js +162 -0
- package/dist/src/lib/analyzers/secrets/secrets-analyzer.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/validators/context-checker.d.ts +56 -0
- package/dist/src/lib/analyzers/secrets/validators/context-checker.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/validators/context-checker.js +199 -0
- package/dist/src/lib/analyzers/secrets/validators/context-checker.js.map +1 -0
- package/dist/src/lib/analyzers/secrets/validators/entropy-checker.d.ts +56 -0
- package/dist/src/lib/analyzers/secrets/validators/entropy-checker.d.ts.map +1 -0
- package/dist/src/lib/analyzers/secrets/validators/entropy-checker.js +102 -0
- package/dist/src/lib/analyzers/secrets/validators/entropy-checker.js.map +1 -0
- package/dist/src/lib/analyzers/security-checks/es6-security.d.ts +38 -0
- package/dist/src/lib/analyzers/security-checks/es6-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/security-checks/es6-security.js +125 -0
- package/dist/src/lib/analyzers/security-checks/es6-security.js.map +1 -0
- package/dist/src/lib/analyzers/security-checks/python-async-security.d.ts +46 -0
- package/dist/src/lib/analyzers/security-checks/python-async-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/security-checks/python-async-security.js +92 -0
- package/dist/src/lib/analyzers/security-checks/python-async-security.js.map +1 -0
- package/dist/src/lib/analyzers/security-checks/react-security.d.ts +49 -0
- package/dist/src/lib/analyzers/security-checks/react-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/security-checks/react-security.js +125 -0
- package/dist/src/lib/analyzers/security-checks/react-security.js.map +1 -0
- package/dist/src/lib/analyzers/types.d.ts +92 -0
- package/dist/src/lib/analyzers/types.d.ts.map +1 -0
- package/dist/src/lib/analyzers/types.js +3 -0
- package/dist/src/lib/analyzers/types.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/access-control.d.ts +19 -0
- package/dist/src/lib/analyzers/typescript/security-checks/access-control.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/access-control.js +210 -0
- package/dist/src/lib/analyzers/typescript/security-checks/access-control.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.d.ts +25 -0
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.js +242 -0
- package/dist/src/lib/analyzers/typescript/security-checks/ai-generated-code.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/authentication.d.ts +28 -0
- package/dist/src/lib/analyzers/typescript/security-checks/authentication.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/authentication.js +357 -0
- package/dist/src/lib/analyzers/typescript/security-checks/authentication.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/code-injection.d.ts +26 -0
- package/dist/src/lib/analyzers/typescript/security-checks/code-injection.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/code-injection.js +380 -0
- package/dist/src/lib/analyzers/typescript/security-checks/code-injection.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/code-quality.d.ts +23 -0
- package/dist/src/lib/analyzers/typescript/security-checks/code-quality.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/code-quality.js +109 -0
- package/dist/src/lib/analyzers/typescript/security-checks/code-quality.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/credentials-crypto.d.ts +21 -0
- package/dist/src/lib/analyzers/typescript/security-checks/credentials-crypto.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/credentials-crypto.js +153 -0
- package/dist/src/lib/analyzers/typescript/security-checks/credentials-crypto.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/enhanced-supply-chain.d.ts +23 -0
- package/dist/src/lib/analyzers/typescript/security-checks/enhanced-supply-chain.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/enhanced-supply-chain.js +146 -0
- package/dist/src/lib/analyzers/typescript/security-checks/enhanced-supply-chain.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/exception-handling.d.ts +23 -0
- package/dist/src/lib/analyzers/typescript/security-checks/exception-handling.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/exception-handling.js +187 -0
- package/dist/src/lib/analyzers/typescript/security-checks/exception-handling.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/information-disclosure.d.ts +19 -0
- package/dist/src/lib/analyzers/typescript/security-checks/information-disclosure.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/information-disclosure.js +97 -0
- package/dist/src/lib/analyzers/typescript/security-checks/information-disclosure.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/injection-attacks.d.ts +29 -0
- package/dist/src/lib/analyzers/typescript/security-checks/injection-attacks.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/injection-attacks.js +319 -0
- package/dist/src/lib/analyzers/typescript/security-checks/injection-attacks.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/logging-failures.d.ts +21 -0
- package/dist/src/lib/analyzers/typescript/security-checks/logging-failures.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/logging-failures.js +121 -0
- package/dist/src/lib/analyzers/typescript/security-checks/logging-failures.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/security-misconfiguration.d.ts +27 -0
- package/dist/src/lib/analyzers/typescript/security-checks/security-misconfiguration.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/security-misconfiguration.js +213 -0
- package/dist/src/lib/analyzers/typescript/security-checks/security-misconfiguration.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/type-security.d.ts +19 -0
- package/dist/src/lib/analyzers/typescript/security-checks/type-security.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/security-checks/type-security.js +59 -0
- package/dist/src/lib/analyzers/typescript/security-checks/type-security.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/type-checker.d.ts +17 -0
- package/dist/src/lib/analyzers/typescript/type-checker.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/type-checker.js +515 -0
- package/dist/src/lib/analyzers/typescript/type-checker.js.map +1 -0
- package/dist/src/lib/analyzers/typescript/utils/createVulnerability.d.ts +58 -0
- package/dist/src/lib/analyzers/typescript/utils/createVulnerability.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript/utils/createVulnerability.js +71 -0
- package/dist/src/lib/analyzers/typescript/utils/createVulnerability.js.map +1 -0
- package/dist/src/lib/analyzers/typescript-analyzer.d.ts +116 -0
- package/dist/src/lib/analyzers/typescript-analyzer.d.ts.map +1 -0
- package/dist/src/lib/analyzers/typescript-analyzer.js +1660 -0
- package/dist/src/lib/analyzers/typescript-analyzer.js.map +1 -0
- package/dist/src/lib/security/compliance-mapping.d.ts +29 -0
- package/dist/src/lib/security/compliance-mapping.d.ts.map +1 -0
- package/dist/src/lib/security/compliance-mapping.js +1342 -0
- package/dist/src/lib/security/compliance-mapping.js.map +1 -0
- package/dist/src/lib/security/severity-scoring.d.ts +47 -0
- package/dist/src/lib/security/severity-scoring.d.ts.map +1 -0
- package/dist/src/lib/security/severity-scoring.js +965 -0
- package/dist/src/lib/security/severity-scoring.js.map +1 -0
- package/dist/src/lib/standards/references.d.ts +16 -0
- package/dist/src/lib/standards/references.d.ts.map +1 -0
- package/dist/src/lib/standards/references.js +1161 -0
- package/dist/src/lib/standards/references.js.map +1 -0
- package/dist/src/lib/types/index.d.ts +167 -0
- package/dist/src/lib/types/index.d.ts.map +1 -0
- package/dist/src/lib/types/index.js +3 -0
- package/dist/src/lib/types/index.js.map +1 -0
- package/dist/src/lib/utils/code-cleaner.d.ts +59 -0
- package/dist/src/lib/utils/code-cleaner.d.ts.map +1 -0
- package/dist/src/lib/utils/code-cleaner.js +283 -0
- package/dist/src/lib/utils/code-cleaner.js.map +1 -0
- package/package.json +51 -0
- package/src/commands/auth.ts +308 -0
- package/src/commands/config.ts +226 -0
- package/src/commands/init.ts +202 -0
- package/src/commands/scan.ts +238 -0
- package/src/config/config-loader.ts +175 -0
- package/src/reporters/cli-reporter.ts +282 -0
- package/src/scanner/local-scanner.ts +250 -0
- package/tsconfig.json +24 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,1720 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ⚠️ SHARED MODULE: Java Security Analyzer
|
|
4
|
+
*
|
|
5
|
+
* CRITICAL: This module is used by BOTH WebTool and GitHub App
|
|
6
|
+
*
|
|
7
|
+
* WebTool uses this for:
|
|
8
|
+
* - /api/analyze endpoint - Interactive single-file analysis (<3s target)
|
|
9
|
+
* - Real-time vulnerability detection for individual developers
|
|
10
|
+
*
|
|
11
|
+
* GitHub App uses this for:
|
|
12
|
+
* - /api/github/webhook - Batch PR analysis (10-30s OK)
|
|
13
|
+
* - Automated security checks for professional teams
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ BEFORE MODIFYING THIS FILE:
|
|
16
|
+
* 1. Run all 96 analyzer tests: npm test analyzers
|
|
17
|
+
* 2. Test WebTool: Paste Java code at /analyze → Verify results
|
|
18
|
+
* 3. Test GitHub: Open PR with Java → Verify webhook comment
|
|
19
|
+
* 4. Verify performance: Analysis must complete in <2s per file
|
|
20
|
+
* 5. Check detection rate: All 18 Java checks must still detect
|
|
21
|
+
*
|
|
22
|
+
* CRITICAL OUTPUT FORMAT (DO NOT CHANGE):
|
|
23
|
+
* - result.security.vulnerabilities - Used by both systems
|
|
24
|
+
* - Each vulnerability has: line, message, severity, cvssScore, owasp, cwe
|
|
25
|
+
* - Changing this structure breaks BOTH WebTool and GitHub UI parsing
|
|
26
|
+
*
|
|
27
|
+
* See: docs/technical/WEBTOOL_GITHUB_SEPARATION.md
|
|
28
|
+
*
|
|
29
|
+
* Last modified: 2025-11-18
|
|
30
|
+
* Last verified (both systems): 2025-11-18
|
|
31
|
+
*/
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.JavaAnalyzer = void 0;
|
|
34
|
+
const references_1 = require("../standards/references");
|
|
35
|
+
const code_cleaner_1 = require("../utils/code-cleaner");
|
|
36
|
+
// Modular security checks
|
|
37
|
+
const injection_attacks_1 = require("./java/security-checks/injection-attacks");
|
|
38
|
+
const deserialization_xxe_1 = require("./java/security-checks/deserialization-xxe");
|
|
39
|
+
const hardcoded_credentials_1 = require("./java/security-checks/hardcoded-credentials");
|
|
40
|
+
const crypto_validation_1 = require("./java/security-checks/crypto-validation");
|
|
41
|
+
const file_operations_1 = require("./java/security-checks/file-operations");
|
|
42
|
+
const unsafe_patterns_1 = require("./java/security-checks/unsafe-patterns");
|
|
43
|
+
const code_quality_1 = require("./java/security-checks/code-quality");
|
|
44
|
+
const framework_security_1 = require("./java/security-checks/framework-security");
|
|
45
|
+
const security_misconfiguration_1 = require("./java/security-checks/security-misconfiguration");
|
|
46
|
+
const exception_handling_1 = require("./java/security-checks/exception-handling");
|
|
47
|
+
const enhanced_supply_chain_1 = require("./java/security-checks/enhanced-supply-chain");
|
|
48
|
+
const access_control_1 = require("./java/security-checks/access-control");
|
|
49
|
+
const insecure_design_1 = require("./java/security-checks/insecure-design");
|
|
50
|
+
const logging_failures_1 = require("./java/security-checks/logging-failures");
|
|
51
|
+
const secrets_analyzer_1 = require("./secrets/secrets-analyzer");
|
|
52
|
+
const ai_generated_code_1 = require("./java/security-checks/ai-generated-code");
|
|
53
|
+
class JavaAnalyzer {
|
|
54
|
+
constructor() {
|
|
55
|
+
this.language = 'java';
|
|
56
|
+
}
|
|
57
|
+
async analyze(input) {
|
|
58
|
+
const result = {
|
|
59
|
+
syntax: { valid: true, errors: [], lineErrors: [] },
|
|
60
|
+
quality: { score: 100, issues: [] },
|
|
61
|
+
performance: { score: 100, suggestions: [] },
|
|
62
|
+
security: { vulnerabilities: [] },
|
|
63
|
+
metrics: { complexity: 1, maintainability: 100, lines: 0, functions: 0 }
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
this.analyzeSyntax(input.code, result);
|
|
67
|
+
this.analyzeQuality(input.code, result);
|
|
68
|
+
this.analyzePerformance(input.code, result);
|
|
69
|
+
this.analyzeSecurity(input.code, result);
|
|
70
|
+
this.calculateMetrics(input.code, result);
|
|
71
|
+
// AI-Generated Code Detection (Phase 1.5, Week 5-7)
|
|
72
|
+
const lines = input.code.split('\n');
|
|
73
|
+
result.security.vulnerabilities.push(...(0, ai_generated_code_1.checkAIGeneratedCode)(lines, input.filename));
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
77
|
+
result.syntax.valid = false;
|
|
78
|
+
result.syntax.errors.push(`Java analysis error: ${errorMessage}`);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
async validateSyntax(code) {
|
|
83
|
+
// Basic Java syntax checks
|
|
84
|
+
if (code.length > 50 && !code.includes('public class') && !code.includes('class ')) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
// Check for balanced parentheses/braces
|
|
88
|
+
let braceCount = 0;
|
|
89
|
+
let parenCount = 0;
|
|
90
|
+
for (const char of code) {
|
|
91
|
+
if (char === '{')
|
|
92
|
+
braceCount++;
|
|
93
|
+
if (char === '}')
|
|
94
|
+
braceCount--;
|
|
95
|
+
if (char === '(')
|
|
96
|
+
parenCount++;
|
|
97
|
+
if (char === ')')
|
|
98
|
+
parenCount--;
|
|
99
|
+
}
|
|
100
|
+
return braceCount === 0 && parenCount === 0;
|
|
101
|
+
}
|
|
102
|
+
getLanguageInfo() {
|
|
103
|
+
return {
|
|
104
|
+
name: 'Java',
|
|
105
|
+
extensions: ['.java', '.class', '.jar'],
|
|
106
|
+
description: 'Enterprise and mobile language (Android)'
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
analyzeSyntax(code, result) {
|
|
110
|
+
const errors = [];
|
|
111
|
+
const lineErrors = [];
|
|
112
|
+
// Check for public class
|
|
113
|
+
if (code.length > 50 && !code.includes('public class') && !code.includes('class ')) {
|
|
114
|
+
errors.push('Java code must have at least one class');
|
|
115
|
+
}
|
|
116
|
+
// Basic syntax checks
|
|
117
|
+
const lines = code.split('\n');
|
|
118
|
+
lines.forEach((line, index) => {
|
|
119
|
+
const lineNumber = index + 1;
|
|
120
|
+
const trimmed = line.trim();
|
|
121
|
+
// CRITICAL FIX: Use CodeCleaner to remove comments (handles strings correctly)
|
|
122
|
+
const codeWithoutComment = code_cleaner_1.CodeCleaner.removeLineComments(line, 'java');
|
|
123
|
+
if (codeWithoutComment &&
|
|
124
|
+
!trimmed.startsWith('//') &&
|
|
125
|
+
!trimmed.startsWith('/*') &&
|
|
126
|
+
!codeWithoutComment.endsWith(';') &&
|
|
127
|
+
!codeWithoutComment.endsWith('{') &&
|
|
128
|
+
!codeWithoutComment.endsWith('}') &&
|
|
129
|
+
!trimmed.startsWith('if') &&
|
|
130
|
+
!trimmed.startsWith('for') &&
|
|
131
|
+
!trimmed.startsWith('while') &&
|
|
132
|
+
!trimmed.startsWith('public') &&
|
|
133
|
+
!trimmed.startsWith('private') &&
|
|
134
|
+
!trimmed.startsWith('protected') &&
|
|
135
|
+
!trimmed.startsWith('class') &&
|
|
136
|
+
!trimmed.startsWith('@') &&
|
|
137
|
+
!trimmed.startsWith('import') &&
|
|
138
|
+
!trimmed.startsWith('package') &&
|
|
139
|
+
(codeWithoutComment.includes('=') ||
|
|
140
|
+
codeWithoutComment.includes('System.out') ||
|
|
141
|
+
trimmed.startsWith('return') ||
|
|
142
|
+
trimmed.startsWith('throw') ||
|
|
143
|
+
trimmed.startsWith('break') ||
|
|
144
|
+
trimmed.startsWith('continue'))) {
|
|
145
|
+
lineErrors.push({
|
|
146
|
+
line: lineNumber,
|
|
147
|
+
error: 'Missing semicolon',
|
|
148
|
+
suggestion: `Add ; at the end: ${codeWithoutComment};`,
|
|
149
|
+
severity: 'error'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
// Advanced detection methods
|
|
154
|
+
this.detectMethodSignatureErrors(code, lineErrors);
|
|
155
|
+
this.detectUnbalancedBraces(code, lineErrors);
|
|
156
|
+
this.detectNullPointerRisks(code, lineErrors);
|
|
157
|
+
this.detectCompilationErrors(code, lineErrors);
|
|
158
|
+
this.detectRuntimeExceptions(code, lineErrors);
|
|
159
|
+
this.detectConcurrencyIssues(code, lineErrors);
|
|
160
|
+
this.detectMemoryLeaks(code, lineErrors);
|
|
161
|
+
this.detectIOExceptions(code, lineErrors);
|
|
162
|
+
this.detectSQLInjection(code, lineErrors);
|
|
163
|
+
this.detectSerializationIssues(code, lineErrors);
|
|
164
|
+
this.detectPerformanceIssues(code, lineErrors);
|
|
165
|
+
this.detectNamingConventions(code, lineErrors);
|
|
166
|
+
this.detectAccessModifierIssues(code, lineErrors);
|
|
167
|
+
this.detectResourceLeaks(code, lineErrors);
|
|
168
|
+
this.detectBoxingUnboxing(code, lineErrors);
|
|
169
|
+
this.detectDeprecatedAPIs(code, lineErrors);
|
|
170
|
+
this.detectExceptionHandling(code, lineErrors);
|
|
171
|
+
this.detectDuplicateVariables(code, lineErrors);
|
|
172
|
+
this.detectMethodNamingIssues(code, lineErrors);
|
|
173
|
+
this.detectMagicNumbers(code, lineErrors);
|
|
174
|
+
this.detectGodClasses(code, lineErrors);
|
|
175
|
+
this.detectTooManyParameters(code, lineErrors);
|
|
176
|
+
this.detectStringComparisonWithEquals(code, lineErrors);
|
|
177
|
+
this.detectEmptyCatchBlocks(code, lineErrors);
|
|
178
|
+
this.detectUninitializedVariables(code, lineErrors);
|
|
179
|
+
this.detectMissingReturnStatements(code, lineErrors);
|
|
180
|
+
this.detectInvalidModifierOrder(code, lineErrors);
|
|
181
|
+
// DON'T add general balance errors - specific methods above already detect these issues
|
|
182
|
+
// with proper line numbers, enabling Auto-Fix functionality
|
|
183
|
+
result.syntax.errors = errors;
|
|
184
|
+
result.syntax.lineErrors = lineErrors;
|
|
185
|
+
result.syntax.valid = errors.length === 0 && lineErrors.filter(e => e.severity === 'error').length === 0;
|
|
186
|
+
if (!result.syntax.valid) {
|
|
187
|
+
result.quality.score -= errors.length * 15;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Detect method signature errors - Missing parentheses, malformed signatures
|
|
192
|
+
*
|
|
193
|
+
* Fixed logic:
|
|
194
|
+
* - Checks ALL lines ending with { (not just method signatures)
|
|
195
|
+
* - Detects missing ) in method signatures, catch blocks, if/while/for statements
|
|
196
|
+
* - Avoids false positives on multi-line declarations (lines without {)
|
|
197
|
+
*/
|
|
198
|
+
detectMethodSignatureErrors(code, lineErrors) {
|
|
199
|
+
/**
|
|
200
|
+
* IMPROVED LOGIC (2025-10-16):
|
|
201
|
+
* - Properly handles strings with parentheses: "text (with parens)"
|
|
202
|
+
* - Uses CodeCleaner to remove comments before counting
|
|
203
|
+
* - Only checks lines ending with { (avoids multi-line false positives)
|
|
204
|
+
* - Handles multi-line parameter lists correctly
|
|
205
|
+
*/
|
|
206
|
+
const lines = code.split('\n');
|
|
207
|
+
lines.forEach((line, index) => {
|
|
208
|
+
const lineNumber = index + 1;
|
|
209
|
+
const trimmed = line.trim();
|
|
210
|
+
// Skip comments and empty lines
|
|
211
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || !trimmed)
|
|
212
|
+
return;
|
|
213
|
+
// CRITICAL: Use CodeCleaner to remove comments (handles both // and /* */ correctly)
|
|
214
|
+
const codeWithoutComment = code_cleaner_1.CodeCleaner.removeLineComments(line, 'java');
|
|
215
|
+
if (!codeWithoutComment.trim())
|
|
216
|
+
return;
|
|
217
|
+
// Only check lines that end with { (potential syntax error location)
|
|
218
|
+
// This avoids false positives on multi-line method declarations like:
|
|
219
|
+
// public void createUser(String name, String email,
|
|
220
|
+
// String address, String phone, <- no { on this line
|
|
221
|
+
if (!codeWithoutComment.endsWith('{'))
|
|
222
|
+
return;
|
|
223
|
+
// IMPROVED: Remove string literals to avoid counting parentheses inside strings
|
|
224
|
+
// Example: String msg = "Hello (world)"; -> String msg = ;
|
|
225
|
+
let codeWithoutStrings = codeWithoutComment;
|
|
226
|
+
// Remove double-quoted strings
|
|
227
|
+
codeWithoutStrings = codeWithoutStrings.replace(/"(?:[^"\\]|\\.)*"/g, '""');
|
|
228
|
+
// Remove single-quoted chars
|
|
229
|
+
codeWithoutStrings = codeWithoutStrings.replace(/'(?:[^'\\]|\\.)'/g, "''");
|
|
230
|
+
// Count parentheses in the cleaned line (no comments, no strings)
|
|
231
|
+
let parenCount = 0;
|
|
232
|
+
for (const char of codeWithoutStrings) {
|
|
233
|
+
if (char === '(')
|
|
234
|
+
parenCount++;
|
|
235
|
+
if (char === ')')
|
|
236
|
+
parenCount--;
|
|
237
|
+
}
|
|
238
|
+
// If there are unclosed parentheses, it's a syntax error
|
|
239
|
+
// Examples:
|
|
240
|
+
// - public void processUser(User user { -> parenCount = 1
|
|
241
|
+
// - } catch (Exception e { -> parenCount = 1
|
|
242
|
+
// - if (condition { -> parenCount = 1
|
|
243
|
+
if (parenCount > 0) {
|
|
244
|
+
lineErrors.push({
|
|
245
|
+
line: lineNumber,
|
|
246
|
+
error: 'Syntax Error: Missing closing parenthesis before opening brace',
|
|
247
|
+
suggestion: 'Add closing parenthesis ) before {',
|
|
248
|
+
severity: 'error',
|
|
249
|
+
references: references_1.javaStandards['compilation-errors'] || []
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
// If there are too many closing parentheses, also report
|
|
253
|
+
// BUT: Skip if this is a multi-line parameter list ending
|
|
254
|
+
// FIX (Dec 12, 2025): Check if line contains ONLY closing paren + { (no opening paren)
|
|
255
|
+
// Example valid multi-line signatures that should NOT be flagged:
|
|
256
|
+
// Line 56: public void createUser(String name, String email,
|
|
257
|
+
// Line 57: String phone, String city) { <- has ) but no (
|
|
258
|
+
const hasOpenParen = codeWithoutStrings.includes('(');
|
|
259
|
+
const hasCloseParen = codeWithoutStrings.includes(')');
|
|
260
|
+
const isMultiLineContinuation = hasCloseParen && !hasOpenParen;
|
|
261
|
+
if (parenCount < 0 && !isMultiLineContinuation) {
|
|
262
|
+
lineErrors.push({
|
|
263
|
+
line: lineNumber,
|
|
264
|
+
error: 'Syntax Error: Extra closing parenthesis',
|
|
265
|
+
suggestion: 'Remove extra ) or add opening parenthesis (',
|
|
266
|
+
severity: 'error',
|
|
267
|
+
references: references_1.javaStandards['compilation-errors'] || []
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Detect unbalanced braces - Missing opening or closing braces
|
|
274
|
+
*
|
|
275
|
+
* Tracks brace balance across the entire file to detect:
|
|
276
|
+
* - Methods/blocks with missing closing }
|
|
277
|
+
* - Extra closing } without matching opening {
|
|
278
|
+
*/
|
|
279
|
+
detectUnbalancedBraces(code, lineErrors) {
|
|
280
|
+
const lines = code.split('\n');
|
|
281
|
+
const openBraces = []; // Stack of line numbers where { was opened
|
|
282
|
+
lines.forEach((line, index) => {
|
|
283
|
+
const lineNumber = index + 1;
|
|
284
|
+
const trimmed = line.trim();
|
|
285
|
+
// Skip full-line comments
|
|
286
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
287
|
+
return;
|
|
288
|
+
// CRITICAL FIX: Use CodeCleaner to properly remove comments
|
|
289
|
+
const codeWithoutComment = code_cleaner_1.CodeCleaner.removeLineComments(line, 'java');
|
|
290
|
+
// PHASE 6 FIX (2025-11-21): Remove string literals before counting braces
|
|
291
|
+
// Previous bug: Braces inside strings like "${jndi:ldap://...}" were counted
|
|
292
|
+
// This caused false positives on classes with JNDI exploit strings
|
|
293
|
+
let codeWithoutStrings = codeWithoutComment;
|
|
294
|
+
// Remove double-quoted strings (most common in Java)
|
|
295
|
+
codeWithoutStrings = codeWithoutStrings.replace(/"(?:[^"\\]|\\.)*"/g, '""');
|
|
296
|
+
// Remove single-quoted chars
|
|
297
|
+
codeWithoutStrings = codeWithoutStrings.replace(/'(?:[^'\\]|\\.)'/g, "''");
|
|
298
|
+
// Count braces in this line
|
|
299
|
+
for (const char of codeWithoutStrings) {
|
|
300
|
+
if (char === '{') {
|
|
301
|
+
openBraces.push(lineNumber);
|
|
302
|
+
}
|
|
303
|
+
else if (char === '}') {
|
|
304
|
+
if (openBraces.length > 0) {
|
|
305
|
+
openBraces.pop(); // Match closing brace with opening brace
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
// Extra closing brace without matching opening brace
|
|
309
|
+
lineErrors.push({
|
|
310
|
+
line: lineNumber,
|
|
311
|
+
error: 'Syntax Error: Extra closing brace (no matching opening brace)',
|
|
312
|
+
suggestion: 'Remove extra } or add opening brace',
|
|
313
|
+
severity: 'error',
|
|
314
|
+
references: references_1.javaStandards['compilation-errors'] || []
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// After processing all lines, check if there are unclosed braces
|
|
321
|
+
if (openBraces.length > 0) {
|
|
322
|
+
// Report the LAST unclosed brace (most likely the problematic one)
|
|
323
|
+
const lastOpenBraceLine = openBraces[openBraces.length - 1];
|
|
324
|
+
lineErrors.push({
|
|
325
|
+
line: lastOpenBraceLine,
|
|
326
|
+
error: 'Syntax Error: Missing closing brace',
|
|
327
|
+
suggestion: 'Add closing brace } to close this block',
|
|
328
|
+
severity: 'error',
|
|
329
|
+
references: references_1.javaStandards['compilation-errors'] || []
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Detect NullPointerException risks - null checks and Optional usage
|
|
335
|
+
*/
|
|
336
|
+
detectNullPointerRisks(code, lineErrors) {
|
|
337
|
+
const lines = code.split('\n');
|
|
338
|
+
lines.forEach((line, index) => {
|
|
339
|
+
const lineNumber = index + 1;
|
|
340
|
+
const trimmed = line.trim();
|
|
341
|
+
// Skip all types of comments
|
|
342
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*') || trimmed.startsWith('*/'))
|
|
343
|
+
return;
|
|
344
|
+
// Detect direct null assignment without checks
|
|
345
|
+
if (line.match(/=\s*null\s*;/) && !line.includes('//')) {
|
|
346
|
+
lineErrors.push({
|
|
347
|
+
line: lineNumber,
|
|
348
|
+
error: 'NullPointerException risk: Null assignment',
|
|
349
|
+
suggestion: 'Consider using Optional<T> or adding null checks',
|
|
350
|
+
severity: 'warning',
|
|
351
|
+
references: references_1.javaStandards['null-pointer'] || []
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
// DISABLED: Too aggressive - generates false positives on every method call
|
|
355
|
+
// Detect method calls on potentially null objects (disabled)
|
|
356
|
+
/*
|
|
357
|
+
if (line.match(/\w+\.\w+\(/) && !line.includes('if') && !line.includes('!=') && !line.includes('null')) {
|
|
358
|
+
const prevLines = lines.slice(Math.max(0, index - 3), index);
|
|
359
|
+
const hasNullCheck = prevLines.some(l => l.includes('!= null') || l.includes('== null'));
|
|
360
|
+
|
|
361
|
+
if (!hasNullCheck && line.includes('get') && !line.includes('Optional')) {
|
|
362
|
+
lineErrors.push({
|
|
363
|
+
line: lineNumber,
|
|
364
|
+
error: 'NullPointerException risk: Method call without null check',
|
|
365
|
+
suggestion: 'Add check: if (object != null) { ... } or use Optional',
|
|
366
|
+
severity: 'info',
|
|
367
|
+
references: javaStandards['null-pointer'] || []
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
*/
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Detect compilation errors - Cannot find symbol, type mismatches
|
|
376
|
+
*/
|
|
377
|
+
detectCompilationErrors(code, lineErrors) {
|
|
378
|
+
const lines = code.split('\n');
|
|
379
|
+
lines.forEach((line, index) => {
|
|
380
|
+
const lineNumber = index + 1;
|
|
381
|
+
if (line.trim().startsWith('//'))
|
|
382
|
+
return;
|
|
383
|
+
// Detect common typos in type names (use word boundaries to avoid false positives)
|
|
384
|
+
const commonTypos = {
|
|
385
|
+
'Strng': 'String',
|
|
386
|
+
'Strin': 'String',
|
|
387
|
+
'Integr': 'Integer',
|
|
388
|
+
'Intger': 'Integer',
|
|
389
|
+
'Booln': 'Boolean',
|
|
390
|
+
'Boolen': 'Boolean',
|
|
391
|
+
'ArrayLst': 'ArrayList',
|
|
392
|
+
'HashMp': 'HashMap'
|
|
393
|
+
};
|
|
394
|
+
Object.entries(commonTypos).forEach(([typo, correct]) => {
|
|
395
|
+
// Use word boundary regex to avoid matching typo inside correct word
|
|
396
|
+
// e.g., don't match "Strin" inside "String"
|
|
397
|
+
const regex = new RegExp(`\\b${typo}\\b`);
|
|
398
|
+
if (regex.test(line)) {
|
|
399
|
+
lineErrors.push({
|
|
400
|
+
line: lineNumber,
|
|
401
|
+
error: `Compilation error: Type "${typo}" does not exist`,
|
|
402
|
+
suggestion: `Use "${correct}" instead of "${typo}"`,
|
|
403
|
+
severity: 'error',
|
|
404
|
+
references: references_1.javaStandards['compilation-errors'] || []
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
// DISABLED: Generates too many duplicate warnings
|
|
409
|
+
// Detect missing import statements (disabled - too noisy)
|
|
410
|
+
/*
|
|
411
|
+
const needsImport = [
|
|
412
|
+
{ pattern: /\bArrayList\b/, import: 'import java.util.ArrayList;' },
|
|
413
|
+
{ pattern: /\bHashMap\b/, import: 'import java.util.HashMap;' },
|
|
414
|
+
{ pattern: /\bList\b/, import: 'import java.util.List;' },
|
|
415
|
+
{ pattern: /\bMap\b/, import: 'import java.util.Map;' },
|
|
416
|
+
{ pattern: /\bScanner\b/, import: 'import java.util.Scanner;' },
|
|
417
|
+
{ pattern: /\bFile\b/, import: 'import java.io.File;' }
|
|
418
|
+
];
|
|
419
|
+
|
|
420
|
+
needsImport.forEach(({ pattern, import: importStmt }) => {
|
|
421
|
+
if (pattern.test(line) && !code.includes(importStmt)) {
|
|
422
|
+
lineErrors.push({
|
|
423
|
+
line: lineNumber,
|
|
424
|
+
error: `Possible compilation error: Missing import`,
|
|
425
|
+
suggestion: `Add: ${importStmt}`,
|
|
426
|
+
severity: 'warning',
|
|
427
|
+
references: javaStandards['compilation-errors'] || []
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
*/
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Detect runtime exceptions - ClassCast, ArrayBounds, IllegalArgument, IllegalState
|
|
436
|
+
*/
|
|
437
|
+
detectRuntimeExceptions(code, lineErrors) {
|
|
438
|
+
const lines = code.split('\n');
|
|
439
|
+
lines.forEach((line, index) => {
|
|
440
|
+
const lineNumber = index + 1;
|
|
441
|
+
if (line.trim().startsWith('//'))
|
|
442
|
+
return;
|
|
443
|
+
// Detect unsafe casting
|
|
444
|
+
if (line.match(/\(\w+\)\s*\w+/) && !line.includes('instanceof')) {
|
|
445
|
+
lineErrors.push({
|
|
446
|
+
line: lineNumber,
|
|
447
|
+
error: 'ClassCastException risk: Cast without check',
|
|
448
|
+
suggestion: 'Use instanceof before casting: if (obj instanceof Type) { Type t = (Type) obj; }',
|
|
449
|
+
severity: 'warning',
|
|
450
|
+
references: references_1.javaStandards['class-cast'] || []
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
// Detect array access without bounds checking
|
|
454
|
+
if (line.match(/\w+\[\d+\]/) || line.match(/\w+\[i\]/)) {
|
|
455
|
+
const arrayMatch = line.match(/(\w+)\[/);
|
|
456
|
+
if (arrayMatch) {
|
|
457
|
+
const arrayName = arrayMatch[1];
|
|
458
|
+
const prevLines = lines.slice(Math.max(0, index - 3), index);
|
|
459
|
+
const hasBoundsCheck = prevLines.some(l => l.includes(`${arrayName}.length`) || l.includes('< length'));
|
|
460
|
+
if (!hasBoundsCheck) {
|
|
461
|
+
lineErrors.push({
|
|
462
|
+
line: lineNumber,
|
|
463
|
+
error: 'ArrayIndexOutOfBoundsException risk: Access without bounds check',
|
|
464
|
+
suggestion: `Check: if (index >= 0 && index < ${arrayName}.length)`,
|
|
465
|
+
severity: 'info',
|
|
466
|
+
references: references_1.javaStandards['array-bounds'] || []
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// DISABLED: Too aggressive - generates too many false positives
|
|
472
|
+
// Detect missing argument validation ONLY for critical methods (disabled for now)
|
|
473
|
+
/*
|
|
474
|
+
if (line.match(/public\s+\w+\s+\w+\s*\([^)]*\w+[^)]*\)/) && !line.includes('void main')) {
|
|
475
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 5, lines.length));
|
|
476
|
+
const hasValidation = nextLines.some(l =>
|
|
477
|
+
l.includes('if') && (l.includes('== null') || l.includes('< 0') || l.includes('isEmpty'))
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
if (!hasValidation) {
|
|
481
|
+
lineErrors.push({
|
|
482
|
+
line: lineNumber,
|
|
483
|
+
error: 'IllegalArgumentException risk: Missing parameter validation',
|
|
484
|
+
suggestion: 'Add validation: if (param == null) throw new IllegalArgumentException("...");',
|
|
485
|
+
severity: 'info',
|
|
486
|
+
references: javaStandards['illegal-argument'] || []
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
*/
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Detect concurrency issues - ConcurrentModification, synchronized, volatile
|
|
495
|
+
*/
|
|
496
|
+
detectConcurrencyIssues(code, lineErrors) {
|
|
497
|
+
const lines = code.split('\n');
|
|
498
|
+
lines.forEach((line, index) => {
|
|
499
|
+
const lineNumber = index + 1;
|
|
500
|
+
if (line.trim().startsWith('//'))
|
|
501
|
+
return;
|
|
502
|
+
// Detect modification during iteration
|
|
503
|
+
if (line.match(/for\s*\(\s*\w+\s+\w+\s*:\s*(\w+)\)/)) {
|
|
504
|
+
const iterMatch = line.match(/for\s*\(\s*\w+\s+\w+\s*:\s*(\w+)\)/);
|
|
505
|
+
if (iterMatch) {
|
|
506
|
+
const collectionName = iterMatch[1];
|
|
507
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 10, lines.length));
|
|
508
|
+
nextLines.forEach((nextLine, offset) => {
|
|
509
|
+
if (nextLine.includes(`${collectionName}.add`) ||
|
|
510
|
+
nextLine.includes(`${collectionName}.remove`)) {
|
|
511
|
+
lineErrors.push({
|
|
512
|
+
line: lineNumber,
|
|
513
|
+
error: `ConcurrentModificationException risk: Modification of ${collectionName} during iteration`,
|
|
514
|
+
suggestion: `Use Iterator.remove() or iterate over a copy: new ArrayList<>(${collectionName})`,
|
|
515
|
+
severity: 'error',
|
|
516
|
+
references: references_1.javaStandards['concurrency-issues'] || []
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// Detect missing synchronization on shared data
|
|
523
|
+
if (line.match(/static\s+\w+\s+\w+\s*=/) && !line.includes('final') && !line.includes('private')) {
|
|
524
|
+
lineErrors.push({
|
|
525
|
+
line: lineNumber,
|
|
526
|
+
error: 'Concurrency risk: Non-final static variable without synchronization',
|
|
527
|
+
suggestion: 'Add synchronized, use volatile, or make final if possible',
|
|
528
|
+
severity: 'warning',
|
|
529
|
+
references: references_1.javaStandards['concurrency-issues'] || []
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
// Detect double-checked locking without volatile
|
|
533
|
+
if (line.includes('synchronized') && line.includes('if')) {
|
|
534
|
+
const prevLines = lines.slice(Math.max(0, index - 3), index);
|
|
535
|
+
const hasVolatile = prevLines.some(l => l.includes('volatile'));
|
|
536
|
+
if (!hasVolatile) {
|
|
537
|
+
lineErrors.push({
|
|
538
|
+
line: lineNumber,
|
|
539
|
+
error: 'Double-checked locking pattern requires volatile',
|
|
540
|
+
suggestion: 'Declare the variable as volatile to ensure visibility between threads',
|
|
541
|
+
severity: 'warning',
|
|
542
|
+
references: references_1.javaStandards['concurrency-issues'] || []
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Detect memory leaks - Unclosed resources, static collections, listeners
|
|
550
|
+
*/
|
|
551
|
+
detectMemoryLeaks(code, lineErrors) {
|
|
552
|
+
const lines = code.split('\n');
|
|
553
|
+
lines.forEach((line, index) => {
|
|
554
|
+
const lineNumber = index + 1;
|
|
555
|
+
if (line.trim().startsWith('//'))
|
|
556
|
+
return;
|
|
557
|
+
// Detect static collections that might grow indefinitely
|
|
558
|
+
if (line.match(/static\s+.*\b(ArrayList|HashMap|HashSet|List|Map|Set)\b/) && !line.includes('final')) {
|
|
559
|
+
lineErrors.push({
|
|
560
|
+
line: lineNumber,
|
|
561
|
+
error: 'Memory leak risk: Static collection can grow indefinitely',
|
|
562
|
+
suggestion: 'Use WeakHashMap, limit size, or clear collection periodically',
|
|
563
|
+
severity: 'warning',
|
|
564
|
+
references: references_1.javaStandards['memory-errors'] || []
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
// Detect listeners without removal
|
|
568
|
+
if (line.includes('addListener') || line.includes('addEventListener')) {
|
|
569
|
+
lineErrors.push({
|
|
570
|
+
line: lineNumber,
|
|
571
|
+
error: 'Memory leak risk: Listener added without removal',
|
|
572
|
+
suggestion: 'Make sure to remove listeners when they are no longer needed',
|
|
573
|
+
severity: 'info',
|
|
574
|
+
references: references_1.javaStandards['memory-errors'] || []
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
// Detect potential infinite recursion
|
|
578
|
+
if (line.match(/public\s+\w+\s+(\w+)\s*\([^)]*\)/)) {
|
|
579
|
+
const methodMatch = line.match(/public\s+\w+\s+(\w+)\s*\([^)]*\)/);
|
|
580
|
+
if (methodMatch) {
|
|
581
|
+
const methodName = methodMatch[1];
|
|
582
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 20, lines.length));
|
|
583
|
+
const hasRecursion = nextLines.some(l => l.includes(`${methodName}(`));
|
|
584
|
+
const hasBaseCase = nextLines.some(l => l.includes('return') && !l.includes(`${methodName}(`));
|
|
585
|
+
if (hasRecursion && !hasBaseCase) {
|
|
586
|
+
lineErrors.push({
|
|
587
|
+
line: lineNumber,
|
|
588
|
+
error: 'StackOverflowError risk: Recursion without apparent base case',
|
|
589
|
+
suggestion: 'Add stopping condition to avoid infinite recursion',
|
|
590
|
+
severity: 'warning',
|
|
591
|
+
references: references_1.javaStandards['memory-errors'] || []
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Detect IO exceptions - File operations without try-with-resources
|
|
600
|
+
*/
|
|
601
|
+
detectIOExceptions(code, lineErrors) {
|
|
602
|
+
const lines = code.split('\n');
|
|
603
|
+
lines.forEach((line, index) => {
|
|
604
|
+
const lineNumber = index + 1;
|
|
605
|
+
if (line.trim().startsWith('//'))
|
|
606
|
+
return;
|
|
607
|
+
// Detect file operations without try-with-resources
|
|
608
|
+
const ioClasses = ['FileReader', 'FileWriter', 'BufferedReader', 'BufferedWriter',
|
|
609
|
+
'FileInputStream', 'FileOutputStream', 'Scanner'];
|
|
610
|
+
ioClasses.forEach(ioClass => {
|
|
611
|
+
if (line.includes(`new ${ioClass}`) && !line.includes('try')) {
|
|
612
|
+
const prevLines = lines.slice(Math.max(0, index - 2), index);
|
|
613
|
+
const hasTryWithResources = prevLines.some(l => l.includes('try ('));
|
|
614
|
+
if (!hasTryWithResources) {
|
|
615
|
+
lineErrors.push({
|
|
616
|
+
line: lineNumber,
|
|
617
|
+
error: `Resource leak risk: ${ioClass} without try-with-resources`,
|
|
618
|
+
suggestion: `Use try-with-resources: try (${ioClass} reader = new ${ioClass}(...)) { ... }`,
|
|
619
|
+
severity: 'warning',
|
|
620
|
+
references: references_1.javaStandards['io-exceptions'] || []
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Detect SQL injection - String concatenation in SQL
|
|
629
|
+
*
|
|
630
|
+
* NOTE: SQL Injection is now detected in analyzeSecurity() with full CVSS scoring,
|
|
631
|
+
* compliance mapping (OWASP/CWE/PCI-DSS), and remediation details.
|
|
632
|
+
* This method is kept for backward compatibility but does NOT add duplicates to lineErrors.
|
|
633
|
+
*/
|
|
634
|
+
detectSQLInjection(code, lineErrors) {
|
|
635
|
+
// SQL Injection detection moved to analyzeSecurity() for comprehensive security analysis
|
|
636
|
+
// Keeping this method empty to maintain the call in analyzeSyntax()
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Detect serialization issues - Unsafe deserialization
|
|
641
|
+
*/
|
|
642
|
+
detectSerializationIssues(code, lineErrors) {
|
|
643
|
+
const lines = code.split('\n');
|
|
644
|
+
lines.forEach((line, index) => {
|
|
645
|
+
const lineNumber = index + 1;
|
|
646
|
+
if (line.trim().startsWith('//'))
|
|
647
|
+
return;
|
|
648
|
+
// Detect unsafe deserialization
|
|
649
|
+
if (line.includes('ObjectInputStream') && !line.includes('ValidatingObjectInputStream')) {
|
|
650
|
+
lineErrors.push({
|
|
651
|
+
line: lineNumber,
|
|
652
|
+
error: 'Security risk: Deserialization without validation',
|
|
653
|
+
suggestion: 'Use ValidatingObjectInputStream or validate allowed classes',
|
|
654
|
+
severity: 'warning',
|
|
655
|
+
references: references_1.javaStandards['serialization'] || []
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
// Detect Serializable without serialVersionUID
|
|
659
|
+
if (line.includes('implements Serializable')) {
|
|
660
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 10, lines.length));
|
|
661
|
+
const hasSerialVersionUID = nextLines.some(l => l.includes('serialVersionUID'));
|
|
662
|
+
if (!hasSerialVersionUID) {
|
|
663
|
+
lineErrors.push({
|
|
664
|
+
line: lineNumber,
|
|
665
|
+
error: 'Serializable class without serialVersionUID',
|
|
666
|
+
suggestion: 'Add: private static final long serialVersionUID = 1L;',
|
|
667
|
+
severity: 'info',
|
|
668
|
+
references: references_1.javaStandards['serialization'] || []
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Detect performance issues - Vector/Hashtable, String concat in loops, inefficient loops
|
|
676
|
+
*/
|
|
677
|
+
detectPerformanceIssues(code, lineErrors) {
|
|
678
|
+
const lines = code.split('\n');
|
|
679
|
+
lines.forEach((line, index) => {
|
|
680
|
+
const lineNumber = index + 1;
|
|
681
|
+
if (line.trim().startsWith('//'))
|
|
682
|
+
return;
|
|
683
|
+
// Detect Vector usage
|
|
684
|
+
if (line.match(/\bVector\b/) && !line.includes('//')) {
|
|
685
|
+
lineErrors.push({
|
|
686
|
+
line: lineNumber,
|
|
687
|
+
error: 'Performance issue: Vector is synchronized (slow)',
|
|
688
|
+
suggestion: 'Use ArrayList for better performance (or CopyOnWriteArrayList if thread-safety is needed)',
|
|
689
|
+
severity: 'warning',
|
|
690
|
+
references: references_1.javaStandards['collections-performance'] || []
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
// Detect Hashtable usage
|
|
694
|
+
if (line.match(/\bHashtable\b/) && !line.includes('//')) {
|
|
695
|
+
lineErrors.push({
|
|
696
|
+
line: lineNumber,
|
|
697
|
+
error: 'Performance issue: Hashtable is synchronized (slow)',
|
|
698
|
+
suggestion: 'Use HashMap for better performance (or ConcurrentHashMap if thread-safety is needed)',
|
|
699
|
+
severity: 'warning',
|
|
700
|
+
references: references_1.javaStandards['collections-performance'] || []
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
// Detect string concatenation in loops
|
|
704
|
+
if (line.match(/for\s*\(/) || line.match(/while\s*\(/)) {
|
|
705
|
+
const loopStartIndex = index;
|
|
706
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 15, lines.length));
|
|
707
|
+
nextLines.forEach((nextLine, offset) => {
|
|
708
|
+
if (nextLine.includes('+=') && (nextLine.includes('"') || nextLine.includes("'"))) {
|
|
709
|
+
lineErrors.push({
|
|
710
|
+
line: loopStartIndex + 1,
|
|
711
|
+
error: 'Performance issue: String concatenation in loop',
|
|
712
|
+
suggestion: 'Use StringBuilder: StringBuilder sb = new StringBuilder(); ... sb.append(...);',
|
|
713
|
+
severity: 'warning',
|
|
714
|
+
references: references_1.javaStandards['string-concatenation'] || []
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
// Detect inefficient collection iteration
|
|
720
|
+
if (line.match(/for\s*\(\s*int\s+i\s*=\s*0.*\.size\(\)/)) {
|
|
721
|
+
lineErrors.push({
|
|
722
|
+
line: lineNumber,
|
|
723
|
+
error: 'Performance issue: Inefficient iteration',
|
|
724
|
+
suggestion: 'Use enhanced for loop: for (Type item : collection) or forEach',
|
|
725
|
+
severity: 'info',
|
|
726
|
+
references: references_1.javaStandards['performance'] || []
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Detect naming convention violations - camelCase, PascalCase, UPPER_CASE
|
|
733
|
+
*/
|
|
734
|
+
detectNamingConventions(code, lineErrors) {
|
|
735
|
+
const lines = code.split('\n');
|
|
736
|
+
lines.forEach((line, index) => {
|
|
737
|
+
const lineNumber = index + 1;
|
|
738
|
+
if (line.trim().startsWith('//'))
|
|
739
|
+
return;
|
|
740
|
+
// Detect class names not starting with uppercase
|
|
741
|
+
const classMatch = line.match(/class\s+([a-z][a-zA-Z0-9]*)/);
|
|
742
|
+
if (classMatch) {
|
|
743
|
+
lineErrors.push({
|
|
744
|
+
line: lineNumber,
|
|
745
|
+
error: `Class name should start with uppercase: "${classMatch[1]}"`,
|
|
746
|
+
suggestion: `Rename to PascalCase: ${classMatch[1].charAt(0).toUpperCase() + classMatch[1].slice(1)}`,
|
|
747
|
+
severity: 'warning',
|
|
748
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
// Detect variable names starting with uppercase (but exclude constants)
|
|
752
|
+
const varMatch = line.match(/\b(int|String|boolean|double|float|long|char|byte|short)\s+([A-Z]\w*)\s*=/);
|
|
753
|
+
if (varMatch && !line.includes('static final')) {
|
|
754
|
+
// Only flag if it's not a constant (constants should be UPPERCASE)
|
|
755
|
+
lineErrors.push({
|
|
756
|
+
line: lineNumber,
|
|
757
|
+
error: `Variable name should start with lowercase: "${varMatch[2]}"`,
|
|
758
|
+
suggestion: `Use camelCase: ${varMatch[2].charAt(0).toLowerCase() + varMatch[2].slice(1)}`,
|
|
759
|
+
severity: 'warning',
|
|
760
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
// Detect constants not in UPPER_CASE
|
|
764
|
+
if (line.match(/static\s+final\s+\w+\s+([a-z][a-zA-Z0-9]*)\s*=/)) {
|
|
765
|
+
const constMatch = line.match(/static\s+final\s+\w+\s+([a-z][a-zA-Z0-9]*)\s*=/);
|
|
766
|
+
if (constMatch) {
|
|
767
|
+
lineErrors.push({
|
|
768
|
+
line: lineNumber,
|
|
769
|
+
error: `Constant should be in UPPER_CASE: "${constMatch[1]}"`,
|
|
770
|
+
suggestion: `Rename to: ${constMatch[1].toUpperCase().replace(/([A-Z])/g, '_$1')}`,
|
|
771
|
+
severity: 'info',
|
|
772
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Detect access modifier issues - Methods without explicit modifiers
|
|
780
|
+
*/
|
|
781
|
+
detectAccessModifierIssues(code, lineErrors) {
|
|
782
|
+
const lines = code.split('\n');
|
|
783
|
+
lines.forEach((line, index) => {
|
|
784
|
+
const lineNumber = index + 1;
|
|
785
|
+
if (line.trim().startsWith('//'))
|
|
786
|
+
return;
|
|
787
|
+
// Detect methods without explicit access modifiers
|
|
788
|
+
if (line.match(/^\s*\w+\s+\w+\s*\(/) &&
|
|
789
|
+
!line.includes('public') &&
|
|
790
|
+
!line.includes('private') &&
|
|
791
|
+
!line.includes('protected')) {
|
|
792
|
+
lineErrors.push({
|
|
793
|
+
line: lineNumber,
|
|
794
|
+
error: 'Method without explicit access modifier (package-private)',
|
|
795
|
+
suggestion: 'Add public, private or protected for clarity',
|
|
796
|
+
severity: 'info',
|
|
797
|
+
references: references_1.javaStandards['access-modifiers'] || []
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
// Detect public fields (should use getters/setters)
|
|
801
|
+
if (line.match(/public\s+\w+\s+\w+\s*;/) && !line.includes('static') && !line.includes('final')) {
|
|
802
|
+
lineErrors.push({
|
|
803
|
+
line: lineNumber,
|
|
804
|
+
error: 'Public field detected - violates encapsulation',
|
|
805
|
+
suggestion: 'Make private and add getters/setters',
|
|
806
|
+
severity: 'warning',
|
|
807
|
+
references: references_1.javaStandards['access-modifiers'] || []
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Detect resource leaks - Missing try-with-resources for AutoCloseable
|
|
814
|
+
*/
|
|
815
|
+
detectResourceLeaks(code, lineErrors) {
|
|
816
|
+
const lines = code.split('\n');
|
|
817
|
+
lines.forEach((line, index) => {
|
|
818
|
+
const lineNumber = index + 1;
|
|
819
|
+
if (line.trim().startsWith('//'))
|
|
820
|
+
return;
|
|
821
|
+
// Detect AutoCloseable resources not in try-with-resources
|
|
822
|
+
const autoCloseableTypes = [
|
|
823
|
+
'InputStream', 'OutputStream', 'Reader', 'Writer',
|
|
824
|
+
'Connection', 'Statement', 'ResultSet',
|
|
825
|
+
'Socket', 'ServerSocket', 'Channel'
|
|
826
|
+
];
|
|
827
|
+
autoCloseableTypes.forEach(type => {
|
|
828
|
+
if (line.includes(`new ${type}`) || line.includes(`= ${type}`)) {
|
|
829
|
+
const prevLines = lines.slice(Math.max(0, index - 2), index);
|
|
830
|
+
const hasTryWithResources = prevLines.some(l => l.includes('try ('));
|
|
831
|
+
const isInTryWithResources = line.includes('try (');
|
|
832
|
+
if (!hasTryWithResources && !isInTryWithResources) {
|
|
833
|
+
lineErrors.push({
|
|
834
|
+
line: lineNumber,
|
|
835
|
+
error: `Resource leak risk: ${type} should be in try-with-resources`,
|
|
836
|
+
suggestion: `Use: try (${type} resource = ...) { ... }`,
|
|
837
|
+
severity: 'warning',
|
|
838
|
+
references: references_1.javaStandards['resource-leaks'] || []
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Detect boxing/unboxing - Unnecessary autoboxing
|
|
847
|
+
*/
|
|
848
|
+
detectBoxingUnboxing(code, lineErrors) {
|
|
849
|
+
const lines = code.split('\n');
|
|
850
|
+
lines.forEach((line, index) => {
|
|
851
|
+
const lineNumber = index + 1;
|
|
852
|
+
if (line.trim().startsWith('//'))
|
|
853
|
+
return;
|
|
854
|
+
// Detect unnecessary Integer.valueOf or new Integer
|
|
855
|
+
if (line.match(/Integer\.valueOf\(\d+\)/) || line.match(/new Integer\(/)) {
|
|
856
|
+
lineErrors.push({
|
|
857
|
+
line: lineNumber,
|
|
858
|
+
error: 'Unnecessary primitive boxing',
|
|
859
|
+
suggestion: 'Use primitive int directly, autoboxing when necessary',
|
|
860
|
+
severity: 'info',
|
|
861
|
+
references: references_1.javaStandards['performance'] || []
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
// Detect primitive wrapper in loop
|
|
865
|
+
if (line.match(/for\s*\(/) || line.match(/while\s*\(/)) {
|
|
866
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 10, lines.length));
|
|
867
|
+
nextLines.forEach(nextLine => {
|
|
868
|
+
if (nextLine.match(/Integer\s+\w+\s*=/) || nextLine.match(/Double\s+\w+\s*=/)) {
|
|
869
|
+
lineErrors.push({
|
|
870
|
+
line: lineNumber,
|
|
871
|
+
error: 'Performance issue: Wrapper in loop causes boxing/unboxing',
|
|
872
|
+
suggestion: 'Use primitive type (int, double) in loops for better performance',
|
|
873
|
+
severity: 'info',
|
|
874
|
+
references: references_1.javaStandards['performance'] || []
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Detect deprecated APIs - Date, Vector, Hashtable, etc.
|
|
883
|
+
*/
|
|
884
|
+
detectDeprecatedAPIs(code, lineErrors) {
|
|
885
|
+
const lines = code.split('\n');
|
|
886
|
+
const deprecatedAPIs = [
|
|
887
|
+
{ old: 'java.util.Date', new: 'java.time.LocalDate or java.time.Instant', pattern: /\bDate\b/ },
|
|
888
|
+
{ old: 'SimpleDateFormat', new: 'DateTimeFormatter', pattern: /SimpleDateFormat/ },
|
|
889
|
+
{ old: 'StringTokenizer', new: 'String.split()', pattern: /StringTokenizer/ },
|
|
890
|
+
{ old: 'Thread.stop()', new: 'Thread interruption', pattern: /\.stop\(\)/ },
|
|
891
|
+
{ old: 'Thread.suspend()', new: 'Thread interruption', pattern: /\.suspend\(\)/ },
|
|
892
|
+
{ old: 'finalize()', new: 'try-with-resources or Cleaner API', pattern: /\bfinalize\s*\(\s*\)/ }
|
|
893
|
+
];
|
|
894
|
+
lines.forEach((line, index) => {
|
|
895
|
+
const lineNumber = index + 1;
|
|
896
|
+
if (line.trim().startsWith('//'))
|
|
897
|
+
return;
|
|
898
|
+
deprecatedAPIs.forEach(({ old, new: newApi, pattern }) => {
|
|
899
|
+
if (pattern.test(line)) {
|
|
900
|
+
lineErrors.push({
|
|
901
|
+
line: lineNumber,
|
|
902
|
+
error: `Deprecated API: ${old} is deprecated`,
|
|
903
|
+
suggestion: `Use ${newApi} instead`,
|
|
904
|
+
severity: 'warning',
|
|
905
|
+
references: references_1.javaStandards['deprecated-apis'] || []
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Detect exception handling issues - Empty catch, catching Exception/Throwable
|
|
913
|
+
*/
|
|
914
|
+
detectExceptionHandling(code, lineErrors) {
|
|
915
|
+
const lines = code.split('\n');
|
|
916
|
+
lines.forEach((line, index) => {
|
|
917
|
+
const lineNumber = index + 1;
|
|
918
|
+
if (line.trim().startsWith('//'))
|
|
919
|
+
return;
|
|
920
|
+
// Detect catching generic Exception
|
|
921
|
+
if (line.match(/catch\s*\(\s*Exception\s+\w+\)/)) {
|
|
922
|
+
lineErrors.push({
|
|
923
|
+
line: lineNumber,
|
|
924
|
+
error: 'Bad practice: Generic Exception catch',
|
|
925
|
+
suggestion: 'Catch specific exceptions (IOException, SQLException, etc.)',
|
|
926
|
+
severity: 'warning',
|
|
927
|
+
references: references_1.javaStandards['exception-handling'] || []
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
// Detect catching Throwable
|
|
931
|
+
if (line.match(/catch\s*\(\s*Throwable\s+\w+\)/)) {
|
|
932
|
+
lineErrors.push({
|
|
933
|
+
line: lineNumber,
|
|
934
|
+
error: 'CRITICAL: Catching Throwable captures system errors',
|
|
935
|
+
suggestion: 'Never catch Throwable, use specific exceptions',
|
|
936
|
+
severity: 'error',
|
|
937
|
+
references: references_1.javaStandards['exception-handling'] || []
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
// Detect empty catch blocks
|
|
941
|
+
if (line.match(/catch\s*\([^)]+\)\s*\{\s*\}/)) {
|
|
942
|
+
lineErrors.push({
|
|
943
|
+
line: lineNumber,
|
|
944
|
+
error: 'CRITICAL: Empty catch block - exception ignored',
|
|
945
|
+
suggestion: 'Handle the exception or at least log: logger.error("...", e);',
|
|
946
|
+
severity: 'error',
|
|
947
|
+
references: references_1.javaStandards['exception-handling'] || []
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
// Detect printStackTrace() usage
|
|
951
|
+
if (line.includes('.printStackTrace()')) {
|
|
952
|
+
lineErrors.push({
|
|
953
|
+
line: lineNumber,
|
|
954
|
+
error: 'Bad practice: printStackTrace() in production',
|
|
955
|
+
suggestion: 'Use a logger: logger.error("Error occurred", e);',
|
|
956
|
+
severity: 'info',
|
|
957
|
+
references: references_1.javaStandards['exception-handling'] || []
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Detect duplicate variable declarations - SYNTAX ERROR (Error 26)
|
|
964
|
+
*
|
|
965
|
+
* Tracks variable declarations within method scope and detects when
|
|
966
|
+
* the same variable name is declared twice, which is a compilation error.
|
|
967
|
+
*/
|
|
968
|
+
detectDuplicateVariables(code, lineErrors) {
|
|
969
|
+
const lines = code.split('\n');
|
|
970
|
+
const variablesByMethod = new Map();
|
|
971
|
+
let currentMethod = null;
|
|
972
|
+
let braceDepth = 0;
|
|
973
|
+
let methodBraceDepth = 0;
|
|
974
|
+
lines.forEach((line, index) => {
|
|
975
|
+
const lineNumber = index + 1;
|
|
976
|
+
const trimmed = line.trim();
|
|
977
|
+
// Skip comments
|
|
978
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
979
|
+
return;
|
|
980
|
+
// CRITICAL FIX: Use CodeCleaner to remove comments (handles strings correctly)
|
|
981
|
+
// Old approach: trimmed.split('//')[0] - doesn't handle strings
|
|
982
|
+
// Example: String url = "https://example.com"; <- split('//')[0] would break this!
|
|
983
|
+
const codeWithoutComment = code_cleaner_1.CodeCleaner.removeLineComments(line, 'java');
|
|
984
|
+
if (!codeWithoutComment.trim())
|
|
985
|
+
return;
|
|
986
|
+
// Track method entry
|
|
987
|
+
if (codeWithoutComment.match(/(?:public|private|protected|static|\s)+\s+\w+\s+(\w+)\s*\([^)]*\)\s*\{/)) {
|
|
988
|
+
const methodMatch = codeWithoutComment.match(/\s(\w+)\s*\(/);
|
|
989
|
+
if (methodMatch) {
|
|
990
|
+
currentMethod = methodMatch[1] + '_' + lineNumber; // Unique ID per method
|
|
991
|
+
variablesByMethod.set(currentMethod, new Map());
|
|
992
|
+
methodBraceDepth = braceDepth;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
// Track brace depth
|
|
996
|
+
for (const char of codeWithoutComment) {
|
|
997
|
+
if (char === '{')
|
|
998
|
+
braceDepth++;
|
|
999
|
+
if (char === '}') {
|
|
1000
|
+
braceDepth--;
|
|
1001
|
+
// Exit method when we close its brace
|
|
1002
|
+
if (currentMethod && braceDepth === methodBraceDepth) {
|
|
1003
|
+
currentMethod = null;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
// Detect variable declarations inside methods
|
|
1008
|
+
// Pattern: type variableName = ... OR type variableName;
|
|
1009
|
+
// Common types: int, String, boolean, double, float, long, char, byte, short, Object, List, Map, etc.
|
|
1010
|
+
if (currentMethod && codeWithoutComment.match(/\b(int|String|boolean|double|float|long|char|byte|short|Integer|Double|Boolean|Object|List|Map|Set|Array|File)\s+(\w+)\s*(=|;)/)) {
|
|
1011
|
+
const varMatch = codeWithoutComment.match(/\b(int|String|boolean|double|float|long|char|byte|short|Integer|Double|Boolean|Object|List|Map|Set|Array|File)\s+(\w+)\s*(=|;)/);
|
|
1012
|
+
if (varMatch) {
|
|
1013
|
+
const varType = varMatch[1];
|
|
1014
|
+
const varName = varMatch[2];
|
|
1015
|
+
const variables = variablesByMethod.get(currentMethod);
|
|
1016
|
+
// Check if this variable was already declared in this method
|
|
1017
|
+
if (variables.has(varName)) {
|
|
1018
|
+
const firstDeclarationLine = variables.get(varName);
|
|
1019
|
+
lineErrors.push({
|
|
1020
|
+
line: lineNumber,
|
|
1021
|
+
error: `Syntax Error: Variable "${varName}" is already defined on line ${firstDeclarationLine}`,
|
|
1022
|
+
suggestion: `Remove duplicate declaration or rename variable (e.g., ${varName}2)`,
|
|
1023
|
+
severity: 'error',
|
|
1024
|
+
references: references_1.javaStandards['compilation-errors'] || []
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
else {
|
|
1028
|
+
// Track this variable declaration
|
|
1029
|
+
variables.set(varName, lineNumber);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Detect method naming issues - Methods not in camelCase (e.g., Process_User_Data)
|
|
1037
|
+
*/
|
|
1038
|
+
detectMethodNamingIssues(code, lineErrors) {
|
|
1039
|
+
const lines = code.split('\n');
|
|
1040
|
+
lines.forEach((line, index) => {
|
|
1041
|
+
const lineNumber = index + 1;
|
|
1042
|
+
const trimmed = line.trim();
|
|
1043
|
+
// Skip comments
|
|
1044
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
1045
|
+
return;
|
|
1046
|
+
// Detect method declarations with snake_case or PascalCase names
|
|
1047
|
+
// Pattern: public/private/protected returnType methodName(...)
|
|
1048
|
+
const methodMatch = line.match(/(?:public|private|protected|static|\s)+\s+(\w+)\s+([A-Z_][a-zA-Z0-9_]*)\s*\(/);
|
|
1049
|
+
if (methodMatch && !line.includes('class ')) {
|
|
1050
|
+
const methodName = methodMatch[2];
|
|
1051
|
+
// Check for snake_case (contains underscore)
|
|
1052
|
+
if (methodName.includes('_')) {
|
|
1053
|
+
lineErrors.push({
|
|
1054
|
+
line: lineNumber,
|
|
1055
|
+
error: `Method name should be camelCase, not snake_case: "${methodName}"`,
|
|
1056
|
+
suggestion: `Rename to camelCase: ${methodName.toLowerCase().replace(/_(.)/g, (_, c) => c.toUpperCase())}`,
|
|
1057
|
+
severity: 'warning',
|
|
1058
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
// Check for PascalCase (starts with uppercase, no underscore)
|
|
1062
|
+
else if (methodName[0] === methodName[0].toUpperCase() && !methodName.includes('_')) {
|
|
1063
|
+
lineErrors.push({
|
|
1064
|
+
line: lineNumber,
|
|
1065
|
+
error: `Method name should be camelCase, not PascalCase: "${methodName}"`,
|
|
1066
|
+
suggestion: `Rename to camelCase: ${methodName[0].toLowerCase() + methodName.slice(1)}`,
|
|
1067
|
+
severity: 'warning',
|
|
1068
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Detect magic numbers - Numeric literals that should be named constants
|
|
1076
|
+
*/
|
|
1077
|
+
detectMagicNumbers(code, lineErrors) {
|
|
1078
|
+
const lines = code.split('\n');
|
|
1079
|
+
let inMultiLineComment = false;
|
|
1080
|
+
lines.forEach((line, index) => {
|
|
1081
|
+
const lineNumber = index + 1;
|
|
1082
|
+
const trimmed = line.trim();
|
|
1083
|
+
// Track multi-line comment blocks (/* ... */)
|
|
1084
|
+
if (trimmed.includes('/*')) {
|
|
1085
|
+
inMultiLineComment = true;
|
|
1086
|
+
}
|
|
1087
|
+
if (trimmed.includes('*/')) {
|
|
1088
|
+
inMultiLineComment = false;
|
|
1089
|
+
return; // Skip the line with */
|
|
1090
|
+
}
|
|
1091
|
+
// Skip comments, constants declarations, and array indices
|
|
1092
|
+
if (trimmed.startsWith('//') || inMultiLineComment ||
|
|
1093
|
+
line.includes('static final') || line.includes('final static'))
|
|
1094
|
+
return;
|
|
1095
|
+
// Remove string literals to avoid detecting numbers inside strings
|
|
1096
|
+
// Example: "Java OWASP 2025" should NOT flag 2025 as magic number
|
|
1097
|
+
const lineWithoutStrings = trimmed.replace(/"[^"]*"/g, '').replace(/'[^']*'/g, '');
|
|
1098
|
+
// Detect magic numbers: integers >= 10 (excluding 0, 1, -1 which are universal)
|
|
1099
|
+
// Pattern: numbers not in declarations or array indices
|
|
1100
|
+
const magicNumberPattern = /\b(1[0-9]|[2-9][0-9]|[1-9][0-9]{2,})\b/g;
|
|
1101
|
+
const matches = [...lineWithoutStrings.matchAll(magicNumberPattern)];
|
|
1102
|
+
if (matches.length > 0) {
|
|
1103
|
+
// Check if it's in a context where magic numbers are acceptable
|
|
1104
|
+
const isArrayIndex = lineWithoutStrings.match(/\[\s*\d+\s*\]/);
|
|
1105
|
+
const isConstantDecl = lineWithoutStrings.match(/=\s*\d+\s*;/) && line.includes('final');
|
|
1106
|
+
// Skip cryptographic operations (SecureRandom, BigInteger, MessageDigest, etc.)
|
|
1107
|
+
const isCryptoOperation = trimmed.match(/SecureRandom|BigInteger|MessageDigest|Cipher|KeyGenerator|SecretKey/i);
|
|
1108
|
+
if (!isArrayIndex && !isConstantDecl && !isCryptoOperation) {
|
|
1109
|
+
matches.forEach(match => {
|
|
1110
|
+
const number = match[0];
|
|
1111
|
+
lineErrors.push({
|
|
1112
|
+
line: lineNumber,
|
|
1113
|
+
error: `Magic number detected: ${number}`,
|
|
1114
|
+
suggestion: `Replace with a named constant: private static final int MEANINGFUL_NAME = ${number};`,
|
|
1115
|
+
severity: 'info',
|
|
1116
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
1117
|
+
});
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Detect God Classes - Classes with too many methods (violates SRP)
|
|
1125
|
+
*/
|
|
1126
|
+
detectGodClasses(code, lineErrors) {
|
|
1127
|
+
const lines = code.split('\n');
|
|
1128
|
+
// Count methods per class
|
|
1129
|
+
let currentClass = null;
|
|
1130
|
+
lines.forEach((line, index) => {
|
|
1131
|
+
const lineNumber = index + 1;
|
|
1132
|
+
const trimmed = line.trim();
|
|
1133
|
+
// Skip comments
|
|
1134
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
1135
|
+
return;
|
|
1136
|
+
// Detect class declaration
|
|
1137
|
+
const classMatch = line.match(/(?:public|private|protected)?\s*class\s+(\w+)/);
|
|
1138
|
+
if (classMatch) {
|
|
1139
|
+
// Report previous class if it was a God class
|
|
1140
|
+
if (currentClass && currentClass.methods > 10) {
|
|
1141
|
+
lineErrors.push({
|
|
1142
|
+
line: currentClass.line,
|
|
1143
|
+
error: `God Class detected: ${currentClass.name} has ${currentClass.methods} methods (>10)`,
|
|
1144
|
+
suggestion: 'Refactor into smaller, focused classes following Single Responsibility Principle',
|
|
1145
|
+
severity: 'warning',
|
|
1146
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
// Start tracking new class
|
|
1150
|
+
currentClass = { name: classMatch[1], line: lineNumber, methods: 0 };
|
|
1151
|
+
}
|
|
1152
|
+
// Count methods in current class
|
|
1153
|
+
if (currentClass && line.match(/(?:public|private|protected)\s+\w+\s+\w+\s*\(/)) {
|
|
1154
|
+
currentClass.methods++;
|
|
1155
|
+
}
|
|
1156
|
+
});
|
|
1157
|
+
// Check last class
|
|
1158
|
+
if (currentClass) {
|
|
1159
|
+
const classInfo = currentClass;
|
|
1160
|
+
if (classInfo.methods > 10) {
|
|
1161
|
+
lineErrors.push({
|
|
1162
|
+
line: classInfo.line,
|
|
1163
|
+
error: `God Class detected: ${classInfo.name} has ${classInfo.methods} methods (>10)`,
|
|
1164
|
+
suggestion: 'Refactor into smaller, focused classes following Single Responsibility Principle',
|
|
1165
|
+
severity: 'warning',
|
|
1166
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Detect methods with too many parameters (>7 is a code smell)
|
|
1173
|
+
* Methods with many parameters are hard to test, maintain, and understand.
|
|
1174
|
+
* Consider using parameter objects or builder pattern.
|
|
1175
|
+
*/
|
|
1176
|
+
detectTooManyParameters(code, lineErrors) {
|
|
1177
|
+
const lines = code.split('\n');
|
|
1178
|
+
const MAX_PARAMETERS = 7; // Industry standard threshold
|
|
1179
|
+
// FIX (Dec 12, 2025): Handle multi-line method signatures
|
|
1180
|
+
// Previous: Only checked single-line method declarations
|
|
1181
|
+
// Now: Collect method signature across multiple lines until closing )
|
|
1182
|
+
lines.forEach((line, index) => {
|
|
1183
|
+
const lineNumber = index + 1;
|
|
1184
|
+
const trimmed = line.trim();
|
|
1185
|
+
// Skip comments
|
|
1186
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
1187
|
+
return;
|
|
1188
|
+
// Detect method declaration start: [access modifier] returnType methodName(
|
|
1189
|
+
// Pattern: public void methodName( or private String getValue(
|
|
1190
|
+
const methodStartMatch = line.match(/(?:public|private|protected)\s+\w+\s+(\w+)\s*\(/);
|
|
1191
|
+
if (methodStartMatch) {
|
|
1192
|
+
const methodName = methodStartMatch[1];
|
|
1193
|
+
// Collect full method signature across multiple lines until we find closing )
|
|
1194
|
+
let fullSignature = line;
|
|
1195
|
+
let currentIndex = index;
|
|
1196
|
+
// If line doesn't have closing ), continue collecting from next lines
|
|
1197
|
+
let openParens = (fullSignature.match(/\(/g) || []).length;
|
|
1198
|
+
let closeParens = (fullSignature.match(/\)/g) || []).length;
|
|
1199
|
+
while (openParens > closeParens && currentIndex < lines.length - 1) {
|
|
1200
|
+
currentIndex++;
|
|
1201
|
+
const nextLine = lines[currentIndex];
|
|
1202
|
+
fullSignature += ' ' + nextLine;
|
|
1203
|
+
openParens += (nextLine.match(/\(/g) || []).length;
|
|
1204
|
+
closeParens += (nextLine.match(/\)/g) || []).length;
|
|
1205
|
+
}
|
|
1206
|
+
// Now extract parameters from full signature
|
|
1207
|
+
const fullMethodMatch = fullSignature.match(/\(([^)]*)\)/);
|
|
1208
|
+
if (fullMethodMatch) {
|
|
1209
|
+
const paramsString = fullMethodMatch[1].trim();
|
|
1210
|
+
// Skip empty parameter lists
|
|
1211
|
+
if (!paramsString)
|
|
1212
|
+
return;
|
|
1213
|
+
// Count parameters by splitting on commas
|
|
1214
|
+
// Need to be careful about generics like List<String, Integer>
|
|
1215
|
+
// Simple heuristic: count commas not inside <> brackets
|
|
1216
|
+
let paramCount = 1; // Start at 1 if we have any parameters
|
|
1217
|
+
let depth = 0;
|
|
1218
|
+
for (const char of paramsString) {
|
|
1219
|
+
if (char === '<')
|
|
1220
|
+
depth++;
|
|
1221
|
+
else if (char === '>')
|
|
1222
|
+
depth--;
|
|
1223
|
+
else if (char === ',' && depth === 0)
|
|
1224
|
+
paramCount++;
|
|
1225
|
+
}
|
|
1226
|
+
if (paramCount > MAX_PARAMETERS) {
|
|
1227
|
+
lineErrors.push({
|
|
1228
|
+
line: lineNumber,
|
|
1229
|
+
error: `Method has too many parameters: ${methodName} has ${paramCount} parameters (max recommended: ${MAX_PARAMETERS})`,
|
|
1230
|
+
suggestion: 'Refactor using parameter objects, builder pattern, or method decomposition',
|
|
1231
|
+
severity: 'warning',
|
|
1232
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Detect string comparison using == instead of .equals()
|
|
1241
|
+
* IMPORTANT: This is a QUALITY/LOGIC issue, NOT a syntax error
|
|
1242
|
+
* Using == compares object references, not string content
|
|
1243
|
+
*/
|
|
1244
|
+
detectStringComparisonWithEquals(code, lineErrors) {
|
|
1245
|
+
const lines = code.split('\n');
|
|
1246
|
+
lines.forEach((line, index) => {
|
|
1247
|
+
const lineNumber = index + 1;
|
|
1248
|
+
const trimmed = line.trim();
|
|
1249
|
+
// Skip comments
|
|
1250
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
1251
|
+
return;
|
|
1252
|
+
// Remove comments from line for analysis
|
|
1253
|
+
const codeWithoutComment = code_cleaner_1.CodeCleaner.removeLineComments(line, 'java');
|
|
1254
|
+
if (!codeWithoutComment.trim())
|
|
1255
|
+
return;
|
|
1256
|
+
// Detect == or != with string literals or String variables
|
|
1257
|
+
// Pattern 1: variable == "string literal"
|
|
1258
|
+
// Pattern 2: "string literal" == variable
|
|
1259
|
+
// Pattern 3: stringVar == anotherStringVar (if variables look like strings)
|
|
1260
|
+
// Check for == or != with string literals
|
|
1261
|
+
const stringLiteralPattern = /(\w+)\s*(==|!=)\s*"([^"]*)"|"([^"]*)"\s*(==|!=)\s*(\w+)/;
|
|
1262
|
+
const match = codeWithoutComment.match(stringLiteralPattern);
|
|
1263
|
+
if (match) {
|
|
1264
|
+
const operator = match[2] || match[5];
|
|
1265
|
+
const suggestion = operator === '==' ? '.equals()' : '!.equals()';
|
|
1266
|
+
lineErrors.push({
|
|
1267
|
+
line: lineNumber,
|
|
1268
|
+
error: `String comparison using ${operator} instead of .equals() - compares object references, not content`,
|
|
1269
|
+
suggestion: `Use ${suggestion} method for string comparison: variable.equals("string")`,
|
|
1270
|
+
severity: 'warning', // QUALITY issue, not syntax error
|
|
1271
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Detect empty catch blocks - Multi-line empty catch blocks
|
|
1278
|
+
*/
|
|
1279
|
+
detectEmptyCatchBlocks(code, lineErrors) {
|
|
1280
|
+
const lines = code.split('\n');
|
|
1281
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1282
|
+
const line = lines[i];
|
|
1283
|
+
const trimmed = line.trim();
|
|
1284
|
+
// Skip comments
|
|
1285
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
1286
|
+
continue;
|
|
1287
|
+
// Detect catch block opening
|
|
1288
|
+
if (trimmed.match(/catch\s*\([^)]+\)\s*\{/)) {
|
|
1289
|
+
// Check if it's a single-line empty catch (already detected elsewhere)
|
|
1290
|
+
if (trimmed.match(/catch\s*\([^)]+\)\s*\{\s*\}/)) {
|
|
1291
|
+
continue; // Already handled by detectExceptionHandling
|
|
1292
|
+
}
|
|
1293
|
+
// Check next few lines for empty catch block
|
|
1294
|
+
let braceDepth = 1;
|
|
1295
|
+
let isEmpty = true;
|
|
1296
|
+
let j = i + 1;
|
|
1297
|
+
while (j < lines.length && braceDepth > 0) {
|
|
1298
|
+
const nextLine = lines[j].trim();
|
|
1299
|
+
// Skip comments
|
|
1300
|
+
if (!nextLine.startsWith('//') && !nextLine.startsWith('/*') && nextLine.length > 0) {
|
|
1301
|
+
// Count braces
|
|
1302
|
+
for (const char of nextLine) {
|
|
1303
|
+
if (char === '{')
|
|
1304
|
+
braceDepth++;
|
|
1305
|
+
if (char === '}')
|
|
1306
|
+
braceDepth--;
|
|
1307
|
+
}
|
|
1308
|
+
// If there's any code (not just closing brace), it's not empty
|
|
1309
|
+
if (nextLine !== '}' && !nextLine.startsWith('}')) {
|
|
1310
|
+
isEmpty = false;
|
|
1311
|
+
break;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
j++;
|
|
1315
|
+
}
|
|
1316
|
+
if (isEmpty && braceDepth === 0) {
|
|
1317
|
+
lineErrors.push({
|
|
1318
|
+
line: i + 1,
|
|
1319
|
+
error: 'Empty catch block detected - exceptions should not be silently ignored',
|
|
1320
|
+
suggestion: 'Handle the exception: log it, rethrow it, or at least add a comment explaining why it\'s safe to ignore',
|
|
1321
|
+
severity: 'error',
|
|
1322
|
+
references: references_1.javaStandards['exception-handling'] || []
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Detect uninitialized variables - Variables declared but not initialized
|
|
1330
|
+
*/
|
|
1331
|
+
detectUninitializedVariables(code, lineErrors) {
|
|
1332
|
+
const lines = code.split('\n');
|
|
1333
|
+
lines.forEach((line, index) => {
|
|
1334
|
+
const lineNumber = index + 1;
|
|
1335
|
+
const trimmed = line.trim();
|
|
1336
|
+
// Skip comments
|
|
1337
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
1338
|
+
return;
|
|
1339
|
+
// Detect variable declaration without initialization
|
|
1340
|
+
// Pattern: type variableName; (without = assignment)
|
|
1341
|
+
const uninitMatch = line.match(/\b(int|String|boolean|double|float|long|char|byte|short|Integer|Double|Boolean|Object|List|Map|Set|Array)\s+(\w+)\s*;/);
|
|
1342
|
+
if (uninitMatch && !line.includes('=')) {
|
|
1343
|
+
const varType = uninitMatch[1];
|
|
1344
|
+
const varName = uninitMatch[2];
|
|
1345
|
+
// SPRING FRAMEWORK FIX (2025-11-22): Skip dependency injection annotated fields
|
|
1346
|
+
// Spring annotations like @Value, @Autowired, @Inject initialize fields at runtime
|
|
1347
|
+
// Checking previous line for these annotations to avoid false positives
|
|
1348
|
+
const prevLine = index > 0 ? lines[index - 1].trim() : '';
|
|
1349
|
+
const springAnnotations = ['@Value(', '@Autowired', '@Inject', '@Resource'];
|
|
1350
|
+
if (springAnnotations.some(annotation => prevLine.includes(annotation))) {
|
|
1351
|
+
// This field is initialized by Spring/Jakarta - skip uninitialized check
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
// Check if variable is used before initialization in next lines
|
|
1355
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 10, lines.length));
|
|
1356
|
+
let isUsedBeforeInit = false;
|
|
1357
|
+
let isInitialized = false;
|
|
1358
|
+
for (const nextLine of nextLines) {
|
|
1359
|
+
// Skip comments
|
|
1360
|
+
if (nextLine.trim().startsWith('//'))
|
|
1361
|
+
continue;
|
|
1362
|
+
// Check if variable is assigned
|
|
1363
|
+
if (nextLine.match(new RegExp(`${varName}\\s*=`))) {
|
|
1364
|
+
isInitialized = true;
|
|
1365
|
+
break;
|
|
1366
|
+
}
|
|
1367
|
+
// Check if variable is used (in expression, method call, etc.)
|
|
1368
|
+
if (nextLine.includes(varName) && !nextLine.match(new RegExp(`\\b${varType}\\s+${varName}\\b`))) {
|
|
1369
|
+
isUsedBeforeInit = true;
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
if (isUsedBeforeInit && !isInitialized) {
|
|
1374
|
+
lineErrors.push({
|
|
1375
|
+
line: lineNumber,
|
|
1376
|
+
error: `Variable "${varName}" may not be initialized before use`,
|
|
1377
|
+
suggestion: `Initialize variable: ${varType} ${varName} = null; // or appropriate default value`,
|
|
1378
|
+
severity: 'error',
|
|
1379
|
+
references: references_1.javaStandards['compilation-errors'] || []
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Detect missing return statements - Non-void methods without return in all paths
|
|
1387
|
+
*/
|
|
1388
|
+
detectMissingReturnStatements(code, lineErrors) {
|
|
1389
|
+
const lines = code.split('\n');
|
|
1390
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1391
|
+
const line = lines[i];
|
|
1392
|
+
const trimmed = line.trim();
|
|
1393
|
+
// Skip comments
|
|
1394
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
1395
|
+
continue;
|
|
1396
|
+
// Detect non-void method declaration
|
|
1397
|
+
const methodMatch = line.match(/(?:public|private|protected)\s+(\w+)\s+(\w+)\s*\([^)]*\)\s*\{/);
|
|
1398
|
+
if (methodMatch && methodMatch[1] !== 'void') {
|
|
1399
|
+
const returnType = methodMatch[1];
|
|
1400
|
+
const methodName = methodMatch[2];
|
|
1401
|
+
// Look for if-else blocks without return in all branches
|
|
1402
|
+
let hasIfElse = false;
|
|
1403
|
+
let hasReturnInIf = false;
|
|
1404
|
+
let hasReturnInElse = false;
|
|
1405
|
+
let braceDepth = 1;
|
|
1406
|
+
let j = i + 1;
|
|
1407
|
+
while (j < lines.length && braceDepth > 0) {
|
|
1408
|
+
const nextLine = lines[j].trim();
|
|
1409
|
+
// Count braces
|
|
1410
|
+
for (const char of nextLine) {
|
|
1411
|
+
if (char === '{')
|
|
1412
|
+
braceDepth++;
|
|
1413
|
+
if (char === '}')
|
|
1414
|
+
braceDepth--;
|
|
1415
|
+
}
|
|
1416
|
+
// Check for if-else
|
|
1417
|
+
if (nextLine.startsWith('if ') || nextLine.startsWith('if(')) {
|
|
1418
|
+
hasIfElse = true;
|
|
1419
|
+
}
|
|
1420
|
+
// Check for return in if block
|
|
1421
|
+
if (hasIfElse && nextLine.includes('return') && braceDepth === 2) {
|
|
1422
|
+
hasReturnInIf = true;
|
|
1423
|
+
}
|
|
1424
|
+
// Check for else
|
|
1425
|
+
if (nextLine.startsWith('else') || nextLine.includes('} else')) {
|
|
1426
|
+
// Check for return in else block
|
|
1427
|
+
const elseStart = j;
|
|
1428
|
+
let elseDepth = braceDepth;
|
|
1429
|
+
for (let k = elseStart; k < Math.min(elseStart + 10, lines.length); k++) {
|
|
1430
|
+
if (lines[k].includes('return')) {
|
|
1431
|
+
hasReturnInElse = true;
|
|
1432
|
+
break;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
j++;
|
|
1437
|
+
}
|
|
1438
|
+
// If there's an if-else but missing return in else branch
|
|
1439
|
+
if (hasIfElse && hasReturnInIf && !hasReturnInElse) {
|
|
1440
|
+
lineErrors.push({
|
|
1441
|
+
line: i + 1,
|
|
1442
|
+
error: `Method "${methodName}" may not return a value in all code paths`,
|
|
1443
|
+
suggestion: `Add return statement in else branch: return ${returnType === 'boolean' ? 'false' : returnType === 'int' ? '0' : 'null'};`,
|
|
1444
|
+
severity: 'error',
|
|
1445
|
+
references: references_1.javaStandards['compilation-errors'] || []
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Detect invalid modifier order - Incorrect order (e.g., "static public" instead of "public static")
|
|
1453
|
+
*/
|
|
1454
|
+
detectInvalidModifierOrder(code, lineErrors) {
|
|
1455
|
+
const lines = code.split('\n');
|
|
1456
|
+
lines.forEach((line, index) => {
|
|
1457
|
+
const lineNumber = index + 1;
|
|
1458
|
+
const trimmed = line.trim();
|
|
1459
|
+
// Skip comments
|
|
1460
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
1461
|
+
return;
|
|
1462
|
+
// Detect invalid modifier order
|
|
1463
|
+
// Correct order: [access modifier] [static] [final] [other modifiers]
|
|
1464
|
+
// Invalid patterns:
|
|
1465
|
+
// - static/final/abstract before access modifiers
|
|
1466
|
+
const invalidOrderPatterns = [
|
|
1467
|
+
{ pattern: /\b(static|final|abstract|synchronized|native|strictfp)\s+(public|private|protected)\b/, correct: 'public static' },
|
|
1468
|
+
{ pattern: /\bfinal\s+static\b/, correct: 'static final' }
|
|
1469
|
+
];
|
|
1470
|
+
invalidOrderPatterns.forEach(({ pattern, correct }) => {
|
|
1471
|
+
if (pattern.test(line)) {
|
|
1472
|
+
const match = line.match(pattern);
|
|
1473
|
+
if (match) {
|
|
1474
|
+
lineErrors.push({
|
|
1475
|
+
line: lineNumber,
|
|
1476
|
+
error: `Invalid modifier order: "${match[0]}"`,
|
|
1477
|
+
suggestion: `Use correct order: ${correct}`,
|
|
1478
|
+
severity: 'warning',
|
|
1479
|
+
references: references_1.javaStandards['naming-conventions'] || []
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
analyzeQuality(code, result) {
|
|
1487
|
+
const issues = [];
|
|
1488
|
+
// Check for class names starting with lowercase
|
|
1489
|
+
const classes = code.match(/class\s+([a-z][a-zA-Z]*)/g);
|
|
1490
|
+
if (classes) {
|
|
1491
|
+
issues.push({
|
|
1492
|
+
type: 'warning',
|
|
1493
|
+
message: 'Class names should start with uppercase (PascalCase)',
|
|
1494
|
+
severity: 'medium'
|
|
1495
|
+
});
|
|
1496
|
+
result.quality.score -= 5;
|
|
1497
|
+
}
|
|
1498
|
+
// Check for very long methods
|
|
1499
|
+
const methods = code.match(/public\s+\w+\s+\w+\s*\([^)]*\)\s*\{[^}]+\}/g);
|
|
1500
|
+
if (methods) {
|
|
1501
|
+
methods.forEach(method => {
|
|
1502
|
+
const lines = method.split('\n').length;
|
|
1503
|
+
if (lines > 20) {
|
|
1504
|
+
issues.push({
|
|
1505
|
+
type: 'warning',
|
|
1506
|
+
message: 'Method too long - consider refactoring',
|
|
1507
|
+
severity: 'medium'
|
|
1508
|
+
});
|
|
1509
|
+
result.quality.score -= 5;
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
// Check for magic numbers usage
|
|
1514
|
+
const magicNumbers = code.match(/[^a-zA-Z_]\d{2,}/g);
|
|
1515
|
+
if (magicNumbers && magicNumbers.length > 2) {
|
|
1516
|
+
issues.push({
|
|
1517
|
+
type: 'info',
|
|
1518
|
+
message: 'Consider using named constants instead of magic numbers',
|
|
1519
|
+
severity: 'low'
|
|
1520
|
+
});
|
|
1521
|
+
result.quality.score -= 3;
|
|
1522
|
+
}
|
|
1523
|
+
// Check for missing JavaDoc in public methods
|
|
1524
|
+
const publicMethods = code.match(/public\s+\w+\s+\w+/g);
|
|
1525
|
+
const javadocCount = (code.match(/\/\*\*[\s\S]*?\*\//g) || []).length;
|
|
1526
|
+
if (publicMethods && publicMethods.length > javadocCount) {
|
|
1527
|
+
issues.push({
|
|
1528
|
+
type: 'info',
|
|
1529
|
+
message: 'Add JavaDoc documentation to public methods',
|
|
1530
|
+
severity: 'low'
|
|
1531
|
+
});
|
|
1532
|
+
result.quality.score -= 2;
|
|
1533
|
+
}
|
|
1534
|
+
result.quality.issues = issues;
|
|
1535
|
+
result.quality.score = Math.max(0, result.quality.score);
|
|
1536
|
+
}
|
|
1537
|
+
analyzePerformance(code, result) {
|
|
1538
|
+
const suggestions = [];
|
|
1539
|
+
// Check for string concatenation in loops
|
|
1540
|
+
if (code.match(/for.*\{[\s\S]*?\+\s*=.*"/)) {
|
|
1541
|
+
suggestions.push('Use StringBuilder for concatenation in loops');
|
|
1542
|
+
result.performance.score -= 15;
|
|
1543
|
+
}
|
|
1544
|
+
// Check Vector vs ArrayList usage
|
|
1545
|
+
if (code.includes('Vector')) {
|
|
1546
|
+
suggestions.push('Use ArrayList instead of Vector for better performance');
|
|
1547
|
+
result.performance.score -= 8;
|
|
1548
|
+
}
|
|
1549
|
+
// Check Hashtable vs HashMap usage
|
|
1550
|
+
if (code.includes('Hashtable')) {
|
|
1551
|
+
suggestions.push('Use HashMap instead of Hashtable (unless synchronization is needed)');
|
|
1552
|
+
result.performance.score -= 8;
|
|
1553
|
+
}
|
|
1554
|
+
// Check for unnecessary loops
|
|
1555
|
+
if (code.match(/for\s*\([^)]+\)\s*\{\s*if\s*\([^)]+\)\s*\{[\s\S]*?\}\s*\}/)) {
|
|
1556
|
+
suggestions.push('Consider using streams with filter() for better readability');
|
|
1557
|
+
result.performance.score -= 5;
|
|
1558
|
+
}
|
|
1559
|
+
// Check System.out.println usage in loops
|
|
1560
|
+
if (code.match(/for.*\{[\s\S]*?System\.out\.println/)) {
|
|
1561
|
+
suggestions.push('Avoid I/O in loops for better performance');
|
|
1562
|
+
result.performance.score -= 10;
|
|
1563
|
+
}
|
|
1564
|
+
result.performance.suggestions = suggestions;
|
|
1565
|
+
result.performance.score = Math.max(0, result.performance.score);
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Analyzes Java code for security vulnerabilities using modular security checks
|
|
1569
|
+
*
|
|
1570
|
+
* This method coordinates all security analysis by delegating to specialized modules:
|
|
1571
|
+
* - Injection attacks (SQL, Command, LDAP, XPath)
|
|
1572
|
+
* - Deserialization and XXE vulnerabilities
|
|
1573
|
+
* - Hardcoded credentials detection
|
|
1574
|
+
* - Cryptographic validation (weak algorithms, insecure random)
|
|
1575
|
+
* - File operation security (path traversal, upload validation)
|
|
1576
|
+
* - Unsafe patterns (reflection, NPE, exception handling)
|
|
1577
|
+
* - Code quality issues (God classes, debug output)
|
|
1578
|
+
*
|
|
1579
|
+
* All checks are performed in parallel for optimal performance.
|
|
1580
|
+
*/
|
|
1581
|
+
analyzeSecurity(code, result) {
|
|
1582
|
+
const lines = code.split('\n');
|
|
1583
|
+
// Run all modular security checks
|
|
1584
|
+
const vulnerabilities = [
|
|
1585
|
+
...(0, injection_attacks_1.checkInjectionAttacks)(lines),
|
|
1586
|
+
...(0, deserialization_xxe_1.checkDeserializationAndXXE)(lines),
|
|
1587
|
+
...(0, hardcoded_credentials_1.checkHardcodedCredentials)(lines),
|
|
1588
|
+
...(0, crypto_validation_1.checkCryptoValidation)(lines),
|
|
1589
|
+
...(0, file_operations_1.checkFileOperations)(lines),
|
|
1590
|
+
...(0, unsafe_patterns_1.checkUnsafePatterns)(lines),
|
|
1591
|
+
...(0, code_quality_1.checkCodeQuality)(lines, code),
|
|
1592
|
+
...(0, framework_security_1.checkFrameworkSecurity)(lines, code),
|
|
1593
|
+
// OWASP A02:2025 - Security Misconfiguration (NEW - Phase 7B)
|
|
1594
|
+
...(0, security_misconfiguration_1.checkSecurityMisconfiguration)(lines),
|
|
1595
|
+
// OWASP A10:2025 - Mishandling of Exceptional Conditions (NEW - Phase 7B)
|
|
1596
|
+
...(0, exception_handling_1.checkExceptionHandling)(lines),
|
|
1597
|
+
// OWASP A03:2025 - Software Supply Chain Failures (Enhanced - Phase 7B)
|
|
1598
|
+
...(0, enhanced_supply_chain_1.checkEnhancedSupplyChain)(lines),
|
|
1599
|
+
// OWASP A01:2025 - Broken Access Control (Phase 7B Day 8)
|
|
1600
|
+
...(0, access_control_1.checkAccessControl)(lines),
|
|
1601
|
+
// OWASP A06:2025 - Insecure Design (Phase 7B Day 9)
|
|
1602
|
+
...(0, insecure_design_1.checkInsecureDesign)(lines),
|
|
1603
|
+
// OWASP A09:2025 - Security Logging and Monitoring Failures (Phase 7B Day 9)
|
|
1604
|
+
...(0, logging_failures_1.checkLoggingFailures)(lines)
|
|
1605
|
+
];
|
|
1606
|
+
// Secrets Detection (Phase 1.5, Week 1)
|
|
1607
|
+
const secretsAnalyzer = (0, secrets_analyzer_1.createSecretsAnalyzer)();
|
|
1608
|
+
vulnerabilities.push(...secretsAnalyzer.analyzeCode(code, 'unknown.java', 'java'));
|
|
1609
|
+
// =============================================================================
|
|
1610
|
+
// DEDUPLICATION: Remove duplicate vulnerabilities on same line
|
|
1611
|
+
// =============================================================================
|
|
1612
|
+
// Fix for Beta Testing Issue: Deserialization and XXE reported twice
|
|
1613
|
+
// Multiple checks can flag the same line (e.g., different detection patterns)
|
|
1614
|
+
// Solution: Keep only the vulnerability with highest CVSS score per line
|
|
1615
|
+
result.security.vulnerabilities = this.deduplicateVulnerabilities(vulnerabilities);
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Deduplicate vulnerabilities that appear on the same line
|
|
1619
|
+
*
|
|
1620
|
+
* Beta Testing Fix (Dec 9, 2025):
|
|
1621
|
+
* - Deserialization reported twice (line 56, CVSS 9.8 and 5.0)
|
|
1622
|
+
* - XXE reported twice (lines 50 and 52)
|
|
1623
|
+
*
|
|
1624
|
+
* Strategy: For each line with multiple vulnerabilities of similar type,
|
|
1625
|
+
* keep only the one with the highest CVSS score.
|
|
1626
|
+
*
|
|
1627
|
+
* @param vulnerabilities - Array of all detected vulnerabilities
|
|
1628
|
+
* @returns Deduplicated array with highest CVSS per line
|
|
1629
|
+
*/
|
|
1630
|
+
/**
|
|
1631
|
+
* P1-5: Generic deduplication for ALL vulnerability types (Dec 30, 2025)
|
|
1632
|
+
* Previous: Only handled deserialization and XXE
|
|
1633
|
+
* Now: Handles all duplicates using category-based deduplication
|
|
1634
|
+
*/
|
|
1635
|
+
deduplicateVulnerabilities(vulnerabilities) {
|
|
1636
|
+
// P1-5: Use category-based deduplication (line + category as unique key)
|
|
1637
|
+
const getCategory = (message) => {
|
|
1638
|
+
const lower = message.toLowerCase();
|
|
1639
|
+
if (lower.includes('hardcoded credential'))
|
|
1640
|
+
return 'hardcoded-credential';
|
|
1641
|
+
if (lower.includes('sql injection'))
|
|
1642
|
+
return 'sql-injection';
|
|
1643
|
+
if (lower.includes('command injection'))
|
|
1644
|
+
return 'command-injection';
|
|
1645
|
+
if (lower.includes('deserialization'))
|
|
1646
|
+
return 'deserialization';
|
|
1647
|
+
if (lower.includes('xxe') || lower.includes('xml external entity'))
|
|
1648
|
+
return 'xxe';
|
|
1649
|
+
if (lower.includes('path traversal'))
|
|
1650
|
+
return 'path-traversal';
|
|
1651
|
+
if (lower.includes('ldap injection'))
|
|
1652
|
+
return 'ldap-injection';
|
|
1653
|
+
if (lower.includes('xss') || lower.includes('cross-site scripting'))
|
|
1654
|
+
return 'xss';
|
|
1655
|
+
if (lower.includes('ssrf'))
|
|
1656
|
+
return 'ssrf';
|
|
1657
|
+
if (lower.includes('weak hash') || lower.includes('md5') || lower.includes('sha1'))
|
|
1658
|
+
return 'weak-hash';
|
|
1659
|
+
if (lower.includes('insecure random'))
|
|
1660
|
+
return 'insecure-random';
|
|
1661
|
+
if (lower.includes('missing csrf'))
|
|
1662
|
+
return 'missing-csrf';
|
|
1663
|
+
if (lower.includes('open redirect'))
|
|
1664
|
+
return 'open-redirect';
|
|
1665
|
+
if (lower.includes('missing authentication'))
|
|
1666
|
+
return 'missing-authentication';
|
|
1667
|
+
if (lower.includes('logging'))
|
|
1668
|
+
return 'logging';
|
|
1669
|
+
// Fallback: use first 30 chars as category
|
|
1670
|
+
return message.substring(0, 30);
|
|
1671
|
+
};
|
|
1672
|
+
const uniqueMap = new Map();
|
|
1673
|
+
vulnerabilities.forEach(vuln => {
|
|
1674
|
+
const line = vuln.line || 0;
|
|
1675
|
+
const category = getCategory(vuln.message || '');
|
|
1676
|
+
const key = `${line}:${category}`;
|
|
1677
|
+
const existing = uniqueMap.get(key);
|
|
1678
|
+
if (!existing) {
|
|
1679
|
+
uniqueMap.set(key, vuln);
|
|
1680
|
+
}
|
|
1681
|
+
else {
|
|
1682
|
+
// Keep higher CVSS score
|
|
1683
|
+
const existingScore = existing.cvssScore || 0;
|
|
1684
|
+
const currentScore = vuln.cvssScore || 0;
|
|
1685
|
+
if (currentScore > existingScore) {
|
|
1686
|
+
uniqueMap.set(key, vuln);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
});
|
|
1690
|
+
return Array.from(uniqueMap.values());
|
|
1691
|
+
}
|
|
1692
|
+
calculateMetrics(code, result) {
|
|
1693
|
+
const lines = code.split('\n');
|
|
1694
|
+
result.metrics.lines = lines.length;
|
|
1695
|
+
const methods = (code.match(/public\s+\w+\s+\w+\s*\(/g) || []).length;
|
|
1696
|
+
result.metrics.functions = methods;
|
|
1697
|
+
// Calculate cyclomatic complexity - Java
|
|
1698
|
+
let complexity = 1;
|
|
1699
|
+
const javaKeywords = ['if', 'else', 'for', 'while', 'switch', 'case', 'catch', 'finally'];
|
|
1700
|
+
javaKeywords.forEach(keyword => {
|
|
1701
|
+
const matches = code.match(new RegExp(`\\b${keyword}\\b`, 'g'));
|
|
1702
|
+
if (matches)
|
|
1703
|
+
complexity += matches.length;
|
|
1704
|
+
});
|
|
1705
|
+
// Java logical operators
|
|
1706
|
+
const andOperators = code.match(/&&/g);
|
|
1707
|
+
const orOperators = code.match(/\|\|/g);
|
|
1708
|
+
const ternaryOperators = code.match(/\?[^:]*:/g);
|
|
1709
|
+
if (andOperators)
|
|
1710
|
+
complexity += andOperators.length;
|
|
1711
|
+
if (orOperators)
|
|
1712
|
+
complexity += orOperators.length;
|
|
1713
|
+
if (ternaryOperators)
|
|
1714
|
+
complexity += ternaryOperators.length;
|
|
1715
|
+
result.metrics.complexity = complexity;
|
|
1716
|
+
result.metrics.maintainability = Math.max(0, 100 - complexity * 3);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
exports.JavaAnalyzer = JavaAnalyzer;
|
|
1720
|
+
//# sourceMappingURL=java-analyzer.js.map
|