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,2141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ⚠️ SHARED MODULE: JavaScript 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 code at /analyze → Verify results appear
|
|
18
|
+
* 3. Test GitHub: Open PR → Verify webhook comment appears
|
|
19
|
+
* 4. Verify performance: Analysis must complete in <2s per file
|
|
20
|
+
* 5. Check detection rate: All test cases must still be detected
|
|
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
|
+
* PROVEN INTERFERENCE:
|
|
28
|
+
* - Version 20251117.14:30: Phase 7 GitHub work broke WebTool
|
|
29
|
+
* - Root cause: Shared module modified without cross-system testing
|
|
30
|
+
*
|
|
31
|
+
* See: docs/technical/WEBTOOL_GITHUB_SEPARATION.md
|
|
32
|
+
*
|
|
33
|
+
* Last modified: 2025-11-18
|
|
34
|
+
* Last verified (both systems): 2025-11-18
|
|
35
|
+
*/
|
|
36
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
37
|
+
if (k2 === undefined) k2 = k;
|
|
38
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
39
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
40
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
41
|
+
}
|
|
42
|
+
Object.defineProperty(o, k2, desc);
|
|
43
|
+
}) : (function(o, m, k, k2) {
|
|
44
|
+
if (k2 === undefined) k2 = k;
|
|
45
|
+
o[k2] = m[k];
|
|
46
|
+
}));
|
|
47
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
48
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
49
|
+
}) : function(o, v) {
|
|
50
|
+
o["default"] = v;
|
|
51
|
+
});
|
|
52
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
53
|
+
var ownKeys = function(o) {
|
|
54
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
55
|
+
var ar = [];
|
|
56
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
57
|
+
return ar;
|
|
58
|
+
};
|
|
59
|
+
return ownKeys(o);
|
|
60
|
+
};
|
|
61
|
+
return function (mod) {
|
|
62
|
+
if (mod && mod.__esModule) return mod;
|
|
63
|
+
var result = {};
|
|
64
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
65
|
+
__setModuleDefault(result, mod);
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
68
|
+
})();
|
|
69
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
70
|
+
exports.JavaScriptAnalyzer = void 0;
|
|
71
|
+
const acorn = __importStar(require("acorn"));
|
|
72
|
+
const references_1 = require("../standards/references");
|
|
73
|
+
const severity_scoring_1 = require("../security/severity-scoring");
|
|
74
|
+
const compliance_mapping_1 = require("../security/compliance-mapping");
|
|
75
|
+
const code_cleaner_1 = require("../utils/code-cleaner");
|
|
76
|
+
const jsx_helpers_1 = require("./helpers/jsx-helpers");
|
|
77
|
+
const variable_tracker_1 = require("./helpers/variable-tracker");
|
|
78
|
+
const react_security_1 = require("./security-checks/react-security");
|
|
79
|
+
const es6_security_1 = require("./security-checks/es6-security");
|
|
80
|
+
const security_misconfiguration_1 = require("./javascript/security-checks/security-misconfiguration");
|
|
81
|
+
const exception_handling_1 = require("./javascript/security-checks/exception-handling");
|
|
82
|
+
const enhanced_supply_chain_1 = require("./javascript/security-checks/enhanced-supply-chain");
|
|
83
|
+
const access_control_1 = require("./javascript/security-checks/access-control");
|
|
84
|
+
const authentication_failures_1 = require("./javascript/security-checks/authentication-failures");
|
|
85
|
+
const insecure_design_1 = require("./javascript/security-checks/insecure-design");
|
|
86
|
+
const software_integrity_1 = require("./javascript/security-checks/software-integrity");
|
|
87
|
+
const ai_generated_code_1 = require("./javascript/security-checks/ai-generated-code");
|
|
88
|
+
// Modular JavaScript Analyzer - Extracted modules (Week 2)
|
|
89
|
+
// Syntax helpers
|
|
90
|
+
const syntax_helpers_1 = require("./javascript/syntax/syntax-helpers");
|
|
91
|
+
const typescript_syntax_1 = require("./javascript/syntax/typescript-syntax");
|
|
92
|
+
// Quality checks
|
|
93
|
+
const ai_hallucinations_1 = require("./javascript/quality-checks/ai-hallucinations");
|
|
94
|
+
const reference_errors_1 = require("./javascript/quality-checks/reference-errors");
|
|
95
|
+
const comparison_issues_1 = require("./javascript/quality-checks/comparison-issues");
|
|
96
|
+
const async_patterns_1 = require("./javascript/quality-checks/async-patterns");
|
|
97
|
+
const code_patterns_1 = require("./javascript/quality-checks/code-patterns");
|
|
98
|
+
// Security checks
|
|
99
|
+
const injection_attacks_1 = require("./javascript/security-checks/injection-attacks");
|
|
100
|
+
const xss_dom_security_1 = require("./javascript/security-checks/xss-dom-security");
|
|
101
|
+
const credential_crypto_1 = require("./javascript/security-checks/credential-crypto");
|
|
102
|
+
const storage_security_1 = require("./javascript/security-checks/storage-security");
|
|
103
|
+
const secrets_analyzer_1 = require("./secrets/secrets-analyzer");
|
|
104
|
+
// Utils
|
|
105
|
+
const metrics_calculator_1 = require("./javascript/utils/metrics-calculator");
|
|
106
|
+
const performance_analyzer_1 = require("./javascript/utils/performance-analyzer");
|
|
107
|
+
const createVulnerability_1 = require("./javascript/utils/createVulnerability");
|
|
108
|
+
class JavaScriptAnalyzer {
|
|
109
|
+
constructor() {
|
|
110
|
+
this.language = 'javascript';
|
|
111
|
+
}
|
|
112
|
+
async analyze(input) {
|
|
113
|
+
const result = {
|
|
114
|
+
syntax: { valid: true, errors: [], lineErrors: [] },
|
|
115
|
+
quality: { score: 100, issues: [] },
|
|
116
|
+
performance: { score: 100, suggestions: [] },
|
|
117
|
+
security: { vulnerabilities: [] },
|
|
118
|
+
metrics: { complexity: 1, maintainability: 100, lines: 0, functions: 0 }
|
|
119
|
+
};
|
|
120
|
+
try {
|
|
121
|
+
await this.analyzeSyntax(input.code, result);
|
|
122
|
+
// Quality-check modules (extracted)
|
|
123
|
+
result.syntax.lineErrors.push(...(0, ai_hallucinations_1.detectAIHallucinations)(input.code, syntax_helpers_1.isInsideTemplateLiteral));
|
|
124
|
+
result.syntax.lineErrors.push(...(0, reference_errors_1.detectReferenceErrors)(input.code, syntax_helpers_1.removeStringLiterals, syntax_helpers_1.isInsideTemplateLiteral));
|
|
125
|
+
result.syntax.lineErrors.push(...(0, comparison_issues_1.detectComparisonIssues)(input.code, syntax_helpers_1.isInsideTemplateLiteral));
|
|
126
|
+
result.syntax.lineErrors.push(...(0, async_patterns_1.detectUnhandledPromises)(input.code, syntax_helpers_1.isInsideTemplateLiteral));
|
|
127
|
+
result.syntax.lineErrors.push(...(0, async_patterns_1.detectThisContextIssues)(input.code, syntax_helpers_1.isInsideTemplateLiteral));
|
|
128
|
+
result.syntax.lineErrors.push(...(0, async_patterns_1.detectCallbackHell)(input.code, syntax_helpers_1.isInsideTemplateLiteral));
|
|
129
|
+
result.syntax.lineErrors.push(...(0, code_patterns_1.detectArrayMutations)(input.code, syntax_helpers_1.isInsideTemplateLiteral));
|
|
130
|
+
result.syntax.lineErrors.push(...(0, code_patterns_1.detectDOMNullChecks)(input.code, syntax_helpers_1.isInsideTemplateLiteral));
|
|
131
|
+
result.syntax.lineErrors.push(...(0, code_patterns_1.detectBlockingOperations)(input.code, syntax_helpers_1.isInsideTemplateLiteral));
|
|
132
|
+
this.analyzeQuality(input.code, result);
|
|
133
|
+
// Performance module (extracted)
|
|
134
|
+
const performanceAnalysis = (0, performance_analyzer_1.analyzePerformance)(input.code);
|
|
135
|
+
result.performance.score = performanceAnalysis.score;
|
|
136
|
+
result.performance.suggestions = performanceAnalysis.suggestions;
|
|
137
|
+
// Security-check modules (extracted)
|
|
138
|
+
result.security.vulnerabilities.push(...(0, injection_attacks_1.checkInjectionAttacks)(input.code, createVulnerability_1.createJavaScriptSecurityVulnerability));
|
|
139
|
+
result.security.vulnerabilities.push(...(0, xss_dom_security_1.checkXSSDOMSecurity)(input.code, createVulnerability_1.createJavaScriptSecurityVulnerability));
|
|
140
|
+
result.security.vulnerabilities.push(...(0, credential_crypto_1.checkCredentialCrypto)(input.code, createVulnerability_1.createJavaScriptSecurityVulnerability));
|
|
141
|
+
result.security.vulnerabilities.push(...(0, storage_security_1.checkStorageSecurity)(input.code, createVulnerability_1.createJavaScriptSecurityVulnerability));
|
|
142
|
+
// Secrets Detection (Phase 1.5, Week 1)
|
|
143
|
+
const secretsAnalyzer = (0, secrets_analyzer_1.createSecretsAnalyzer)();
|
|
144
|
+
result.security.vulnerabilities.push(...secretsAnalyzer.analyzeCode(input.code, input.filename || 'unknown.js', 'javascript'));
|
|
145
|
+
// AI-Generated Code Detection (Phase 1.5, Week 5-7)
|
|
146
|
+
result.security.vulnerabilities.push(...(0, ai_generated_code_1.checkAIGeneratedCode)(input.code.split('\n'), input.filename));
|
|
147
|
+
// Additional security checks (React, ES6, Node.js, OWASP 2025)
|
|
148
|
+
this.analyzeSecurity(input.code, result);
|
|
149
|
+
// Metrics module (extracted)
|
|
150
|
+
const metrics = (0, metrics_calculator_1.calculateMetrics)(input.code, typescript_syntax_1.isTypeScriptCode);
|
|
151
|
+
result.metrics.lines = metrics.lines;
|
|
152
|
+
result.metrics.functions = metrics.functions;
|
|
153
|
+
result.metrics.complexity = metrics.complexity;
|
|
154
|
+
result.metrics.maintainability = metrics.maintainability;
|
|
155
|
+
// Re-validate syntax.valid after all error detections (Phase 1 adds errors after initial validation)
|
|
156
|
+
const errorCount = result.syntax.lineErrors?.filter(e => e.severity === 'error').length || 0;
|
|
157
|
+
if (errorCount > 0 || result.syntax.errors.length > 0) {
|
|
158
|
+
result.syntax.valid = false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown Error';
|
|
163
|
+
result.syntax.valid = false;
|
|
164
|
+
result.syntax.errors.push(`Analysis error: ${errorMessage}`);
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
async validateSyntax(code) {
|
|
169
|
+
try {
|
|
170
|
+
acorn.parse(code, {
|
|
171
|
+
ecmaVersion: 2022,
|
|
172
|
+
sourceType: 'module',
|
|
173
|
+
allowReturnOutsideFunction: true
|
|
174
|
+
});
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
getLanguageInfo() {
|
|
182
|
+
return {
|
|
183
|
+
name: 'JavaScript',
|
|
184
|
+
extensions: ['.js', '.jsx', '.ts', '.tsx', '.mjs'],
|
|
185
|
+
description: 'Language for web development and Node.js'
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
isTypeScriptCode(code) {
|
|
189
|
+
// Detect common TypeScript syntax
|
|
190
|
+
const typeScriptPatterns = [
|
|
191
|
+
/interface\s+\w+/, // Interface declarations
|
|
192
|
+
/type\s+\w+\s*=/, // type aliases
|
|
193
|
+
/:\s*\w+(\[\])?/, // type annotations
|
|
194
|
+
/export\s+interface/, // exported interfaces
|
|
195
|
+
/export\s+type/, // exported types
|
|
196
|
+
/<.*>/, // generic types
|
|
197
|
+
/as\s+\w+/, // type assertions
|
|
198
|
+
/enum\s+\w+/, // enum declarations
|
|
199
|
+
/namespace\s+\w+/, // namespace declarations
|
|
200
|
+
/declare\s+/, // declare statements
|
|
201
|
+
/readonly\s+/, // readonly modifier
|
|
202
|
+
/public\s+|private\s+|protected\s+/, // access modifiers
|
|
203
|
+
];
|
|
204
|
+
return typeScriptPatterns.some(pattern => pattern.test(code));
|
|
205
|
+
}
|
|
206
|
+
checkBasicTypeScriptSyntax(code, errors, lineErrors) {
|
|
207
|
+
const lines = code.split('\n');
|
|
208
|
+
// Track template literal state across lines
|
|
209
|
+
// Template literals can span multiple lines: const str = `line1
|
|
210
|
+
// line2`;
|
|
211
|
+
let insideTemplateLiteral = false;
|
|
212
|
+
// First pass: basic line-by-line checks
|
|
213
|
+
lines.forEach((line, index) => {
|
|
214
|
+
const lineNumber = index + 1;
|
|
215
|
+
const trimmed = line.trim();
|
|
216
|
+
// Count backticks on this line to track template literal state
|
|
217
|
+
const backtickCount = (line.match(/`/g) || []).length;
|
|
218
|
+
// Toggle state for each backtick found
|
|
219
|
+
if (backtickCount > 0) {
|
|
220
|
+
for (let i = 0; i < backtickCount; i++) {
|
|
221
|
+
insideTemplateLiteral = !insideTemplateLiteral;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Skip ALL checks if we're inside a template literal
|
|
225
|
+
// Template literals contain arbitrary text (Python, SQL, HTML, etc.)
|
|
226
|
+
// This line is BETWEEN opening and closing backticks = it's a string!
|
|
227
|
+
if (insideTemplateLiteral && backtickCount === 0) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// Skip empty lines and comments completely
|
|
231
|
+
if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
// AI Hallucination: Check for .push() on strings (Python/list confusion)
|
|
235
|
+
// Strings in JS are immutable, use += or array methods
|
|
236
|
+
// IMPORTANT: This is a SEMANTIC error (runtime TypeError), not syntax error
|
|
237
|
+
// The code IS valid JavaScript - it will parse successfully but fail at runtime
|
|
238
|
+
if (trimmed.match(/(['"`].*['"`]|String\(|\.toString\(\)|\.toLowerCase\(\)|\.toUpperCase\(\)).*\.push\(/) ||
|
|
239
|
+
trimmed.match(/\w+\.push\(/) && (trimmed.includes("''") || trimmed.includes('""') || trimmed.includes('``') ||
|
|
240
|
+
trimmed.match(/=\s*['"`]/))) {
|
|
241
|
+
lineErrors.push({
|
|
242
|
+
line: lineNumber,
|
|
243
|
+
error: 'Strings don\'t have .push() method (common AI hallucination)',
|
|
244
|
+
suggestion: 'Strings are immutable in JS. Use: str += value, str = str + value, or convert to array first',
|
|
245
|
+
severity: 'warning' // Changed from 'error' - this is semantic, not syntax
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
// Check for missing semicolons in property definitions
|
|
249
|
+
if ((0, typescript_syntax_1.shouldHaveSemicolon)(trimmed)) {
|
|
250
|
+
lineErrors.push({
|
|
251
|
+
line: lineNumber,
|
|
252
|
+
error: 'Missing semicolon',
|
|
253
|
+
suggestion: `Add ; at the end: ${trimmed};`,
|
|
254
|
+
severity: 'error'
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
// Check for unclosed strings
|
|
258
|
+
if ((0, typescript_syntax_1.hasUnclosedString)(trimmed)) {
|
|
259
|
+
lineErrors.push({
|
|
260
|
+
line: lineNumber,
|
|
261
|
+
error: 'Unclosed string',
|
|
262
|
+
suggestion: 'Check the quotes on this line',
|
|
263
|
+
severity: 'error'
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
// Second pass: structural validation
|
|
268
|
+
this.validateTypeScriptStructure(code, errors, lineErrors);
|
|
269
|
+
}
|
|
270
|
+
validateTypeScriptStructure(code, errors, lineErrors) {
|
|
271
|
+
const lines = code.split('\n');
|
|
272
|
+
// Track Interface blocks
|
|
273
|
+
let insideInterface = false;
|
|
274
|
+
let interfaceStartLine = 0;
|
|
275
|
+
let braceCount = 0;
|
|
276
|
+
let expectedProperties = [];
|
|
277
|
+
lines.forEach((line, index) => {
|
|
278
|
+
const lineNumber = index + 1;
|
|
279
|
+
const trimmed = line.trim();
|
|
280
|
+
// Skip comments and empty lines
|
|
281
|
+
if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('/*')) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
// Detect Interface start
|
|
285
|
+
if (trimmed.startsWith('export Interface') || trimmed.startsWith('Interface')) {
|
|
286
|
+
insideInterface = true;
|
|
287
|
+
interfaceStartLine = lineNumber;
|
|
288
|
+
braceCount = 0;
|
|
289
|
+
}
|
|
290
|
+
if (insideInterface) {
|
|
291
|
+
// Count braces
|
|
292
|
+
braceCount += (line.match(/\{/g) || []).length;
|
|
293
|
+
braceCount -= (line.match(/\}/g) || []).length;
|
|
294
|
+
// Check for malformed property definitions
|
|
295
|
+
if (trimmed.includes(':') && !trimmed.startsWith('export') && !trimmed.startsWith('Interface')) {
|
|
296
|
+
// This should be a property definition
|
|
297
|
+
if (!(0, typescript_syntax_1.isValidPropertyDefinition)(trimmed)) {
|
|
298
|
+
const propertyName = trimmed.split(':')[0].trim();
|
|
299
|
+
const typePart = trimmed.split(':')[1]?.trim() || '';
|
|
300
|
+
lineErrors.push({
|
|
301
|
+
line: lineNumber,
|
|
302
|
+
error: `Property "${propertyName}" has incorrect syntax`,
|
|
303
|
+
suggestion: `Fix to: ${propertyName}: correctType;`,
|
|
304
|
+
severity: 'error'
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Check for orphaned closing braces
|
|
309
|
+
if (trimmed === '}' && braceCount === 0) {
|
|
310
|
+
insideInterface = false;
|
|
311
|
+
}
|
|
312
|
+
// Detect incomplete property lines
|
|
313
|
+
if ((0, typescript_syntax_1.isIncompleteProperty)(trimmed)) {
|
|
314
|
+
const propertyName = trimmed.includes(':') ? trimmed.split(':')[0].trim() : 'Property';
|
|
315
|
+
lineErrors.push({
|
|
316
|
+
line: lineNumber,
|
|
317
|
+
error: `Property "${propertyName}" is incomplete`,
|
|
318
|
+
suggestion: `Complete the definition: ${propertyName}: string; (for example)`,
|
|
319
|
+
severity: 'error'
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
// Check for unclosed interfaces
|
|
325
|
+
if (insideInterface && braceCount > 0) {
|
|
326
|
+
errors.push(`Interface started on line ${interfaceStartLine} was not closed`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
isValidPropertyDefinition(line) {
|
|
330
|
+
const trimmed = line.trim();
|
|
331
|
+
// Simple check: if it has a colon and doesn't start with comment, it should be a valid property
|
|
332
|
+
if (!trimmed.includes(':')) {
|
|
333
|
+
return true; // Not a property line
|
|
334
|
+
}
|
|
335
|
+
// Skip Interface declarations and other non-property lines
|
|
336
|
+
if (trimmed.startsWith('Interface') || trimmed.startsWith('export') ||
|
|
337
|
+
trimmed.includes('{') || trimmed.includes('}')) {
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
// Skip valid TypeScript patterns
|
|
341
|
+
if (trimmed.includes('[') && trimmed.includes(']')) {
|
|
342
|
+
// Index signatures like [key: string]: string; are always valid
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
if (trimmed.includes('(') && trimmed.includes(')')) {
|
|
346
|
+
// Function signatures like method(): void; are always valid
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
// For commented properties, they are invalid if they would have been properties
|
|
350
|
+
if (trimmed.startsWith('//')) {
|
|
351
|
+
return false; // Commented property is invalid
|
|
352
|
+
}
|
|
353
|
+
// Very basic pattern: word : something
|
|
354
|
+
const basicPattern = /^\s*(readonly\s+)?\w+\s*\??\s*:\s*.+$/;
|
|
355
|
+
return basicPattern.test(trimmed);
|
|
356
|
+
}
|
|
357
|
+
isIncompleteProperty(line) {
|
|
358
|
+
const trimmed = line.trim();
|
|
359
|
+
// Check for commented properties that should be active
|
|
360
|
+
if (trimmed.startsWith('//') && trimmed.includes(':')) {
|
|
361
|
+
// This is a commented property - flag as issue
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
// Check for incomplete property syntax
|
|
365
|
+
if (trimmed.includes(':')) {
|
|
366
|
+
// Skip Interface/export lines
|
|
367
|
+
if (trimmed.startsWith('Interface') || trimmed.startsWith('export') ||
|
|
368
|
+
trimmed.includes('{') || trimmed.includes('}')) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
// Skip valid TypeScript patterns
|
|
372
|
+
if (trimmed.includes('[') && trimmed.includes(']')) {
|
|
373
|
+
// Index signatures like [key: string]: string; are always valid
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
if (trimmed.includes('(') && trimmed.includes(')')) {
|
|
377
|
+
// Function signatures like method(): void; are always valid
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
// Check if it's missing type or has wrong syntax
|
|
381
|
+
const hasValidType = /:\s*.+/.test(trimmed);
|
|
382
|
+
if (!hasValidType) {
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
shouldHaveSemicolon(line) {
|
|
389
|
+
// Lines that should end with semicolon in TypeScript
|
|
390
|
+
const trimmed = line.trim();
|
|
391
|
+
// CRITICAL FIX: Strip comments before checking line endings
|
|
392
|
+
// This prevents false positives like: return x; // comment ending with )
|
|
393
|
+
const commentIndex = trimmed.indexOf('//');
|
|
394
|
+
const codeOnly = commentIndex > 0
|
|
395
|
+
? trimmed.substring(0, commentIndex).trimEnd()
|
|
396
|
+
: trimmed;
|
|
397
|
+
// Skip if already has semicolon, or ends with special characters
|
|
398
|
+
if (codeOnly.endsWith(';') || codeOnly.endsWith('{') || codeOnly.endsWith('}') ||
|
|
399
|
+
codeOnly.endsWith(',') || codeOnly.endsWith('(') || codeOnly.endsWith(')')) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
// Skip Interface/type property definitions (they use different syntax)
|
|
403
|
+
if (codeOnly.includes(':') && !codeOnly.includes('=') && !codeOnly.includes('(')) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
// Skip control structures, declarations, etc.
|
|
407
|
+
if (codeOnly.startsWith('if') || codeOnly.startsWith('for') || codeOnly.startsWith('while') ||
|
|
408
|
+
codeOnly.startsWith('Interface') || codeOnly.startsWith('type') || codeOnly.startsWith('export') ||
|
|
409
|
+
codeOnly.startsWith('import') || codeOnly.startsWith('class') || codeOnly.startsWith('function') ||
|
|
410
|
+
codeOnly.startsWith('const') || codeOnly.startsWith('let') || codeOnly.startsWith('var')) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
// Lines that likely need semicolons (check code only, not comments)
|
|
414
|
+
return codeOnly.includes('=') || codeOnly.includes('return') || codeOnly.includes('throw') ||
|
|
415
|
+
codeOnly.includes('break') || codeOnly.includes('continue') ||
|
|
416
|
+
/\w+\(.*\)$/.test(codeOnly); // function calls
|
|
417
|
+
}
|
|
418
|
+
hasUnclosedString(line) {
|
|
419
|
+
// CRITICAL FIX: Remove comments before checking quotes
|
|
420
|
+
// Problem: Quotes in comments were triggering false positives
|
|
421
|
+
// Example: str.push(char); // ERROR: Strings don't have .push() method'
|
|
422
|
+
// ^ This quote was counted!
|
|
423
|
+
const lineWithoutComments = code_cleaner_1.CodeCleaner.removeLineComments(line, 'javascript');
|
|
424
|
+
const singleQuotes = (lineWithoutComments.match(/'/g) || []).length;
|
|
425
|
+
const doubleQuotes = (lineWithoutComments.match(/"/g) || []).length;
|
|
426
|
+
// CRITICAL FIX (2025-11-18): Do NOT check backticks line-by-line
|
|
427
|
+
// Template literals (backticks) are DESIGNED to span multiple lines in JavaScript
|
|
428
|
+
// Example: const str = `line 1
|
|
429
|
+
// line 2`; <- This is VALID JavaScript!
|
|
430
|
+
//
|
|
431
|
+
// Checking backticks % 2 on individual lines creates FALSE POSITIVES
|
|
432
|
+
// User bug report: const pythonTestCode = `def process_data(data):
|
|
433
|
+
// ^ One backtick is VALID here (template literal continues below)
|
|
434
|
+
//
|
|
435
|
+
// ONLY check single and double quotes (they don't span multiple lines normally)
|
|
436
|
+
return (singleQuotes % 2 !== 0) || (doubleQuotes % 2 !== 0);
|
|
437
|
+
}
|
|
438
|
+
hasTypeScriptTypeError(line) {
|
|
439
|
+
const trimmed = line.trim();
|
|
440
|
+
// Skip non-property lines
|
|
441
|
+
if (!trimmed.includes(':') || trimmed.startsWith('Interface') || trimmed.startsWith('export') ||
|
|
442
|
+
trimmed.includes('{') || trimmed.includes('}')) {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
// Skip valid TypeScript patterns
|
|
446
|
+
if (trimmed.includes('[') && trimmed.includes(']')) {
|
|
447
|
+
// Index signatures like [key: string]: string;
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
// Check for function signatures - but ONLY skip if it's a method signature (no 'function' keyword)
|
|
451
|
+
// We want to check function declarations like: function foo(): boolen {
|
|
452
|
+
if (trimmed.includes('(') && trimmed.includes(')') && !trimmed.startsWith('function')) {
|
|
453
|
+
// Method signatures like method(): void; (inside interfaces)
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
// Extract the type part after the colon
|
|
457
|
+
// For function declarations, we want the return type (after closing paren)
|
|
458
|
+
// For properties, we want the type after the property name
|
|
459
|
+
let colonIndex = trimmed.indexOf(':');
|
|
460
|
+
if (colonIndex === -1)
|
|
461
|
+
return false;
|
|
462
|
+
// If this is a function declaration, find the colon AFTER the closing paren
|
|
463
|
+
if (trimmed.startsWith('function') && trimmed.includes(')')) {
|
|
464
|
+
const closingParenIndex = trimmed.lastIndexOf(')');
|
|
465
|
+
const colonAfterParen = trimmed.indexOf(':', closingParenIndex);
|
|
466
|
+
if (colonAfterParen !== -1) {
|
|
467
|
+
colonIndex = colonAfterParen;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
const typePart = trimmed.substring(colonIndex + 1).trim();
|
|
471
|
+
// Remove semicolon, comments, opening brace, and any trailing characters
|
|
472
|
+
const typeWithoutSemicolon = typePart
|
|
473
|
+
.split(';')[0] // Remove semicolon and everything after
|
|
474
|
+
.split('//')[0] // Remove line comments
|
|
475
|
+
.split('/*')[0] // Remove block comments
|
|
476
|
+
.split('{')[0] // Remove opening brace (for function bodies)
|
|
477
|
+
.trim();
|
|
478
|
+
// Common TypeScript type typos
|
|
479
|
+
const commonTypos = {
|
|
480
|
+
'strng': 'string',
|
|
481
|
+
'strig': 'string',
|
|
482
|
+
'stirng': 'string',
|
|
483
|
+
'numbr': 'number',
|
|
484
|
+
'numebr': 'number',
|
|
485
|
+
'bolean': 'boolean',
|
|
486
|
+
'boolea': 'boolean',
|
|
487
|
+
'boolen': 'boolean',
|
|
488
|
+
'undefind': 'undefined',
|
|
489
|
+
'nul': 'null',
|
|
490
|
+
'voic': 'void',
|
|
491
|
+
'aray': 'array',
|
|
492
|
+
'Array': 'Array', // This one is correct but often confused
|
|
493
|
+
'dat': 'Date',
|
|
494
|
+
'Dat': 'Date',
|
|
495
|
+
'objec': 'object',
|
|
496
|
+
'Object': 'Object'
|
|
497
|
+
};
|
|
498
|
+
// Check if the type matches any known typos
|
|
499
|
+
for (const typo in commonTypos) {
|
|
500
|
+
if (typeWithoutSemicolon === typo) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
// Check for other common patterns that suggest typos
|
|
505
|
+
if (typeWithoutSemicolon.length > 2 && typeWithoutSemicolon.length < 8) {
|
|
506
|
+
// Basic heuristics for detecting potential type typos
|
|
507
|
+
const validPrimitiveTypes = ['string', 'number', 'boolean', 'undefined', 'null', 'void', 'any', 'unknown', 'never'];
|
|
508
|
+
const validComplexTypes = ['Date', 'Array', 'Object', 'Function', 'Promise'];
|
|
509
|
+
// If it looks like it could be a primitive type but isn't exact match
|
|
510
|
+
if (!validPrimitiveTypes.includes(typeWithoutSemicolon) &&
|
|
511
|
+
!validComplexTypes.includes(typeWithoutSemicolon) &&
|
|
512
|
+
!/^[A-Z]/.test(typeWithoutSemicolon) && // Not starting with capital (custom type)
|
|
513
|
+
!typeWithoutSemicolon.includes('[]') && // Not array type
|
|
514
|
+
!typeWithoutSemicolon.includes('|') && // Not union type
|
|
515
|
+
!typeWithoutSemicolon.includes('&') && // Not intersection type
|
|
516
|
+
!typeWithoutSemicolon.includes('<') && // Not generic type
|
|
517
|
+
!/^\w+$/.test(typeWithoutSemicolon)) { // Not a simple word
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
suggestTypeCorrection(line) {
|
|
524
|
+
const trimmed = line.trim();
|
|
525
|
+
let colonIndex = trimmed.indexOf(':');
|
|
526
|
+
// If this is a function declaration, find the colon AFTER the closing paren
|
|
527
|
+
if (trimmed.startsWith('function') && trimmed.includes(')')) {
|
|
528
|
+
const closingParenIndex = trimmed.lastIndexOf(')');
|
|
529
|
+
const colonAfterParen = trimmed.indexOf(':', closingParenIndex);
|
|
530
|
+
if (colonAfterParen !== -1) {
|
|
531
|
+
colonIndex = colonAfterParen;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
const typePart = trimmed.substring(colonIndex + 1).trim();
|
|
535
|
+
// Remove semicolon, comments, opening brace, and any trailing characters
|
|
536
|
+
const typeWithoutSemicolon = typePart
|
|
537
|
+
.split(';')[0] // Remove semicolon and everything after
|
|
538
|
+
.split('//')[0] // Remove line comments
|
|
539
|
+
.split('/*')[0] // Remove block comments
|
|
540
|
+
.split('{')[0] // Remove opening brace (for function bodies)
|
|
541
|
+
.trim();
|
|
542
|
+
const commonTypos = {
|
|
543
|
+
'strng': 'string',
|
|
544
|
+
'strig': 'string',
|
|
545
|
+
'stirng': 'string',
|
|
546
|
+
'numbr': 'number',
|
|
547
|
+
'numebr': 'number',
|
|
548
|
+
'bolean': 'boolean',
|
|
549
|
+
'boolea': 'boolean',
|
|
550
|
+
'boolen': 'boolean',
|
|
551
|
+
'undefind': 'undefined',
|
|
552
|
+
'nul': 'null',
|
|
553
|
+
'voic': 'void',
|
|
554
|
+
'aray': 'array',
|
|
555
|
+
'dat': 'Date',
|
|
556
|
+
'Dat': 'Date',
|
|
557
|
+
'objec': 'object'
|
|
558
|
+
};
|
|
559
|
+
if (commonTypos[typeWithoutSemicolon]) {
|
|
560
|
+
const correctedType = commonTypos[typeWithoutSemicolon];
|
|
561
|
+
const correctedLine = trimmed.replace(typeWithoutSemicolon, correctedType);
|
|
562
|
+
return `Possible typo. Suggestion: ${correctedLine}`;
|
|
563
|
+
}
|
|
564
|
+
return `Check type "${typeWithoutSemicolon}". Common types: string, number, boolean, Date`;
|
|
565
|
+
}
|
|
566
|
+
isMissingComma(currentLine, nextLine) {
|
|
567
|
+
// Skip empty lines, comments, and structural characters
|
|
568
|
+
if (!currentLine || !nextLine)
|
|
569
|
+
return false;
|
|
570
|
+
if (currentLine.startsWith('//') || nextLine.startsWith('//'))
|
|
571
|
+
return false;
|
|
572
|
+
if (currentLine.startsWith('/*') || nextLine.startsWith('/*'))
|
|
573
|
+
return false;
|
|
574
|
+
// Skip if current line already ends with comma, semicolon, opening/closing braces
|
|
575
|
+
const lastChar = currentLine.trimEnd().slice(-1);
|
|
576
|
+
if ([',', ';', '{', '}', '[', ']'].includes(lastChar))
|
|
577
|
+
return false;
|
|
578
|
+
// Skip if next line is a closing brace or bracket
|
|
579
|
+
if (['}', ']', ')'].includes(nextLine[0]))
|
|
580
|
+
return false;
|
|
581
|
+
// Check if current line looks like an object property: "key: value"
|
|
582
|
+
const hasColon = currentLine.includes(':');
|
|
583
|
+
if (!hasColon)
|
|
584
|
+
return false;
|
|
585
|
+
// Check if next line looks like another property (has colon and is not a URL)
|
|
586
|
+
const nextHasColon = nextLine.includes(':');
|
|
587
|
+
if (!nextHasColon)
|
|
588
|
+
return false;
|
|
589
|
+
// Exclude URLs and other false positives
|
|
590
|
+
if (currentLine.includes('://') || nextLine.includes('://'))
|
|
591
|
+
return false;
|
|
592
|
+
if (currentLine.includes('?') && currentLine.includes(':'))
|
|
593
|
+
return false; // Ternary operator
|
|
594
|
+
// Check if lines look like property definitions
|
|
595
|
+
const propertyPattern = /^\s*[\w$]+\s*:\s*.+/;
|
|
596
|
+
if (!propertyPattern.test(currentLine))
|
|
597
|
+
return false;
|
|
598
|
+
if (!propertyPattern.test(nextLine))
|
|
599
|
+
return false;
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
async analyzeSyntax(code, result) {
|
|
603
|
+
const errors = [];
|
|
604
|
+
const lineErrors = [];
|
|
605
|
+
// Line by line analysis
|
|
606
|
+
const lines = code.split('\n');
|
|
607
|
+
lines.forEach((line, index) => {
|
|
608
|
+
const lineNumber = index + 1;
|
|
609
|
+
// BUG FIX (2025-11-18): Remove string content before syntax checks
|
|
610
|
+
// Previous bug: 'Language...' (ellipsis in string) flagged as "double dots" error
|
|
611
|
+
// CRITICAL FIX (2025-11-21): Also remove comments before syntax checks
|
|
612
|
+
// Previous bug: Comments with ".." in attack examples flagged as syntax errors
|
|
613
|
+
const lineWithoutComments = code_cleaner_1.CodeCleaner.removeLineComments(line, 'javascript');
|
|
614
|
+
const lineWithoutStrings = (0, syntax_helpers_1.removeStringLiterals)(lineWithoutComments);
|
|
615
|
+
// Check for double dots (but not triple dots which is spread operator)
|
|
616
|
+
// FIXED: Now checks cleaned line without string content AND comments
|
|
617
|
+
if (lineWithoutStrings.includes('..') && !lineWithoutStrings.includes('...')) {
|
|
618
|
+
const position = lineWithoutStrings.indexOf('..');
|
|
619
|
+
lineErrors.push({
|
|
620
|
+
line: lineNumber,
|
|
621
|
+
error: `Double dots found at position ${position + 1}`,
|
|
622
|
+
suggestion: 'Remove the extra dot. Example: console.log() instead of console.log..()',
|
|
623
|
+
severity: 'error'
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
// Check for dot before parentheses (console.log.() etc)
|
|
627
|
+
// FIXED: Now checks cleaned line without string content
|
|
628
|
+
const dotParenMatch = lineWithoutStrings.match(/(\w+)\.(\w+)\.(\()/);
|
|
629
|
+
if (dotParenMatch) {
|
|
630
|
+
const [fullMatch, object, method] = dotParenMatch;
|
|
631
|
+
lineErrors.push({
|
|
632
|
+
line: lineNumber,
|
|
633
|
+
error: `Syntax error: extra dot before parentheses in "${object}.${method}.("`,
|
|
634
|
+
suggestion: `Fix: ${object}.${method}(`,
|
|
635
|
+
severity: 'error'
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
// Check for use of var
|
|
639
|
+
// FIXED: Use cleaned line to avoid flagging "var" inside strings
|
|
640
|
+
if (lineWithoutStrings.match(/\bvar\s+/)) {
|
|
641
|
+
lineErrors.push({
|
|
642
|
+
line: lineNumber,
|
|
643
|
+
error: 'Use of var detected',
|
|
644
|
+
suggestion: 'Use const for constant values or let for variables that change',
|
|
645
|
+
severity: 'warning'
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
// Note: .length caching moved to performance suggestions section
|
|
649
|
+
// Not flagged as a line error to avoid cluttering the editor
|
|
650
|
+
// Check for unclosed quotes (must use raw line to detect quote issues)
|
|
651
|
+
// BUG FIX (2025-11-18): Use CodeCleaner.removeLineComments() instead of split('//')
|
|
652
|
+
// Previous bug: split('//') also splits URLs like http://localhost
|
|
653
|
+
// Example: 'http://localhost:3000' → 'http:' (loses everything after //)
|
|
654
|
+
const codeWithoutComments = code_cleaner_1.CodeCleaner.removeLineComments(line, 'javascript');
|
|
655
|
+
const singleQuotes = (codeWithoutComments.match(/'/g) || []).length;
|
|
656
|
+
const doubleQuotes = (codeWithoutComments.match(/"/g) || []).length;
|
|
657
|
+
if (singleQuotes % 2 !== 0) {
|
|
658
|
+
lineErrors.push({
|
|
659
|
+
line: lineNumber,
|
|
660
|
+
error: 'Unclosed single quotes',
|
|
661
|
+
suggestion: 'Check that all \' quotes are closed correctly',
|
|
662
|
+
severity: 'error'
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
if (doubleQuotes % 2 !== 0) {
|
|
666
|
+
lineErrors.push({
|
|
667
|
+
line: lineNumber,
|
|
668
|
+
error: 'Unclosed double quotes',
|
|
669
|
+
suggestion: 'Check that all " quotes are closed correctly',
|
|
670
|
+
severity: 'error'
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
// Check for common semantic errors: .push() on strings (Python/Java confusion)
|
|
674
|
+
// FIXED: Use cleaned line to avoid flagging .push() inside string literals
|
|
675
|
+
if (lineWithoutStrings.match(/\b(str|string|text|name|msg|message|word|title)\s*\.\s*push\s*\(/i)) {
|
|
676
|
+
lineErrors.push({
|
|
677
|
+
line: lineNumber,
|
|
678
|
+
error: 'Strings don\'t have .push() method',
|
|
679
|
+
suggestion: 'Use string concatenation (+=) or convert to array with .split("") first',
|
|
680
|
+
severity: 'error'
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
// Check balanced parentheses (ignore comments) - version with line tracking
|
|
685
|
+
let parenBalance = 0;
|
|
686
|
+
let braceBalance = 0;
|
|
687
|
+
let bracketBalance = 0;
|
|
688
|
+
let inLineComment = false;
|
|
689
|
+
let inBlockComment = false;
|
|
690
|
+
let inString = false;
|
|
691
|
+
let stringChar = '';
|
|
692
|
+
let currentLine = 1;
|
|
693
|
+
// Track opening positions for better error reporting
|
|
694
|
+
const openBraces = [];
|
|
695
|
+
const missingClosures = [];
|
|
696
|
+
for (let i = 0; i < code.length; i++) {
|
|
697
|
+
const char = code[i];
|
|
698
|
+
const nextChar = code[i + 1];
|
|
699
|
+
// Track line numbers
|
|
700
|
+
if (char === '\n') {
|
|
701
|
+
currentLine++;
|
|
702
|
+
}
|
|
703
|
+
// Handle line comments
|
|
704
|
+
if (!inBlockComment && !inString && char === '/' && nextChar === '/') {
|
|
705
|
+
inLineComment = true;
|
|
706
|
+
i++; // Skip next char
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
// End line comment
|
|
710
|
+
if (inLineComment && char === '\n') {
|
|
711
|
+
inLineComment = false;
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
// Handle block comments
|
|
715
|
+
if (!inLineComment && !inString && char === '/' && nextChar === '*') {
|
|
716
|
+
inBlockComment = true;
|
|
717
|
+
i++; // Skip next char
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
if (inBlockComment && char === '*' && nextChar === '/') {
|
|
721
|
+
inBlockComment = false;
|
|
722
|
+
i++; // Skip next char
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
// Handle strings
|
|
726
|
+
if (!inLineComment && !inBlockComment && (char === '"' || char === "'")) {
|
|
727
|
+
if (!inString) {
|
|
728
|
+
inString = true;
|
|
729
|
+
stringChar = char;
|
|
730
|
+
}
|
|
731
|
+
else if (char === stringChar && code[i - 1] !== '\\') {
|
|
732
|
+
inString = false;
|
|
733
|
+
stringChar = '';
|
|
734
|
+
}
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
// Only count braces outside comments and strings
|
|
738
|
+
if (!inLineComment && !inBlockComment && !inString) {
|
|
739
|
+
if (char === '(') {
|
|
740
|
+
parenBalance++;
|
|
741
|
+
openBraces.push({ line: currentLine, char: '(' });
|
|
742
|
+
}
|
|
743
|
+
else if (char === ')') {
|
|
744
|
+
parenBalance--;
|
|
745
|
+
// Find matching opening parenthesis
|
|
746
|
+
for (let j = openBraces.length - 1; j >= 0; j--) {
|
|
747
|
+
if (openBraces[j].char === '(') {
|
|
748
|
+
openBraces.splice(j, 1);
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
else if (char === '{') {
|
|
754
|
+
braceBalance++;
|
|
755
|
+
openBraces.push({ line: currentLine, char: '{' });
|
|
756
|
+
}
|
|
757
|
+
else if (char === '}') {
|
|
758
|
+
braceBalance--;
|
|
759
|
+
// Find matching opening brace
|
|
760
|
+
for (let j = openBraces.length - 1; j >= 0; j--) {
|
|
761
|
+
if (openBraces[j].char === '{') {
|
|
762
|
+
openBraces.splice(j, 1);
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
else if (char === '[') {
|
|
768
|
+
bracketBalance++;
|
|
769
|
+
openBraces.push({ line: currentLine, char: '[' });
|
|
770
|
+
}
|
|
771
|
+
else if (char === ']') {
|
|
772
|
+
bracketBalance--;
|
|
773
|
+
// Find matching opening bracket
|
|
774
|
+
for (let j = openBraces.length - 1; j >= 0; j--) {
|
|
775
|
+
if (openBraces[j].char === '[') {
|
|
776
|
+
openBraces.splice(j, 1);
|
|
777
|
+
break;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
// Create line-specific errors for unmatched braces
|
|
784
|
+
const hasParenErrors = openBraces.some(b => b.char === '(');
|
|
785
|
+
const hasBraceErrors = openBraces.some(b => b.char === '{');
|
|
786
|
+
const hasBracketErrors = openBraces.some(b => b.char === '[');
|
|
787
|
+
openBraces.forEach(brace => {
|
|
788
|
+
const closingChar = brace.char === '{' ? '}' : brace.char === '(' ? ')' : ']';
|
|
789
|
+
const typeName = brace.char === '{' ? 'brace' : brace.char === '(' ? 'parenthesis' : 'bracket';
|
|
790
|
+
lineErrors.push({
|
|
791
|
+
line: brace.line,
|
|
792
|
+
error: `${typeName.charAt(0).toUpperCase() + typeName.slice(1)} "${brace.char}" was not closed`,
|
|
793
|
+
suggestion: `Add "${closingChar}" to close the ${typeName} opened on line ${brace.line}`,
|
|
794
|
+
severity: 'error'
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
// DON'T add general balance errors - we now have specific line errors above
|
|
798
|
+
// This prevents "phantom" errors without line numbers that can't use Auto-Fix
|
|
799
|
+
// Check basic syntax (for all files)
|
|
800
|
+
const isTypeScript = (0, typescript_syntax_1.isTypeScriptCode)(code);
|
|
801
|
+
if (!isTypeScript) {
|
|
802
|
+
// Pure JavaScript - use acorn parser
|
|
803
|
+
try {
|
|
804
|
+
acorn.parse(code, {
|
|
805
|
+
ecmaVersion: 2022,
|
|
806
|
+
sourceType: 'module',
|
|
807
|
+
allowReturnOutsideFunction: true,
|
|
808
|
+
locations: true // Enable line/column tracking
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
catch (error) {
|
|
812
|
+
const errorMessage = error.message || 'Syntax error';
|
|
813
|
+
// Extract line number from Acorn error
|
|
814
|
+
const lineNumber = error.loc?.line || (0, syntax_helpers_1.findErrorLine)(code, error.pos);
|
|
815
|
+
if (lineNumber) {
|
|
816
|
+
// Create specific line error
|
|
817
|
+
const suggestion = (0, syntax_helpers_1.getSuggestionForSyntaxError)(errorMessage);
|
|
818
|
+
lineErrors.push({
|
|
819
|
+
line: lineNumber,
|
|
820
|
+
error: errorMessage,
|
|
821
|
+
suggestion: suggestion,
|
|
822
|
+
severity: 'error'
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
// Fallback to general error
|
|
827
|
+
errors.push(`Syntax error: ${errorMessage}`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
// TypeScript - do basic checks without full parser
|
|
833
|
+
this.checkBasicTypeScriptSyntax(code, errors, lineErrors);
|
|
834
|
+
}
|
|
835
|
+
result.syntax.errors = errors;
|
|
836
|
+
result.syntax.lineErrors = lineErrors;
|
|
837
|
+
result.syntax.valid = errors.length === 0 && lineErrors.filter(e => e.severity === 'error').length === 0;
|
|
838
|
+
// Penalize score based on severity
|
|
839
|
+
const errorCount = lineErrors.filter(e => e.severity === 'error').length;
|
|
840
|
+
const warningCount = lineErrors.filter(e => e.severity === 'warning').length;
|
|
841
|
+
if (errorCount > 0) {
|
|
842
|
+
result.quality.score -= errorCount * 20;
|
|
843
|
+
}
|
|
844
|
+
if (warningCount > 0) {
|
|
845
|
+
result.quality.score -= warningCount * 10;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
// Helper to find line number from character position
|
|
849
|
+
findErrorLine(code, pos) {
|
|
850
|
+
if (!pos)
|
|
851
|
+
return null;
|
|
852
|
+
const lines = code.substring(0, pos).split('\n');
|
|
853
|
+
return lines.length;
|
|
854
|
+
}
|
|
855
|
+
// Helper to provide suggestions for common syntax errors
|
|
856
|
+
getSuggestionForSyntaxError(errorMessage) {
|
|
857
|
+
if (errorMessage.includes('Unexpected token')) {
|
|
858
|
+
if (errorMessage.includes('}')) {
|
|
859
|
+
return 'Check if there is a missing closing parenthesis or brace before this line';
|
|
860
|
+
}
|
|
861
|
+
if (errorMessage.includes(')')) {
|
|
862
|
+
return 'Add the missing closing parenthesis \")\"';
|
|
863
|
+
}
|
|
864
|
+
return 'Check the code syntax and fix the identified errors';
|
|
865
|
+
}
|
|
866
|
+
if (errorMessage.includes('Unterminated string')) {
|
|
867
|
+
return 'Close the string with matching quotes';
|
|
868
|
+
}
|
|
869
|
+
if (errorMessage.includes('Missing comma')) {
|
|
870
|
+
return 'Add a comma between object properties';
|
|
871
|
+
}
|
|
872
|
+
return 'Fix the identified syntax error';
|
|
873
|
+
}
|
|
874
|
+
// Option B: Detect AI hallucinations (non-existent methods, typos)
|
|
875
|
+
detectAIHallucinations(code, result) {
|
|
876
|
+
const lines = code.split('\n');
|
|
877
|
+
const lineErrors = result.syntax.lineErrors || [];
|
|
878
|
+
let inMultiLineComment = false;
|
|
879
|
+
// Common AI hallucinations: method names that don't exist
|
|
880
|
+
// Optimized: Combine all patterns into a single regex for better performance
|
|
881
|
+
const hallucinationMap = new Map([
|
|
882
|
+
['append', { correct: '.push(', description: 'Arrays do not have .append() method in JavaScript, use .push()' }],
|
|
883
|
+
['remove', { correct: '.splice() or .filter()', description: 'Arrays do not have .remove() method in JavaScript' }],
|
|
884
|
+
['toUppercase', { correct: '.toUpperCase(', description: 'Correct method is .toUpperCase() (with capital C)' }],
|
|
885
|
+
['toLowercase', { correct: '.toLowerCase(', description: 'Correct method is .toLowerCase() (with capital C)' }],
|
|
886
|
+
['contains', { correct: '.includes(', description: 'Arrays/Strings use .includes() in JavaScript, not .contains()' }],
|
|
887
|
+
['length()', { correct: '.length', description: '.length is a property, not a method (no parentheses)' }],
|
|
888
|
+
['size', { correct: '.length or .size', description: 'Arrays use .length; Map/Set use .size (no parentheses)' }],
|
|
889
|
+
]);
|
|
890
|
+
// Combined regex pattern for all hallucinations (single pass)
|
|
891
|
+
const combinedPattern = /\.(append|remove|toUppercase|toLowercase|contains|length\(\)|size)\(/g;
|
|
892
|
+
lines.forEach((line, index) => {
|
|
893
|
+
const lineNumber = index + 1;
|
|
894
|
+
const trimmed = line.trim();
|
|
895
|
+
// Track multi-line comments
|
|
896
|
+
if (trimmed.includes('/*'))
|
|
897
|
+
inMultiLineComment = true;
|
|
898
|
+
if (trimmed.includes('*/')) {
|
|
899
|
+
inMultiLineComment = false;
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
// Skip comments and empty lines
|
|
903
|
+
if (!trimmed || inMultiLineComment || trimmed.startsWith('//') || trimmed.startsWith('*')) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
// Skip lines inside template literals (they contain arbitrary text, not JavaScript)
|
|
907
|
+
if ((0, syntax_helpers_1.isInsideTemplateLiteral)(code, lineNumber)) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
// Single regex test for all patterns - much faster
|
|
911
|
+
const matches = line.matchAll(combinedPattern);
|
|
912
|
+
for (const match of matches) {
|
|
913
|
+
const method = match[1];
|
|
914
|
+
const details = hallucinationMap.get(method);
|
|
915
|
+
if (details) {
|
|
916
|
+
// Get references for method-specific issues
|
|
917
|
+
let references = references_1.javascriptStandards['syntax-errors'] || [];
|
|
918
|
+
// Add specific references for string/array methods (without category field)
|
|
919
|
+
if (method === 'toUppercase' || method === 'toLowercase') {
|
|
920
|
+
references = [
|
|
921
|
+
{
|
|
922
|
+
title: 'MDN: String.prototype.toUpperCase()',
|
|
923
|
+
url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase',
|
|
924
|
+
description: 'Correct method name and usage for converting strings to uppercase'
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
title: 'MDN: String.prototype.toLowerCase()',
|
|
928
|
+
url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase',
|
|
929
|
+
description: 'Correct method name and usage for converting strings to lowercase'
|
|
930
|
+
}
|
|
931
|
+
];
|
|
932
|
+
}
|
|
933
|
+
else if (method === 'append' || method === 'remove') {
|
|
934
|
+
references = [
|
|
935
|
+
{
|
|
936
|
+
title: 'MDN: Array.prototype.push()',
|
|
937
|
+
url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push',
|
|
938
|
+
description: 'Add elements to the end of an array'
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
title: 'MDN: Array.prototype.splice()',
|
|
942
|
+
url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice',
|
|
943
|
+
description: 'Remove or replace elements from an array'
|
|
944
|
+
}
|
|
945
|
+
];
|
|
946
|
+
}
|
|
947
|
+
else if (method === 'contains') {
|
|
948
|
+
references = [
|
|
949
|
+
{
|
|
950
|
+
title: 'MDN: Array.prototype.includes()',
|
|
951
|
+
url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes',
|
|
952
|
+
description: 'Check if an array includes a certain value'
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
title: 'MDN: String.prototype.includes()',
|
|
956
|
+
url: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes',
|
|
957
|
+
description: 'Check if a string contains a substring'
|
|
958
|
+
}
|
|
959
|
+
];
|
|
960
|
+
}
|
|
961
|
+
lineErrors.push({
|
|
962
|
+
line: lineNumber,
|
|
963
|
+
error: `AI Hallucination: ${details.description}`,
|
|
964
|
+
suggestion: `Replace with: ${details.correct}`,
|
|
965
|
+
severity: 'error',
|
|
966
|
+
references
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
result.syntax.lineErrors = lineErrors;
|
|
972
|
+
}
|
|
973
|
+
// Phase 1.1: Detect ReferenceError - Undeclared variables (Optimized single-pass)
|
|
974
|
+
detectReferenceErrors(code, result) {
|
|
975
|
+
const lines = code.split('\n');
|
|
976
|
+
const lineErrors = result.syntax.lineErrors || [];
|
|
977
|
+
// Get built-in globals and skip patterns
|
|
978
|
+
const builtIns = (0, variable_tracker_1.getBuiltIns)();
|
|
979
|
+
const skipPatterns = (0, variable_tracker_1.getSkipPatterns)();
|
|
980
|
+
// Find all variable declarations
|
|
981
|
+
const declaredVars = (0, variable_tracker_1.findDeclarations)(lines, code, syntax_helpers_1.isInsideTemplateLiteral);
|
|
982
|
+
// Find all variable usages
|
|
983
|
+
const usages = (0, variable_tracker_1.findUsages)(lines, code, builtIns, skipPatterns, syntax_helpers_1.removeStringLiterals, syntax_helpers_1.isInsideTemplateLiteral);
|
|
984
|
+
// Detect JSX in code to filter out JSX element false positives
|
|
985
|
+
const hasJSX = (0, jsx_helpers_1.detectJSX)(code);
|
|
986
|
+
// Check for undeclared variables and add to line errors
|
|
987
|
+
const undeclaredErrors = (0, variable_tracker_1.checkUndeclaredVariables)(usages, declaredVars, lines, hasJSX);
|
|
988
|
+
lineErrors.push(...undeclaredErrors);
|
|
989
|
+
result.syntax.lineErrors = lineErrors;
|
|
990
|
+
}
|
|
991
|
+
// Phase 1.2: Detect == vs === comparison issues
|
|
992
|
+
detectComparisonIssues(code, result) {
|
|
993
|
+
const lines = code.split('\n');
|
|
994
|
+
const lineErrors = result.syntax.lineErrors || [];
|
|
995
|
+
let inMultiLineComment = false;
|
|
996
|
+
lines.forEach((line, index) => {
|
|
997
|
+
const lineNumber = index + 1;
|
|
998
|
+
const trimmed = line.trim();
|
|
999
|
+
// Track multi-line comments
|
|
1000
|
+
if (trimmed.includes('/*'))
|
|
1001
|
+
inMultiLineComment = true;
|
|
1002
|
+
if (trimmed.includes('*/')) {
|
|
1003
|
+
inMultiLineComment = false;
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
// Skip comments
|
|
1007
|
+
if (!trimmed || inMultiLineComment || trimmed.startsWith('//') || trimmed.startsWith('*')) {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
// Skip lines inside template literals (they contain arbitrary text, not JavaScript)
|
|
1011
|
+
if ((0, syntax_helpers_1.isInsideTemplateLiteral)(code, lineNumber)) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
// Detect == (but not ===)
|
|
1015
|
+
const looseEqualityMatch = line.match(/[^=!<>]==(=?)[^=]/);
|
|
1016
|
+
if (looseEqualityMatch && !looseEqualityMatch[1]) { // Not ===
|
|
1017
|
+
lineErrors.push({
|
|
1018
|
+
line: lineNumber,
|
|
1019
|
+
error: 'Non-strict comparison (==) detected',
|
|
1020
|
+
suggestion: 'Use === for strict comparison (avoids type coercion)',
|
|
1021
|
+
severity: 'warning'
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
// Detect != (but not !==)
|
|
1025
|
+
const looseInequalityMatch = line.match(/!=(=?)[^=]/);
|
|
1026
|
+
if (looseInequalityMatch && !looseInequalityMatch[1]) { // Not !==
|
|
1027
|
+
lineErrors.push({
|
|
1028
|
+
line: lineNumber,
|
|
1029
|
+
error: 'Non-strict comparison (!=) detected',
|
|
1030
|
+
suggestion: 'Use !== for strict comparison (avoids type coercion)',
|
|
1031
|
+
severity: 'warning'
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
// FIX (2025-11-18): Detect assignment in condition (= instead of == or ===)
|
|
1035
|
+
// Pattern: if (variable = value) or while (variable = value)
|
|
1036
|
+
// This is a critical bug where = (assignment) is used instead of === (comparison)
|
|
1037
|
+
const assignmentInCondition = line.match(/\b(if|while|for)\s*\([^)]*[^=!<>]=[^=][^)]*\)/);
|
|
1038
|
+
if (assignmentInCondition) {
|
|
1039
|
+
// Verify it's not part of an arrow function or initialization (for loops)
|
|
1040
|
+
const isForLoopInit = assignmentInCondition[1] === 'for' && (line.includes('let ') || line.includes('var ') || line.includes('const '));
|
|
1041
|
+
const isArrowFunction = line.includes('=>');
|
|
1042
|
+
if (!isForLoopInit && !isArrowFunction) {
|
|
1043
|
+
lineErrors.push({
|
|
1044
|
+
line: lineNumber,
|
|
1045
|
+
error: 'Assignment in condition - did you mean === or == ?',
|
|
1046
|
+
suggestion: 'Use === for comparison. If assignment is intentional, wrap in parentheses: if ((x = getValue()))',
|
|
1047
|
+
severity: 'error'
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
result.syntax.lineErrors = lineErrors;
|
|
1053
|
+
}
|
|
1054
|
+
// Phase 1.3: Detect unhandled Promises
|
|
1055
|
+
detectUnhandledPromises(code, result) {
|
|
1056
|
+
const lines = code.split('\n');
|
|
1057
|
+
const lineErrors = result.syntax.lineErrors || [];
|
|
1058
|
+
let inMultiLineComment = false;
|
|
1059
|
+
// Track Promise chains to find missing .catch()
|
|
1060
|
+
let inPromiseChain = false;
|
|
1061
|
+
let promiseChainStartLine = 0;
|
|
1062
|
+
let hasCatch = false;
|
|
1063
|
+
lines.forEach((line, index) => {
|
|
1064
|
+
const lineNumber = index + 1;
|
|
1065
|
+
const trimmed = line.trim();
|
|
1066
|
+
// Track multi-line comments
|
|
1067
|
+
if (trimmed.includes('/*'))
|
|
1068
|
+
inMultiLineComment = true;
|
|
1069
|
+
if (trimmed.includes('*/')) {
|
|
1070
|
+
inMultiLineComment = false;
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
// Skip comments
|
|
1074
|
+
if (!trimmed || inMultiLineComment || trimmed.startsWith('//') || trimmed.startsWith('*')) {
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
// Skip lines inside template literals (they contain arbitrary text, not JavaScript)
|
|
1078
|
+
if ((0, syntax_helpers_1.isInsideTemplateLiteral)(code, lineNumber)) {
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
// Detect start of Promise chain (fetch, new Promise, or return promise)
|
|
1082
|
+
if (!inPromiseChain && (trimmed.includes('.then(') || trimmed.match(/\b(fetch|axios|Promise\.)\(/))) {
|
|
1083
|
+
inPromiseChain = true;
|
|
1084
|
+
promiseChainStartLine = lineNumber;
|
|
1085
|
+
hasCatch = false;
|
|
1086
|
+
}
|
|
1087
|
+
// Detect .catch( in the chain
|
|
1088
|
+
if (inPromiseChain && trimmed.includes('.catch(')) {
|
|
1089
|
+
hasCatch = true;
|
|
1090
|
+
}
|
|
1091
|
+
// End of Promise chain (line ends with ; and doesn't start with .)
|
|
1092
|
+
if (inPromiseChain && trimmed.endsWith(';') && !trimmed.startsWith('.')) {
|
|
1093
|
+
if (!hasCatch) {
|
|
1094
|
+
lineErrors.push({
|
|
1095
|
+
line: promiseChainStartLine,
|
|
1096
|
+
error: 'Promise without error handling (.catch)',
|
|
1097
|
+
suggestion: 'Add .catch(err => console.error(err)) at the end of the Promise chain',
|
|
1098
|
+
severity: 'warning'
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
inPromiseChain = false;
|
|
1102
|
+
hasCatch = false;
|
|
1103
|
+
}
|
|
1104
|
+
// Continue chain if line starts with . (method chaining)
|
|
1105
|
+
if (inPromiseChain && trimmed.startsWith('.') && !trimmed.endsWith(';')) {
|
|
1106
|
+
// Chain continues, don't reset
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
// Check async functions without try/catch
|
|
1110
|
+
lines.forEach((line, index) => {
|
|
1111
|
+
const lineNumber = index + 1;
|
|
1112
|
+
const trimmed = line.trim();
|
|
1113
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
// Detect async function without try/catch in the vicinity
|
|
1117
|
+
if (trimmed.includes('async ') && trimmed.includes('await ')) {
|
|
1118
|
+
// Check if there's a try block nearby (simple heuristic)
|
|
1119
|
+
const nextLines = lines.slice(index, Math.min(index + 5, lines.length)).join(' ');
|
|
1120
|
+
if (!nextLines.includes('try') && !nextLines.includes('.catch(')) {
|
|
1121
|
+
lineErrors.push({
|
|
1122
|
+
line: lineNumber,
|
|
1123
|
+
error: 'async/await without error handling',
|
|
1124
|
+
suggestion: 'Wrap the code in try/catch or add .catch() to the Promise',
|
|
1125
|
+
severity: 'warning'
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
result.syntax.lineErrors = lineErrors;
|
|
1131
|
+
}
|
|
1132
|
+
// Phase 1.4: Detect 'this' context issues
|
|
1133
|
+
detectThisContextIssues(code, result) {
|
|
1134
|
+
const lines = code.split('\n');
|
|
1135
|
+
const lineErrors = result.syntax.lineErrors || [];
|
|
1136
|
+
let inMultiLineComment = false;
|
|
1137
|
+
lines.forEach((line, index) => {
|
|
1138
|
+
const lineNumber = index + 1;
|
|
1139
|
+
const trimmed = line.trim();
|
|
1140
|
+
// Track multi-line comments
|
|
1141
|
+
if (trimmed.includes('/*'))
|
|
1142
|
+
inMultiLineComment = true;
|
|
1143
|
+
if (trimmed.includes('*/')) {
|
|
1144
|
+
inMultiLineComment = false;
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
// Skip comments
|
|
1148
|
+
if (!trimmed || inMultiLineComment || trimmed.startsWith('//') || trimmed.startsWith('*')) {
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
// Skip lines inside template literals (they contain arbitrary text, not JavaScript)
|
|
1152
|
+
if ((0, syntax_helpers_1.isInsideTemplateLiteral)(code, lineNumber)) {
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
// Detect regular function inside callback that uses 'this'
|
|
1156
|
+
// Pattern: .then(function() { ... this. ... })
|
|
1157
|
+
const callbackWithThisMatch = line.match(/\.(then|forEach|map|filter|reduce|some|every|find)\s*\(\s*function\s*\([^)]*\)\s*\{[^}]*this\./);
|
|
1158
|
+
if (callbackWithThisMatch) {
|
|
1159
|
+
lineErrors.push({
|
|
1160
|
+
line: lineNumber,
|
|
1161
|
+
error: 'Use of "this" in regular function may cause context error',
|
|
1162
|
+
suggestion: 'Use arrow function (() => {}) to preserve "this" context',
|
|
1163
|
+
severity: 'warning'
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
// Detect 'this' in regular function passed to array methods
|
|
1167
|
+
if (trimmed.includes('function(') && trimmed.match(/\.(map|filter|forEach|reduce|some|every|find)\(/)) {
|
|
1168
|
+
// Look ahead to see if 'this' is used in following lines
|
|
1169
|
+
const nextLines = lines.slice(index, Math.min(index + 3, lines.length));
|
|
1170
|
+
const hasThis = nextLines.some(l => l.includes('this.'));
|
|
1171
|
+
if (hasThis) {
|
|
1172
|
+
lineErrors.push({
|
|
1173
|
+
line: lineNumber,
|
|
1174
|
+
error: 'Function callback with "this" may lose context',
|
|
1175
|
+
suggestion: 'Use arrow function or .bind(this) to maintain context',
|
|
1176
|
+
severity: 'warning'
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
result.syntax.lineErrors = lineErrors;
|
|
1182
|
+
}
|
|
1183
|
+
// Phase 2.1: Detect callback hell (excessive nesting)
|
|
1184
|
+
detectCallbackHell(code, result) {
|
|
1185
|
+
const lines = code.split('\n');
|
|
1186
|
+
const lineErrors = result.syntax.lineErrors || [];
|
|
1187
|
+
let inMultiLineComment = false;
|
|
1188
|
+
let currentNesting = 0;
|
|
1189
|
+
let maxNesting = 0;
|
|
1190
|
+
let nestingStartLine = 0;
|
|
1191
|
+
lines.forEach((line, index) => {
|
|
1192
|
+
const lineNumber = index + 1;
|
|
1193
|
+
const trimmed = line.trim();
|
|
1194
|
+
// Track multi-line comments
|
|
1195
|
+
if (trimmed.includes('/*'))
|
|
1196
|
+
inMultiLineComment = true;
|
|
1197
|
+
if (trimmed.includes('*/')) {
|
|
1198
|
+
inMultiLineComment = false;
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
// Skip comments
|
|
1202
|
+
if (!trimmed || inMultiLineComment || trimmed.startsWith('//') || trimmed.startsWith('*')) {
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
// Skip lines inside template literals (they contain arbitrary text, not JavaScript)
|
|
1206
|
+
if ((0, syntax_helpers_1.isInsideTemplateLiteral)(code, lineNumber)) {
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
// Count opening braces in callbacks
|
|
1210
|
+
if (trimmed.includes('function(') || trimmed.includes('function (') ||
|
|
1211
|
+
trimmed.includes('=>') && trimmed.includes('{')) {
|
|
1212
|
+
if (currentNesting === 0) {
|
|
1213
|
+
nestingStartLine = lineNumber;
|
|
1214
|
+
}
|
|
1215
|
+
currentNesting++;
|
|
1216
|
+
if (currentNesting > maxNesting) {
|
|
1217
|
+
maxNesting = currentNesting;
|
|
1218
|
+
}
|
|
1219
|
+
// Warn if nesting exceeds 3 levels
|
|
1220
|
+
if (currentNesting > 3) {
|
|
1221
|
+
lineErrors.push({
|
|
1222
|
+
line: nestingStartLine,
|
|
1223
|
+
error: `Callback Hell: ${currentNesting} levels of nested callbacks`,
|
|
1224
|
+
suggestion: 'Refactor using Promises, async/await, or extract named functions',
|
|
1225
|
+
severity: 'warning'
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
// Count closing braces
|
|
1230
|
+
const openBraces = (trimmed.match(/\{/g) || []).length;
|
|
1231
|
+
const closeBraces = (trimmed.match(/\}/g) || []).length;
|
|
1232
|
+
currentNesting += openBraces - closeBraces;
|
|
1233
|
+
// Reset if we're back to level 0
|
|
1234
|
+
if (currentNesting <= 0) {
|
|
1235
|
+
currentNesting = 0;
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
result.syntax.lineErrors = lineErrors;
|
|
1239
|
+
}
|
|
1240
|
+
// Phase 2.2: Detect unintentional array mutations
|
|
1241
|
+
detectArrayMutations(code, result) {
|
|
1242
|
+
const lines = code.split('\n');
|
|
1243
|
+
const lineErrors = result.syntax.lineErrors || [];
|
|
1244
|
+
let inMultiLineComment = false;
|
|
1245
|
+
const mutatingMethods = [
|
|
1246
|
+
{ method: '.sort(', message: '.sort() modifies the original array', suggestion: 'Use [...array].sort() or array.toSorted()' },
|
|
1247
|
+
{ method: '.reverse(', message: '.reverse() modifies the original array', suggestion: 'Use [...array].reverse() or array.toReversed()' },
|
|
1248
|
+
{ method: '.splice(', message: '.splice() modifies the original array', suggestion: 'Use .slice() or .toSpliced() if you do not want to mutate' },
|
|
1249
|
+
{ method: '.fill(', message: '.fill() modifies the original array', suggestion: 'Create a copy first: [...array].fill()' }
|
|
1250
|
+
];
|
|
1251
|
+
lines.forEach((line, index) => {
|
|
1252
|
+
const lineNumber = index + 1;
|
|
1253
|
+
const trimmed = line.trim();
|
|
1254
|
+
// Track multi-line comments
|
|
1255
|
+
if (trimmed.includes('/*'))
|
|
1256
|
+
inMultiLineComment = true;
|
|
1257
|
+
if (trimmed.includes('*/')) {
|
|
1258
|
+
inMultiLineComment = false;
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
// Skip comments
|
|
1262
|
+
if (!trimmed || inMultiLineComment || trimmed.startsWith('//') || trimmed.startsWith('*')) {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
// Skip lines inside template literals (they contain arbitrary text, not JavaScript)
|
|
1266
|
+
if ((0, syntax_helpers_1.isInsideTemplateLiteral)(code, lineNumber)) {
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
mutatingMethods.forEach(({ method, message, suggestion }) => {
|
|
1270
|
+
if (line.includes(method)) {
|
|
1271
|
+
// Check if result is being used (assigned or chained)
|
|
1272
|
+
const beforeMethod = line.substring(0, line.indexOf(method));
|
|
1273
|
+
const afterMethod = line.substring(line.indexOf(method) + method.length);
|
|
1274
|
+
// If method is not assigned and not chained with dot, likely unintentional mutation
|
|
1275
|
+
const isAssigned = beforeMethod.includes('=') || beforeMethod.includes('const ') ||
|
|
1276
|
+
beforeMethod.includes('let ') || beforeMethod.includes('var ');
|
|
1277
|
+
const isChained = afterMethod.trim().startsWith('.');
|
|
1278
|
+
if (!isAssigned && !isChained) {
|
|
1279
|
+
lineErrors.push({
|
|
1280
|
+
line: lineNumber,
|
|
1281
|
+
error: `Unintentional mutation: ${message}`,
|
|
1282
|
+
suggestion,
|
|
1283
|
+
severity: 'warning'
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
});
|
|
1288
|
+
});
|
|
1289
|
+
result.syntax.lineErrors = lineErrors;
|
|
1290
|
+
}
|
|
1291
|
+
// Phase 2.3: Detect DOM queries without null checks
|
|
1292
|
+
detectDOMNullChecks(code, result) {
|
|
1293
|
+
const lines = code.split('\n');
|
|
1294
|
+
const lineErrors = result.syntax.lineErrors || [];
|
|
1295
|
+
let inMultiLineComment = false;
|
|
1296
|
+
const domMethods = [
|
|
1297
|
+
'getElementById(',
|
|
1298
|
+
'querySelector(',
|
|
1299
|
+
'getElementsByClassName(',
|
|
1300
|
+
'getElementsByTagName(',
|
|
1301
|
+
'getElementsByName('
|
|
1302
|
+
];
|
|
1303
|
+
lines.forEach((line, index) => {
|
|
1304
|
+
const lineNumber = index + 1;
|
|
1305
|
+
const trimmed = line.trim();
|
|
1306
|
+
// Track multi-line comments
|
|
1307
|
+
if (trimmed.includes('/*'))
|
|
1308
|
+
inMultiLineComment = true;
|
|
1309
|
+
if (trimmed.includes('*/')) {
|
|
1310
|
+
inMultiLineComment = false;
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
// Skip comments
|
|
1314
|
+
if (!trimmed || inMultiLineComment || trimmed.startsWith('//') || trimmed.startsWith('*')) {
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1317
|
+
// Skip lines inside template literals (they contain arbitrary text, not JavaScript)
|
|
1318
|
+
if ((0, syntax_helpers_1.isInsideTemplateLiteral)(code, lineNumber)) {
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
domMethods.forEach(method => {
|
|
1322
|
+
if (line.includes(method)) {
|
|
1323
|
+
// Check if there's a null check in the next few lines
|
|
1324
|
+
const nextLines = lines.slice(index, Math.min(index + 3, lines.length));
|
|
1325
|
+
const hasNullCheck = nextLines.some(l => l.includes('if (') && (l.includes('!== null') || l.includes('!= null') || l.includes('&&') || l.includes('?.')));
|
|
1326
|
+
if (!hasNullCheck) {
|
|
1327
|
+
lineErrors.push({
|
|
1328
|
+
line: lineNumber,
|
|
1329
|
+
error: `DOM query without null check: ${method}`,
|
|
1330
|
+
suggestion: 'Add check: if (element !== null) { ... } or use optional chaining (?.))',
|
|
1331
|
+
severity: 'warning'
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1336
|
+
});
|
|
1337
|
+
result.syntax.lineErrors = lineErrors;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Option B: Detect blocking operations that can impact performance
|
|
1341
|
+
* - JSON.parse() in loops
|
|
1342
|
+
* - Heavy synchronous operations in loops
|
|
1343
|
+
* - Large string operations in loops
|
|
1344
|
+
* - Regex operations in loops
|
|
1345
|
+
* - DOM manipulation in loops
|
|
1346
|
+
*/
|
|
1347
|
+
detectBlockingOperations(code, result) {
|
|
1348
|
+
const lines = code.split('\n');
|
|
1349
|
+
const lineErrors = result.syntax.lineErrors || [];
|
|
1350
|
+
let inMultiLineComment = false;
|
|
1351
|
+
let loopNesting = 0;
|
|
1352
|
+
let loopStartLines = [];
|
|
1353
|
+
let braceDepth = 0;
|
|
1354
|
+
const loopBraceDepths = [];
|
|
1355
|
+
lines.forEach((line, index) => {
|
|
1356
|
+
const trimmed = line.trim();
|
|
1357
|
+
const lineNumber = index + 1;
|
|
1358
|
+
// Track multi-line comments
|
|
1359
|
+
if (trimmed.includes('/*'))
|
|
1360
|
+
inMultiLineComment = true;
|
|
1361
|
+
if (trimmed.includes('*/')) {
|
|
1362
|
+
inMultiLineComment = false;
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
// Skip comments
|
|
1366
|
+
if (!trimmed || inMultiLineComment || trimmed.startsWith('//') || trimmed.startsWith('*')) {
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
// Skip lines inside template literals (they contain arbitrary text, not JavaScript)
|
|
1370
|
+
if ((0, syntax_helpers_1.isInsideTemplateLiteral)(code, lineNumber)) {
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
// Track brace depth for all code
|
|
1374
|
+
const openBraces = (line.match(/\{/g) || []).length;
|
|
1375
|
+
const closeBraces = (line.match(/\}/g) || []).length;
|
|
1376
|
+
braceDepth += openBraces - closeBraces;
|
|
1377
|
+
// Detect loop start
|
|
1378
|
+
const isForLoop = /\b(for|while)\s*\(/.test(trimmed);
|
|
1379
|
+
const isArrayMethod = /\.(forEach|map|filter|reduce|some|every|find)\s*\(/.test(trimmed);
|
|
1380
|
+
if (isForLoop || isArrayMethod) {
|
|
1381
|
+
loopNesting++;
|
|
1382
|
+
loopStartLines.push(lineNumber);
|
|
1383
|
+
loopBraceDepths.push(braceDepth);
|
|
1384
|
+
}
|
|
1385
|
+
// Check for blocking operations inside loops
|
|
1386
|
+
if (loopNesting > 0) {
|
|
1387
|
+
// 1. JSON.parse() in loops
|
|
1388
|
+
if (trimmed.includes('JSON.parse(') && !trimmed.includes('//')) {
|
|
1389
|
+
lineErrors.push({
|
|
1390
|
+
line: lineNumber,
|
|
1391
|
+
error: 'Blocking operation: JSON.parse() inside loop',
|
|
1392
|
+
suggestion: 'Move JSON.parse() outside the loop or process the data before iteration',
|
|
1393
|
+
severity: 'warning'
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
// 2. JSON.stringify() in loops
|
|
1397
|
+
if (trimmed.includes('JSON.stringify(') && !trimmed.includes('//')) {
|
|
1398
|
+
lineErrors.push({
|
|
1399
|
+
line: lineNumber,
|
|
1400
|
+
error: 'Blocking operation: JSON.stringify() inside loop',
|
|
1401
|
+
suggestion: 'Move JSON.stringify() outside the loop to avoid repeated serialization',
|
|
1402
|
+
severity: 'warning'
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
// 3. Complex regex operations in loops (only inline regex literals, not variables)
|
|
1406
|
+
// Match: /pattern/.test() or new RegExp() but NOT: variable.test()
|
|
1407
|
+
// Remove comments to avoid false positives from comments containing /
|
|
1408
|
+
const withoutComments = trimmed.split('//')[0];
|
|
1409
|
+
const hasNewRegExp = /new\s+RegExp\(/.test(withoutComments);
|
|
1410
|
+
// Only flag if .test( is immediately preceded by a regex literal ending (/ or flag)
|
|
1411
|
+
// This checks the character RIGHT before .test( is / or g,i,m,u,y
|
|
1412
|
+
const testIndex = withoutComments.indexOf('.test(');
|
|
1413
|
+
let hasInlineRegexLiteral = false;
|
|
1414
|
+
if (testIndex > 0) {
|
|
1415
|
+
const charBeforeTest = withoutComments.charAt(testIndex - 1);
|
|
1416
|
+
// Check if it's a regex literal ending: / or a flag (g, i, m, u, y)
|
|
1417
|
+
hasInlineRegexLiteral = charBeforeTest === '/' || /[gimuy]/.test(charBeforeTest);
|
|
1418
|
+
}
|
|
1419
|
+
if (hasInlineRegexLiteral || hasNewRegExp) {
|
|
1420
|
+
lineErrors.push({
|
|
1421
|
+
line: lineNumber,
|
|
1422
|
+
error: 'Blocking operation: Complex regex inside loop',
|
|
1423
|
+
suggestion: 'Define the regex outside the loop: const regex = /pattern/; before the loop',
|
|
1424
|
+
severity: 'warning'
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
// 4. DOM manipulation in loops
|
|
1428
|
+
const domMethods = [
|
|
1429
|
+
'getElementById', 'getElementsByClassName', 'getElementsByTagName',
|
|
1430
|
+
'querySelector', 'querySelectorAll', 'createElement',
|
|
1431
|
+
'appendChild', 'removeChild', 'insertBefore'
|
|
1432
|
+
];
|
|
1433
|
+
const hasDOMManipulation = domMethods.some(method => trimmed.includes(method + '(') || trimmed.includes('.' + method + '('));
|
|
1434
|
+
if (hasDOMManipulation && !trimmed.includes('//')) {
|
|
1435
|
+
lineErrors.push({
|
|
1436
|
+
line: lineNumber,
|
|
1437
|
+
error: 'Blocking operation: DOM manipulation inside loop',
|
|
1438
|
+
suggestion: 'Accumulate changes and manipulate DOM once after the loop (Document Fragment)',
|
|
1439
|
+
severity: 'warning'
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
// 5. Large string concatenation in loops
|
|
1443
|
+
if (/\+=\s*['"`]/.test(trimmed) && !trimmed.includes('//')) {
|
|
1444
|
+
lineErrors.push({
|
|
1445
|
+
line: lineNumber,
|
|
1446
|
+
error: 'Blocking operation: String concatenation with += inside loop',
|
|
1447
|
+
suggestion: 'Use array.push() inside the loop and array.join() at the end',
|
|
1448
|
+
severity: 'warning'
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
// 6. Synchronous file operations (Node.js)
|
|
1452
|
+
if (/fs\.(readFileSync|writeFileSync|existsSync)/.test(trimmed) && !trimmed.includes('//')) {
|
|
1453
|
+
lineErrors.push({
|
|
1454
|
+
line: lineNumber,
|
|
1455
|
+
error: 'Blocking operation: Synchronous file operation inside loop',
|
|
1456
|
+
suggestion: 'Use async versions (readFile, writeFile) or move outside the loop',
|
|
1457
|
+
severity: 'warning'
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
// 7. localStorage/sessionStorage access in loops
|
|
1461
|
+
if (/(localStorage|sessionStorage)\.(getItem|setItem|removeItem)/.test(trimmed) && !trimmed.includes('//')) {
|
|
1462
|
+
lineErrors.push({
|
|
1463
|
+
line: lineNumber,
|
|
1464
|
+
error: 'Blocking operation: Storage access inside loop',
|
|
1465
|
+
suggestion: 'Accumulate the data and perform a single storage operation after the loop',
|
|
1466
|
+
severity: 'warning'
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
// 8. Nested loops (performance warning)
|
|
1470
|
+
if (loopNesting > 2) {
|
|
1471
|
+
if (isForLoop || isArrayMethod) {
|
|
1472
|
+
lineErrors.push({
|
|
1473
|
+
line: lineNumber,
|
|
1474
|
+
error: `Performance: ${loopNesting} levels of nested loops`,
|
|
1475
|
+
suggestion: 'Consider refactoring with optimized data structures (Map, Set) or more efficient algorithms',
|
|
1476
|
+
severity: 'warning'
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
// Detect loop end by tracking brace depth
|
|
1482
|
+
if (loopNesting > 0 && loopBraceDepths.length > 0) {
|
|
1483
|
+
if (braceDepth < loopBraceDepths[loopBraceDepths.length - 1]) {
|
|
1484
|
+
loopNesting--;
|
|
1485
|
+
loopStartLines.pop();
|
|
1486
|
+
loopBraceDepths.pop();
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
});
|
|
1490
|
+
result.syntax.lineErrors = lineErrors;
|
|
1491
|
+
}
|
|
1492
|
+
analyzeQuality(code, result) {
|
|
1493
|
+
const issues = [];
|
|
1494
|
+
// Check for use of var
|
|
1495
|
+
const varUsage = code.match(/\bvar\s+/g);
|
|
1496
|
+
if (varUsage) {
|
|
1497
|
+
issues.push({
|
|
1498
|
+
type: 'warning',
|
|
1499
|
+
message: `Use const/let instead of var (${varUsage.length} occurrences)`,
|
|
1500
|
+
severity: 'medium'
|
|
1501
|
+
});
|
|
1502
|
+
result.quality.score -= varUsage.length * 5;
|
|
1503
|
+
}
|
|
1504
|
+
// Check for anonymous functions that could be arrow functions
|
|
1505
|
+
const anonymousFunctions = code.match(/function\s*\(/g);
|
|
1506
|
+
if (anonymousFunctions) {
|
|
1507
|
+
issues.push({
|
|
1508
|
+
type: 'info',
|
|
1509
|
+
message: 'Consider using arrow functions for anonymous functions',
|
|
1510
|
+
severity: 'low'
|
|
1511
|
+
});
|
|
1512
|
+
result.quality.score -= 3;
|
|
1513
|
+
}
|
|
1514
|
+
// Check for console.log in production
|
|
1515
|
+
const consoleLogs = code.match(/console\.log/g);
|
|
1516
|
+
if (consoleLogs && consoleLogs.length > 2) {
|
|
1517
|
+
issues.push({
|
|
1518
|
+
type: 'warning',
|
|
1519
|
+
message: `Too many console.log found (${consoleLogs.length}) - remove in production`,
|
|
1520
|
+
severity: 'medium'
|
|
1521
|
+
});
|
|
1522
|
+
result.quality.score -= 5;
|
|
1523
|
+
}
|
|
1524
|
+
// Check for single-letter variable names
|
|
1525
|
+
const singleLetterVars = code.match(/\b[a-z]\s*=/g);
|
|
1526
|
+
if (singleLetterVars) {
|
|
1527
|
+
issues.push({
|
|
1528
|
+
type: 'info',
|
|
1529
|
+
message: 'Use more descriptive variable names',
|
|
1530
|
+
severity: 'low'
|
|
1531
|
+
});
|
|
1532
|
+
result.quality.score -= 3;
|
|
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 loops with uncached .length
|
|
1540
|
+
if (code.includes('.length') && code.includes('for')) {
|
|
1541
|
+
if (!code.includes('len =') && !code.includes('length =')) {
|
|
1542
|
+
suggestions.push('Cache array .length in loops for better performance');
|
|
1543
|
+
result.performance.score -= 8;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
// Check for string concatenation in loops
|
|
1547
|
+
if (code.match(/for.*\{[\s\S]*?\+\s*=.*string|string.*\+\s*=/)) {
|
|
1548
|
+
suggestions.push('Avoid string concatenation in loops - use array.join()');
|
|
1549
|
+
result.performance.score -= 15;
|
|
1550
|
+
}
|
|
1551
|
+
// Check for repeated DOM queries
|
|
1552
|
+
const domQueries = (code.match(/document\.(getElementById|querySelector)/g) || []).length;
|
|
1553
|
+
if (domQueries > 2) {
|
|
1554
|
+
suggestions.push('Cache DOM references to avoid repeated queries');
|
|
1555
|
+
result.performance.score -= 10;
|
|
1556
|
+
}
|
|
1557
|
+
// Check for setTimeout(0)
|
|
1558
|
+
if (code.includes('setTimeout') && code.includes('0')) {
|
|
1559
|
+
suggestions.push('Use requestAnimationFrame instead of setTimeout(0)');
|
|
1560
|
+
result.performance.score -= 5;
|
|
1561
|
+
}
|
|
1562
|
+
result.performance.suggestions = suggestions;
|
|
1563
|
+
result.performance.score = Math.max(0, result.performance.score);
|
|
1564
|
+
}
|
|
1565
|
+
createSecurityVulnerability(vulnerabilityType, message, suggestion, lineNumber, attackDescription, exploitExample, realWorldImpact, remediationBefore, remediationAfter, remediationExplanation) {
|
|
1566
|
+
const scoring = (0, severity_scoring_1.calculateSeverityScore)(vulnerabilityType);
|
|
1567
|
+
const compliance = (0, compliance_mapping_1.getComplianceMapping)(vulnerabilityType);
|
|
1568
|
+
return {
|
|
1569
|
+
severity: scoring.severity,
|
|
1570
|
+
message,
|
|
1571
|
+
suggestion,
|
|
1572
|
+
line: lineNumber,
|
|
1573
|
+
category: vulnerabilityType, // PHASE 6 (2025-11-21): Added category for test compatibility
|
|
1574
|
+
cvssScore: scoring.cvssScore,
|
|
1575
|
+
exploitLikelihood: scoring.exploitLikelihood,
|
|
1576
|
+
impact: scoring.impact,
|
|
1577
|
+
owasp: compliance.owasp,
|
|
1578
|
+
cwe: compliance.cwe,
|
|
1579
|
+
pciDss: compliance.pciDss,
|
|
1580
|
+
attackVector: {
|
|
1581
|
+
description: attackDescription,
|
|
1582
|
+
exploitExample,
|
|
1583
|
+
realWorldImpact
|
|
1584
|
+
},
|
|
1585
|
+
remediation: {
|
|
1586
|
+
before: remediationBefore,
|
|
1587
|
+
after: remediationAfter,
|
|
1588
|
+
explanation: remediationExplanation
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
analyzeSecurity(code, result) {
|
|
1593
|
+
const vulnerabilities = [];
|
|
1594
|
+
const lines = code.split('\n');
|
|
1595
|
+
let inMultiLineComment = false;
|
|
1596
|
+
lines.forEach((line, index) => {
|
|
1597
|
+
const lineNumber = index + 1;
|
|
1598
|
+
const trimmed = line.trim();
|
|
1599
|
+
// CRITICAL: Track multi-line comment blocks (/* ... */)
|
|
1600
|
+
// FIX (Dec 6, 2025): Added proper multi-line comment tracking like Java analyzer
|
|
1601
|
+
if (trimmed.includes('/*')) {
|
|
1602
|
+
inMultiLineComment = true;
|
|
1603
|
+
}
|
|
1604
|
+
if (trimmed.includes('*/')) {
|
|
1605
|
+
inMultiLineComment = false;
|
|
1606
|
+
return; // Skip the line with */
|
|
1607
|
+
}
|
|
1608
|
+
// Skip all lines inside multi-line comments and single-line comments
|
|
1609
|
+
if (!trimmed || inMultiLineComment || trimmed.startsWith('//') || trimmed.startsWith('*')) {
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
// OWASP A03:2021 - Injection
|
|
1613
|
+
// 1. eval() - CRITICAL
|
|
1614
|
+
// FIX (Dec 9, 2025): Skip eval() inside string literals (e.g., "This uses eval()")
|
|
1615
|
+
const lineWithoutStrings = (0, syntax_helpers_1.removeStringLiterals)(trimmed);
|
|
1616
|
+
if (lineWithoutStrings.includes('eval(')) {
|
|
1617
|
+
vulnerabilities.push(this.createSecurityVulnerability('eval-usage', 'eval() allows arbitrary code execution', 'Use JSON.parse() for data or refactor code to avoid dynamic execution', lineNumber, 'An attacker can inject malicious code through any input that reaches eval(), enabling complete control over the application\'s execution context.', 'eval(userInput) where userInput = "require(\'child_process\').exec(\'rm -rf /\')"', [
|
|
1618
|
+
'Remote Code Execution (RCE)',
|
|
1619
|
+
'Complete system compromise',
|
|
1620
|
+
'Data theft and exfiltration',
|
|
1621
|
+
'Malware installation'
|
|
1622
|
+
], 'const result = eval(userInput);', 'const result = JSON.parse(userInput); // For data only', 'Replace eval() with JSON.parse() for data parsing, or refactor code to avoid dynamic execution entirely'));
|
|
1623
|
+
}
|
|
1624
|
+
// 2. Function constructor - HIGH
|
|
1625
|
+
if (trimmed.match(/new\s+Function\s*\(/)) {
|
|
1626
|
+
vulnerabilities.push(this.createSecurityVulnerability('function-constructor', 'Function constructor allows code injection similar to eval()', 'Avoid creating functions dynamically from strings', lineNumber, 'The Function constructor creates functions from strings at runtime, allowing arbitrary code execution if the input is attacker-controlled.', 'new Function(userInput)() where userInput = "return process.env"', [
|
|
1627
|
+
'Code injection',
|
|
1628
|
+
'Access to sensitive data',
|
|
1629
|
+
'Bypass of security restrictions',
|
|
1630
|
+
'Remote code execution in certain contexts'
|
|
1631
|
+
], 'const fn = new Function(userCode); fn();', '// Refactor to avoid dynamic code generation\n// Use predefined functions or safer alternatives', 'Eliminate dynamic function creation. Use predefined functions, configuration objects, or refactor the architecture'));
|
|
1632
|
+
}
|
|
1633
|
+
// 3. setTimeout/setInterval with strings or variables - MEDIUM
|
|
1634
|
+
// Detects: setTimeout("code", 1000) OR setTimeout(code, 1000) where code is a variable
|
|
1635
|
+
if (trimmed.match(/set(Timeout|Interval)\s*\(\s*['"]/) ||
|
|
1636
|
+
trimmed.match(/set(Timeout|Interval)\s*\(\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*,/)) {
|
|
1637
|
+
vulnerabilities.push(this.createSecurityVulnerability('settimeout-string', 'setTimeout/setInterval with string or code variable executes code like eval()', 'Use function reference or arrow function instead', lineNumber, 'Passing a string or variable containing code to setTimeout/setInterval causes it to be evaluated as code, similar to eval().', 'setTimeout("alert(userInput)", 1000) or setTimeout(code, 1000) where code/userInput is attacker-controlled', [
|
|
1638
|
+
'Code injection via timing functions',
|
|
1639
|
+
'XSS attacks',
|
|
1640
|
+
'Bypass of CSP (Content Security Policy)'
|
|
1641
|
+
], 'setTimeout("doSomething()", 1000); // or setTimeout(code, 1000);', 'setTimeout(() => doSomething(), 1000);', 'Always pass a function reference or arrow function to setTimeout/setInterval, never a string or variable containing code'));
|
|
1642
|
+
}
|
|
1643
|
+
// OWASP A03:2021 - XSS (Cross-Site Scripting)
|
|
1644
|
+
// 4. innerHTML with variables - HIGH
|
|
1645
|
+
if (trimmed.match(/\.innerHTML\s*=/) && (trimmed.includes('+') || trimmed.includes('${'))) {
|
|
1646
|
+
vulnerabilities.push(this.createSecurityVulnerability('xss', 'XSS: innerHTML with unsanitized user content', 'Use textContent, DOMPurify.sanitize(), or createElement()', lineNumber, 'An attacker can inject malicious JavaScript code through user input, stealing session cookies, credentials, or performing actions on behalf of the user.', 'element.innerHTML = "<div>" + userInput + "</div>" where userInput = "<img src=x onerror=alert(document.cookie)>"', [
|
|
1647
|
+
'Session hijacking (cookie theft)',
|
|
1648
|
+
'Credential theft (keylogging)',
|
|
1649
|
+
'Phishing attacks',
|
|
1650
|
+
'Malware distribution',
|
|
1651
|
+
'Defacement'
|
|
1652
|
+
], 'element.innerHTML = "<div>" + userContent + "</div>";', 'element.textContent = userContent; // Safe for plain text\n// Or: element.innerHTML = DOMPurify.sanitize(userContent);', 'Use textContent for plain text, or sanitize HTML with DOMPurify before setting innerHTML'));
|
|
1653
|
+
}
|
|
1654
|
+
// 5. outerHTML - HIGH
|
|
1655
|
+
if (trimmed.match(/\.outerHTML\s*=/) && (trimmed.includes('+') || trimmed.includes('${'))) {
|
|
1656
|
+
vulnerabilities.push(this.createSecurityVulnerability('xss', 'XSS: outerHTML with unsanitized variables', 'Use safe DOM methods or DOMPurify.sanitize()', lineNumber, 'Setting outerHTML with user content allows XSS attacks by replacing the entire element with malicious HTML.', 'element.outerHTML = userHTML where userHTML contains <img src=x onerror=alert(1)>', [
|
|
1657
|
+
'Cross-site scripting (XSS)',
|
|
1658
|
+
'Session hijacking',
|
|
1659
|
+
'Credential theft',
|
|
1660
|
+
'Malware distribution'
|
|
1661
|
+
], 'element.outerHTML = "<div>" + userContent + "</div>";', 'const div = document.createElement("div");\ndiv.textContent = userContent;\nelement.replaceWith(div);', 'Create elements using createElement() and set content with textContent, or sanitize HTML with DOMPurify'));
|
|
1662
|
+
}
|
|
1663
|
+
// 6. document.write - MEDIUM
|
|
1664
|
+
if (trimmed.includes('document.write')) {
|
|
1665
|
+
vulnerabilities.push(this.createSecurityVulnerability('document-write', 'document.write is deprecated and can cause XSS', 'Use createElement() and appendChild() instead', lineNumber, 'document.write() is synchronous, deprecated, and can be exploited for XSS if used with untrusted data.', 'document.write("<div>" + userInput + "</div>")', [
|
|
1666
|
+
'XSS vulnerability',
|
|
1667
|
+
'Performance issues (parser-blocking)',
|
|
1668
|
+
'Overwrites page content if called after page load'
|
|
1669
|
+
], 'document.write("<h1>" + title + "</h1>");', 'const h1 = document.createElement("h1");\nh1.textContent = title;\ndocument.body.appendChild(h1);', 'Use modern DOM APIs: createElement(), textContent, and appendChild()'));
|
|
1670
|
+
}
|
|
1671
|
+
// 7. dangerouslySetInnerHTML (React) - HIGH
|
|
1672
|
+
const dangerousHTML = (0, react_security_1.detectDangerouslySetInnerHTML)(trimmed, lineNumber, this.createSecurityVulnerability.bind(this));
|
|
1673
|
+
if (dangerousHTML)
|
|
1674
|
+
vulnerabilities.push(dangerousHTML);
|
|
1675
|
+
// 8. Missing key prop in React lists - MEDIUM
|
|
1676
|
+
const missingKey = (0, react_security_1.detectMissingKeyProp)(trimmed, lineNumber, lines, index, this.createSecurityVulnerability.bind(this));
|
|
1677
|
+
if (missingKey)
|
|
1678
|
+
vulnerabilities.push(missingKey);
|
|
1679
|
+
// 9. Unsafe href with user input (React XSS) - HIGH
|
|
1680
|
+
const unsafeHref = (0, react_security_1.detectUnsafeHref)(trimmed, lineNumber, this.createSecurityVulnerability.bind(this));
|
|
1681
|
+
if (unsafeHref)
|
|
1682
|
+
vulnerabilities.push(unsafeHref);
|
|
1683
|
+
// 10. Direct state mutation in React - MEDIUM
|
|
1684
|
+
const stateMutation = (0, react_security_1.detectStateMutation)(trimmed, lineNumber, this.createSecurityVulnerability.bind(this));
|
|
1685
|
+
if (stateMutation)
|
|
1686
|
+
vulnerabilities.push(stateMutation);
|
|
1687
|
+
// ES6+ Security Patterns
|
|
1688
|
+
// 11. Object.assign() prototype pollution - HIGH
|
|
1689
|
+
const prototypePollution = (0, es6_security_1.detectPrototypePollution)(trimmed, lineNumber, this.createSecurityVulnerability.bind(this));
|
|
1690
|
+
if (prototypePollution)
|
|
1691
|
+
vulnerabilities.push(prototypePollution);
|
|
1692
|
+
// 12. Unsafe URL() constructor with user input - MEDIUM
|
|
1693
|
+
const urlInjection = (0, es6_security_1.detectURLInjection)(trimmed, lineNumber, this.createSecurityVulnerability.bind(this));
|
|
1694
|
+
if (urlInjection)
|
|
1695
|
+
vulnerabilities.push(urlInjection);
|
|
1696
|
+
// 13. Template literal injection - HIGH
|
|
1697
|
+
const templateInjection = (0, es6_security_1.detectTemplateLiteralInjection)(trimmed, lineNumber, this.createSecurityVulnerability.bind(this));
|
|
1698
|
+
if (templateInjection)
|
|
1699
|
+
vulnerabilities.push(templateInjection);
|
|
1700
|
+
// NOTE: OWASP 2025 checks (A02, A10, A03) moved outside forEach loop for efficiency
|
|
1701
|
+
// They are now called once with all lines after line-by-line analysis completes
|
|
1702
|
+
// See lines after forEach loop closes
|
|
1703
|
+
// OWASP A07:2021 - Authentication & Identification Failures
|
|
1704
|
+
// 8. Hardcoded credentials - CRITICAL
|
|
1705
|
+
// Enhanced to support TypeScript type annotations: const API_SECRET: string = 'value'
|
|
1706
|
+
// Matches: variable names containing secret/password/token/key followed by = and a string
|
|
1707
|
+
if (trimmed.match(/(password|passwd|pwd|secret|token|api[-_]?key|private[-_]?key|auth|encryption[-_]?key)/i) &&
|
|
1708
|
+
trimmed.match(/[:=]\s*['"`]/) &&
|
|
1709
|
+
!trimmed.includes('process.env') &&
|
|
1710
|
+
!trimmed.includes('config.') &&
|
|
1711
|
+
!trimmed.startsWith('//')) {
|
|
1712
|
+
vulnerabilities.push(this.createSecurityVulnerability('hardcoded-credentials', 'Hardcoded credentials exposed in source code', 'Use environment variables (process.env) or secret management services', lineNumber, 'Hardcoded credentials in source code are visible to anyone with access to the repository, including attackers who gain access to the codebase.', 'const password = "MySecretPass123" // Visible in Git history forever', [
|
|
1713
|
+
'Unauthorized access to systems',
|
|
1714
|
+
'Account takeover',
|
|
1715
|
+
'Data breach',
|
|
1716
|
+
'Lateral movement in infrastructure',
|
|
1717
|
+
'Cannot be rotated without code changes'
|
|
1718
|
+
], 'const apiKey = "sk-1234567890abcdef";', 'const apiKey = process.env.API_KEY; // Store in .env file (add to .gitignore)', 'Store secrets in environment variables or secret management services (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault)'));
|
|
1719
|
+
}
|
|
1720
|
+
// OWASP A02:2021 - Cryptographic Failures
|
|
1721
|
+
// 9. Math.random() for security - MEDIUM
|
|
1722
|
+
if (trimmed.match(/Math\.random\(\)/)) {
|
|
1723
|
+
vulnerabilities.push(this.createSecurityVulnerability('weak-random', 'Math.random() is cryptographically weak and predictable', 'Use crypto.randomBytes() (Node.js) or crypto.getRandomValues() (Browser)', lineNumber, 'Math.random() uses a pseudorandom number generator that is predictable and unsuitable for security purposes like token generation.', 'const token = Math.random().toString(36) // Predictable, can be brute-forced', [
|
|
1724
|
+
'Predictable tokens/session IDs',
|
|
1725
|
+
'Session hijacking',
|
|
1726
|
+
'Authentication bypass',
|
|
1727
|
+
'Weak cryptographic keys'
|
|
1728
|
+
], 'const token = Math.random().toString(36).substr(2);', '// Node.js:\nconst token = require("crypto").randomBytes(32).toString("hex");\n// Browser:\nconst token = crypto.getRandomValues(new Uint8Array(32));', 'Use cryptographically secure random number generators for all security-sensitive operations'));
|
|
1729
|
+
}
|
|
1730
|
+
// 9b. Weak cryptographic algorithms (MD5, SHA1) - HIGH
|
|
1731
|
+
// FIX (Dec 9, 2025): Added detection for weak hash algorithms vulnerable to collision attacks
|
|
1732
|
+
if (trimmed.match(/createHash\s*\(\s*['"](?:md5|sha1)['"]\s*\)/i) ||
|
|
1733
|
+
trimmed.match(/crypto\.(?:md5|sha1)/i) ||
|
|
1734
|
+
trimmed.match(/\.(?:md5|sha1)\s*\(/i)) {
|
|
1735
|
+
vulnerabilities.push(this.createSecurityVulnerability('weak-cryptographic-algorithm', 'Weak cryptographic algorithm (MD5/SHA1) vulnerable to collision attacks', 'Use SHA-256 or stronger (SHA-384, SHA-512, SHA-3)', lineNumber, 'MD5 and SHA-1 are cryptographically broken and vulnerable to collision attacks. Attackers can generate different inputs that produce the same hash, compromising data integrity and digital signatures.', 'crypto.createHash("md5").update(password).digest("hex") // Attacker can generate collisions', [
|
|
1736
|
+
'Hash collision attacks',
|
|
1737
|
+
'Password cracking (rainbow tables)',
|
|
1738
|
+
'Digital signature forgery',
|
|
1739
|
+
'Data integrity compromise',
|
|
1740
|
+
'Compliance violations (PCI-DSS, FIPS)'
|
|
1741
|
+
], 'const hash = crypto.createHash("md5").update(data).digest("hex");', 'const hash = crypto.createHash("sha256").update(data).digest("hex"); // Use SHA-256 or stronger', 'Replace MD5/SHA-1 with SHA-256, SHA-384, SHA-512, or SHA-3. For password hashing, use bcrypt, scrypt, or Argon2'));
|
|
1742
|
+
}
|
|
1743
|
+
// 10. localStorage for sensitive data - MEDIUM
|
|
1744
|
+
if (trimmed.match(/localStorage\.(setItem|set)\([^)]*(?:token|password|key|secret)/i)) {
|
|
1745
|
+
vulnerabilities.push(this.createSecurityVulnerability('insecure-storage', 'localStorage is not secure for sensitive data', 'Use httpOnly cookies or server-side sessions', lineNumber, 'localStorage is accessible to any JavaScript on the page, making it vulnerable to XSS attacks. Stored data persists indefinitely.', 'localStorage.setItem("authToken", token) // Accessible to any script, vulnerable to XSS', [
|
|
1746
|
+
'Token theft via XSS',
|
|
1747
|
+
'Credential exposure',
|
|
1748
|
+
'No protection from malicious scripts',
|
|
1749
|
+
'Data persists across sessions (privacy issue)'
|
|
1750
|
+
], 'localStorage.setItem("authToken", jwtToken);', '// Server-side: Set httpOnly cookie (not accessible to JavaScript)\nres.cookie("authToken", jwtToken, { httpOnly: true, secure: true, sameSite: "strict" });', 'Store sensitive data server-side or in httpOnly cookies. Use sessionStorage for temporary data, but never for sensitive information'));
|
|
1751
|
+
}
|
|
1752
|
+
// 10b. Insecure cookie configuration - HIGH
|
|
1753
|
+
// FIX (Dec 9, 2025): Added detection for cookies without security flags (httpOnly, secure, sameSite)
|
|
1754
|
+
if (trimmed.match(/\.cookie\s*\([^)]*\)/) && trimmed.match(/res\.|response\./)) {
|
|
1755
|
+
// Check if security flags are missing
|
|
1756
|
+
const hasHttpOnly = trimmed.match(/httpOnly\s*:\s*true/i);
|
|
1757
|
+
const hasSecure = trimmed.match(/secure\s*:\s*true/i);
|
|
1758
|
+
const hasSameSite = trimmed.match(/sameSite/i);
|
|
1759
|
+
if (!hasHttpOnly || !hasSecure || !hasSameSite) {
|
|
1760
|
+
vulnerabilities.push(this.createSecurityVulnerability('insecure-cookie', 'Cookie set without security flags (httpOnly, secure, sameSite)', 'Add httpOnly: true, secure: true, sameSite: "strict" to cookie options', lineNumber, 'Cookies without security flags are vulnerable to XSS attacks (JavaScript access), man-in-the-middle attacks (transmitted over HTTP), and CSRF attacks (cross-site request forgery).', 'res.cookie("sessionId", token) // No httpOnly = XSS can steal, no secure = transmitted over HTTP', [
|
|
1761
|
+
'Session hijacking via XSS (cookie theft)',
|
|
1762
|
+
'Man-in-the-middle attacks (HTTP transmission)',
|
|
1763
|
+
'CSRF attacks (cross-site requests)',
|
|
1764
|
+
'Session fixation attacks'
|
|
1765
|
+
], 'res.cookie("sessionId", sessionId);', 'res.cookie("sessionId", sessionId, {\n httpOnly: true, // Not accessible to JavaScript\n secure: true, // Only sent over HTTPS\n sameSite: "strict" // Prevents CSRF attacks\n});', 'Always set httpOnly (prevents XSS), secure (HTTPS only), and sameSite (prevents CSRF) flags on session cookies'));
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
// OWASP A01:2021 - Broken Access Control
|
|
1769
|
+
// 10c. Open redirect vulnerability - MEDIUM
|
|
1770
|
+
// FIX (Dec 9, 2025): Added detection for unvalidated URL redirects
|
|
1771
|
+
if (trimmed.match(/\.redirect\s*\(/) || trimmed.match(/location\.href\s*=/) || trimmed.match(/window\.location\s*=/)) {
|
|
1772
|
+
// Check if user input is used in redirect (query params, user data)
|
|
1773
|
+
const hasUserInput = trimmed.match(/req\.query|req\.body|req\.params|params\.|query\.|body\./) ||
|
|
1774
|
+
trimmed.match(/\$\{.*\}/) || // Template literals
|
|
1775
|
+
trimmed.match(/\+\s*[\w.]+/); // Concatenation with variables
|
|
1776
|
+
if (hasUserInput) {
|
|
1777
|
+
vulnerabilities.push(this.createSecurityVulnerability('open-redirect', 'Open redirect vulnerability - unvalidated URL redirect', 'Validate redirect URLs against a whitelist of allowed domains', lineNumber, 'Unvalidated redirects allow attackers to redirect users to phishing sites or malicious domains. Users trust the legitimate domain in the URL bar, making phishing attacks more effective.', 'res.redirect(req.query.returnUrl) where returnUrl = "http://evil.com/phishing" // User redirected to attacker site', [
|
|
1778
|
+
'Phishing attacks (credential theft)',
|
|
1779
|
+
'Malware distribution',
|
|
1780
|
+
'OAuth token theft',
|
|
1781
|
+
'User trust exploitation',
|
|
1782
|
+
'SEO poisoning'
|
|
1783
|
+
], 'res.redirect(req.query.returnUrl);', 'const allowedDomains = ["example.com", "app.example.com"];\nconst url = new URL(req.query.returnUrl);\nif (allowedDomains.includes(url.hostname)) {\n res.redirect(req.query.returnUrl);\n} else {\n res.redirect("/"); // Safe default\n}', 'Always validate redirect URLs against a whitelist of allowed domains. Never redirect to arbitrary user-supplied URLs'));
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
// OWASP A08:2021 - Software and Data Integrity Failures
|
|
1787
|
+
// 11. Prototype pollution - HIGH
|
|
1788
|
+
// Detects: Literal __proto__ OR generic bracket notation assignment with variables (common in object merging)
|
|
1789
|
+
const hasLiteralProto = trimmed.match(/__proto__|constructor\[.*\]|prototype\[/);
|
|
1790
|
+
const hasBracketAssignment = trimmed.match(/\[[a-zA-Z_$][a-zA-Z0-9_$]*\]\s*=/) && // obj[variable] =
|
|
1791
|
+
!trimmed.match(/\[['"`]/) && // NOT obj["literal"]
|
|
1792
|
+
!trimmed.match(/\[\d+\]/) && // NOT obj[0]
|
|
1793
|
+
!trimmed.includes('Object.create') && // Skip safe patterns
|
|
1794
|
+
!trimmed.includes('Map') && // Skip Map usage
|
|
1795
|
+
!trimmed.includes('Set'); // Skip Set usage
|
|
1796
|
+
// Check if current or previous lines have filtering for __proto__/constructor/prototype (safe pattern)
|
|
1797
|
+
const hasKeyFiltering = (trimmed.includes('__proto__') && trimmed.includes('!==')) || // Current line
|
|
1798
|
+
(index > 0 && lines[index - 1].includes('__proto__') && lines[index - 1].includes('!==')) ||
|
|
1799
|
+
(index > 1 && lines[index - 2].includes('__proto__') && lines[index - 2].includes('!==')) ||
|
|
1800
|
+
(index > 2 && lines[index - 3].includes('__proto__') && lines[index - 3].includes('!=='));
|
|
1801
|
+
if ((hasLiteralProto || hasBracketAssignment) && !hasKeyFiltering) {
|
|
1802
|
+
vulnerabilities.push(this.createSecurityVulnerability('prototype-pollution', 'Prototype pollution vulnerability detected', 'Validate object keys before assignment, use Object.create(null), or use Map instead', lineNumber, 'Prototype pollution allows attackers to inject properties into Object.prototype, affecting all objects and potentially leading to privilege escalation or code execution.', 'obj[key] = value where key = "__proto__" injects properties into all objects', [
|
|
1803
|
+
'Privilege escalation',
|
|
1804
|
+
'Authentication bypass',
|
|
1805
|
+
'Denial of Service',
|
|
1806
|
+
'Remote code execution (in specific contexts)',
|
|
1807
|
+
'Bypass of security checks'
|
|
1808
|
+
], 'const obj = {}; obj[userKey] = userValue; // If userKey = "__proto__", pollutes all objects', 'const obj = Object.create(null); // No prototype\n// Or validate keys:\nif (!["__proto__", "constructor", "prototype"].includes(userKey)) {\n obj[userKey] = userValue;\n}', 'Always validate object keys before assignment. Use Object.create(null) for objects without prototype, or use Map instead'));
|
|
1809
|
+
}
|
|
1810
|
+
// 12. SQL Injection patterns - CRITICAL
|
|
1811
|
+
// Fixed: Exclude safe parameterized query placeholders ($1, $2, etc.)
|
|
1812
|
+
if ((trimmed.match(/SELECT.*FROM.*WHERE.*[+`]/i) || trimmed.match(/SELECT.*FROM.*WHERE.*\$\{/i)) ||
|
|
1813
|
+
trimmed.match(/query\s*=\s*[`'"].*\+/) ||
|
|
1814
|
+
trimmed.match(/execute\(.*\+/)) {
|
|
1815
|
+
vulnerabilities.push(this.createSecurityVulnerability('sql-injection', 'SQL Injection vulnerability detected', 'Use parameterized queries or prepared statements', lineNumber, 'An attacker can inject malicious SQL code through user input, bypassing authentication and accessing/modifying the entire database.', 'username = "admin\' OR \'1\'=\'1" results in: SELECT * FROM users WHERE username = \'admin\' OR \'1\'=\'1\'', [
|
|
1816
|
+
'Full database access (read/write/delete)',
|
|
1817
|
+
'Authentication bypass',
|
|
1818
|
+
'Data exfiltration (passwords, credit cards, PII)',
|
|
1819
|
+
'Data destruction (DROP TABLE attacks)',
|
|
1820
|
+
'Privilege escalation'
|
|
1821
|
+
], 'const query = "SELECT * FROM users WHERE id = " + userId;', 'const query = "SELECT * FROM users WHERE id = ?"; \ndb.query(query, [userId]);', 'Use parameterized queries where inputs are passed as separate parameters, never concatenated into the SQL string'));
|
|
1822
|
+
}
|
|
1823
|
+
// 13. Command Injection - CRITICAL
|
|
1824
|
+
if (trimmed.match(/exec\(|spawn\(|execFile\(/) && (trimmed.includes('+') || trimmed.includes('${'))) {
|
|
1825
|
+
vulnerabilities.push(this.createSecurityVulnerability('command-injection', 'Command Injection vulnerability detected', 'Use execFile() with array arguments, validate/sanitize all inputs', lineNumber, 'Concatenating user input into shell commands allows attackers to execute arbitrary system commands with the application\'s privileges.', 'exec("ls " + userInput) where userInput = "; rm -rf /" executes malicious command', [
|
|
1826
|
+
'Remote Code Execution (RCE)',
|
|
1827
|
+
'Complete system compromise',
|
|
1828
|
+
'Data deletion',
|
|
1829
|
+
'Privilege escalation',
|
|
1830
|
+
'Backdoor installation'
|
|
1831
|
+
], 'const { exec } = require("child_process");\nexec("ls " + userDir);', 'const { execFile } = require("child_process");\nexecFile("ls", [userDir]); // Arguments passed safely as array', 'Use execFile() with array of arguments, never concatenate strings. Validate all inputs against whitelist'));
|
|
1832
|
+
}
|
|
1833
|
+
// 14. Path Traversal - HIGH
|
|
1834
|
+
// FIX (Dec 9, 2025): Improved detection to catch direct user input in file operations
|
|
1835
|
+
if (trimmed.match(/readFile|writeFile|unlink|rmdir|createReadStream|createWriteStream/)) {
|
|
1836
|
+
// Check for traversal patterns OR user input
|
|
1837
|
+
const hasTraversalPattern = trimmed.match(/\.\.\/|\.\.\\|\+.*path/);
|
|
1838
|
+
const hasUserInput = trimmed.match(/req\.query|req\.body|req\.params|params\.|query\.|body\./);
|
|
1839
|
+
if (hasTraversalPattern || hasUserInput) {
|
|
1840
|
+
vulnerabilities.push(this.createSecurityVulnerability('path-traversal', 'Path Traversal vulnerability - unrestricted file access', 'Use path.resolve(), validate against whitelist, restrict to safe directories', lineNumber, 'Path traversal allows attackers to access files outside intended directories using "../" sequences or by directly passing malicious paths, potentially reading sensitive files like /etc/passwd, configuration files, or source code.', 'fs.readFile(req.query.filePath) where filePath = "../../../etc/passwd" // Attacker can read any file', [
|
|
1841
|
+
'Unauthorized file access',
|
|
1842
|
+
'Sensitive data exposure (passwords, keys, source code)',
|
|
1843
|
+
'Configuration file theft',
|
|
1844
|
+
'Potential for code execution if writable files accessed'
|
|
1845
|
+
], 'const content = fs.readFileSync(req.query.filePath);', 'const path = require("path");\nconst baseDir = "/safe/upload/directory";\nconst safePath = path.resolve(baseDir, req.query.filePath);\nif (!safePath.startsWith(baseDir)) {\n throw new Error("Invalid path - traversal detected");\n}\nconst content = fs.readFileSync(safePath);', 'Always resolve and validate paths using path.resolve(). Verify the resolved path starts with your allowed base directory. Never trust user input directly in file operations'));
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
// 15. Regex DoS (ReDoS) - MEDIUM
|
|
1849
|
+
// FIX (Dec 12, 2025): Enhanced to detect more ReDoS patterns
|
|
1850
|
+
// Pattern 1: (something)+ or (something)* where 'something' contains quantifiers
|
|
1851
|
+
// Pattern 2: ([...])+ or ([...])* - redundant quantifier on character class
|
|
1852
|
+
// Pattern 3: (nested(group)+)+ - nested groups with quantifiers
|
|
1853
|
+
if (trimmed.match(/new\s+RegExp|\/.*\(.*\)/)) {
|
|
1854
|
+
// Check for dangerous nested quantifier patterns:
|
|
1855
|
+
// 1. (...+...)+ or (...*...)+ - quantifier inside group, quantifier outside
|
|
1856
|
+
const nestedQuantInside = trimmed.match(/\([^)]*[+*][^)]*\)[+*]/);
|
|
1857
|
+
// 2. ([...])+ or ([...])* - redundant quantifier on character class (inefficient pattern)
|
|
1858
|
+
const redundantQuantifier = trimmed.match(/\(\[[^\]]+\]\)[+*]/);
|
|
1859
|
+
// 3. (...)+ where ... is complex (multiple chars/groups)
|
|
1860
|
+
const complexGroupQuant = trimmed.match(/\([^)]{3,}\)[+*]/);
|
|
1861
|
+
if (nestedQuantInside || redundantQuantifier || (complexGroupQuant && nestedQuantInside)) {
|
|
1862
|
+
vulnerabilities.push(this.createSecurityVulnerability('regex-dos', 'Regular expression with nested/redundant quantifiers can cause ReDoS', 'Simplify regex pattern: use [a-z]+ instead of ([a-z])+, or use timeout/safe-regex libraries', lineNumber, 'Regular expressions with nested quantifiers can have exponential time complexity, allowing attackers to cause Denial of Service with carefully crafted input. Patterns like ([a-z])+ are redundant and should be simplified to [a-z]+.', 'const regex = /^([a-z0-9_])+@/; // Redundant - testing "aaa...aX" causes backtracking', [
|
|
1863
|
+
'Denial of Service (DoS)',
|
|
1864
|
+
'Application freeze/timeout',
|
|
1865
|
+
'CPU exhaustion',
|
|
1866
|
+
'Service unavailability'
|
|
1867
|
+
], 'const regex = /^([a-zA-Z0-9_\\.-])+@/; // Nested quantifiers', 'const regex = /^[a-zA-Z0-9_\\.-]+@/; // Simplified - remove outer parentheses and quantifier\n// Or use timeout:\nconst { match } = require("regex-with-timeout");\nmatch(input, pattern, { timeout: 100 });', 'Avoid nested quantifiers in regex. Simplify ([...])+ to [...]+ for character classes. Use tools like safe-regex to detect vulnerable patterns, or implement timeouts'));
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
// 16. Missing error handling - LOW
|
|
1871
|
+
if ((trimmed.includes('fetch(') || trimmed.includes('await ')) &&
|
|
1872
|
+
!code.includes('try') && !code.includes('.catch')) {
|
|
1873
|
+
vulnerabilities.push(this.createSecurityVulnerability('missing-error-handling', 'Async operation without error handling', 'Add try/catch block or .catch() handler', lineNumber, 'Unhandled promise rejections can crash Node.js applications or leak sensitive error details to users.', 'await fetch(url); // If fails, crashes application or exposes stack trace', [
|
|
1874
|
+
'Application crashes',
|
|
1875
|
+
'Stack trace information disclosure',
|
|
1876
|
+
'Poor user experience',
|
|
1877
|
+
'Potential security information leakage'
|
|
1878
|
+
], 'const data = await fetch(url).then(r => r.json());', 'try {\n const data = await fetch(url).then(r => r.json());\n} catch (error) {\n console.error("Fetch failed:", error.message);\n // Handle error appropriately\n}', 'Always wrap async operations in try/catch or use .catch(). Never expose error details to users in production'));
|
|
1879
|
+
}
|
|
1880
|
+
// 17. console.log in production - LOW
|
|
1881
|
+
// RESTORED (Dec 6, 2025): This check is needed for JavaScript files
|
|
1882
|
+
// TypeScript analyzer handles .ts files, JavaScript analyzer handles .js files
|
|
1883
|
+
// The duplicate detection issue was for .ts files analyzed by BOTH analyzers
|
|
1884
|
+
// This check should run for pure JavaScript files only
|
|
1885
|
+
if (trimmed.includes('console.log')) {
|
|
1886
|
+
vulnerabilities.push(this.createSecurityVulnerability('console-log-production', 'console.log found - remove in production code', 'Use a proper logging library (winston, pino) with appropriate log levels', lineNumber, 'console.log statements can expose sensitive data in production logs and impact performance.', 'console.log("User password:", password); // Exposes secrets in logs', [
|
|
1887
|
+
'Sensitive data exposure in logs',
|
|
1888
|
+
'Performance impact (synchronous I/O)',
|
|
1889
|
+
'No log level control',
|
|
1890
|
+
'Difficult to filter in production'
|
|
1891
|
+
], 'console.log("Debug:", sensitiveData);', 'logger.debug("Processing request"); // Use proper logging library', 'Replace console.log with a logging library that supports log levels and can be disabled in production'));
|
|
1892
|
+
}
|
|
1893
|
+
// 18. Insecure cookie - MEDIUM
|
|
1894
|
+
// Detects: document.cookie = ... without httpOnly, secure, and sameSite flags
|
|
1895
|
+
if (trimmed.includes('document.cookie') && trimmed.includes('=') &&
|
|
1896
|
+
!trimmed.includes('httpOnly') && !trimmed.includes('secure') &&
|
|
1897
|
+
!trimmed.startsWith('//')) {
|
|
1898
|
+
vulnerabilities.push(this.createSecurityVulnerability('insecure-cookie', 'Cookie set without security flags (httpOnly, secure, sameSite)', 'Set cookies server-side with httpOnly, secure, and sameSite flags', lineNumber, 'Cookies set via document.cookie are vulnerable to XSS attacks and can be stolen by malicious scripts. They lack security flags and persist insecurely.', 'document.cookie = "token=" + authToken; // Accessible to JavaScript, vulnerable to XSS theft', [
|
|
1899
|
+
'Cookie theft via XSS',
|
|
1900
|
+
'Session hijacking',
|
|
1901
|
+
'CSRF attacks (without sameSite)',
|
|
1902
|
+
'Man-in-the-middle attacks (without secure flag)',
|
|
1903
|
+
'No HttpOnly protection'
|
|
1904
|
+
], 'document.cookie = `token=${token}; path=/`;', '// Server-side (Node.js/Express):\nres.cookie("token", token, {\n httpOnly: true, // Not accessible to JavaScript\n secure: true, // Only sent over HTTPS\n sameSite: "strict" // CSRF protection\n});', 'Always set sensitive cookies server-side with httpOnly, secure, and sameSite flags. Never use document.cookie for authentication tokens'));
|
|
1905
|
+
}
|
|
1906
|
+
// PHASE 6 WEEK 1 DAY 2: Node.js-Specific Security Checks
|
|
1907
|
+
// 1. Code Injection via require() - CRITICAL
|
|
1908
|
+
// PHASE 6 FIX (2025-11-21): Enhanced detection for string concatenation and property access
|
|
1909
|
+
// Detects: require(variable), require('path' + variable), require(req.params.x)
|
|
1910
|
+
const hasRequireWithVariable = trimmed.match(/require\s*\(\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*[\),]/) &&
|
|
1911
|
+
!trimmed.match(/require\s*\(\s*['"`]/);
|
|
1912
|
+
const hasRequireWithConcatenation = trimmed.match(/require\s*\(/) &&
|
|
1913
|
+
(trimmed.match(/['"`][^'"]*['"]\s*\+/) ||
|
|
1914
|
+
trimmed.match(/\+\s*['"`]/) ||
|
|
1915
|
+
trimmed.match(/\$\{[^}]*\}/)); // Template literals
|
|
1916
|
+
// req.params.x or obj.prop - but NOT string literals like "socket.io"
|
|
1917
|
+
const hasRequireWithPropertyAccess = trimmed.match(/require\s*\([^)]*\.[^)]+\)/) &&
|
|
1918
|
+
!trimmed.match(/require\s*\(\s*['"`]/); // Exclude string literals
|
|
1919
|
+
if (hasRequireWithVariable || hasRequireWithConcatenation || hasRequireWithPropertyAccess) {
|
|
1920
|
+
vulnerabilities.push(this.createSecurityVulnerability('nodejs-require-injection', 'Node.js: Dynamic require() with user input allows arbitrary code execution', 'Use a whitelist of allowed modules or avoid dynamic require() entirely', lineNumber, 'An attacker can load arbitrary modules by controlling the require() argument, leading to remote code execution and complete server compromise.', 'require(req.query.module) where query.module = "child_process" allows executing system commands', [
|
|
1921
|
+
'Remote Code Execution (RCE)',
|
|
1922
|
+
'Complete server compromise',
|
|
1923
|
+
'Data exfiltration',
|
|
1924
|
+
'Malware installation',
|
|
1925
|
+
'Denial of Service'
|
|
1926
|
+
], 'const module = require(userInput);', 'const ALLOWED_MODULES = { "fs": "fs", "path": "path" };\nconst moduleName = ALLOWED_MODULES[userInput];\nif (!moduleName) throw new Error("Invalid module");\nconst module = require(moduleName);', 'Never use user input directly in require(). Use a whitelist of allowed modules or refactor to avoid dynamic loading'));
|
|
1927
|
+
}
|
|
1928
|
+
// 2. Path Traversal in require() - HIGH
|
|
1929
|
+
if (trimmed.match(/require\s*\(\s*['"`]\.\.\//) ||
|
|
1930
|
+
(trimmed.match(/require\s*\(/) && trimmed.match(/[+\$\{]/) && trimmed.includes('../'))) {
|
|
1931
|
+
vulnerabilities.push(this.createSecurityVulnerability('nodejs-path-traversal', 'Node.js: Path traversal in require() allows loading arbitrary files', 'Validate and sanitize file paths, use path.resolve() and check if result is within allowed directory', lineNumber, 'An attacker can traverse directories and load sensitive files or execute unauthorized code by manipulating the file path.', 'require("../controllers/" + req.params.file) where params.file = "../../../../etc/passwd" (if cached by require)', [
|
|
1932
|
+
'Unauthorized file access',
|
|
1933
|
+
'Sensitive data exposure',
|
|
1934
|
+
'Code execution via loading malicious files',
|
|
1935
|
+
'Directory traversal attacks'
|
|
1936
|
+
], 'const file = require("../controllers/" + filename);', 'const path = require("path");\nconst basePath = path.resolve(__dirname, "../controllers");\nconst fullPath = path.resolve(basePath, filename);\nif (!fullPath.startsWith(basePath)) throw new Error("Invalid path");\nconst file = require(fullPath);', 'Always validate file paths, use path.resolve() to normalize, and verify the result is within the allowed directory'));
|
|
1937
|
+
}
|
|
1938
|
+
// 3. Missing Security Headers (helmet) - MEDIUM
|
|
1939
|
+
// RESTORED (Dec 6, 2025): Detects Express apps without helmet() middleware
|
|
1940
|
+
// Now checks for: 1) Express app exists, 2) helmet is NOT actually used
|
|
1941
|
+
// FIX: Check for actual helmet usage patterns, not just the word in comments
|
|
1942
|
+
// Patterns that indicate helmet IS being used:
|
|
1943
|
+
// - require('helmet') or require("helmet") - helmet is imported
|
|
1944
|
+
// - app.use(helmet - helmet middleware is applied
|
|
1945
|
+
// FIX (Dec 6, 2025): Strip comments before checking for helmet import/usage
|
|
1946
|
+
// This prevents false negatives when comments mention helmet
|
|
1947
|
+
const codeWithoutComments = code.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
1948
|
+
const hasHelmetImport = codeWithoutComments.includes("require('helmet')") || codeWithoutComments.includes('require("helmet")') ||
|
|
1949
|
+
codeWithoutComments.includes("from 'helmet'") || codeWithoutComments.includes('from "helmet"');
|
|
1950
|
+
const hasHelmetUsage = codeWithoutComments.match(/app\.use\s*\(\s*helmet/);
|
|
1951
|
+
const isExpressApp = trimmed.includes('express()') || trimmed.match(/=\s*express\s*\(/);
|
|
1952
|
+
if (isExpressApp && !hasHelmetImport && !hasHelmetUsage) {
|
|
1953
|
+
vulnerabilities.push(this.createSecurityVulnerability('nodejs-missing-helmet', 'Express app without helmet() security headers middleware', 'Add app.use(helmet()) to set secure HTTP headers (CSP, HSTS, X-Frame-Options)', lineNumber, 'Express applications without helmet() are vulnerable to common web attacks. Helmet sets security headers that protect against XSS, clickjacking, and other attacks.', 'Express app serves responses without Content-Security-Policy, X-Frame-Options, or HSTS headers', [
|
|
1954
|
+
'Cross-Site Scripting (XSS)',
|
|
1955
|
+
'Clickjacking attacks',
|
|
1956
|
+
'MIME type sniffing attacks',
|
|
1957
|
+
'Missing HTTPS enforcement (HSTS)',
|
|
1958
|
+
'Information disclosure via headers'
|
|
1959
|
+
], 'const app = express();\napp.get("/", (req, res) => res.send("Hello"));', 'const helmet = require("helmet");\nconst app = express();\napp.use(helmet()); // Secure headers\napp.get("/", (req, res) => res.send("Hello"));', 'Install and use helmet: npm install helmet, then add app.use(helmet()) before route definitions'));
|
|
1960
|
+
}
|
|
1961
|
+
// 4. Unsafe Request Handling (req.query/req.params/req.body without validation) - HIGH
|
|
1962
|
+
// PHASE 6 FIX (2025-11-21): Added detection for whole-object assignment
|
|
1963
|
+
// Detects: req.body.prop, req.body["prop"], AND const x = req.body;
|
|
1964
|
+
if ((trimmed.match(/req\.(query|params|body)\[['"]\w+['"]\]/) ||
|
|
1965
|
+
trimmed.match(/req\.(query|params|body)\.\w+/) ||
|
|
1966
|
+
trimmed.match(/=\s*req\.(query|params|body)\s*[;,\)]/) // NEW: whole-object assignment
|
|
1967
|
+
) &&
|
|
1968
|
+
!trimmed.includes('validate') && !trimmed.includes('sanitize') &&
|
|
1969
|
+
!trimmed.includes('parseInt') && !trimmed.includes('Number(')) {
|
|
1970
|
+
vulnerabilities.push(this.createSecurityVulnerability('nodejs-unsafe-request-params', 'Node.js: Unvalidated req.query/req.params/req.body used in sensitive operations', 'Validate and sanitize all user input before use, especially in database queries or system commands', lineNumber, 'User input from req.query, req.params, or req.body is used directly without validation, enabling SQL injection, command injection, or other attacks.', 'db.query(`SELECT * FROM users WHERE id = ${req.query.id}`) where query.id = "1 OR 1=1" (SQL injection)', [
|
|
1971
|
+
'SQL Injection',
|
|
1972
|
+
'NoSQL Injection',
|
|
1973
|
+
'Command Injection',
|
|
1974
|
+
'Path Traversal',
|
|
1975
|
+
'XSS via reflected input'
|
|
1976
|
+
], 'const userId = req.query.id;\ndb.query(`SELECT * FROM users WHERE id = ${userId}`);', 'const userId = parseInt(req.query.id, 10);\nif (!userId || userId < 1) throw new Error("Invalid ID");\ndb.query("SELECT * FROM users WHERE id = ?\", [userId]);', 'Always validate and sanitize user input. Use parameterized queries for databases, whitelists for file paths, and type checking for numbers'));
|
|
1977
|
+
}
|
|
1978
|
+
// 5. Command Injection (child_process.exec) - CRITICAL
|
|
1979
|
+
if (trimmed.match(/exec\s*\(/) &&
|
|
1980
|
+
(trimmed.match(/[+\$\{`]/) ||
|
|
1981
|
+
trimmed.match(/exec\s*\(\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*[\),]/))) {
|
|
1982
|
+
vulnerabilities.push(this.createSecurityVulnerability('nodejs-command-injection', 'Node.js: Command injection via child_process.exec() with unsanitized user input', 'Use child_process.execFile() with argument array, or validate/sanitize input with strict whitelist', lineNumber, 'An attacker can execute arbitrary system commands by injecting shell metacharacters (;, |, &, $(), etc.) into user input passed to exec().', 'exec("ping " + req.body.host) where body.host = "google.com; rm -rf /" executes both commands', [
|
|
1983
|
+
'Remote Code Execution (RCE)',
|
|
1984
|
+
'Complete server compromise',
|
|
1985
|
+
'Data deletion (rm -rf)',
|
|
1986
|
+
'Backdoor installation',
|
|
1987
|
+
'Lateral movement in network'
|
|
1988
|
+
], 'exec("ping " + userInput);', 'const { execFile } = require("child_process");\nexecFile("ping", ["-c", "4", userInput], (err, stdout) => {\n // execFile doesn\'t use shell, preventing injection\n});', 'Never use exec() with user input. Use execFile() with argument array to prevent shell injection, or use strict whitelist validation'));
|
|
1989
|
+
}
|
|
1990
|
+
});
|
|
1991
|
+
// OWASP 2025 Checks (Phase 7B) - Called once with all lines for efficiency
|
|
1992
|
+
// These modules handle line-by-line iteration internally and return properly formatted vulnerabilities
|
|
1993
|
+
// OWASP A02:2025 - Security Misconfiguration (8 checks)
|
|
1994
|
+
// Debug mode, error exposure, CORS, headers, sessions, default creds, admin interfaces, HTTP methods
|
|
1995
|
+
vulnerabilities.push(...(0, security_misconfiguration_1.checkSecurityMisconfiguration)(lines));
|
|
1996
|
+
// OWASP A10:2025 - Mishandling of Exceptional Conditions (5 checks)
|
|
1997
|
+
// Empty catch, promise rejections, missing error logging, swallowed errors, missing finally
|
|
1998
|
+
vulnerabilities.push(...(0, exception_handling_1.checkExceptionHandling)(lines));
|
|
1999
|
+
// OWASP A03:2025 - Software Supply Chain Failures (5 checks)
|
|
2000
|
+
// Package integrity, dependency verification, update mechanisms, vendored deps, build chain
|
|
2001
|
+
vulnerabilities.push(...(0, enhanced_supply_chain_1.checkEnhancedSupplyChain)(lines));
|
|
2002
|
+
// OWASP A01:2025 - Broken Access Control (2 checks) - Phase 7B Day 11
|
|
2003
|
+
// Missing authentication middleware, client-side only authorization
|
|
2004
|
+
vulnerabilities.push(...(0, access_control_1.checkAccessControl)(lines));
|
|
2005
|
+
// OWASP A07:2025 - Authentication Failures (2 checks) - Phase 7B Day 11
|
|
2006
|
+
// Missing MFA/2FA, no rate limiting on login
|
|
2007
|
+
vulnerabilities.push(...(0, authentication_failures_1.checkAuthenticationFailures)(lines));
|
|
2008
|
+
// OWASP A04:2025 - Insecure Design (4 checks) - NEW Dec 30, 2025
|
|
2009
|
+
// Client-controlled pricing, quantity limits, race conditions, idempotency
|
|
2010
|
+
vulnerabilities.push(...(0, insecure_design_1.checkInsecureDesign)(lines));
|
|
2011
|
+
// OWASP A08:2025 - Software and Data Integrity Failures (6 checks) - NEW Dec 30, 2025
|
|
2012
|
+
// Dynamic script loading, SRI, code download verification, HTTPS, package integrity
|
|
2013
|
+
vulnerabilities.push(...(0, software_integrity_1.checkSoftwareIntegrity)(lines));
|
|
2014
|
+
// =============================================================================
|
|
2015
|
+
// DEDUPLICATION: Remove duplicate vulnerabilities on same line
|
|
2016
|
+
// =============================================================================
|
|
2017
|
+
// Fix for Beta Testing Issue: Command injection reported twice, Callback hell reported 7 times
|
|
2018
|
+
// MERGE with vulnerabilities from extracted modules (added before this method was called)
|
|
2019
|
+
const allVulnerabilities = [...result.security.vulnerabilities, ...vulnerabilities];
|
|
2020
|
+
result.security.vulnerabilities = this.deduplicateVulnerabilities(allVulnerabilities);
|
|
2021
|
+
}
|
|
2022
|
+
calculateMetrics(code, result) {
|
|
2023
|
+
const lines = code.split('\n');
|
|
2024
|
+
result.metrics.lines = lines.length;
|
|
2025
|
+
// Count functions
|
|
2026
|
+
const functions = (code.match(/function\s+\w+|const\s+\w+\s*=.*=>/g) || []).length;
|
|
2027
|
+
result.metrics.functions = functions;
|
|
2028
|
+
// Calculate cyclomatic complexity
|
|
2029
|
+
let complexity = 1;
|
|
2030
|
+
const safeKeywords = ['if', 'else', 'for', 'while', 'switch', 'case', 'catch'];
|
|
2031
|
+
safeKeywords.forEach(keyword => {
|
|
2032
|
+
const matches = code.match(new RegExp(`\\b${keyword}\\b`, 'g'));
|
|
2033
|
+
if (matches)
|
|
2034
|
+
complexity += matches.length;
|
|
2035
|
+
});
|
|
2036
|
+
// Logical operators (exclude TypeScript syntax)
|
|
2037
|
+
const isTypeScript = (0, typescript_syntax_1.isTypeScriptCode)(code);
|
|
2038
|
+
if (!isTypeScript) {
|
|
2039
|
+
// Only in pure JavaScript, count logical operators
|
|
2040
|
+
const andOperators = code.match(/&&/g);
|
|
2041
|
+
const orOperators = code.match(/\|\|/g);
|
|
2042
|
+
const ternaryOperators = code.match(/\?[^:]*:/g);
|
|
2043
|
+
if (andOperators)
|
|
2044
|
+
complexity += andOperators.length;
|
|
2045
|
+
if (orOperators)
|
|
2046
|
+
complexity += orOperators.length;
|
|
2047
|
+
if (ternaryOperators)
|
|
2048
|
+
complexity += ternaryOperators.length;
|
|
2049
|
+
}
|
|
2050
|
+
else {
|
|
2051
|
+
// In TypeScript, be more conservative with complexity
|
|
2052
|
+
// Only count logical operators in code context, not types
|
|
2053
|
+
const logicalAnd = code.match(/\s&&\s/g); // With spaces = probably logic
|
|
2054
|
+
const logicalOr = code.match(/\s\|\|\s/g); // With spaces = probably logic
|
|
2055
|
+
if (logicalAnd)
|
|
2056
|
+
complexity += logicalAnd.length;
|
|
2057
|
+
if (logicalOr)
|
|
2058
|
+
complexity += logicalOr.length;
|
|
2059
|
+
}
|
|
2060
|
+
result.metrics.complexity = complexity;
|
|
2061
|
+
result.metrics.maintainability = Math.max(0, 100 - complexity * 3);
|
|
2062
|
+
}
|
|
2063
|
+
/**
|
|
2064
|
+
* Deduplicate vulnerabilities on the same line
|
|
2065
|
+
*
|
|
2066
|
+
* P1-5: Generic deduplication for ALL vulnerability types (Dec 30, 2025)
|
|
2067
|
+
* Previous: Only handled command injection and callback hell
|
|
2068
|
+
* Now: Handles all duplicates (hardcoded credentials, XSS, SQL injection, etc.)
|
|
2069
|
+
*
|
|
2070
|
+
* Strategy: Use line + category as unique key, keep highest CVSS score
|
|
2071
|
+
*
|
|
2072
|
+
* @param vulnerabilities - Array of vulnerabilities to deduplicate
|
|
2073
|
+
* @returns Deduplicated array with one vulnerability per line+category
|
|
2074
|
+
*/
|
|
2075
|
+
deduplicateVulnerabilities(vulnerabilities) {
|
|
2076
|
+
// P1-5: Generic deduplication using line + category as unique key
|
|
2077
|
+
// Extract category from message (first significant keyword)
|
|
2078
|
+
const getCategory = (message) => {
|
|
2079
|
+
const lower = message.toLowerCase();
|
|
2080
|
+
// Extract main vulnerability type from message
|
|
2081
|
+
if (lower.includes('hardcoded credential'))
|
|
2082
|
+
return 'hardcoded-credential';
|
|
2083
|
+
if (lower.includes('command injection'))
|
|
2084
|
+
return 'command-injection';
|
|
2085
|
+
if (lower.includes('callback hell'))
|
|
2086
|
+
return 'callback-hell';
|
|
2087
|
+
if (lower.includes('sql injection'))
|
|
2088
|
+
return 'sql-injection';
|
|
2089
|
+
if (lower.includes('nosql injection'))
|
|
2090
|
+
return 'nosql-injection';
|
|
2091
|
+
if (lower.includes('xss') || lower.includes('cross-site scripting'))
|
|
2092
|
+
return 'xss';
|
|
2093
|
+
if (lower.includes('ssrf'))
|
|
2094
|
+
return 'ssrf';
|
|
2095
|
+
if (lower.includes('xxe'))
|
|
2096
|
+
return 'xxe';
|
|
2097
|
+
if (lower.includes('insecure random'))
|
|
2098
|
+
return 'insecure-random';
|
|
2099
|
+
if (lower.includes('weak hash'))
|
|
2100
|
+
return 'weak-hash';
|
|
2101
|
+
if (lower.includes('missing helmet'))
|
|
2102
|
+
return 'missing-helmet';
|
|
2103
|
+
if (lower.includes('missing csrf'))
|
|
2104
|
+
return 'missing-csrf';
|
|
2105
|
+
if (lower.includes('eval'))
|
|
2106
|
+
return 'eval';
|
|
2107
|
+
if (lower.includes('prototype pollution'))
|
|
2108
|
+
return 'prototype-pollution';
|
|
2109
|
+
if (lower.includes('deserialization'))
|
|
2110
|
+
return 'deserialization';
|
|
2111
|
+
if (lower.includes('path traversal'))
|
|
2112
|
+
return 'path-traversal';
|
|
2113
|
+
if (lower.includes('open redirect'))
|
|
2114
|
+
return 'open-redirect';
|
|
2115
|
+
// Fallback: use first 30 chars of message as category
|
|
2116
|
+
return message.substring(0, 30);
|
|
2117
|
+
};
|
|
2118
|
+
const uniqueMap = new Map();
|
|
2119
|
+
vulnerabilities.forEach(vuln => {
|
|
2120
|
+
const line = vuln.line || 0;
|
|
2121
|
+
const category = getCategory(vuln.message || '');
|
|
2122
|
+
const key = `${line}:${category}`;
|
|
2123
|
+
const existing = uniqueMap.get(key);
|
|
2124
|
+
if (!existing) {
|
|
2125
|
+
// First occurrence of this line + category
|
|
2126
|
+
uniqueMap.set(key, vuln);
|
|
2127
|
+
}
|
|
2128
|
+
else {
|
|
2129
|
+
// Duplicate detected - keep higher CVSS score
|
|
2130
|
+
const existingScore = existing.cvssScore || 0;
|
|
2131
|
+
const currentScore = vuln.cvssScore || 0;
|
|
2132
|
+
if (currentScore > existingScore) {
|
|
2133
|
+
uniqueMap.set(key, vuln);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
});
|
|
2137
|
+
return Array.from(uniqueMap.values());
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
exports.JavaScriptAnalyzer = JavaScriptAnalyzer;
|
|
2141
|
+
//# sourceMappingURL=javascript-analyzer.js.map
|