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,1660 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypeScriptAnalyzer = void 0;
|
|
4
|
+
const references_1 = require("../standards/references");
|
|
5
|
+
const code_cleaner_1 = require("../utils/code-cleaner");
|
|
6
|
+
// Modular TypeScript Security Checks (2025-12-01)
|
|
7
|
+
const injection_attacks_1 = require("./typescript/security-checks/injection-attacks");
|
|
8
|
+
const credentials_crypto_1 = require("./typescript/security-checks/credentials-crypto");
|
|
9
|
+
const code_injection_1 = require("./typescript/security-checks/code-injection");
|
|
10
|
+
const code_quality_1 = require("./typescript/security-checks/code-quality");
|
|
11
|
+
const type_security_1 = require("./typescript/security-checks/type-security");
|
|
12
|
+
const security_misconfiguration_1 = require("./typescript/security-checks/security-misconfiguration");
|
|
13
|
+
const exception_handling_1 = require("./typescript/security-checks/exception-handling");
|
|
14
|
+
const enhanced_supply_chain_1 = require("./typescript/security-checks/enhanced-supply-chain");
|
|
15
|
+
const access_control_1 = require("./typescript/security-checks/access-control");
|
|
16
|
+
const information_disclosure_1 = require("./typescript/security-checks/information-disclosure");
|
|
17
|
+
const authentication_1 = require("./typescript/security-checks/authentication");
|
|
18
|
+
const logging_failures_1 = require("./typescript/security-checks/logging-failures");
|
|
19
|
+
const secrets_analyzer_1 = require("./secrets/secrets-analyzer");
|
|
20
|
+
const ai_generated_code_1 = require("./typescript/security-checks/ai-generated-code");
|
|
21
|
+
// TypeScript Compiler API Integration (2025-12-02)
|
|
22
|
+
const type_checker_1 = require("./typescript/type-checker");
|
|
23
|
+
class TypeScriptAnalyzer {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.language = 'typescript';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Remove string literal content from a line of code
|
|
29
|
+
* This prevents false positives when analyzing string content as code
|
|
30
|
+
* Example: 'MyP@ssw0rd!' → ' ' (preserves quotes, replaces content with spaces)
|
|
31
|
+
*
|
|
32
|
+
* FIX (2025-11-18): Added to fix false positive detection of ! in string literals
|
|
33
|
+
* Bug: "const PASSWORD = 'MyP@ssw0rd!';" was detected as non-null assertion
|
|
34
|
+
*/
|
|
35
|
+
removeStringLiterals(line) {
|
|
36
|
+
let result = '';
|
|
37
|
+
let inString = false;
|
|
38
|
+
let stringChar = '';
|
|
39
|
+
let escaped = false;
|
|
40
|
+
for (let i = 0; i < line.length; i++) {
|
|
41
|
+
const char = line[i];
|
|
42
|
+
// Handle escape sequences
|
|
43
|
+
if (escaped) {
|
|
44
|
+
result += inString ? ' ' : char;
|
|
45
|
+
escaped = false;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (char === '\\') {
|
|
49
|
+
escaped = true;
|
|
50
|
+
result += inString ? ' ' : char;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Handle string delimiters
|
|
54
|
+
if (char === '"' || char === "'" || char === '`') {
|
|
55
|
+
if (!inString) {
|
|
56
|
+
inString = true;
|
|
57
|
+
stringChar = char;
|
|
58
|
+
result += char; // Keep the quote
|
|
59
|
+
}
|
|
60
|
+
else if (char === stringChar) {
|
|
61
|
+
inString = false;
|
|
62
|
+
stringChar = '';
|
|
63
|
+
result += char; // Keep the quote
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
result += ' '; // Replace with space
|
|
67
|
+
}
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// Inside string - replace content with spaces
|
|
71
|
+
if (inString) {
|
|
72
|
+
result += ' ';
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
result += char;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
async analyze(input) {
|
|
81
|
+
const result = {
|
|
82
|
+
syntax: { valid: true, errors: [], lineErrors: [] },
|
|
83
|
+
quality: { score: 100, issues: [] },
|
|
84
|
+
performance: { score: 100, suggestions: [] },
|
|
85
|
+
security: { vulnerabilities: [] },
|
|
86
|
+
metrics: { complexity: 1, maintainability: 100, lines: 0, functions: 0 }
|
|
87
|
+
};
|
|
88
|
+
try {
|
|
89
|
+
// CRITICAL (Dec 30, 2025): ALL analysis methods run independently
|
|
90
|
+
// Security checks MUST run even if type errors exist
|
|
91
|
+
// User testing revealed: "Both security AND type errors should be detected simultaneously"
|
|
92
|
+
// Fix: Ensure no early returns skip security analysis
|
|
93
|
+
this.analyzeSyntax(input.code, result);
|
|
94
|
+
this.analyzeQuality(input.code, result);
|
|
95
|
+
this.analyzePerformance(input.code, result);
|
|
96
|
+
// ALWAYS run security analysis, regardless of syntax/type errors
|
|
97
|
+
this.analyzeSecurity(input.code, result);
|
|
98
|
+
this.calculateMetrics(input.code, result);
|
|
99
|
+
// AI-Generated Code Detection (Phase 1.5, Week 5-7)
|
|
100
|
+
const lines = input.code.split('\n');
|
|
101
|
+
result.security.vulnerabilities.push(...(0, ai_generated_code_1.checkAIGeneratedCode)(lines, input.filename));
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
105
|
+
result.syntax.valid = false;
|
|
106
|
+
result.syntax.errors.push(`TypeScript analysis error: ${errorMessage}`);
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
async validateSyntax(code) {
|
|
111
|
+
// Basic TypeScript syntax checks
|
|
112
|
+
const lines = code.split('\n');
|
|
113
|
+
for (let i = 0; i < lines.length; i++) {
|
|
114
|
+
const line = lines[i].trim();
|
|
115
|
+
if (!line || line.startsWith('//'))
|
|
116
|
+
continue;
|
|
117
|
+
// Check basic type annotations
|
|
118
|
+
if (line.match(/:\s*[A-Z]\w*\s*=/) && !line.includes('interface') && !line.includes('type')) {
|
|
119
|
+
return true; // Valid TypeScript type annotation
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
getLanguageInfo() {
|
|
125
|
+
return {
|
|
126
|
+
name: 'TypeScript',
|
|
127
|
+
extensions: ['.ts', '.tsx', '.d.ts'],
|
|
128
|
+
description: 'Typed superset of JavaScript for scalable applications'
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
analyzeSyntax(code, result) {
|
|
132
|
+
const errors = [];
|
|
133
|
+
const lineErrors = [];
|
|
134
|
+
// TypeScript-specific advanced detection (16 methods)
|
|
135
|
+
// PHASE 6 WEEK 1 DAY 1: Added duplicate identifier detection
|
|
136
|
+
this.detectTypeErrors(code, lineErrors);
|
|
137
|
+
this.detectTypeAssertions(code, lineErrors);
|
|
138
|
+
this.detectGenericIssues(code, lineErrors);
|
|
139
|
+
this.detectUnionIntersectionIssues(code, lineErrors);
|
|
140
|
+
this.detectClassInterfaceIssues(code, lineErrors);
|
|
141
|
+
this.detectAsyncPromiseIssues(code, lineErrors);
|
|
142
|
+
this.detectModuleIssues(code, lineErrors);
|
|
143
|
+
this.detectDecoratorIssues(code, lineErrors);
|
|
144
|
+
this.detectMappedConditionalTypes(code, lineErrors);
|
|
145
|
+
this.detectStrictModeViolations(code, lineErrors);
|
|
146
|
+
this.detectAnyPropagation(code, lineErrors);
|
|
147
|
+
this.detectPerformanceIssues(code, lineErrors);
|
|
148
|
+
this.detectNamingConventions(code, lineErrors);
|
|
149
|
+
this.detectConfigurationIssues(code, lineErrors);
|
|
150
|
+
this.detectReactTypeScriptIssues(code, lineErrors);
|
|
151
|
+
this.detectDuplicateIdentifiers(code, lineErrors); // PHASE 6: NEW
|
|
152
|
+
// Balance checks
|
|
153
|
+
this.checkBracketBalance(code, errors, lineErrors);
|
|
154
|
+
result.syntax.errors = errors;
|
|
155
|
+
result.syntax.lineErrors = lineErrors;
|
|
156
|
+
result.syntax.valid = errors.length === 0 && lineErrors.filter(e => e.severity === 'error').length === 0;
|
|
157
|
+
if (!result.syntax.valid || lineErrors.length > 0) {
|
|
158
|
+
result.quality.score -= lineErrors.length * 15;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Detect type errors - Type assignability, null/undefined, property access
|
|
163
|
+
*/
|
|
164
|
+
detectTypeErrors(code, lineErrors) {
|
|
165
|
+
const lines = code.split('\n');
|
|
166
|
+
lines.forEach((line, index) => {
|
|
167
|
+
const lineNumber = index + 1;
|
|
168
|
+
if (line.trim().startsWith('//'))
|
|
169
|
+
return;
|
|
170
|
+
// Detect unsafe null/undefined access
|
|
171
|
+
if (line.match(/\.\w+/) && !line.includes('?.') && !line.includes('if') && !line.includes('&&')) {
|
|
172
|
+
const prevLines = lines.slice(Math.max(0, index - 3), index);
|
|
173
|
+
const hasNullCheck = prevLines.some(l => l.includes('!= null') || l.includes('!== undefined'));
|
|
174
|
+
if (!hasNullCheck && line.includes('get') && !line.includes('Optional')) {
|
|
175
|
+
lineErrors.push({
|
|
176
|
+
line: lineNumber,
|
|
177
|
+
error: 'Potentially unsafe access: property may be null/undefined',
|
|
178
|
+
suggestion: 'Use optional chaining (?.) or check for null: if (obj != null) { ... }',
|
|
179
|
+
severity: 'warning',
|
|
180
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
181
|
+
securityRelevant: false
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Detect missing type annotations on function parameters
|
|
186
|
+
if (line.match(/function\s+\w+\s*\([^)]*\w+[^:)]*\)/) || line.match(/\(\s*\w+\s*\)\s*=>/)) {
|
|
187
|
+
const hasTypeAnnotation = line.match(/:\s*\w+/);
|
|
188
|
+
if (!hasTypeAnnotation && !line.includes('any')) {
|
|
189
|
+
lineErrors.push({
|
|
190
|
+
line: lineNumber,
|
|
191
|
+
error: 'Function parameter missing type annotation',
|
|
192
|
+
suggestion: 'Add type to parameter: (param: string) => ...',
|
|
193
|
+
severity: 'info',
|
|
194
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
195
|
+
securityRelevant: false
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// FIX (2025-11-18): Detect assignment in condition (= instead of == or ===)
|
|
200
|
+
// Pattern: if (variable = value) or while (variable = value)
|
|
201
|
+
// This is a critical bug where = (assignment) is used instead of === (comparison)
|
|
202
|
+
const assignmentInCondition = line.match(/\b(if|while|for)\s*\([^)]*[^=!<>]=[^=][^)]*\)/);
|
|
203
|
+
if (assignmentInCondition) {
|
|
204
|
+
// Verify it's not part of an arrow function or initialization (for loops)
|
|
205
|
+
const isForLoopInit = assignmentInCondition[1] === 'for' && (line.includes('let ') || line.includes('var ') || line.includes('const '));
|
|
206
|
+
const isArrowFunction = line.includes('=>');
|
|
207
|
+
if (!isForLoopInit && !isArrowFunction) {
|
|
208
|
+
lineErrors.push({
|
|
209
|
+
line: lineNumber,
|
|
210
|
+
error: 'Assignment in condition - did you mean === or == ?',
|
|
211
|
+
suggestion: 'Use === for comparison. If assignment is intentional, wrap in parentheses: if ((x = getValue()))',
|
|
212
|
+
severity: 'error',
|
|
213
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
214
|
+
securityRelevant: false
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Detect type compatibility issues with common patterns
|
|
219
|
+
if (line.includes('=') && line.includes(':') && !line.includes('//')) {
|
|
220
|
+
// Check for common type mismatches
|
|
221
|
+
if (line.match(/:\s*string.*=\s*\d+/) || line.match(/:\s*number.*=\s*["']/)) {
|
|
222
|
+
lineErrors.push({
|
|
223
|
+
line: lineNumber,
|
|
224
|
+
error: 'Type incompatibility: value does not match type annotation',
|
|
225
|
+
suggestion: 'Ensure the value matches the declared type',
|
|
226
|
+
severity: 'error',
|
|
227
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
228
|
+
securityRelevant: false
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// FIX (2025-11-18): Detect type guard bypass pattern
|
|
233
|
+
// Pattern: function that returns boolean but should use type predicate
|
|
234
|
+
// Example: function isString(value: unknown): boolean { return typeof value === 'string'; }
|
|
235
|
+
// Should be: function isString(value: unknown): value is string { ... }
|
|
236
|
+
const typeGuardPattern = line.match(/function\s+(\w+)\s*\([^)]*(\w+):\s*(unknown|any)[^)]*\):\s*boolean/);
|
|
237
|
+
if (typeGuardPattern) {
|
|
238
|
+
const functionName = typeGuardPattern[1];
|
|
239
|
+
const paramName = typeGuardPattern[2];
|
|
240
|
+
// Check next 5 lines for typeof checks (function body)
|
|
241
|
+
const nextLines = lines.slice(index, Math.min(index + 6, lines.length));
|
|
242
|
+
const hasTypeOfCheck = nextLines.some(l => l.includes('typeof') && (l.includes("=== 'string'") ||
|
|
243
|
+
l.includes("=== 'number'") ||
|
|
244
|
+
l.includes("=== 'boolean'") ||
|
|
245
|
+
l.includes("=== 'object'") ||
|
|
246
|
+
l.includes('instanceof')));
|
|
247
|
+
if (hasTypeOfCheck) {
|
|
248
|
+
// Determine the type being checked
|
|
249
|
+
let detectedType = 'Type';
|
|
250
|
+
if (nextLines.some(l => l.includes("=== 'string'")))
|
|
251
|
+
detectedType = 'string';
|
|
252
|
+
else if (nextLines.some(l => l.includes("=== 'number'")))
|
|
253
|
+
detectedType = 'number';
|
|
254
|
+
else if (nextLines.some(l => l.includes("=== 'boolean'")))
|
|
255
|
+
detectedType = 'boolean';
|
|
256
|
+
else if (nextLines.some(l => l.includes("=== 'object'")))
|
|
257
|
+
detectedType = 'object';
|
|
258
|
+
lineErrors.push({
|
|
259
|
+
line: lineNumber,
|
|
260
|
+
error: `Type guard function should use type predicate instead of boolean`,
|
|
261
|
+
suggestion: `Change return type to: ${paramName} is ${detectedType}`,
|
|
262
|
+
severity: 'warning',
|
|
263
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
264
|
+
securityRelevant: false
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// AI Hallucination: Check for .push() on strings (Python/list confusion)
|
|
269
|
+
// Strings in TS/JS are immutable, use += or array methods
|
|
270
|
+
// CRITICAL FIX: Remove comments before checking to prevent false positives
|
|
271
|
+
// Example: str += value; // ERROR: Don't use .push() on strings
|
|
272
|
+
// ^^^^^ This would trigger false positive!
|
|
273
|
+
const lineWithoutComments = code_cleaner_1.CodeCleaner.removeLineComments(line, 'javascript'); // TypeScript uses JS comment syntax
|
|
274
|
+
// Check current line for string indicators
|
|
275
|
+
const hasStringIndicator = lineWithoutComments.includes("''") || lineWithoutComments.includes('""') || lineWithoutComments.includes('``') ||
|
|
276
|
+
lineWithoutComments.match(/=\s*['"`]/) || lineWithoutComments.match(/:\s*string/);
|
|
277
|
+
// Check previous lines for string type annotations (multi-line function params)
|
|
278
|
+
let prevLineHasStringType = false;
|
|
279
|
+
if (index > 0) {
|
|
280
|
+
const prevLine = lines[index - 1];
|
|
281
|
+
prevLineHasStringType = prevLine.match(/:\s*string/) !== null;
|
|
282
|
+
}
|
|
283
|
+
if (lineWithoutComments.match(/(['"`].*['"`]|String\(|\.toString\(\)|\.toLowerCase\(\)|\.toUpperCase\(\)).*\.push\(/) ||
|
|
284
|
+
(lineWithoutComments.match(/\w+\.push\(/) && (hasStringIndicator || prevLineHasStringType))) {
|
|
285
|
+
lineErrors.push({
|
|
286
|
+
line: lineNumber,
|
|
287
|
+
error: 'AI Hallucination: .push() does not exist on TypeScript strings',
|
|
288
|
+
suggestion: 'Strings are immutable in TS. Use: str += value, str = str + value, or convert to array: str.split("").push()',
|
|
289
|
+
severity: 'error',
|
|
290
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
291
|
+
securityRelevant: false
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
// Assignment in condition (common error: = instead of ===)
|
|
295
|
+
// Pattern: if (x = y) instead of if (x === y)
|
|
296
|
+
if (line.match(/if\s*\([^)]*[^=!<>]=\s*[^=]/) && !line.includes('==')) {
|
|
297
|
+
lineErrors.push({
|
|
298
|
+
line: lineNumber,
|
|
299
|
+
error: 'Assignment in condition detected - likely mistake',
|
|
300
|
+
suggestion: 'Use === or == for comparison, not = for assignment: if (x === y)',
|
|
301
|
+
severity: 'error',
|
|
302
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
303
|
+
securityRelevant: false
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Detect type assertions - Unsafe 'as', non-null assertions '!', type predicates
|
|
310
|
+
*/
|
|
311
|
+
detectTypeAssertions(code, lineErrors) {
|
|
312
|
+
const lines = code.split('\n');
|
|
313
|
+
lines.forEach((line, index) => {
|
|
314
|
+
const lineNumber = index + 1;
|
|
315
|
+
if (line.trim().startsWith('//'))
|
|
316
|
+
return;
|
|
317
|
+
// Detect 'as any' or 'as unknown' - unsafe type assertions
|
|
318
|
+
if (line.match(/as\s+any\b/) || line.match(/as\s+unknown\b/)) {
|
|
319
|
+
lineErrors.push({
|
|
320
|
+
line: lineNumber,
|
|
321
|
+
error: 'Unsafe type assertion: use of "as any" or "as unknown"',
|
|
322
|
+
suggestion: 'Avoid "as any". Use specific types or type guards for type safety',
|
|
323
|
+
severity: 'error', // Changed from 'warning' to 'error' for Monaco highlighting
|
|
324
|
+
references: references_1.typescriptStandards['type-assertions'] || [],
|
|
325
|
+
securityRelevant: true
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
// CRITICAL FIX: Detect type assertions on 'unknown' parameter without validation
|
|
329
|
+
// Pattern: function foo(data: unknown) { return data as SomeType; }
|
|
330
|
+
// This is risky because it bypasses runtime validation
|
|
331
|
+
if (line.match(/\bdata\s+as\s+[A-Z]\w+/) || line.match(/\bvalue\s+as\s+[A-Z]\w+/)) {
|
|
332
|
+
// Look backward to see if the parameter is typed as unknown
|
|
333
|
+
const lookBackLines = lines.slice(Math.max(0, index - 5), index + 1);
|
|
334
|
+
const hasUnknownParam = lookBackLines.some(l => l.includes('data: unknown') || l.includes('value: unknown'));
|
|
335
|
+
if (hasUnknownParam) {
|
|
336
|
+
lineErrors.push({
|
|
337
|
+
line: lineNumber,
|
|
338
|
+
error: 'Type assertion on unknown without runtime validation',
|
|
339
|
+
suggestion: 'Add runtime validation before type assertion: if (typeof data === "object" && data !== null && "requiredField" in data) { ... }',
|
|
340
|
+
severity: 'warning',
|
|
341
|
+
references: references_1.typescriptStandards['type-assertions'] || [],
|
|
342
|
+
securityRelevant: true
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// Detect non-null assertion operator (!)
|
|
347
|
+
// FIX (2025-11-18): Remove string content first to avoid false positives
|
|
348
|
+
// Previous bug: 'MyP@ssw0rd!' was detected as non-null assertion
|
|
349
|
+
// CRITICAL FIX: Added pattern for array access with non-null assertion: array[0]!
|
|
350
|
+
const lineWithoutStrings = this.removeStringLiterals(line);
|
|
351
|
+
if (lineWithoutStrings.match(/\w+!\s*\./) || lineWithoutStrings.match(/\w+!\s*\[/) || lineWithoutStrings.match(/\w+!\s*;/) || lineWithoutStrings.match(/\]\s*!/)) {
|
|
352
|
+
lineErrors.push({
|
|
353
|
+
line: lineNumber,
|
|
354
|
+
error: 'Non-null assertion (!) detected - bypasses null checks',
|
|
355
|
+
suggestion: 'Use optional chaining (?.) or check explicitly: if (value != null) or check array length',
|
|
356
|
+
severity: 'error', // Changed from 'warning' to 'error' for Monaco highlighting
|
|
357
|
+
references: references_1.typescriptStandards['type-assertions'] || [],
|
|
358
|
+
securityRelevant: true
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
// Detect empty type assertion (casting empty object to interface/type)
|
|
362
|
+
// Pattern: {} as User, {} as Config, etc.
|
|
363
|
+
if (line.match(/\{\s*\}\s+as\s+[A-Z]\w+/)) {
|
|
364
|
+
lineErrors.push({
|
|
365
|
+
line: lineNumber,
|
|
366
|
+
error: 'Empty type assertion - creating invalid object',
|
|
367
|
+
suggestion: 'Initialize with actual values or use a factory function',
|
|
368
|
+
severity: 'error',
|
|
369
|
+
references: references_1.typescriptStandards['type-assertions'] || [],
|
|
370
|
+
securityRelevant: false
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
// Detect double type assertions (as X as Y)
|
|
374
|
+
if (line.match(/as\s+\w+\s+as\s+\w+/)) {
|
|
375
|
+
lineErrors.push({
|
|
376
|
+
line: lineNumber,
|
|
377
|
+
error: 'Double type assertion detected - dangerous practice',
|
|
378
|
+
suggestion: 'Avoid double assertions. Redesign code for correct types',
|
|
379
|
+
severity: 'error',
|
|
380
|
+
references: references_1.typescriptStandards['type-assertions'] || [],
|
|
381
|
+
securityRelevant: true
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
// FIX #1 (Dec 16, 2025): Detect general type assertions and classify by security relevance
|
|
385
|
+
// Pattern: something as Type (but not 'as any', 'as unknown', double assertions handled above)
|
|
386
|
+
const typeAssertionPattern = /\s+as\s+([A-Za-z]\w*)/;
|
|
387
|
+
const assertionMatch = line.match(typeAssertionPattern);
|
|
388
|
+
if (assertionMatch &&
|
|
389
|
+
!line.includes('as any') &&
|
|
390
|
+
!line.includes('as unknown') &&
|
|
391
|
+
!line.match(/as\s+\w+\s+as\s+\w+/)) { // Not double assertion
|
|
392
|
+
// Check if this is a security-relevant type assertion
|
|
393
|
+
const isSecurityRelevant = this.isSecurityRelevantAssertion(line);
|
|
394
|
+
if (isSecurityRelevant) {
|
|
395
|
+
// Security-relevant assertions: Medium severity
|
|
396
|
+
lineErrors.push({
|
|
397
|
+
line: lineNumber,
|
|
398
|
+
error: 'Unsafe type assertion with potential security implications',
|
|
399
|
+
suggestion: 'Add runtime validation before type assertion, especially for authentication or authorization contexts',
|
|
400
|
+
severity: 'warning', // Medium severity for security issues
|
|
401
|
+
references: references_1.typescriptStandards['type-assertions'] || [],
|
|
402
|
+
securityRelevant: true
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
// Non-security type assertions (environment variables, config): INFO severity
|
|
407
|
+
lineErrors.push({
|
|
408
|
+
line: lineNumber,
|
|
409
|
+
error: 'Type assertion without runtime validation (code quality)',
|
|
410
|
+
suggestion: 'Consider adding runtime type check or use a type-safe alternative. For environment variables, connection failure will occur if undefined.',
|
|
411
|
+
severity: 'info', // INFO severity for code quality
|
|
412
|
+
references: references_1.typescriptStandards['type-assertions'] || [],
|
|
413
|
+
securityRelevant: false
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Determine if a type assertion is security-relevant
|
|
421
|
+
* Security-relevant: authentication, authorization, input validation, cryptography
|
|
422
|
+
* Non-security: environment variables, config values, type conversions
|
|
423
|
+
*/
|
|
424
|
+
isSecurityRelevantAssertion(line) {
|
|
425
|
+
const lowerLine = line.toLowerCase();
|
|
426
|
+
// Security-relevant keywords
|
|
427
|
+
const securityKeywords = [
|
|
428
|
+
'auth', 'token', 'password', 'secret', 'key', 'credential',
|
|
429
|
+
'session', 'user', 'role', 'permission', 'access',
|
|
430
|
+
'validate', 'sanitize', 'escape'
|
|
431
|
+
];
|
|
432
|
+
// Non-security keywords (environment/config)
|
|
433
|
+
const configKeywords = [
|
|
434
|
+
'process.env', 'config.', 'settings.', 'options.',
|
|
435
|
+
'port', 'host', 'url', 'path'
|
|
436
|
+
];
|
|
437
|
+
// Check if line contains config keywords (non-security)
|
|
438
|
+
const hasConfigKeyword = configKeywords.some(keyword => lowerLine.includes(keyword.toLowerCase()));
|
|
439
|
+
if (hasConfigKeyword) {
|
|
440
|
+
return false; // Not security-relevant
|
|
441
|
+
}
|
|
442
|
+
// Check if line contains security keywords
|
|
443
|
+
const hasSecurityKeyword = securityKeywords.some(keyword => lowerLine.includes(keyword));
|
|
444
|
+
if (hasSecurityKeyword) {
|
|
445
|
+
return true; // Security-relevant
|
|
446
|
+
}
|
|
447
|
+
// Default: treat as non-security (INFO) to avoid false positives
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Detect generic issues - Missing type arguments, constraint violations
|
|
452
|
+
*/
|
|
453
|
+
detectGenericIssues(code, lineErrors) {
|
|
454
|
+
const lines = code.split('\n');
|
|
455
|
+
lines.forEach((line, index) => {
|
|
456
|
+
const lineNumber = index + 1;
|
|
457
|
+
if (line.trim().startsWith('//'))
|
|
458
|
+
return;
|
|
459
|
+
// Detect Array without type argument
|
|
460
|
+
if (line.match(/:\s*Array\s*[^<]/) && !line.includes('//')) {
|
|
461
|
+
lineErrors.push({
|
|
462
|
+
line: lineNumber,
|
|
463
|
+
error: 'Array without generic type argument',
|
|
464
|
+
suggestion: 'Specify the type: Array<string> or use string[]',
|
|
465
|
+
severity: 'warning',
|
|
466
|
+
references: references_1.typescriptStandards['generics'] || [],
|
|
467
|
+
securityRelevant: false
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
// Detect Promise without type argument
|
|
471
|
+
if (line.match(/:\s*Promise\s*[^<]/) || line.match(/new\s+Promise\s*\(/)) {
|
|
472
|
+
if (!line.includes('<')) {
|
|
473
|
+
lineErrors.push({
|
|
474
|
+
line: lineNumber,
|
|
475
|
+
error: 'Promise without generic type specified',
|
|
476
|
+
suggestion: 'Specify the return type: Promise<string>',
|
|
477
|
+
severity: 'warning',
|
|
478
|
+
references: references_1.typescriptStandards['generics'] || [],
|
|
479
|
+
securityRelevant: false
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// Detect generic constraint violations
|
|
484
|
+
if (line.match(/<T\s+extends\s+\w+>/) && line.includes('=')) {
|
|
485
|
+
const constraintMatch = line.match(/<T\s+extends\s+(\w+)>/);
|
|
486
|
+
if (constraintMatch && line.includes('as')) {
|
|
487
|
+
lineErrors.push({
|
|
488
|
+
line: lineNumber,
|
|
489
|
+
error: 'Possible generic constraint violation',
|
|
490
|
+
suggestion: 'Ensure the type satisfies the specified constraint',
|
|
491
|
+
severity: 'info',
|
|
492
|
+
references: references_1.typescriptStandards['generics'] || [],
|
|
493
|
+
securityRelevant: false
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Detect union/intersection issues - Discriminated unions, type narrowing failures
|
|
501
|
+
*/
|
|
502
|
+
detectUnionIntersectionIssues(code, lineErrors) {
|
|
503
|
+
const lines = code.split('\n');
|
|
504
|
+
lines.forEach((line, index) => {
|
|
505
|
+
const lineNumber = index + 1;
|
|
506
|
+
if (line.trim().startsWith('//'))
|
|
507
|
+
return;
|
|
508
|
+
// Detect union type without discriminant property
|
|
509
|
+
if (line.match(/type\s+\w+\s*=\s*\{[^}]*\}\s*\|\s*\{[^}]*\}/)) {
|
|
510
|
+
if (!line.includes('kind:') && !line.includes('type:') && !line.includes('tag:')) {
|
|
511
|
+
lineErrors.push({
|
|
512
|
+
line: lineNumber,
|
|
513
|
+
error: 'Union type without discriminant property',
|
|
514
|
+
suggestion: 'Add discriminant property: { kind: "A", ... } | { kind: "B", ... }',
|
|
515
|
+
severity: 'info',
|
|
516
|
+
references: references_1.typescriptStandards['unions-intersections'] || [],
|
|
517
|
+
securityRelevant: false
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// Detect accessing union type property without narrowing
|
|
522
|
+
// FIXED: Only match union type pipe |, not OR operator ||
|
|
523
|
+
// FIXED: Recognize optional chaining ?. as a form of null narrowing
|
|
524
|
+
const hasUnionTypePipe = line.match(/[^|]\|[^|]/) !== null; // Single | not ||
|
|
525
|
+
const hasOptionalChaining = line.includes('?.'); // ?. is null-safe access
|
|
526
|
+
if (line.match(/\.\w+/) && hasUnionTypePipe && !hasOptionalChaining) {
|
|
527
|
+
const prevLines = lines.slice(Math.max(0, index - 3), index);
|
|
528
|
+
const hasNarrowing = prevLines.some(l => l.includes('typeof') || l.includes('instanceof') || l.includes('in '));
|
|
529
|
+
if (!hasNarrowing) {
|
|
530
|
+
lineErrors.push({
|
|
531
|
+
line: lineNumber,
|
|
532
|
+
error: 'Union type property access without narrowing',
|
|
533
|
+
suggestion: 'Use type guard: if (typeof x === "string") or if ("prop" in obj)',
|
|
534
|
+
severity: 'warning',
|
|
535
|
+
references: references_1.typescriptStandards['unions-intersections'] || [],
|
|
536
|
+
securityRelevant: false
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// Detect complex intersection types
|
|
541
|
+
if ((line.match(/&/g) || []).length > 3) {
|
|
542
|
+
lineErrors.push({
|
|
543
|
+
line: lineNumber,
|
|
544
|
+
error: 'Intersection type too complex',
|
|
545
|
+
suggestion: 'Consider using interface or type alias to simplify',
|
|
546
|
+
severity: 'info',
|
|
547
|
+
references: references_1.typescriptStandards['unions-intersections'] || [],
|
|
548
|
+
securityRelevant: false
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Detect class/interface issues - Implementation errors, abstract violations
|
|
555
|
+
*/
|
|
556
|
+
detectClassInterfaceIssues(code, lineErrors) {
|
|
557
|
+
const lines = code.split('\n');
|
|
558
|
+
lines.forEach((line, index) => {
|
|
559
|
+
const lineNumber = index + 1;
|
|
560
|
+
if (line.trim().startsWith('//'))
|
|
561
|
+
return;
|
|
562
|
+
// Detect class implementing interface without all methods
|
|
563
|
+
if (line.match(/class\s+\w+\s+implements\s+\w+/)) {
|
|
564
|
+
lineErrors.push({
|
|
565
|
+
line: lineNumber,
|
|
566
|
+
error: 'Verify that all interface methods are implemented',
|
|
567
|
+
suggestion: 'Implement all required interface methods',
|
|
568
|
+
severity: 'info',
|
|
569
|
+
references: references_1.typescriptStandards['classes-interfaces'] || [],
|
|
570
|
+
securityRelevant: false
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
// Detect abstract method without abstract keyword on class
|
|
574
|
+
if (line.match(/abstract\s+\w+\s*\(/) && !lines.slice(Math.max(0, index - 5), index).some(l => l.includes('abstract class'))) {
|
|
575
|
+
lineErrors.push({
|
|
576
|
+
line: lineNumber,
|
|
577
|
+
error: 'Abstract method in non-abstract class',
|
|
578
|
+
suggestion: 'Declare the class as abstract or implement the method',
|
|
579
|
+
severity: 'error',
|
|
580
|
+
references: references_1.typescriptStandards['classes-interfaces'] || [],
|
|
581
|
+
securityRelevant: false
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
// Detect missing access modifiers
|
|
585
|
+
if (line.match(/^\s*\w+\s*\(/) && !line.includes('function') && !line.includes('//')) {
|
|
586
|
+
const prevLine = lines[index - 1]?.trim();
|
|
587
|
+
if (prevLine && prevLine.includes('class')) {
|
|
588
|
+
if (!line.includes('public') && !line.includes('private') && !line.includes('protected')) {
|
|
589
|
+
lineErrors.push({
|
|
590
|
+
line: lineNumber,
|
|
591
|
+
error: 'Class method without explicit access modifier',
|
|
592
|
+
suggestion: 'Add public, private, or protected for clarity',
|
|
593
|
+
severity: 'info',
|
|
594
|
+
references: references_1.typescriptStandards['classes-interfaces'] || [],
|
|
595
|
+
securityRelevant: false
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
// Detect interface with 'I' prefix (anti-pattern)
|
|
601
|
+
if (line.match(/interface\s+I[A-Z]\w+/)) {
|
|
602
|
+
lineErrors.push({
|
|
603
|
+
line: lineNumber,
|
|
604
|
+
error: 'Interface with "I" prefix - anti-pattern in TypeScript',
|
|
605
|
+
suggestion: 'Remove the "I" prefix: interface User instead of IUser',
|
|
606
|
+
severity: 'info',
|
|
607
|
+
references: references_1.typescriptStandards['classes-interfaces'] || [],
|
|
608
|
+
securityRelevant: false
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Detect async/Promise issues - Untyped Promises, async without await
|
|
615
|
+
*/
|
|
616
|
+
detectAsyncPromiseIssues(code, lineErrors) {
|
|
617
|
+
const lines = code.split('\n');
|
|
618
|
+
lines.forEach((line, index) => {
|
|
619
|
+
const lineNumber = index + 1;
|
|
620
|
+
if (line.trim().startsWith('//'))
|
|
621
|
+
return;
|
|
622
|
+
// Detect async function without await
|
|
623
|
+
if (line.includes('async') && line.includes('function')) {
|
|
624
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 20, lines.length));
|
|
625
|
+
const hasAwait = nextLines.some(l => l.includes('await'));
|
|
626
|
+
if (!hasAwait) {
|
|
627
|
+
lineErrors.push({
|
|
628
|
+
line: lineNumber,
|
|
629
|
+
error: 'Async function without await usage',
|
|
630
|
+
suggestion: 'Remove async or use await for asynchronous operations',
|
|
631
|
+
severity: 'warning',
|
|
632
|
+
references: references_1.typescriptStandards['async-promises'] || [],
|
|
633
|
+
securityRelevant: false
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
// Detect Promise without error handling
|
|
638
|
+
if (line.includes('.then(') && !line.includes('.catch(')) {
|
|
639
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 5, lines.length));
|
|
640
|
+
const hasCatch = nextLines.some(l => l.includes('.catch('));
|
|
641
|
+
if (!hasCatch) {
|
|
642
|
+
lineErrors.push({
|
|
643
|
+
line: lineNumber,
|
|
644
|
+
error: 'Promise without error handling (.catch)',
|
|
645
|
+
suggestion: 'Add .catch() or use try/catch with async/await',
|
|
646
|
+
severity: 'warning',
|
|
647
|
+
references: references_1.typescriptStandards['async-promises'] || [],
|
|
648
|
+
securityRelevant: false
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
// Detect floating Promise (not awaited or returned)
|
|
653
|
+
// FIXED: Recognize .then()/.catch() chaining as valid promise handling
|
|
654
|
+
if (line.match(/^\s*\w+\(/) && !line.includes('await') && !line.includes('return')) {
|
|
655
|
+
if (line.includes('fetch') || line.includes('Promise')) {
|
|
656
|
+
// Check if the next lines have .then() or .catch() (promise chaining)
|
|
657
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 4, lines.length));
|
|
658
|
+
const hasPromiseChaining = line.includes('.then') ||
|
|
659
|
+
nextLines.some(l => l.trim().startsWith('.then') || l.trim().startsWith('.catch'));
|
|
660
|
+
if (!hasPromiseChaining) {
|
|
661
|
+
lineErrors.push({
|
|
662
|
+
line: lineNumber,
|
|
663
|
+
error: 'Promise not awaited - floating Promise',
|
|
664
|
+
suggestion: 'Use await or return to ensure Promise is handled',
|
|
665
|
+
severity: 'error',
|
|
666
|
+
references: references_1.typescriptStandards['async-promises'] || [],
|
|
667
|
+
securityRelevant: false
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Detect module issues - Import/export problems, circular dependencies
|
|
676
|
+
*/
|
|
677
|
+
detectModuleIssues(code, lineErrors) {
|
|
678
|
+
const lines = code.split('\n');
|
|
679
|
+
const imports = new Set();
|
|
680
|
+
let inMultiLineComment = false;
|
|
681
|
+
lines.forEach((line, index) => {
|
|
682
|
+
const lineNumber = index + 1;
|
|
683
|
+
const trimmed = line.trim();
|
|
684
|
+
// CRITICAL: Track multi-line comment blocks (/* ... */)
|
|
685
|
+
if (trimmed.includes('/*')) {
|
|
686
|
+
inMultiLineComment = true;
|
|
687
|
+
}
|
|
688
|
+
if (trimmed.includes('*/')) {
|
|
689
|
+
inMultiLineComment = false;
|
|
690
|
+
return; // Skip the line with */
|
|
691
|
+
}
|
|
692
|
+
// CRITICAL: Skip all lines inside multi-line comments and single-line comments
|
|
693
|
+
if (!trimmed ||
|
|
694
|
+
inMultiLineComment ||
|
|
695
|
+
trimmed.startsWith('//') ||
|
|
696
|
+
trimmed.startsWith('*')) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
// Track imports
|
|
700
|
+
if (line.includes('import')) {
|
|
701
|
+
const importMatch = line.match(/import\s+.*\s+from\s+['"]([^'"]+)['"]/);
|
|
702
|
+
if (importMatch) {
|
|
703
|
+
imports.add(importMatch[1]);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
// Detect import without type-only when importing types
|
|
707
|
+
if (line.match(/import\s+\{[^}]*\}\s+from/) && !line.includes('type ')) {
|
|
708
|
+
if (line.includes('Interface') || line.includes('Type')) {
|
|
709
|
+
lineErrors.push({
|
|
710
|
+
line: lineNumber,
|
|
711
|
+
error: 'Type import without "type" keyword',
|
|
712
|
+
suggestion: 'Use: import type { Interface } from "..." for type-only imports',
|
|
713
|
+
severity: 'info',
|
|
714
|
+
references: references_1.typescriptStandards['modules'] || [],
|
|
715
|
+
securityRelevant: false
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
// Detect dynamic imports without proper typing
|
|
720
|
+
if (line.includes('import(') && !line.includes('await')) {
|
|
721
|
+
lineErrors.push({
|
|
722
|
+
line: lineNumber,
|
|
723
|
+
error: 'Dynamic import without await',
|
|
724
|
+
suggestion: 'Use: const module = await import("...")',
|
|
725
|
+
severity: 'warning',
|
|
726
|
+
references: references_1.typescriptStandards['modules'] || [],
|
|
727
|
+
securityRelevant: false
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
// Detect potential circular dependency patterns
|
|
731
|
+
if (line.includes('export') && line.includes('*') && line.includes('from')) {
|
|
732
|
+
lineErrors.push({
|
|
733
|
+
line: lineNumber,
|
|
734
|
+
error: 'Export * may cause circular dependencies',
|
|
735
|
+
suggestion: 'Export only what is necessary to avoid circular dependencies',
|
|
736
|
+
severity: 'info',
|
|
737
|
+
references: references_1.typescriptStandards['modules'] || [],
|
|
738
|
+
securityRelevant: false
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Detect decorator issues - Experimental decorators usage
|
|
745
|
+
*/
|
|
746
|
+
detectDecoratorIssues(code, lineErrors) {
|
|
747
|
+
const lines = code.split('\n');
|
|
748
|
+
lines.forEach((line, index) => {
|
|
749
|
+
const lineNumber = index + 1;
|
|
750
|
+
if (line.trim().startsWith('//'))
|
|
751
|
+
return;
|
|
752
|
+
// Detect decorator usage
|
|
753
|
+
if (line.trim().startsWith('@')) {
|
|
754
|
+
lineErrors.push({
|
|
755
|
+
line: lineNumber,
|
|
756
|
+
error: 'Decorator detected - experimental feature',
|
|
757
|
+
suggestion: 'Enable experimentalDecorators in tsconfig.json',
|
|
758
|
+
severity: 'info',
|
|
759
|
+
references: references_1.typescriptStandards['decorators'] || [],
|
|
760
|
+
securityRelevant: false
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
// Detect parameter decorators without reflect-metadata
|
|
764
|
+
if (line.match(/@\w+\s*\([^)]*\)\s*\w+:/)) {
|
|
765
|
+
if (!code.includes('reflect-metadata')) {
|
|
766
|
+
lineErrors.push({
|
|
767
|
+
line: lineNumber,
|
|
768
|
+
error: 'Parameter decorator without reflect-metadata',
|
|
769
|
+
suggestion: 'Install and import reflect-metadata for decorator metadata',
|
|
770
|
+
severity: 'warning',
|
|
771
|
+
references: references_1.typescriptStandards['decorators'] || [],
|
|
772
|
+
securityRelevant: false
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Detect mapped/conditional type issues - Complex types, recursive limits
|
|
780
|
+
*/
|
|
781
|
+
detectMappedConditionalTypes(code, lineErrors) {
|
|
782
|
+
const lines = code.split('\n');
|
|
783
|
+
lines.forEach((line, index) => {
|
|
784
|
+
const lineNumber = index + 1;
|
|
785
|
+
if (line.trim().startsWith('//'))
|
|
786
|
+
return;
|
|
787
|
+
// Detect conditional types
|
|
788
|
+
if (line.includes('extends') && line.includes('?') && line.includes(':')) {
|
|
789
|
+
if ((line.match(/\?/g) || []).length > 2) {
|
|
790
|
+
lineErrors.push({
|
|
791
|
+
line: lineNumber,
|
|
792
|
+
error: 'Conditional type too complex',
|
|
793
|
+
suggestion: 'Simplify or split into multiple type aliases',
|
|
794
|
+
severity: 'info',
|
|
795
|
+
references: references_1.typescriptStandards['mapped-conditional'] || [],
|
|
796
|
+
securityRelevant: false
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
// Detect mapped types with complex transformations
|
|
801
|
+
if (line.match(/\[K\s+in\s+keyof/) && line.includes('?') && line.includes('-')) {
|
|
802
|
+
lineErrors.push({
|
|
803
|
+
line: lineNumber,
|
|
804
|
+
error: 'Mapped type with multiple transformations',
|
|
805
|
+
suggestion: 'Consider breaking into intermediate types for better readability',
|
|
806
|
+
severity: 'info',
|
|
807
|
+
references: references_1.typescriptStandards['mapped-conditional'] || [],
|
|
808
|
+
securityRelevant: false
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
// Detect recursive type aliases
|
|
812
|
+
if (line.match(/type\s+(\w+)\s*=/) && line.includes('Array<')) {
|
|
813
|
+
const typeNameMatch = line.match(/type\s+(\w+)\s*=/);
|
|
814
|
+
if (typeNameMatch && line.includes(typeNameMatch[1])) {
|
|
815
|
+
lineErrors.push({
|
|
816
|
+
line: lineNumber,
|
|
817
|
+
error: 'Recursive type alias detected',
|
|
818
|
+
suggestion: 'Beware of infinite recursion. Use interface for recursive types',
|
|
819
|
+
severity: 'warning',
|
|
820
|
+
references: references_1.typescriptStandards['mapped-conditional'] || [],
|
|
821
|
+
securityRelevant: false
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Detect strict mode violations - Implicit any, null checks violations
|
|
829
|
+
*/
|
|
830
|
+
detectStrictModeViolations(code, lineErrors) {
|
|
831
|
+
const lines = code.split('\n');
|
|
832
|
+
lines.forEach((line, index) => {
|
|
833
|
+
const lineNumber = index + 1;
|
|
834
|
+
if (line.trim().startsWith('//'))
|
|
835
|
+
return;
|
|
836
|
+
// Detect implicit any from function parameters without types
|
|
837
|
+
// CRITICAL FIX: Improved regex to catch more patterns
|
|
838
|
+
// Matches: function foo(a, b) or function foo(param) without type annotations
|
|
839
|
+
const functionMatch = line.match(/function\s+\w+\s*\(([^)]*)\)/);
|
|
840
|
+
if (functionMatch) {
|
|
841
|
+
const params = functionMatch[1].trim();
|
|
842
|
+
// Check if params exist and don't have type annotations (no : character)
|
|
843
|
+
if (params.length > 0 && !params.includes(':')) {
|
|
844
|
+
lineErrors.push({
|
|
845
|
+
line: lineNumber,
|
|
846
|
+
error: 'Implicit any - function parameters without type annotations',
|
|
847
|
+
suggestion: 'Add types to parameters: function calculate(a: number, b: number) { ... }',
|
|
848
|
+
severity: 'error', // Changed from 'warning' to 'error' for Monaco highlighting
|
|
849
|
+
references: references_1.typescriptStandards['strict-mode'] || [],
|
|
850
|
+
securityRelevant: false
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
// Detect potential null/undefined without strict null checks
|
|
855
|
+
if (line.match(/\?\s*:/) && !line.includes('null') && !line.includes('undefined')) {
|
|
856
|
+
lineErrors.push({
|
|
857
|
+
line: lineNumber,
|
|
858
|
+
error: 'Optional type may include undefined',
|
|
859
|
+
suggestion: 'Enable strictNullChecks for better type safety',
|
|
860
|
+
severity: 'info',
|
|
861
|
+
references: references_1.typescriptStandards['strict-mode'] || [],
|
|
862
|
+
securityRelevant: false
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
// Detect this without explicit typing
|
|
866
|
+
if (line.includes('this.') && !line.includes('function') && !line.includes('class')) {
|
|
867
|
+
if (!code.includes('// @ts-ignore')) {
|
|
868
|
+
lineErrors.push({
|
|
869
|
+
line: lineNumber,
|
|
870
|
+
error: 'Use of "this" without explicit context',
|
|
871
|
+
suggestion: 'Consider adding explicit type or using arrow functions',
|
|
872
|
+
severity: 'info',
|
|
873
|
+
references: references_1.typescriptStandards['strict-mode'] || [],
|
|
874
|
+
securityRelevant: false
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Detect any propagation - 'any' type spreading through code
|
|
882
|
+
*/
|
|
883
|
+
detectAnyPropagation(code, lineErrors) {
|
|
884
|
+
const lines = code.split('\n');
|
|
885
|
+
lines.forEach((line, index) => {
|
|
886
|
+
const lineNumber = index + 1;
|
|
887
|
+
if (line.trim().startsWith('//'))
|
|
888
|
+
return;
|
|
889
|
+
// Detect explicit 'any' type annotations
|
|
890
|
+
if (line.match(/:\s*any\b/) && !line.includes('// @ts-')) {
|
|
891
|
+
lineErrors.push({
|
|
892
|
+
line: lineNumber,
|
|
893
|
+
error: 'Type "any" detected - eliminates type checking',
|
|
894
|
+
suggestion: 'Use specific type or unknown for type safety',
|
|
895
|
+
severity: 'warning',
|
|
896
|
+
references: references_1.typescriptStandards['any-propagation'] || [],
|
|
897
|
+
securityRelevant: false
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
// Detect any[] arrays
|
|
901
|
+
if (line.match(/:\s*any\[\]/) || line.match(/Array<any>/)) {
|
|
902
|
+
lineErrors.push({
|
|
903
|
+
line: lineNumber,
|
|
904
|
+
error: 'Array of any detected',
|
|
905
|
+
suggestion: 'Specify array type: string[] or Array<string>',
|
|
906
|
+
severity: 'warning',
|
|
907
|
+
references: references_1.typescriptStandards['any-propagation'] || [],
|
|
908
|
+
securityRelevant: false
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
// Detect functions returning any
|
|
912
|
+
if (line.match(/\):\s*any\b/)) {
|
|
913
|
+
lineErrors.push({
|
|
914
|
+
line: lineNumber,
|
|
915
|
+
error: 'Function returns any - propagates lack of typing',
|
|
916
|
+
suggestion: 'Specify the correct return type',
|
|
917
|
+
severity: 'warning',
|
|
918
|
+
references: references_1.typescriptStandards['any-propagation'] || [],
|
|
919
|
+
securityRelevant: false
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Detect performance issues - Type complexity, compilation performance
|
|
926
|
+
*/
|
|
927
|
+
detectPerformanceIssues(code, lineErrors) {
|
|
928
|
+
const lines = code.split('\n');
|
|
929
|
+
lines.forEach((line, index) => {
|
|
930
|
+
const lineNumber = index + 1;
|
|
931
|
+
if (line.trim().startsWith('//'))
|
|
932
|
+
return;
|
|
933
|
+
// Detect excessive union types
|
|
934
|
+
const unionCount = (line.match(/\|/g) || []).length;
|
|
935
|
+
if (unionCount > 5) {
|
|
936
|
+
lineErrors.push({
|
|
937
|
+
line: lineNumber,
|
|
938
|
+
error: `Union type too large (${unionCount} types)`,
|
|
939
|
+
suggestion: 'Simplify union or use type narrowing for better performance',
|
|
940
|
+
severity: 'warning',
|
|
941
|
+
references: references_1.typescriptStandards['performance'] || [],
|
|
942
|
+
securityRelevant: false
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
// Detect deep nesting of generic types
|
|
946
|
+
const genericDepth = (line.match(/</g) || []).length;
|
|
947
|
+
if (genericDepth > 4) {
|
|
948
|
+
lineErrors.push({
|
|
949
|
+
line: lineNumber,
|
|
950
|
+
error: `Generics nested too deeply (depth ${genericDepth})`,
|
|
951
|
+
suggestion: 'Simplify type structure for better compilation performance',
|
|
952
|
+
severity: 'info',
|
|
953
|
+
references: references_1.typescriptStandards['performance'] || [],
|
|
954
|
+
securityRelevant: false
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
// Detect indexed access with complex types
|
|
958
|
+
if (line.match(/\[.*\[.*\]/)) {
|
|
959
|
+
lineErrors.push({
|
|
960
|
+
line: lineNumber,
|
|
961
|
+
error: 'Complex indexed access may affect performance',
|
|
962
|
+
suggestion: 'Consider creating intermediate type aliases',
|
|
963
|
+
severity: 'info',
|
|
964
|
+
references: references_1.typescriptStandards['performance'] || [],
|
|
965
|
+
securityRelevant: false
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Detect naming convention violations - TypeScript-specific naming
|
|
972
|
+
*/
|
|
973
|
+
detectNamingConventions(code, lineErrors) {
|
|
974
|
+
const lines = code.split('\n');
|
|
975
|
+
lines.forEach((line, index) => {
|
|
976
|
+
const lineNumber = index + 1;
|
|
977
|
+
if (line.trim().startsWith('//'))
|
|
978
|
+
return;
|
|
979
|
+
// Detect interface with 'I' prefix (anti-pattern in TypeScript)
|
|
980
|
+
if (line.match(/interface\s+I[A-Z]\w+/)) {
|
|
981
|
+
const interfaceMatch = line.match(/interface\s+(I[A-Z]\w+)/);
|
|
982
|
+
if (interfaceMatch) {
|
|
983
|
+
const interfaceName = interfaceMatch[1];
|
|
984
|
+
const suggestedName = interfaceName.substring(1);
|
|
985
|
+
lineErrors.push({
|
|
986
|
+
line: lineNumber,
|
|
987
|
+
error: `Interface "${interfaceName}" uses "I" prefix - anti-pattern in TypeScript`,
|
|
988
|
+
suggestion: `Rename to: interface ${suggestedName}`,
|
|
989
|
+
severity: 'info',
|
|
990
|
+
references: references_1.typescriptStandards['classes-interfaces'] || [],
|
|
991
|
+
securityRelevant: false
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
// Detect type aliases not in PascalCase
|
|
996
|
+
if (line.match(/type\s+([a-z][a-zA-Z0-9]*)\s*=/)) {
|
|
997
|
+
const typeMatch = line.match(/type\s+([a-z][a-zA-Z0-9]*)\s*=/);
|
|
998
|
+
if (typeMatch) {
|
|
999
|
+
lineErrors.push({
|
|
1000
|
+
line: lineNumber,
|
|
1001
|
+
error: `Type alias should start with uppercase: "${typeMatch[1]}"`,
|
|
1002
|
+
suggestion: `Use PascalCase: ${typeMatch[1].charAt(0).toUpperCase() + typeMatch[1].slice(1)}`,
|
|
1003
|
+
severity: 'info',
|
|
1004
|
+
references: references_1.typescriptStandards['classes-interfaces'] || [],
|
|
1005
|
+
securityRelevant: false
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
// Detect enum members not in UPPER_CASE
|
|
1010
|
+
if (line.match(/enum\s+\w+/)) {
|
|
1011
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 10, lines.length));
|
|
1012
|
+
nextLines.forEach((nextLine, offset) => {
|
|
1013
|
+
if (nextLine.match(/^\s*[a-z]\w*\s*[,=]/) && !nextLine.includes('}')) {
|
|
1014
|
+
lineErrors.push({
|
|
1015
|
+
line: lineNumber + offset + 1,
|
|
1016
|
+
error: 'Enum member should be in PascalCase or UPPER_CASE',
|
|
1017
|
+
suggestion: 'Use: SUCCESS or Success for enum members',
|
|
1018
|
+
severity: 'info',
|
|
1019
|
+
references: references_1.typescriptStandards['classes-interfaces'] || [],
|
|
1020
|
+
securityRelevant: false
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Detect configuration issues - tsconfig problems, module resolution
|
|
1029
|
+
*/
|
|
1030
|
+
detectConfigurationIssues(code, lineErrors) {
|
|
1031
|
+
const lines = code.split('\n');
|
|
1032
|
+
lines.forEach((line, index) => {
|
|
1033
|
+
const lineNumber = index + 1;
|
|
1034
|
+
if (line.trim().startsWith('//'))
|
|
1035
|
+
return;
|
|
1036
|
+
// Detect usage of features requiring specific tsconfig
|
|
1037
|
+
if (line.includes('experimentalDecorators') || line.trim().startsWith('@')) {
|
|
1038
|
+
if (!code.includes('// experimentalDecorators: true')) {
|
|
1039
|
+
lineErrors.push({
|
|
1040
|
+
line: lineNumber,
|
|
1041
|
+
error: 'Decorators require experimentalDecorators in tsconfig',
|
|
1042
|
+
suggestion: 'Add "experimentalDecorators": true in tsconfig.json',
|
|
1043
|
+
severity: 'info',
|
|
1044
|
+
references: references_1.typescriptStandards['decorators'] || [],
|
|
1045
|
+
securityRelevant: false
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
// Detect JSX usage without proper configuration
|
|
1050
|
+
if (line.includes('<') && line.includes('/>') && !line.includes('//')) {
|
|
1051
|
+
if (!code.includes('jsx:')) {
|
|
1052
|
+
lineErrors.push({
|
|
1053
|
+
line: lineNumber,
|
|
1054
|
+
error: 'JSX requires configuration in tsconfig',
|
|
1055
|
+
suggestion: 'Add "jsx": "react" or "jsx": "react-jsx" in tsconfig.json',
|
|
1056
|
+
severity: 'info',
|
|
1057
|
+
references: references_1.typescriptStandards['modules'] || [],
|
|
1058
|
+
securityRelevant: false
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
// Detect module resolution issues
|
|
1063
|
+
if (line.includes('import') && line.includes('"@/')) {
|
|
1064
|
+
lineErrors.push({
|
|
1065
|
+
line: lineNumber,
|
|
1066
|
+
error: 'Path alias "@/" requires paths configuration',
|
|
1067
|
+
suggestion: 'Configure "paths" and "baseUrl" in tsconfig.json',
|
|
1068
|
+
severity: 'info',
|
|
1069
|
+
references: references_1.typescriptStandards['modules'] || [],
|
|
1070
|
+
securityRelevant: false
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Detect React + TypeScript specific issues
|
|
1077
|
+
*/
|
|
1078
|
+
detectReactTypeScriptIssues(code, lineErrors) {
|
|
1079
|
+
const lines = code.split('\n');
|
|
1080
|
+
const isReactFile = code.includes('React') || code.includes('react');
|
|
1081
|
+
if (!isReactFile)
|
|
1082
|
+
return;
|
|
1083
|
+
lines.forEach((line, index) => {
|
|
1084
|
+
const lineNumber = index + 1;
|
|
1085
|
+
if (line.trim().startsWith('//'))
|
|
1086
|
+
return;
|
|
1087
|
+
// Detect React.FC without props typing
|
|
1088
|
+
if (line.match(/React\.FC\b/) && !line.includes('<')) {
|
|
1089
|
+
lineErrors.push({
|
|
1090
|
+
line: lineNumber,
|
|
1091
|
+
error: 'React.FC without props type',
|
|
1092
|
+
suggestion: 'Specify props: React.FC<Props> or use function component',
|
|
1093
|
+
severity: 'warning',
|
|
1094
|
+
references: references_1.typescriptStandards['generics'] || [],
|
|
1095
|
+
securityRelevant: false
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
// Detect event handlers without proper typing
|
|
1099
|
+
if (line.includes('onClick') || line.includes('onChange') || line.includes('onSubmit')) {
|
|
1100
|
+
if (line.match(/\([^)]*\)/) && !line.includes('event:') && !line.includes('e:')) {
|
|
1101
|
+
lineErrors.push({
|
|
1102
|
+
line: lineNumber,
|
|
1103
|
+
error: 'Event handler without event type',
|
|
1104
|
+
suggestion: 'Use: (event: React.MouseEvent) => ... or (e: React.ChangeEvent<HTMLInputElement>)',
|
|
1105
|
+
severity: 'info',
|
|
1106
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
1107
|
+
securityRelevant: false
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
// Detect useState without generic type
|
|
1112
|
+
if (line.includes('useState(') && !line.includes('<')) {
|
|
1113
|
+
if (!line.match(/useState\(\s*[0-9'"]/)) { // Skip primitive initializers
|
|
1114
|
+
lineErrors.push({
|
|
1115
|
+
line: lineNumber,
|
|
1116
|
+
error: 'useState without explicit generic type',
|
|
1117
|
+
suggestion: 'Specify type: useState<Type>(initialValue)',
|
|
1118
|
+
severity: 'info',
|
|
1119
|
+
references: references_1.typescriptStandards['generics'] || [],
|
|
1120
|
+
securityRelevant: false
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
// Detect useRef without type
|
|
1125
|
+
if (line.includes('useRef(') && !line.includes('<')) {
|
|
1126
|
+
lineErrors.push({
|
|
1127
|
+
line: lineNumber,
|
|
1128
|
+
error: 'useRef without generic type',
|
|
1129
|
+
suggestion: 'Specify type: useRef<HTMLDivElement>(null)',
|
|
1130
|
+
severity: 'warning',
|
|
1131
|
+
references: references_1.typescriptStandards['generics'] || [],
|
|
1132
|
+
securityRelevant: false
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
// Detect useEffect without proper dependency typing
|
|
1136
|
+
if (line.includes('useEffect(')) {
|
|
1137
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 10, lines.length));
|
|
1138
|
+
const hasDepsArray = nextLines.some(l => l.includes(']'));
|
|
1139
|
+
if (!hasDepsArray) {
|
|
1140
|
+
lineErrors.push({
|
|
1141
|
+
line: lineNumber,
|
|
1142
|
+
error: 'useEffect without dependency array',
|
|
1143
|
+
suggestion: 'Add dependency array: useEffect(() => { ... }, [deps])',
|
|
1144
|
+
severity: 'warning',
|
|
1145
|
+
references: references_1.typescriptStandards['async-promises'] || [],
|
|
1146
|
+
securityRelevant: false
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
checkBracketBalance(code, errors, lineErrors) {
|
|
1153
|
+
let parenBalance = 0;
|
|
1154
|
+
let braceBalance = 0;
|
|
1155
|
+
let bracketBalance = 0;
|
|
1156
|
+
let inLineComment = false;
|
|
1157
|
+
let inBlockComment = false; // NEW: Track multi-line /* */ comments
|
|
1158
|
+
let inString = false;
|
|
1159
|
+
let stringChar = '';
|
|
1160
|
+
let stringStartLine = 0; // Track where string started
|
|
1161
|
+
let currentLine = 1;
|
|
1162
|
+
let inTemplateLiteralExpression = false; // FIX (2025-11-18): Track ${ } inside template literals
|
|
1163
|
+
let templateExpressionDepth = 0; // Track nested braces inside ${...}
|
|
1164
|
+
const openBraces = [];
|
|
1165
|
+
for (let i = 0; i < code.length; i++) {
|
|
1166
|
+
const char = code[i];
|
|
1167
|
+
const nextChar = code[i + 1];
|
|
1168
|
+
if (char === '\n') {
|
|
1169
|
+
currentLine++;
|
|
1170
|
+
inLineComment = false;
|
|
1171
|
+
// CRITICAL FIX: If we're still in a string at end of line, report unclosed string
|
|
1172
|
+
// BUT: Allow multi-line template literals (backticks can span multiple lines)
|
|
1173
|
+
if (inString && stringChar !== '`') {
|
|
1174
|
+
lineErrors.push({
|
|
1175
|
+
line: stringStartLine,
|
|
1176
|
+
error: `Unclosed ${stringChar === '"' ? 'double quote' : 'single quote'} string`,
|
|
1177
|
+
suggestion: `Add ${stringChar} to close the string on line ${stringStartLine}`,
|
|
1178
|
+
severity: 'error',
|
|
1179
|
+
securityRelevant: false
|
|
1180
|
+
});
|
|
1181
|
+
// Reset string state
|
|
1182
|
+
inString = false;
|
|
1183
|
+
stringChar = '';
|
|
1184
|
+
stringStartLine = 0;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
// NEW: Detect block comment start /* (but not inside strings)
|
|
1188
|
+
if (!inString && !inLineComment && char === '/' && nextChar === '*') {
|
|
1189
|
+
inBlockComment = true;
|
|
1190
|
+
i++; // Skip the *
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
// NEW: Detect block comment end */ (but not inside strings)
|
|
1194
|
+
if (!inString && inBlockComment && char === '*' && nextChar === '/') {
|
|
1195
|
+
inBlockComment = false;
|
|
1196
|
+
i++; // Skip the /
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
if (!inString && char === '/' && nextChar === '/') {
|
|
1200
|
+
inLineComment = true;
|
|
1201
|
+
i++;
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
// NEW: Skip all processing while in any type of comment
|
|
1205
|
+
if (inLineComment || inBlockComment)
|
|
1206
|
+
continue;
|
|
1207
|
+
// FIX (2025-11-18): Handle template literal expressions ${...}
|
|
1208
|
+
// When inside a template literal (`), check for ${ which starts an expression
|
|
1209
|
+
if (inString && stringChar === '`' && char === '$' && nextChar === '{') {
|
|
1210
|
+
inTemplateLiteralExpression = true;
|
|
1211
|
+
templateExpressionDepth = 1;
|
|
1212
|
+
i++; // Skip the {
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
// Track braces inside template literal expressions
|
|
1216
|
+
if (inTemplateLiteralExpression) {
|
|
1217
|
+
if (char === '{') {
|
|
1218
|
+
templateExpressionDepth++;
|
|
1219
|
+
}
|
|
1220
|
+
else if (char === '}') {
|
|
1221
|
+
templateExpressionDepth--;
|
|
1222
|
+
if (templateExpressionDepth === 0) {
|
|
1223
|
+
inTemplateLiteralExpression = false; // Exit expression mode
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
continue; // Don't process other chars while in expression
|
|
1227
|
+
}
|
|
1228
|
+
if ((char === '"' || char === "'" || char === '`') && code[i - 1] !== '\\') {
|
|
1229
|
+
if (!inString) {
|
|
1230
|
+
inString = true;
|
|
1231
|
+
stringChar = char;
|
|
1232
|
+
stringStartLine = currentLine; // Track line where string opened
|
|
1233
|
+
}
|
|
1234
|
+
else if (char === stringChar) {
|
|
1235
|
+
inString = false;
|
|
1236
|
+
stringChar = '';
|
|
1237
|
+
stringStartLine = 0;
|
|
1238
|
+
}
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
if (!inLineComment && !inString) {
|
|
1242
|
+
if (char === '(') {
|
|
1243
|
+
parenBalance++;
|
|
1244
|
+
openBraces.push({ line: currentLine, char: '(' });
|
|
1245
|
+
}
|
|
1246
|
+
else if (char === ')') {
|
|
1247
|
+
parenBalance--;
|
|
1248
|
+
for (let j = openBraces.length - 1; j >= 0; j--) {
|
|
1249
|
+
if (openBraces[j].char === '(') {
|
|
1250
|
+
openBraces.splice(j, 1);
|
|
1251
|
+
break;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
else if (char === '{') {
|
|
1256
|
+
braceBalance++;
|
|
1257
|
+
openBraces.push({ line: currentLine, char: '{' });
|
|
1258
|
+
}
|
|
1259
|
+
else if (char === '}') {
|
|
1260
|
+
braceBalance--;
|
|
1261
|
+
for (let j = openBraces.length - 1; j >= 0; j--) {
|
|
1262
|
+
if (openBraces[j].char === '{') {
|
|
1263
|
+
openBraces.splice(j, 1);
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
else if (char === '[') {
|
|
1269
|
+
bracketBalance++;
|
|
1270
|
+
openBraces.push({ line: currentLine, char: '[' });
|
|
1271
|
+
}
|
|
1272
|
+
else if (char === ']') {
|
|
1273
|
+
bracketBalance--;
|
|
1274
|
+
for (let j = openBraces.length - 1; j >= 0; j--) {
|
|
1275
|
+
if (openBraces[j].char === '[') {
|
|
1276
|
+
openBraces.splice(j, 1);
|
|
1277
|
+
break;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
// CRITICAL FIX: Check for unclosed strings at end of file (including template literals)
|
|
1284
|
+
if (inString && stringStartLine > 0) {
|
|
1285
|
+
const stringTypeName = stringChar === '`' ? 'template literal' :
|
|
1286
|
+
stringChar === '"' ? 'double quote string' : 'single quote string';
|
|
1287
|
+
lineErrors.push({
|
|
1288
|
+
line: stringStartLine,
|
|
1289
|
+
error: `Unclosed ${stringTypeName}`,
|
|
1290
|
+
suggestion: `Add ${stringChar} to close the ${stringTypeName}`,
|
|
1291
|
+
severity: 'error',
|
|
1292
|
+
securityRelevant: false
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
openBraces.forEach(brace => {
|
|
1296
|
+
const closingChar = brace.char === '{' ? '}' : brace.char === '(' ? ')' : ']';
|
|
1297
|
+
const typeName = brace.char === '{' ? 'brace' : brace.char === '(' ? 'parenthesis' : 'bracket';
|
|
1298
|
+
lineErrors.push({
|
|
1299
|
+
line: brace.line,
|
|
1300
|
+
error: `${typeName.charAt(0).toUpperCase() + typeName.slice(1)} "${brace.char}" was not closed`,
|
|
1301
|
+
suggestion: `Add "${closingChar}" to close the ${typeName} opened on line ${brace.line}`,
|
|
1302
|
+
severity: 'error',
|
|
1303
|
+
securityRelevant: false
|
|
1304
|
+
});
|
|
1305
|
+
});
|
|
1306
|
+
// DON'T add general balance errors - the code above already adds specific line errors
|
|
1307
|
+
// with proper line numbers, enabling Auto-Fix functionality
|
|
1308
|
+
}
|
|
1309
|
+
analyzeQuality(code, result) {
|
|
1310
|
+
const issues = [];
|
|
1311
|
+
// Check for 'any' usage
|
|
1312
|
+
const anyCount = (code.match(/:\s*any\b/g) || []).length;
|
|
1313
|
+
if (anyCount > 0) {
|
|
1314
|
+
issues.push({
|
|
1315
|
+
type: 'warning',
|
|
1316
|
+
message: `${anyCount} use(s) of "any" found - compromises type safety`,
|
|
1317
|
+
severity: 'medium'
|
|
1318
|
+
});
|
|
1319
|
+
result.quality.score -= anyCount * 5;
|
|
1320
|
+
}
|
|
1321
|
+
// Check for missing return types
|
|
1322
|
+
const functionsWithoutReturnType = (code.match(/function\s+\w+\s*\([^)]*\)\s*\{/g) || []).length;
|
|
1323
|
+
if (functionsWithoutReturnType > 0) {
|
|
1324
|
+
issues.push({
|
|
1325
|
+
type: 'info',
|
|
1326
|
+
message: 'Functions without explicit return type found',
|
|
1327
|
+
severity: 'low'
|
|
1328
|
+
});
|
|
1329
|
+
result.quality.score -= functionsWithoutReturnType * 3;
|
|
1330
|
+
}
|
|
1331
|
+
// Check for interface I-prefix
|
|
1332
|
+
const iPrefixCount = (code.match(/interface\s+I[A-Z]\w+/g) || []).length;
|
|
1333
|
+
if (iPrefixCount > 0) {
|
|
1334
|
+
issues.push({
|
|
1335
|
+
type: 'info',
|
|
1336
|
+
message: 'Interfaces with "I" prefix - anti-pattern in TypeScript',
|
|
1337
|
+
severity: 'low'
|
|
1338
|
+
});
|
|
1339
|
+
result.quality.score -= iPrefixCount * 2;
|
|
1340
|
+
}
|
|
1341
|
+
result.quality.issues = issues;
|
|
1342
|
+
result.quality.score = Math.max(0, result.quality.score);
|
|
1343
|
+
}
|
|
1344
|
+
analyzePerformance(code, result) {
|
|
1345
|
+
const suggestions = [];
|
|
1346
|
+
// Check for excessive union types
|
|
1347
|
+
const lines = code.split('\n');
|
|
1348
|
+
lines.forEach(line => {
|
|
1349
|
+
const unionCount = (line.match(/\|/g) || []).length;
|
|
1350
|
+
if (unionCount > 5) {
|
|
1351
|
+
suggestions.push('Simplify large union types for better compilation performance');
|
|
1352
|
+
result.performance.score -= 10;
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
// Check for deep generic nesting
|
|
1356
|
+
const deepGenerics = code.match(/<<<</g);
|
|
1357
|
+
if (deepGenerics) {
|
|
1358
|
+
suggestions.push('Reduce generic nesting for better performance');
|
|
1359
|
+
result.performance.score -= 15;
|
|
1360
|
+
}
|
|
1361
|
+
result.performance.suggestions = suggestions;
|
|
1362
|
+
result.performance.score = Math.max(0, result.performance.score);
|
|
1363
|
+
}
|
|
1364
|
+
analyzeSecurity(code, result) {
|
|
1365
|
+
const lines = code.split('\n');
|
|
1366
|
+
const vulnerabilities = [];
|
|
1367
|
+
// MODULAR SECURITY CHECKS (2025-12-01)
|
|
1368
|
+
// All security checks extracted to focused modules for better maintainability
|
|
1369
|
+
// Injection Attacks (Checks #1-7)
|
|
1370
|
+
vulnerabilities.push(...(0, injection_attacks_1.checkInjectionAttacks)(lines));
|
|
1371
|
+
// Credentials & Cryptography (Checks #8-10)
|
|
1372
|
+
vulnerabilities.push(...(0, credentials_crypto_1.checkCredentialsAndCrypto)(lines));
|
|
1373
|
+
// Code Injection (Checks #11-14)
|
|
1374
|
+
vulnerabilities.push(...(0, code_injection_1.checkCodeInjection)(lines));
|
|
1375
|
+
// Code Quality (Checks #15-18)
|
|
1376
|
+
vulnerabilities.push(...(0, code_quality_1.checkCodeQuality)(code, lines));
|
|
1377
|
+
// TypeScript Type Security (Checks #19-20)
|
|
1378
|
+
vulnerabilities.push(...(0, type_security_1.checkTypeSecurity)(lines));
|
|
1379
|
+
// OWASP A02:2025 - Security Misconfiguration (NEW - Phase 7B)
|
|
1380
|
+
vulnerabilities.push(...(0, security_misconfiguration_1.checkSecurityMisconfiguration)(lines));
|
|
1381
|
+
// OWASP A10:2025 - Mishandling of Exceptional Conditions (NEW - Phase 7B)
|
|
1382
|
+
vulnerabilities.push(...(0, exception_handling_1.checkExceptionHandling)(lines));
|
|
1383
|
+
// OWASP A03:2025 - Software Supply Chain Failures (Enhanced - Phase 7B)
|
|
1384
|
+
vulnerabilities.push(...(0, enhanced_supply_chain_1.checkEnhancedSupplyChain)(lines));
|
|
1385
|
+
// OWASP A01:2021 - Broken Access Control (Check #85 - Dec 16, 2025)
|
|
1386
|
+
vulnerabilities.push(...(0, access_control_1.checkAccessControl)(lines));
|
|
1387
|
+
// OWASP A05:2021 - Security Misconfiguration - Information Disclosure (Check #86 - Dec 16, 2025)
|
|
1388
|
+
vulnerabilities.push(...(0, information_disclosure_1.checkInformationDisclosure)(lines));
|
|
1389
|
+
// OWASP A07:2021 - Identification and Authentication Failures (Checks #88, #89, #90, #91 - Dec 31, 2025)
|
|
1390
|
+
vulnerabilities.push(...(0, authentication_1.checkAuthentication)(lines));
|
|
1391
|
+
// OWASP A09:2025 - Security Logging and Monitoring Failures (Checks #92, #93 - Dec 31, 2025)
|
|
1392
|
+
vulnerabilities.push(...(0, logging_failures_1.checkLoggingFailures)(lines));
|
|
1393
|
+
// NEW: TypeScript Compiler API Type Checking (2025-12-02)
|
|
1394
|
+
// This adds comprehensive type error detection (95%+ coverage)
|
|
1395
|
+
try {
|
|
1396
|
+
const diagnostics = (0, type_checker_1.getTypeScriptDiagnostics)(code, 'temp.ts');
|
|
1397
|
+
const typeIssues = (0, type_checker_1.convertDiagnosticsToIssues)(diagnostics);
|
|
1398
|
+
// Convert SecurityIssue[] to SecurityVulnerability[]
|
|
1399
|
+
const typeVulnerabilities = typeIssues.map(issue => ({
|
|
1400
|
+
severity: issue.severity,
|
|
1401
|
+
message: issue.message,
|
|
1402
|
+
line: issue.line,
|
|
1403
|
+
suggestion: issue.suggestion,
|
|
1404
|
+
category: 'type-checking',
|
|
1405
|
+
cvssScore: issue.cvssScore,
|
|
1406
|
+
exploitLikelihood: issue.exploitLikelihood,
|
|
1407
|
+
impact: issue.impact,
|
|
1408
|
+
owasp: issue.owasp,
|
|
1409
|
+
cwe: issue.cwe
|
|
1410
|
+
}));
|
|
1411
|
+
vulnerabilities.push(...typeVulnerabilities);
|
|
1412
|
+
}
|
|
1413
|
+
catch (error) {
|
|
1414
|
+
// Gracefully degrade - if type checking fails, continue with pattern-based checks
|
|
1415
|
+
console.error('TypeScript type checking error:', error);
|
|
1416
|
+
}
|
|
1417
|
+
// Secrets Detection (Phase 1.5, Week 1)
|
|
1418
|
+
const secretsAnalyzer = (0, secrets_analyzer_1.createSecretsAnalyzer)();
|
|
1419
|
+
vulnerabilities.push(...secretsAnalyzer.analyzeCode(code, 'unknown.ts', 'typescript'));
|
|
1420
|
+
// P1-5: Deduplicate vulnerabilities on same line (Dec 30, 2025)
|
|
1421
|
+
// Fix for Beta Testing Issue: Same vulnerability detected multiple times on same line
|
|
1422
|
+
result.security.vulnerabilities = this.deduplicateVulnerabilities(vulnerabilities);
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* P1-5: Generic deduplication for ALL vulnerability types (Dec 30, 2025)
|
|
1426
|
+
* Prevents same vulnerability from being reported multiple times on the same line
|
|
1427
|
+
*
|
|
1428
|
+
* Strategy: Use line + category as unique key, keep highest CVSS score
|
|
1429
|
+
*/
|
|
1430
|
+
deduplicateVulnerabilities(vulnerabilities) {
|
|
1431
|
+
const getCategory = (message) => {
|
|
1432
|
+
const lower = message.toLowerCase();
|
|
1433
|
+
if (lower.includes('hardcoded credential'))
|
|
1434
|
+
return 'hardcoded-credential';
|
|
1435
|
+
if (lower.includes('sql injection'))
|
|
1436
|
+
return 'sql-injection';
|
|
1437
|
+
if (lower.includes('nosql injection'))
|
|
1438
|
+
return 'nosql-injection';
|
|
1439
|
+
if (lower.includes('command injection'))
|
|
1440
|
+
return 'command-injection';
|
|
1441
|
+
if (lower.includes('ldap injection'))
|
|
1442
|
+
return 'ldap-injection';
|
|
1443
|
+
if (lower.includes('xss') || lower.includes('cross-site scripting'))
|
|
1444
|
+
return 'xss';
|
|
1445
|
+
if (lower.includes('ssrf'))
|
|
1446
|
+
return 'ssrf';
|
|
1447
|
+
if (lower.includes('xxe') || lower.includes('xml external entity'))
|
|
1448
|
+
return 'xxe';
|
|
1449
|
+
if (lower.includes('deserialization'))
|
|
1450
|
+
return 'deserialization';
|
|
1451
|
+
if (lower.includes('prototype pollution'))
|
|
1452
|
+
return 'prototype-pollution';
|
|
1453
|
+
if (lower.includes('weak hash') || lower.includes('md5') || lower.includes('sha1'))
|
|
1454
|
+
return 'weak-hash';
|
|
1455
|
+
if (lower.includes('insecure random'))
|
|
1456
|
+
return 'insecure-random';
|
|
1457
|
+
if (lower.includes('missing helmet'))
|
|
1458
|
+
return 'missing-helmet';
|
|
1459
|
+
if (lower.includes('missing csrf'))
|
|
1460
|
+
return 'missing-csrf';
|
|
1461
|
+
if (lower.includes('eval'))
|
|
1462
|
+
return 'eval';
|
|
1463
|
+
if (lower.includes('path traversal'))
|
|
1464
|
+
return 'path-traversal';
|
|
1465
|
+
if (lower.includes('open redirect'))
|
|
1466
|
+
return 'open-redirect';
|
|
1467
|
+
if (lower.includes('jwt'))
|
|
1468
|
+
return 'jwt';
|
|
1469
|
+
if (lower.includes('cors'))
|
|
1470
|
+
return 'cors';
|
|
1471
|
+
if (lower.includes('insecure tls') || lower.includes('reject unauthorized'))
|
|
1472
|
+
return 'insecure-tls';
|
|
1473
|
+
// Fallback: use first 30 chars as category
|
|
1474
|
+
return message.substring(0, 30);
|
|
1475
|
+
};
|
|
1476
|
+
const uniqueMap = new Map();
|
|
1477
|
+
vulnerabilities.forEach(vuln => {
|
|
1478
|
+
const line = vuln.line || 0;
|
|
1479
|
+
const category = getCategory(vuln.message || '');
|
|
1480
|
+
const key = `${line}:${category}`;
|
|
1481
|
+
const existing = uniqueMap.get(key);
|
|
1482
|
+
if (!existing) {
|
|
1483
|
+
uniqueMap.set(key, vuln);
|
|
1484
|
+
}
|
|
1485
|
+
else {
|
|
1486
|
+
// Keep higher CVSS score
|
|
1487
|
+
const existingScore = existing.cvssScore || 0;
|
|
1488
|
+
const currentScore = vuln.cvssScore || 0;
|
|
1489
|
+
if (currentScore > existingScore) {
|
|
1490
|
+
uniqueMap.set(key, vuln);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
return Array.from(uniqueMap.values());
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* PHASE 6 WEEK 1 DAY 1: Detect duplicate identifier declarations
|
|
1498
|
+
*
|
|
1499
|
+
* Detects when variables, functions, classes, interfaces, or types are declared
|
|
1500
|
+
* multiple times in the same scope, which causes TypeScript compilation errors.
|
|
1501
|
+
*
|
|
1502
|
+
* Examples:
|
|
1503
|
+
* - const API_KEY = 'key1'; const API_KEY = 'key2'; → Duplicate
|
|
1504
|
+
* - function foo() {} function foo() {} → Duplicate
|
|
1505
|
+
* - interface User {} interface User {} → Duplicate (unless extending)
|
|
1506
|
+
*
|
|
1507
|
+
* Note: This is a simplified implementation that tracks identifiers at file scope.
|
|
1508
|
+
* Full scope tracking (block scope, function scope) would require AST parsing.
|
|
1509
|
+
*/
|
|
1510
|
+
detectDuplicateIdentifiers(code, lineErrors) {
|
|
1511
|
+
const lines = code.split('\n');
|
|
1512
|
+
const declaredIdentifiers = new Map();
|
|
1513
|
+
lines.forEach((line, index) => {
|
|
1514
|
+
const lineNumber = index + 1;
|
|
1515
|
+
const trimmed = line.trim();
|
|
1516
|
+
// Skip comments and empty lines
|
|
1517
|
+
if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
// Pattern 1: const/let/var declarations (with optional export)
|
|
1521
|
+
const varMatch = trimmed.match(/^\s*(?:export\s+)?(const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
1522
|
+
if (varMatch) {
|
|
1523
|
+
const identifierType = varMatch[1]; // const, let, or var
|
|
1524
|
+
const identifierName = varMatch[2];
|
|
1525
|
+
if (declaredIdentifiers.has(identifierName)) {
|
|
1526
|
+
const previous = declaredIdentifiers.get(identifierName);
|
|
1527
|
+
lineErrors.push({
|
|
1528
|
+
line: lineNumber,
|
|
1529
|
+
error: `Duplicate identifier: '${identifierName}' was already declared on line ${previous.line}`,
|
|
1530
|
+
suggestion: `Rename this ${identifierType} or remove the duplicate declaration`,
|
|
1531
|
+
severity: 'error',
|
|
1532
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
1533
|
+
securityRelevant: false
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
else {
|
|
1537
|
+
declaredIdentifiers.set(identifierName, { line: lineNumber, type: identifierType });
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
// Pattern 2: Function declarations
|
|
1541
|
+
const funcMatch = trimmed.match(/^\s*function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
1542
|
+
if (funcMatch) {
|
|
1543
|
+
const identifierName = funcMatch[1];
|
|
1544
|
+
if (declaredIdentifiers.has(identifierName)) {
|
|
1545
|
+
const previous = declaredIdentifiers.get(identifierName);
|
|
1546
|
+
lineErrors.push({
|
|
1547
|
+
line: lineNumber,
|
|
1548
|
+
error: `Duplicate function declaration: '${identifierName}' was already declared on line ${previous.line}`,
|
|
1549
|
+
suggestion: `Rename this function or remove the duplicate declaration`,
|
|
1550
|
+
severity: 'error',
|
|
1551
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
1552
|
+
securityRelevant: false
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
else {
|
|
1556
|
+
declaredIdentifiers.set(identifierName, { line: lineNumber, type: 'function' });
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
// Pattern 3: Class declarations
|
|
1560
|
+
const classMatch = trimmed.match(/^\s*(?:export\s+)?class\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
1561
|
+
if (classMatch) {
|
|
1562
|
+
const identifierName = classMatch[1];
|
|
1563
|
+
if (declaredIdentifiers.has(identifierName)) {
|
|
1564
|
+
const previous = declaredIdentifiers.get(identifierName);
|
|
1565
|
+
lineErrors.push({
|
|
1566
|
+
line: lineNumber,
|
|
1567
|
+
error: `Duplicate class declaration: '${identifierName}' was already declared on line ${previous.line}`,
|
|
1568
|
+
suggestion: `Rename this class or remove the duplicate declaration`,
|
|
1569
|
+
severity: 'error',
|
|
1570
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
1571
|
+
securityRelevant: false
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
else {
|
|
1575
|
+
declaredIdentifiers.set(identifierName, { line: lineNumber, type: 'class' });
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
// Pattern 4: Interface declarations (allow interface merging - only warn if suspicious)
|
|
1579
|
+
const interfaceMatch = trimmed.match(/^\s*(?:export\s+)?interface\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
1580
|
+
if (interfaceMatch) {
|
|
1581
|
+
const identifierName = interfaceMatch[1];
|
|
1582
|
+
if (declaredIdentifiers.has(identifierName)) {
|
|
1583
|
+
const previous = declaredIdentifiers.get(identifierName);
|
|
1584
|
+
// Interface merging is allowed in TypeScript, but warn if it might be unintentional
|
|
1585
|
+
if (previous.type === 'interface') {
|
|
1586
|
+
lineErrors.push({
|
|
1587
|
+
line: lineNumber,
|
|
1588
|
+
error: `Duplicate interface declaration: '${identifierName}' was already declared on line ${previous.line} (interface merging may be intentional)`,
|
|
1589
|
+
suggestion: `If intentional (interface merging), this is OK. Otherwise, rename one of the interfaces.`,
|
|
1590
|
+
severity: 'warning', // Warning, not error, because interface merging is valid
|
|
1591
|
+
references: references_1.typescriptStandards['class-interface'] || [],
|
|
1592
|
+
securityRelevant: false
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
else {
|
|
1597
|
+
declaredIdentifiers.set(identifierName, { line: lineNumber, type: 'interface' });
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
// Pattern 5: Type alias declarations
|
|
1601
|
+
const typeMatch = trimmed.match(/^\s*(?:export\s+)?type\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
1602
|
+
if (typeMatch) {
|
|
1603
|
+
const identifierName = typeMatch[1];
|
|
1604
|
+
if (declaredIdentifiers.has(identifierName)) {
|
|
1605
|
+
const previous = declaredIdentifiers.get(identifierName);
|
|
1606
|
+
lineErrors.push({
|
|
1607
|
+
line: lineNumber,
|
|
1608
|
+
error: `Duplicate type alias: '${identifierName}' was already declared on line ${previous.line}`,
|
|
1609
|
+
suggestion: `Rename this type or remove the duplicate declaration`,
|
|
1610
|
+
severity: 'error',
|
|
1611
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
1612
|
+
securityRelevant: false
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
else {
|
|
1616
|
+
declaredIdentifiers.set(identifierName, { line: lineNumber, type: 'type' });
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
// Pattern 6: Enum declarations
|
|
1620
|
+
const enumMatch = trimmed.match(/^\s*(?:export\s+)?enum\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
1621
|
+
if (enumMatch) {
|
|
1622
|
+
const identifierName = enumMatch[1];
|
|
1623
|
+
if (declaredIdentifiers.has(identifierName)) {
|
|
1624
|
+
const previous = declaredIdentifiers.get(identifierName);
|
|
1625
|
+
lineErrors.push({
|
|
1626
|
+
line: lineNumber,
|
|
1627
|
+
error: `Duplicate enum declaration: '${identifierName}' was already declared on line ${previous.line}`,
|
|
1628
|
+
suggestion: `Rename this enum or remove the duplicate declaration`,
|
|
1629
|
+
severity: 'error',
|
|
1630
|
+
references: references_1.typescriptStandards['type-errors'] || [],
|
|
1631
|
+
securityRelevant: false
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
else {
|
|
1635
|
+
declaredIdentifiers.set(identifierName, { line: lineNumber, type: 'enum' });
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
calculateMetrics(code, result) {
|
|
1641
|
+
const lines = code.split('\n');
|
|
1642
|
+
result.metrics.lines = lines.length;
|
|
1643
|
+
const functions = (code.match(/function\s+\w+/g) || []).length;
|
|
1644
|
+
const arrowFunctions = (code.match(/=>\s*\{/g) || []).length;
|
|
1645
|
+
result.metrics.functions = functions + arrowFunctions;
|
|
1646
|
+
let complexity = 1;
|
|
1647
|
+
const keywords = ['if', 'else', 'for', 'while', 'switch', 'case', 'catch', '&&', '||', '?'];
|
|
1648
|
+
keywords.forEach(keyword => {
|
|
1649
|
+
// Escape special regex characters
|
|
1650
|
+
const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1651
|
+
const matches = code.match(new RegExp(`\\b${escapedKeyword}\\b`, 'g'));
|
|
1652
|
+
if (matches)
|
|
1653
|
+
complexity += matches.length;
|
|
1654
|
+
});
|
|
1655
|
+
result.metrics.complexity = complexity;
|
|
1656
|
+
result.metrics.maintainability = Math.max(0, 100 - complexity * 3);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
exports.TypeScriptAnalyzer = TypeScriptAnalyzer;
|
|
1660
|
+
//# sourceMappingURL=typescript-analyzer.js.map
|