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,1600 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ⚠️ SHARED MODULE: Python Security Analyzer
|
|
4
|
+
*
|
|
5
|
+
* CRITICAL: This module is used by BOTH WebTool and GitHub App
|
|
6
|
+
*
|
|
7
|
+
* WebTool uses this for:
|
|
8
|
+
* - /api/analyze endpoint - Interactive single-file analysis (<3s target)
|
|
9
|
+
* - Real-time vulnerability detection for individual developers
|
|
10
|
+
*
|
|
11
|
+
* GitHub App uses this for:
|
|
12
|
+
* - /api/github/webhook - Batch PR analysis (10-30s OK)
|
|
13
|
+
* - Automated security checks for professional teams
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ BEFORE MODIFYING THIS FILE:
|
|
16
|
+
* 1. Run all 96 analyzer tests: npm test analyzers
|
|
17
|
+
* 2. Test WebTool: Paste Python code at /analyze → Verify results
|
|
18
|
+
* 3. Test GitHub: Open PR with Python → Verify webhook comment
|
|
19
|
+
* 4. Verify performance: Analysis must complete in <2s per file
|
|
20
|
+
* 5. Check detection rate: All 19 Python checks must still detect
|
|
21
|
+
*
|
|
22
|
+
* CRITICAL OUTPUT FORMAT (DO NOT CHANGE):
|
|
23
|
+
* - result.security.vulnerabilities - Used by both systems
|
|
24
|
+
* - Each vulnerability has: line, message, severity, cvssScore, owasp, cwe
|
|
25
|
+
* - Changing this structure breaks BOTH WebTool and GitHub UI parsing
|
|
26
|
+
*
|
|
27
|
+
* See: docs/technical/WEBTOOL_GITHUB_SEPARATION.md
|
|
28
|
+
*
|
|
29
|
+
* Last modified: 2025-11-18
|
|
30
|
+
* Last verified (both systems): 2025-11-18
|
|
31
|
+
*/
|
|
32
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
35
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
36
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
37
|
+
}
|
|
38
|
+
Object.defineProperty(o, k2, desc);
|
|
39
|
+
}) : (function(o, m, k, k2) {
|
|
40
|
+
if (k2 === undefined) k2 = k;
|
|
41
|
+
o[k2] = m[k];
|
|
42
|
+
}));
|
|
43
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
44
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
45
|
+
}) : function(o, v) {
|
|
46
|
+
o["default"] = v;
|
|
47
|
+
});
|
|
48
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
49
|
+
var ownKeys = function(o) {
|
|
50
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
51
|
+
var ar = [];
|
|
52
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
53
|
+
return ar;
|
|
54
|
+
};
|
|
55
|
+
return ownKeys(o);
|
|
56
|
+
};
|
|
57
|
+
return function (mod) {
|
|
58
|
+
if (mod && mod.__esModule) return mod;
|
|
59
|
+
var result = {};
|
|
60
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
61
|
+
__setModuleDefault(result, mod);
|
|
62
|
+
return result;
|
|
63
|
+
};
|
|
64
|
+
})();
|
|
65
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
66
|
+
exports.PythonAnalyzer = void 0;
|
|
67
|
+
const references_1 = require("../standards/references");
|
|
68
|
+
const severity_scoring_1 = require("../security/severity-scoring");
|
|
69
|
+
const compliance_mapping_1 = require("../security/compliance-mapping");
|
|
70
|
+
const code_cleaner_1 = require("../utils/code-cleaner");
|
|
71
|
+
const PythonAsyncSecurity = __importStar(require("./security-checks/python-async-security"));
|
|
72
|
+
const injection_attacks_1 = require("./python/security-checks/injection-attacks");
|
|
73
|
+
const credentials_crypto_1 = require("./python/security-checks/credentials-crypto");
|
|
74
|
+
const deserialization_1 = require("./python/security-checks/deserialization");
|
|
75
|
+
const web_security_1 = require("./python/security-checks/web-security");
|
|
76
|
+
const code_quality_1 = require("./python/security-checks/code-quality");
|
|
77
|
+
const django_security_1 = require("./python/security-checks/django-security");
|
|
78
|
+
const flask_security_1 = require("./python/security-checks/flask-security");
|
|
79
|
+
const security_misconfiguration_1 = require("./python/security-checks/security-misconfiguration");
|
|
80
|
+
const exception_handling_1 = require("./python/security-checks/exception-handling");
|
|
81
|
+
const enhanced_supply_chain_1 = require("./python/security-checks/enhanced-supply-chain");
|
|
82
|
+
const access_control_1 = require("./python/security-checks/access-control");
|
|
83
|
+
const crypto_failures_1 = require("./python/security-checks/crypto-failures");
|
|
84
|
+
const insecure_design_1 = require("./python/security-checks/insecure-design");
|
|
85
|
+
const logging_failures_1 = require("./python/security-checks/logging-failures");
|
|
86
|
+
const data_integrity_1 = require("./python/security-checks/data-integrity");
|
|
87
|
+
const nosql_injection_1 = require("./python/security-checks/nosql-injection");
|
|
88
|
+
const ssrf_detection_1 = require("./python/security-checks/ssrf-detection");
|
|
89
|
+
const authentication_flaws_1 = require("./python/security-checks/authentication-flaws");
|
|
90
|
+
const secrets_analyzer_1 = require("./secrets/secrets-analyzer");
|
|
91
|
+
const ai_generated_code_1 = require("./python/security-checks/ai-generated-code");
|
|
92
|
+
class PythonAnalyzer {
|
|
93
|
+
constructor() {
|
|
94
|
+
this.language = 'python';
|
|
95
|
+
}
|
|
96
|
+
async analyze(input) {
|
|
97
|
+
const result = {
|
|
98
|
+
syntax: { valid: true, errors: [], lineErrors: [] },
|
|
99
|
+
quality: { score: 100, issues: [] },
|
|
100
|
+
performance: { score: 100, suggestions: [] },
|
|
101
|
+
security: { vulnerabilities: [] },
|
|
102
|
+
metrics: { complexity: 1, maintainability: 100, lines: 0, functions: 0 }
|
|
103
|
+
};
|
|
104
|
+
try {
|
|
105
|
+
this.analyzeSyntax(input.code, result);
|
|
106
|
+
this.analyzeQuality(input.code, result);
|
|
107
|
+
this.analyzePerformance(input.code, result);
|
|
108
|
+
this.analyzeSecurity(input.code, result);
|
|
109
|
+
this.calculateMetrics(input.code, result);
|
|
110
|
+
// AI-Generated Code Detection (Phase 1.5, Week 5-7)
|
|
111
|
+
const lines = input.code.split('\n');
|
|
112
|
+
result.security.vulnerabilities.push(...(0, ai_generated_code_1.checkAIGeneratedCode)(lines, input.filename));
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
116
|
+
result.syntax.valid = false;
|
|
117
|
+
result.syntax.errors.push(`Python analysis error: ${errorMessage}`);
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
async validateSyntax(code) {
|
|
122
|
+
// Basic Python syntax checks
|
|
123
|
+
const lines = code.split('\n');
|
|
124
|
+
for (let i = 0; i < lines.length; i++) {
|
|
125
|
+
const line = lines[i].trim();
|
|
126
|
+
if (!line || line.startsWith('#'))
|
|
127
|
+
continue;
|
|
128
|
+
// Verificar estruturas que devem terminar com :
|
|
129
|
+
if (line.match(/^(if|for|while|def|class|try|except|finally|with|elif)\s/) && !line.endsWith(':')) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
// Verificar comentários JavaScript em código Python
|
|
133
|
+
if (line.includes('//')) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
getLanguageInfo() {
|
|
140
|
+
return {
|
|
141
|
+
name: 'Python',
|
|
142
|
+
extensions: ['.py', '.pyw', '.pyc', '.pyo', '.pyd'],
|
|
143
|
+
description: 'Language for data science, web, and automation'
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
analyzeSyntax(code, result) {
|
|
147
|
+
const errors = [];
|
|
148
|
+
const lineErrors = [];
|
|
149
|
+
const processedLines = new Set(); // Track lines that already have errors
|
|
150
|
+
const lines = code.split('\n');
|
|
151
|
+
let inMultilineString = false; // Track if we're inside a triple-quoted string
|
|
152
|
+
// First pass: Priority syntax errors (critical issues first)
|
|
153
|
+
lines.forEach((line, index) => {
|
|
154
|
+
const lineNumber = index + 1;
|
|
155
|
+
const trimmed = line.trim();
|
|
156
|
+
// CRITICAL: Track Python triple-quote strings (""" ... """ or ''' ... ''')
|
|
157
|
+
const hasTripleQuote = trimmed.includes('"""') || trimmed.includes("'''");
|
|
158
|
+
if (hasTripleQuote) {
|
|
159
|
+
if (!inMultilineString) {
|
|
160
|
+
// Start of multi-line string
|
|
161
|
+
inMultilineString = true;
|
|
162
|
+
// Check if it closes on the same line (single-line docstring)
|
|
163
|
+
const tripleQuoteCount = (trimmed.match(/"""/g) || []).length + (trimmed.match(/'''/g) || []).length;
|
|
164
|
+
if (tripleQuoteCount >= 2) {
|
|
165
|
+
// Opens and closes on same line, reset flag
|
|
166
|
+
inMultilineString = false;
|
|
167
|
+
}
|
|
168
|
+
return; // Skip this line
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// End of multi-line string
|
|
172
|
+
inMultilineString = false;
|
|
173
|
+
return; // Skip this line
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Skip lines inside multi-line strings, empty lines, and comments
|
|
177
|
+
if (inMultilineString || !trimmed || trimmed.startsWith('#'))
|
|
178
|
+
return;
|
|
179
|
+
// Priority 1: Check for unclosed strings (general detection)
|
|
180
|
+
// CRITICAL FIX: Remove comments to prevent false positives
|
|
181
|
+
// Example: message = "Hello World # Missing closing quote
|
|
182
|
+
const lineWithoutComments = code_cleaner_1.CodeCleaner.removeLineComments(line, 'python');
|
|
183
|
+
// Detect unclosed strings: count unescaped quotes
|
|
184
|
+
const checkUnclosedString = (text, quoteChar) => {
|
|
185
|
+
let count = 0;
|
|
186
|
+
let escaped = false;
|
|
187
|
+
for (let i = 0; i < text.length; i++) {
|
|
188
|
+
if (escaped) {
|
|
189
|
+
escaped = false;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (text[i] === '\\') {
|
|
193
|
+
escaped = true;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (text[i] === quoteChar) {
|
|
197
|
+
count++;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// Odd count means unclosed string
|
|
201
|
+
return count % 2 !== 0;
|
|
202
|
+
};
|
|
203
|
+
// Check for unclosed double quotes
|
|
204
|
+
if (checkUnclosedString(lineWithoutComments, '"')) {
|
|
205
|
+
lineErrors.push({
|
|
206
|
+
line: lineNumber,
|
|
207
|
+
error: 'Unclosed string - missing closing double quote (")',
|
|
208
|
+
suggestion: 'Add " to close the string',
|
|
209
|
+
severity: 'error'
|
|
210
|
+
});
|
|
211
|
+
// Don't return - continue checking for other errors
|
|
212
|
+
}
|
|
213
|
+
// Check for unclosed single quotes
|
|
214
|
+
if (checkUnclosedString(lineWithoutComments, "'")) {
|
|
215
|
+
lineErrors.push({
|
|
216
|
+
line: lineNumber,
|
|
217
|
+
error: "Unclosed string - missing closing single quote (')",
|
|
218
|
+
suggestion: "Add ' to close the string",
|
|
219
|
+
severity: 'error'
|
|
220
|
+
});
|
|
221
|
+
// Don't return - continue checking for other errors
|
|
222
|
+
}
|
|
223
|
+
// Priority 2: Check for unclosed f-strings (improved pattern)
|
|
224
|
+
const fStringMatches = lineWithoutComments.match(/f["'][^"']*\{[^}]*$/);
|
|
225
|
+
if (fStringMatches) {
|
|
226
|
+
lineErrors.push({
|
|
227
|
+
line: lineNumber,
|
|
228
|
+
error: 'f-string with unclosed brace',
|
|
229
|
+
suggestion: 'Add } to close the expression in the f-string',
|
|
230
|
+
severity: 'error'
|
|
231
|
+
});
|
|
232
|
+
// Don't return - continue checking for other errors
|
|
233
|
+
}
|
|
234
|
+
// Priority 3: Check for format string argument mismatch (improved logic)
|
|
235
|
+
// Use cleaned line to avoid false positives from comments
|
|
236
|
+
const formatMatch = lineWithoutComments.match(/\"([^\"]*)\"\s*\.format\s*\(([^)]*)\)/);
|
|
237
|
+
if (formatMatch) {
|
|
238
|
+
const formatStr = formatMatch[1];
|
|
239
|
+
const argsStr = formatMatch[2];
|
|
240
|
+
// Count placeholders in the format string
|
|
241
|
+
const placeholders = (formatStr.match(/\{\}/g) || []).length;
|
|
242
|
+
// Count arguments more accurately
|
|
243
|
+
let argsCount = 0;
|
|
244
|
+
if (argsStr.trim()) {
|
|
245
|
+
// Split by comma, but be careful with nested structures
|
|
246
|
+
const args = argsStr.split(',').map(arg => arg.trim()).filter(arg => arg.length > 0);
|
|
247
|
+
argsCount = args.length;
|
|
248
|
+
}
|
|
249
|
+
if (argsCount > placeholders) {
|
|
250
|
+
lineErrors.push({
|
|
251
|
+
line: lineNumber,
|
|
252
|
+
error: 'Extra arguments in .format()',
|
|
253
|
+
suggestion: `Has ${argsCount} arguments but only ${placeholders} placeholder(s) {}`,
|
|
254
|
+
severity: 'error'
|
|
255
|
+
});
|
|
256
|
+
// Continue checking for other errors
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Priority 4: JavaScript-style comments in Python
|
|
260
|
+
// CRITICAL FIX: Check in original line (before comment removal) BUT avoid strings
|
|
261
|
+
// We want to detect: const x = 5; // JavaScript comment
|
|
262
|
+
// But not detect: url = "https://example.com"
|
|
263
|
+
if (line.includes('//') && !line.match(/["'].*\/\/.*["']/)) {
|
|
264
|
+
lineErrors.push({
|
|
265
|
+
line: lineNumber,
|
|
266
|
+
error: 'Python uses # for comments, not //',
|
|
267
|
+
suggestion: 'Change // to # at the beginning of the comment',
|
|
268
|
+
severity: 'error'
|
|
269
|
+
});
|
|
270
|
+
// Continue checking
|
|
271
|
+
}
|
|
272
|
+
// Priority 5: Missing colons in control structures
|
|
273
|
+
// CRITICAL FIX: Check lineWithoutComments (not trimmed) to avoid false positives from comments
|
|
274
|
+
// Example: def foo(): # comment <- This should NOT trigger error!
|
|
275
|
+
if ((trimmed.startsWith('if ') ||
|
|
276
|
+
trimmed.startsWith('for ') ||
|
|
277
|
+
trimmed.startsWith('while ') ||
|
|
278
|
+
trimmed.startsWith('def ') ||
|
|
279
|
+
trimmed.startsWith('class ') ||
|
|
280
|
+
trimmed.startsWith('try') ||
|
|
281
|
+
trimmed.startsWith('except') ||
|
|
282
|
+
trimmed.startsWith('finally') ||
|
|
283
|
+
trimmed.startsWith('with ') ||
|
|
284
|
+
trimmed.startsWith('elif ')) &&
|
|
285
|
+
!lineWithoutComments.trimEnd().endsWith(':')) {
|
|
286
|
+
lineErrors.push({
|
|
287
|
+
line: lineNumber,
|
|
288
|
+
error: 'Missing colon at the end of statement',
|
|
289
|
+
suggestion: `Add : at the end: ${lineWithoutComments.trimEnd()}:`,
|
|
290
|
+
severity: 'error'
|
|
291
|
+
});
|
|
292
|
+
// Continue checking
|
|
293
|
+
}
|
|
294
|
+
// Priority 6: Assignment in conditional (if x = 10: instead of if x == 10:)
|
|
295
|
+
// CRITICAL: Detect single = in if/while/elif but allow := (walrus operator)
|
|
296
|
+
// Use cleaned line to avoid false positives from comments
|
|
297
|
+
if ((trimmed.startsWith('if ') || trimmed.startsWith('elif ') || trimmed.startsWith('while ')) &&
|
|
298
|
+
lineWithoutComments.match(/\s+\w+\s*=\s*[^=]/) &&
|
|
299
|
+
!lineWithoutComments.includes(':=')) {
|
|
300
|
+
lineErrors.push({
|
|
301
|
+
line: lineNumber,
|
|
302
|
+
error: 'Assignment (=) in conditional statement - should use comparison (==)',
|
|
303
|
+
suggestion: 'Change = to == for comparison, or use := for assignment (walrus operator)',
|
|
304
|
+
severity: 'error'
|
|
305
|
+
});
|
|
306
|
+
// Continue checking
|
|
307
|
+
}
|
|
308
|
+
// Priority 6: Python 3 print syntax
|
|
309
|
+
// Use cleaned line to avoid false positives from comments
|
|
310
|
+
if (lineWithoutComments.match(/\bprint\s+[^(]/)) {
|
|
311
|
+
lineErrors.push({
|
|
312
|
+
line: lineNumber,
|
|
313
|
+
error: 'print in Python 3 requires parentheses',
|
|
314
|
+
suggestion: 'Use print("message") instead of print "message"',
|
|
315
|
+
severity: 'error'
|
|
316
|
+
});
|
|
317
|
+
// Continue checking
|
|
318
|
+
}
|
|
319
|
+
// Priority 7: Invalid escape sequences in strings
|
|
320
|
+
// Python only recognizes specific escape sequences: \n, \t, \r, \\, \', \", etc.
|
|
321
|
+
// Common error: "C:\new\folder" has invalid \n and \f (should be raw string or escaped)
|
|
322
|
+
// Use cleaned line to avoid false positives from comments
|
|
323
|
+
// Check for invalid escape sequences in double-quoted strings
|
|
324
|
+
const doubleQuotedMatches = lineWithoutComments.matchAll(/"([^"]*)"/g);
|
|
325
|
+
for (const match of doubleQuotedMatches) {
|
|
326
|
+
const stringContent = match[1];
|
|
327
|
+
// Check for invalid escape sequences (backslash followed by character that's not a valid escape)
|
|
328
|
+
// Valid escapes: \n, \t, \r, \\, \', \", \a, \b, \f, \v, \0, \x, \u, \N, and octal
|
|
329
|
+
const invalidEscapes = stringContent.match(/\\([^ntr\\'\"abfv0xuN0-7])/g);
|
|
330
|
+
if (invalidEscapes) {
|
|
331
|
+
lineErrors.push({
|
|
332
|
+
line: lineNumber,
|
|
333
|
+
error: `Invalid escape sequence(s): ${invalidEscapes.join(', ')}`,
|
|
334
|
+
suggestion: 'Use raw string (r"...") for paths or escape the backslash (\\\\)',
|
|
335
|
+
severity: 'error'
|
|
336
|
+
});
|
|
337
|
+
break; // Only report once per line
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Check for invalid escape sequences in single-quoted strings
|
|
341
|
+
const singleQuotedMatches = lineWithoutComments.matchAll(/'([^']*)'/g);
|
|
342
|
+
for (const match of singleQuotedMatches) {
|
|
343
|
+
const stringContent = match[1];
|
|
344
|
+
const invalidEscapes = stringContent.match(/\\([^ntr\\'\"abfv0xuN0-7])/g);
|
|
345
|
+
if (invalidEscapes) {
|
|
346
|
+
lineErrors.push({
|
|
347
|
+
line: lineNumber,
|
|
348
|
+
error: `Invalid escape sequence(s): ${invalidEscapes.join(', ')}`,
|
|
349
|
+
suggestion: 'Use raw string (r"...") for paths or escape the backslash (\\\\)',
|
|
350
|
+
severity: 'error'
|
|
351
|
+
});
|
|
352
|
+
break; // Only report once per line
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Priority 8: Malformed f-strings (missing opening quote)
|
|
356
|
+
// Pattern: print(f{...} or similar where f is followed by { without quotes
|
|
357
|
+
// Use cleaned line to avoid false positives from comments
|
|
358
|
+
if (lineWithoutComments.match(/\bf\s*\{[^}]*\}/)) {
|
|
359
|
+
lineErrors.push({
|
|
360
|
+
line: lineNumber,
|
|
361
|
+
error: 'Malformed f-string - missing quotes',
|
|
362
|
+
suggestion: 'f-strings must start with quotes: f"{...}" or f\'{...}\'',
|
|
363
|
+
severity: 'error'
|
|
364
|
+
});
|
|
365
|
+
// Continue checking
|
|
366
|
+
}
|
|
367
|
+
// Priority 9: Missing parentheses on method calls
|
|
368
|
+
// Pattern: .date > or .date < or .date = (should be .date())
|
|
369
|
+
// Use cleaned line to avoid false positives from comments
|
|
370
|
+
if (lineWithoutComments.match(/\.\w+\s*[><=!]/)) {
|
|
371
|
+
const match = lineWithoutComments.match(/\.(\w+)\s*([><=!])/);
|
|
372
|
+
if (match) {
|
|
373
|
+
const methodName = match[1];
|
|
374
|
+
// Common Python methods that require parentheses
|
|
375
|
+
const methodsRequiringParens = ['date', 'time', 'now', 'today', 'strip', 'lower', 'upper', 'count', 'copy'];
|
|
376
|
+
if (methodsRequiringParens.includes(methodName)) {
|
|
377
|
+
lineErrors.push({
|
|
378
|
+
line: lineNumber,
|
|
379
|
+
error: `Method .${methodName} requires parentheses`,
|
|
380
|
+
suggestion: `Add parentheses: .${methodName}()`,
|
|
381
|
+
severity: 'error'
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Continue checking
|
|
386
|
+
}
|
|
387
|
+
// Priority 10: Trailing comma in assignments (not in lists/tuples)
|
|
388
|
+
// Pattern: self.var = value, at end of line
|
|
389
|
+
// Use cleaned line to avoid false positives from comments
|
|
390
|
+
if (lineWithoutComments.match(/=\s*[^,\[\{]+,\s*$/) && !lineWithoutComments.includes('[') && !lineWithoutComments.includes('{')) {
|
|
391
|
+
lineErrors.push({
|
|
392
|
+
line: lineNumber,
|
|
393
|
+
error: 'Extra comma at the end of assignment',
|
|
394
|
+
suggestion: 'Remove the trailing comma (or were you trying to create a tuple?)',
|
|
395
|
+
severity: 'error'
|
|
396
|
+
});
|
|
397
|
+
// Continue checking
|
|
398
|
+
}
|
|
399
|
+
// Priority 11: Incomplete ternary operator (missing value after else)
|
|
400
|
+
// Pattern: var = "value" if condition else (missing value)
|
|
401
|
+
// Use cleaned line to avoid false positives from comments
|
|
402
|
+
if (lineWithoutComments.match(/=.*\bif\b.*\belse\s*$/)) {
|
|
403
|
+
lineErrors.push({
|
|
404
|
+
line: lineNumber,
|
|
405
|
+
error: 'Incomplete ternary operator - missing value after "else"',
|
|
406
|
+
suggestion: 'Complete: variable = value_if_true if condition else value_if_false',
|
|
407
|
+
severity: 'error'
|
|
408
|
+
});
|
|
409
|
+
// Continue checking
|
|
410
|
+
}
|
|
411
|
+
// Priority 12: AI Hallucination - .push() instead of .append()
|
|
412
|
+
// JavaScript method being used in Python
|
|
413
|
+
// Use cleaned line to avoid false positives from comments
|
|
414
|
+
if (lineWithoutComments.match(/\.\s*push\s*\(/)) {
|
|
415
|
+
lineErrors.push({
|
|
416
|
+
line: lineNumber,
|
|
417
|
+
error: 'Method .push() does not exist in Python - use .append()',
|
|
418
|
+
suggestion: 'In Python, lists use .append() instead of .push()',
|
|
419
|
+
severity: 'error'
|
|
420
|
+
});
|
|
421
|
+
// Continue checking
|
|
422
|
+
}
|
|
423
|
+
// Priority 13: Empty parentheses in class definition (not inheriting)
|
|
424
|
+
// Pattern: class ClassName(): (should be class ClassName:)
|
|
425
|
+
// Use cleaned line to avoid false positives from comments
|
|
426
|
+
if (lineWithoutComments.match(/^\s*class\s+\w+\s*\(\s*\)\s*:/)) {
|
|
427
|
+
lineErrors.push({
|
|
428
|
+
line: lineNumber,
|
|
429
|
+
error: 'Unnecessary empty parentheses in class definition',
|
|
430
|
+
suggestion: 'Remove empty parentheses: class ClassName: (or add base class if inheriting)',
|
|
431
|
+
severity: 'warning'
|
|
432
|
+
});
|
|
433
|
+
// Continue checking
|
|
434
|
+
}
|
|
435
|
+
// Priority 14: Check for missing commas in dictionaries
|
|
436
|
+
// Pattern: key: value followed by a new key (without comma)
|
|
437
|
+
// Example: 'age': int(record.get('age', 0))
|
|
438
|
+
// 'active': ... <- missing comma before 'active'
|
|
439
|
+
if (trimmed.match(/['"].*['"]\s*:\s*/) && !trimmed.includes('#')) {
|
|
440
|
+
// This line starts a dict key
|
|
441
|
+
const nextLineIndex = index + 1;
|
|
442
|
+
if (nextLineIndex < lines.length) {
|
|
443
|
+
const nextLine = lines[nextLineIndex].trim();
|
|
444
|
+
// Check if next line also has a dict key and current line doesn't end with comma or opening brace
|
|
445
|
+
if (nextLine.match(/^['"].*['"]\s*:/) &&
|
|
446
|
+
!trimmed.endsWith(',') &&
|
|
447
|
+
!trimmed.endsWith('{') &&
|
|
448
|
+
!nextLine.startsWith('}')) {
|
|
449
|
+
lineErrors.push({
|
|
450
|
+
line: lineNumber,
|
|
451
|
+
error: 'Missing comma in dictionary',
|
|
452
|
+
suggestion: 'Add comma at the end of this line before the next key',
|
|
453
|
+
severity: 'error'
|
|
454
|
+
});
|
|
455
|
+
// Continue checking
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// Priority 15: Check for trailing commas before comments
|
|
460
|
+
// Example: float(record['amount'], # comment
|
|
461
|
+
// CRITICAL FIX: Exclude list/tuple items (trailing commas are valid in Python)
|
|
462
|
+
// Valid: "item", # comment (in a list)
|
|
463
|
+
// Invalid: float(x, # missing closing paren
|
|
464
|
+
if (trimmed.match(/,\s*#/)) {
|
|
465
|
+
const beforeComment = trimmed.split('#')[0].trim();
|
|
466
|
+
// Check if this is a valid list/tuple/dict item with trailing comma
|
|
467
|
+
const isListItem = beforeComment.match(/^["'][^"']*["']\s*,\s*$/) || // String with trailing comma
|
|
468
|
+
beforeComment.match(/^[^=()]+,\s*$/); // Expression with trailing comma
|
|
469
|
+
// Check if line has unmatched opening bracket (likely a function call issue)
|
|
470
|
+
const hasOpenParen = trimmed.includes('(') && !trimmed.includes(')');
|
|
471
|
+
// Only flag if it's likely a function call with missing argument, not a list item
|
|
472
|
+
if (beforeComment.endsWith(',') && hasOpenParen && !isListItem) {
|
|
473
|
+
lineErrors.push({
|
|
474
|
+
line: lineNumber,
|
|
475
|
+
error: 'Extra comma before comment',
|
|
476
|
+
suggestion: 'Remove the extra comma or add another argument',
|
|
477
|
+
severity: 'error'
|
|
478
|
+
});
|
|
479
|
+
// Continue checking
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
// NEW: Check for missing imports (scan whole code)
|
|
484
|
+
this.checkMissingImports(code, lineErrors);
|
|
485
|
+
// NEW: Check for unclosed function/method calls across multiple lines
|
|
486
|
+
this.checkUnbalancedParentheses(code, lineErrors);
|
|
487
|
+
// Advanced detections (similar to JavaScript analyzer)
|
|
488
|
+
this.detectAIHallucinations(code, lineErrors);
|
|
489
|
+
this.detectIndentationErrors(code, lineErrors);
|
|
490
|
+
this.detectTypeErrors(code, lineErrors);
|
|
491
|
+
this.detectNameErrors(code, lineErrors);
|
|
492
|
+
this.detectAttributeErrors(code, lineErrors);
|
|
493
|
+
this.detectIndexErrors(code, lineErrors);
|
|
494
|
+
this.detectKeyErrors(code, lineErrors);
|
|
495
|
+
this.detectMutableDefaults(code, lineErrors);
|
|
496
|
+
this.detectLoopModification(code, lineErrors);
|
|
497
|
+
this.detectScopeIssues(code, lineErrors);
|
|
498
|
+
// Second pass: Bracket/parentheses checking (only if no critical errors found)
|
|
499
|
+
if (lineErrors.filter(e => e.severity === 'error').length === 0) {
|
|
500
|
+
this.checkBracketBalance(code, errors, lineErrors);
|
|
501
|
+
}
|
|
502
|
+
// Third pass: Indentation warnings (ALWAYS run - helps identify root cause of syntax errors)
|
|
503
|
+
lines.forEach((line, index) => {
|
|
504
|
+
const lineNumber = index + 1;
|
|
505
|
+
const trimmed = line.trim();
|
|
506
|
+
// Only check indentation for non-empty, non-comment lines that are indented
|
|
507
|
+
if (trimmed && !trimmed.startsWith('#') && line.startsWith(' ') && lineNumber > 1) {
|
|
508
|
+
const indentation = line.search(/\S/);
|
|
509
|
+
// Skip indentation check for definition lines
|
|
510
|
+
const isDefinitionLine = trimmed.startsWith('def ') || trimmed.startsWith('class ') ||
|
|
511
|
+
trimmed.startsWith('if ') || trimmed.startsWith('for ') ||
|
|
512
|
+
trimmed.startsWith('while ') || trimmed.startsWith('try:') ||
|
|
513
|
+
trimmed.startsWith('except') || trimmed.startsWith('finally');
|
|
514
|
+
if (!isDefinitionLine && indentation % 4 !== 0) {
|
|
515
|
+
lineErrors.push({
|
|
516
|
+
line: lineNumber,
|
|
517
|
+
error: `Incorrect indentation (${indentation} spaces)`,
|
|
518
|
+
suggestion: 'Use multiples of 4 spaces for indentation in Python',
|
|
519
|
+
severity: 'warning'
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
result.syntax.errors = errors;
|
|
525
|
+
result.syntax.lineErrors = lineErrors;
|
|
526
|
+
result.syntax.valid = errors.length === 0 && lineErrors.filter(e => e.severity === 'error').length === 0;
|
|
527
|
+
if (!result.syntax.valid || lineErrors.length > 0) {
|
|
528
|
+
result.quality.score -= lineErrors.length * 15;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
checkBracketBalance(code, errors, lineErrors) {
|
|
532
|
+
let parenBalance = 0;
|
|
533
|
+
let braceBalance = 0;
|
|
534
|
+
let bracketBalance = 0;
|
|
535
|
+
let inLineComment = false;
|
|
536
|
+
let inString = false;
|
|
537
|
+
let inMultilineString = false;
|
|
538
|
+
let stringChar = '';
|
|
539
|
+
let currentLine = 1;
|
|
540
|
+
// Track opening positions for better error reporting
|
|
541
|
+
const openBraces = [];
|
|
542
|
+
for (let i = 0; i < code.length; i++) {
|
|
543
|
+
const char = code[i];
|
|
544
|
+
const nextChar = code[i + 1];
|
|
545
|
+
const nextNextChar = code[i + 2];
|
|
546
|
+
// Track line numbers
|
|
547
|
+
if (char === '\n') {
|
|
548
|
+
currentLine++;
|
|
549
|
+
inLineComment = false; // End line comment
|
|
550
|
+
}
|
|
551
|
+
// Handle line comments
|
|
552
|
+
if (!inString && !inMultilineString && char === '#') {
|
|
553
|
+
inLineComment = true;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
// Skip if in comment
|
|
557
|
+
if (inLineComment)
|
|
558
|
+
continue;
|
|
559
|
+
// Handle multiline strings (triple quotes)
|
|
560
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
561
|
+
if (nextChar === char && nextNextChar === char) {
|
|
562
|
+
if (!inMultilineString) {
|
|
563
|
+
inMultilineString = true;
|
|
564
|
+
stringChar = char;
|
|
565
|
+
}
|
|
566
|
+
else if (char === stringChar) {
|
|
567
|
+
inMultilineString = false;
|
|
568
|
+
stringChar = '';
|
|
569
|
+
}
|
|
570
|
+
i += 2; // Skip next two chars
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
// Handle single line strings
|
|
575
|
+
if (!inMultilineString && (char === '"' || char === "'")) {
|
|
576
|
+
if (!inString) {
|
|
577
|
+
inString = true;
|
|
578
|
+
stringChar = char;
|
|
579
|
+
}
|
|
580
|
+
else if (char === stringChar && code[i - 1] !== '\\') {
|
|
581
|
+
inString = false;
|
|
582
|
+
stringChar = '';
|
|
583
|
+
}
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
// Only count brackets outside comments and strings
|
|
587
|
+
if (!inLineComment && !inString && !inMultilineString) {
|
|
588
|
+
if (char === '(') {
|
|
589
|
+
parenBalance++;
|
|
590
|
+
openBraces.push({ line: currentLine, char: '(' });
|
|
591
|
+
}
|
|
592
|
+
else if (char === ')') {
|
|
593
|
+
parenBalance--;
|
|
594
|
+
// Find matching opening parenthesis
|
|
595
|
+
for (let j = openBraces.length - 1; j >= 0; j--) {
|
|
596
|
+
if (openBraces[j].char === '(') {
|
|
597
|
+
openBraces.splice(j, 1);
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
else if (char === '{') {
|
|
603
|
+
braceBalance++;
|
|
604
|
+
openBraces.push({ line: currentLine, char: '{' });
|
|
605
|
+
}
|
|
606
|
+
else if (char === '}') {
|
|
607
|
+
braceBalance--;
|
|
608
|
+
// Find matching opening brace
|
|
609
|
+
for (let j = openBraces.length - 1; j >= 0; j--) {
|
|
610
|
+
if (openBraces[j].char === '{') {
|
|
611
|
+
openBraces.splice(j, 1);
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
else if (char === '[') {
|
|
617
|
+
bracketBalance++;
|
|
618
|
+
openBraces.push({ line: currentLine, char: '[' });
|
|
619
|
+
}
|
|
620
|
+
else if (char === ']') {
|
|
621
|
+
bracketBalance--;
|
|
622
|
+
// Find matching opening bracket
|
|
623
|
+
for (let j = openBraces.length - 1; j >= 0; j--) {
|
|
624
|
+
if (openBraces[j].char === '[') {
|
|
625
|
+
openBraces.splice(j, 1);
|
|
626
|
+
break;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
// Create line-specific errors for unmatched brackets
|
|
633
|
+
openBraces.forEach(brace => {
|
|
634
|
+
const closingChar = brace.char === '{' ? '}' : brace.char === '(' ? ')' : ']';
|
|
635
|
+
const typeName = brace.char === '{' ? 'brace' : brace.char === '(' ? 'parenthesis' : 'bracket';
|
|
636
|
+
lineErrors.push({
|
|
637
|
+
line: brace.line,
|
|
638
|
+
error: `${typeName.charAt(0).toUpperCase() + typeName.slice(1)} "${brace.char}" was not closed`,
|
|
639
|
+
suggestion: `Add "${closingChar}" to close the ${typeName} opened on line ${brace.line}`,
|
|
640
|
+
severity: 'error'
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
// DON'T add general balance errors - the code above already adds specific line errors
|
|
644
|
+
// with proper line numbers, enabling Auto-Fix functionality
|
|
645
|
+
}
|
|
646
|
+
analyzeQuality(code, result) {
|
|
647
|
+
const issues = [];
|
|
648
|
+
// Check for snake_case variable names
|
|
649
|
+
const camelCaseVars = code.match(/\b[a-z]+[A-Z][a-zA-Z]*\s*=/g);
|
|
650
|
+
if (camelCaseVars) {
|
|
651
|
+
issues.push({
|
|
652
|
+
type: 'warning',
|
|
653
|
+
message: 'Use snake_case for variable names in Python',
|
|
654
|
+
severity: 'medium'
|
|
655
|
+
});
|
|
656
|
+
result.quality.score -= 5;
|
|
657
|
+
}
|
|
658
|
+
// Check for unused imports
|
|
659
|
+
const imports = code.match(/import\s+(\w+)/g);
|
|
660
|
+
if (imports) {
|
|
661
|
+
imports.forEach(imp => {
|
|
662
|
+
const moduleName = imp.split(' ')[1];
|
|
663
|
+
if (!code.includes(moduleName + '.') && !code.includes(moduleName + '(')) {
|
|
664
|
+
issues.push({
|
|
665
|
+
type: 'warning',
|
|
666
|
+
message: `Import '${moduleName}' is unused`,
|
|
667
|
+
severity: 'low'
|
|
668
|
+
});
|
|
669
|
+
result.quality.score -= 3;
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
// Check for lines that are too long (PEP 8)
|
|
674
|
+
const lines = code.split('\n');
|
|
675
|
+
lines.forEach((line, index) => {
|
|
676
|
+
if (line.length > 79) {
|
|
677
|
+
issues.push({
|
|
678
|
+
type: 'info',
|
|
679
|
+
message: `Line ${index + 1} exceeds 79 characters (PEP 8)`,
|
|
680
|
+
line: index + 1,
|
|
681
|
+
severity: 'low'
|
|
682
|
+
});
|
|
683
|
+
result.quality.score -= 1;
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
// Check for multiple statements on one line
|
|
687
|
+
const multipleStatements = code.match(/;[^#\n]*\w/g);
|
|
688
|
+
if (multipleStatements) {
|
|
689
|
+
issues.push({
|
|
690
|
+
type: 'warning',
|
|
691
|
+
message: 'Avoid multiple statements on one line (PEP 8)',
|
|
692
|
+
severity: 'medium'
|
|
693
|
+
});
|
|
694
|
+
result.quality.score -= 5;
|
|
695
|
+
}
|
|
696
|
+
result.quality.issues = issues;
|
|
697
|
+
result.quality.score = Math.max(0, result.quality.score);
|
|
698
|
+
}
|
|
699
|
+
analyzePerformance(code, result) {
|
|
700
|
+
const suggestions = [];
|
|
701
|
+
// Check for loops with range(len())
|
|
702
|
+
if (code.includes('range(len(')) {
|
|
703
|
+
suggestions.push('Use enumerate() instead of range(len())');
|
|
704
|
+
result.performance.score -= 8;
|
|
705
|
+
}
|
|
706
|
+
// Check for string concatenation in loops
|
|
707
|
+
if (code.match(/for.*:[\s\S]*?\+\s*=.*str/)) {
|
|
708
|
+
suggestions.push('Use join() instead of concatenation in loops');
|
|
709
|
+
result.performance.score -= 12;
|
|
710
|
+
}
|
|
711
|
+
// Check for append usage in loops to create lists
|
|
712
|
+
if (code.match(/for.*:[\s\S]*?\.append\(/)) {
|
|
713
|
+
suggestions.push('Consider using list comprehension for better performance');
|
|
714
|
+
result.performance.score -= 5;
|
|
715
|
+
}
|
|
716
|
+
// Check for global usage
|
|
717
|
+
if (code.includes('global ')) {
|
|
718
|
+
suggestions.push('Avoid excessive use of global variables');
|
|
719
|
+
result.performance.score -= 8;
|
|
720
|
+
}
|
|
721
|
+
result.performance.suggestions = suggestions;
|
|
722
|
+
result.performance.score = Math.max(0, result.performance.score);
|
|
723
|
+
}
|
|
724
|
+
createSecurityVulnerability(vulnerabilityType, message, suggestion, lineNumber, attackDescription, exploitExample, realWorldImpact, remediationBefore, remediationAfter, remediationExplanation) {
|
|
725
|
+
const scoring = (0, severity_scoring_1.calculateSeverityScore)(vulnerabilityType);
|
|
726
|
+
const compliance = (0, compliance_mapping_1.getComplianceMapping)(vulnerabilityType);
|
|
727
|
+
return {
|
|
728
|
+
severity: scoring.severity,
|
|
729
|
+
message,
|
|
730
|
+
suggestion,
|
|
731
|
+
line: lineNumber,
|
|
732
|
+
cvssScore: scoring.cvssScore,
|
|
733
|
+
exploitLikelihood: scoring.exploitLikelihood,
|
|
734
|
+
impact: scoring.impact,
|
|
735
|
+
owasp: compliance.owasp,
|
|
736
|
+
cwe: compliance.cwe,
|
|
737
|
+
pciDss: compliance.pciDss,
|
|
738
|
+
attackVector: {
|
|
739
|
+
description: attackDescription,
|
|
740
|
+
exploitExample,
|
|
741
|
+
realWorldImpact
|
|
742
|
+
},
|
|
743
|
+
remediation: {
|
|
744
|
+
before: remediationBefore,
|
|
745
|
+
after: remediationAfter,
|
|
746
|
+
explanation: remediationExplanation
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
analyzeSecurity(code, result) {
|
|
751
|
+
const vulnerabilities = [];
|
|
752
|
+
const lines = code.split('\n');
|
|
753
|
+
// PHASE 6 ENHANCEMENT (2025-11-21): Data flow analysis for SQL injection
|
|
754
|
+
// Track variables assigned with unsafe SQL string formatting
|
|
755
|
+
const unsafeSqlVariables = new Map(); // variable name -> line number
|
|
756
|
+
// PHASE 6 FIX (2025-11-22): Data flow analysis for Markup() XSS
|
|
757
|
+
// Track variables assigned from user input (request.args, request.form, etc.)
|
|
758
|
+
const userInputVariables = new Map(); // variable name -> line number
|
|
759
|
+
// Async/await context tracking
|
|
760
|
+
let inAsyncContext = false;
|
|
761
|
+
let asyncFunctionIndent = 0;
|
|
762
|
+
// =============================================================================
|
|
763
|
+
// ASYNC/AWAIT CHECKS (Using Modular Security Checks)
|
|
764
|
+
// =============================================================================
|
|
765
|
+
// Helper functions extracted to: ./security-checks/python-async-security.ts
|
|
766
|
+
// This improves maintainability and follows the same pattern as JavaScript analyzer
|
|
767
|
+
// First pass: Identify unsafe SQL query construction and user input variables
|
|
768
|
+
lines.forEach((line, index) => {
|
|
769
|
+
const lineNumber = index + 1;
|
|
770
|
+
const trimmed = line.trim();
|
|
771
|
+
// Skip comments and empty lines
|
|
772
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
773
|
+
return;
|
|
774
|
+
// Track user input variables: variable = request.args.get(...) or similar
|
|
775
|
+
// Patterns:
|
|
776
|
+
// - request.args.get('key'), request.form.get('field')
|
|
777
|
+
// - request.json(), request.get_json()
|
|
778
|
+
// - request.args['key'], request.form['field']
|
|
779
|
+
const userInputMatch = trimmed.match(/^(\w+)\s*=\s*request\.(args|form|data|json|values|params|get_json|cookies)(?:\.get\([^)]*\)|\[[^\]]+\]|\(\))?/);
|
|
780
|
+
if (userInputMatch) {
|
|
781
|
+
const variableName = userInputMatch[1];
|
|
782
|
+
userInputVariables.set(variableName, lineNumber);
|
|
783
|
+
}
|
|
784
|
+
// Detect SQL string formatting: variable = "SQL..." with formatting
|
|
785
|
+
// Patterns: %, .format(), f-strings, string concatenation with +
|
|
786
|
+
const hasSqlKeywords = /\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|UNION)\b/i.test(trimmed);
|
|
787
|
+
const hasStringFormatting = trimmed.includes('%') || // % formatting
|
|
788
|
+
trimmed.includes('.format(') || // .format() method
|
|
789
|
+
/f["']/.test(trimmed) || // f-strings
|
|
790
|
+
/["'][^"']*\+/.test(trimmed); // string concatenation with +
|
|
791
|
+
// Check for variable assignment: variable_name = "SQL query"
|
|
792
|
+
const assignmentMatch = trimmed.match(/^(\w+)\s*=\s*["'f]/);
|
|
793
|
+
if (assignmentMatch && hasSqlKeywords && hasStringFormatting) {
|
|
794
|
+
const variableName = assignmentMatch[1];
|
|
795
|
+
unsafeSqlVariables.set(variableName, lineNumber);
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
// =============================================================================
|
|
799
|
+
// MODULAR SECURITY CHECKS (Refactored 2025-12-01)
|
|
800
|
+
// =============================================================================
|
|
801
|
+
// Checks #1-31: Extracted to dedicated modules for better maintainability
|
|
802
|
+
// 2,547 lines → 8 focused modules (~200-300 lines each)
|
|
803
|
+
// Injection Attacks (Checks #1-6): eval, exec, compile, SQL injection, command injection
|
|
804
|
+
vulnerabilities.push(...(0, injection_attacks_1.checkInjectionAttacks)(lines, unsafeSqlVariables));
|
|
805
|
+
// Credentials & Crypto (Checks #7-8): Hardcoded credentials, weak crypto, random module
|
|
806
|
+
vulnerabilities.push(...(0, credentials_crypto_1.checkCredentialsAndCrypto)(lines));
|
|
807
|
+
// Deserialization (Checks #9-10): pickle.load(), yaml.load() without SafeLoader
|
|
808
|
+
vulnerabilities.push(...(0, deserialization_1.checkDeserialization)(lines));
|
|
809
|
+
// Web Security (Checks #11-12): Path traversal, XSS
|
|
810
|
+
vulnerabilities.push(...(0, web_security_1.checkWebSecurity)(lines, userInputVariables));
|
|
811
|
+
// Code Quality (Checks #13-21): assert, input(), ReDoS, tempfile.mktemp, imports
|
|
812
|
+
vulnerabilities.push(...(0, code_quality_1.checkCodeQuality)(lines));
|
|
813
|
+
// Django Framework (Checks #22-27): CSRF, DEBUG, mark_safe, ORM, login_required, SECRET_KEY
|
|
814
|
+
vulnerabilities.push(...(0, django_security_1.checkDjangoSecurity)(lines, unsafeSqlVariables));
|
|
815
|
+
// Flask Framework (Checks #28-31): Debug mode, CSRF, SSTI, Markup, SECRET_KEY
|
|
816
|
+
vulnerabilities.push(...(0, flask_security_1.checkFlaskSecurity)(lines, userInputVariables));
|
|
817
|
+
// OWASP A02:2025 - Security Misconfiguration (NEW - Phase 7B)
|
|
818
|
+
// Security Misconfiguration (Checks #32-39): Django/Flask debug, AWS credentials, database config
|
|
819
|
+
vulnerabilities.push(...(0, security_misconfiguration_1.checkSecurityMisconfiguration)(lines));
|
|
820
|
+
// OWASP A10:2025 - Mishandling of Exceptional Conditions (NEW - Phase 7B)
|
|
821
|
+
// Exception Handling (Checks #40-44): Bare except, exposed details, silent suppression, resource cleanup
|
|
822
|
+
vulnerabilities.push(...(0, exception_handling_1.checkExceptionHandling)(lines));
|
|
823
|
+
// OWASP A03:2025 - Software Supply Chain Failures (Enhanced - Phase 7B)
|
|
824
|
+
// Enhanced Supply Chain (Checks #45-49): Dynamic imports, runtime installation, typosquatting, untrusted sources
|
|
825
|
+
vulnerabilities.push(...(0, enhanced_supply_chain_1.checkEnhancedSupplyChain)(lines));
|
|
826
|
+
// OWASP A01:2025 - Broken Access Control (NEW - Phase 7B Day 3)
|
|
827
|
+
// Access Control (Checks #50-51): Missing authentication decorators, IDOR
|
|
828
|
+
vulnerabilities.push(...(0, access_control_1.checkAccessControl)(lines));
|
|
829
|
+
// OWASP A04:2025 - Cryptographic Failures (NEW - Phase 7B Day 3)
|
|
830
|
+
// Crypto Failures (Checks #52-53): Weak algorithms (MD5/SHA1/DES), insecure random
|
|
831
|
+
vulnerabilities.push(...(0, crypto_failures_1.checkCryptoFailures)(lines));
|
|
832
|
+
// OWASP A06:2025 - Insecure Design (NEW - Phase 7B Day 4)
|
|
833
|
+
// Insecure Design (Checks #54-55): Missing rate limiting, mass assignment
|
|
834
|
+
vulnerabilities.push(...(0, insecure_design_1.checkInsecureDesign)(lines));
|
|
835
|
+
// OWASP A09:2025 - Logging Failures (NEW - Phase 7B Day 4)
|
|
836
|
+
// Logging Failures (Checks #56-57): Missing security logging, sensitive data logging
|
|
837
|
+
vulnerabilities.push(...(0, logging_failures_1.checkLoggingFailures)(lines));
|
|
838
|
+
// OWASP A08:2025 - Software and Data Integrity Failures (NEW - Phase 7B Day 5)
|
|
839
|
+
// Data Integrity (Check #58): Insecure deserialization (pickle)
|
|
840
|
+
vulnerabilities.push(...(0, data_integrity_1.checkDataIntegrity)(lines));
|
|
841
|
+
// OWASP A03:2021 - Injection (NoSQL) - Phase 0 Priority 0 (CRITICAL)
|
|
842
|
+
// NoSQL Injection (Checks #35-39): MongoDB, Cassandra, Redis, $where JavaScript injection
|
|
843
|
+
vulnerabilities.push(...(0, nosql_injection_1.checkNoSQLInjection)(lines, userInputVariables));
|
|
844
|
+
// OWASP A10:2021 - Server-Side Request Forgery - Phase 0 Priority 0 (CRITICAL)
|
|
845
|
+
// SSRF (Checks #40-44): requests, urllib, httplib, URL validation, internal IPs
|
|
846
|
+
vulnerabilities.push(...(0, ssrf_detection_1.checkSSRF)(lines, userInputVariables));
|
|
847
|
+
// OWASP A07:2021 - Identification and Authentication Failures - Phase 0 Priority 0 (CRITICAL)
|
|
848
|
+
// Authentication Logic Flaws (Checks #45-48): Plaintext passwords, weak tokens, master password backdoors, fail-open authorization
|
|
849
|
+
vulnerabilities.push(...(0, authentication_flaws_1.checkAuthenticationFlaws)(lines));
|
|
850
|
+
// =============================================================================
|
|
851
|
+
// ASYNC/AWAIT CHECKS (Modular - Using PythonAsyncSecurity helpers)
|
|
852
|
+
// =============================================================================
|
|
853
|
+
lines.forEach((line, index) => {
|
|
854
|
+
const lineNumber = index + 1;
|
|
855
|
+
const trimmed = line.trim();
|
|
856
|
+
// Skip comments and empty lines
|
|
857
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
// =============================================================================
|
|
861
|
+
// NOTE: Legacy inline checks #1-31 were REMOVED on Dec 7, 2025
|
|
862
|
+
// All security checks are now handled by modular functions (lines 848-878)
|
|
863
|
+
// This eliminated 1107 lines of duplicate code
|
|
864
|
+
// =============================================================================
|
|
865
|
+
// =============================================================================
|
|
866
|
+
// ASYNC/AWAIT CHECKS (Using Modular Helpers)
|
|
867
|
+
// =============================================================================
|
|
868
|
+
// Track async function context
|
|
869
|
+
if (PythonAsyncSecurity.isAsyncFunctionStart(line)) {
|
|
870
|
+
inAsyncContext = true;
|
|
871
|
+
// CRITICAL FIX: Track indent of FUNCTION BODY (not the def line)
|
|
872
|
+
// Function body is typically +4 spaces from def line
|
|
873
|
+
asyncFunctionIndent = (line.length - line.trimStart().length) + 4;
|
|
874
|
+
}
|
|
875
|
+
// Check for async/await issues
|
|
876
|
+
const missingAwaitVuln = PythonAsyncSecurity.detectMissingAwait(line, lineNumber, inAsyncContext, this.createSecurityVulnerability.bind(this));
|
|
877
|
+
if (missingAwaitVuln) {
|
|
878
|
+
vulnerabilities.push(missingAwaitVuln);
|
|
879
|
+
}
|
|
880
|
+
const asyncioRunVuln = PythonAsyncSecurity.detectAsyncioRunMisuse(line, lineNumber, inAsyncContext, this.createSecurityVulnerability.bind(this));
|
|
881
|
+
if (asyncioRunVuln) {
|
|
882
|
+
vulnerabilities.push(asyncioRunVuln);
|
|
883
|
+
}
|
|
884
|
+
// End async context when dedenting back to module level
|
|
885
|
+
if (inAsyncContext && PythonAsyncSecurity.isAsyncFunctionEnd(line, asyncFunctionIndent)) {
|
|
886
|
+
inAsyncContext = false;
|
|
887
|
+
}
|
|
888
|
+
// =============================================================================
|
|
889
|
+
// END ASYNC/AWAIT CHECKS
|
|
890
|
+
// =============================================================================
|
|
891
|
+
});
|
|
892
|
+
// Secrets Detection (Phase 1.5, Week 1)
|
|
893
|
+
const secretsAnalyzer = (0, secrets_analyzer_1.createSecretsAnalyzer)();
|
|
894
|
+
vulnerabilities.push(...secretsAnalyzer.analyzeCode(code, 'unknown.py', 'python'));
|
|
895
|
+
// =============================================================================
|
|
896
|
+
// DEDUPLICATION: Remove duplicate vulnerabilities on same line
|
|
897
|
+
// =============================================================================
|
|
898
|
+
// Fix for Beta Testing Issue: Line 48 reported twice (hardcoded credentials)
|
|
899
|
+
// Multiple checks can flag the same line (e.g., credentials-crypto.ts + security-misconfiguration.ts)
|
|
900
|
+
// Solution: Keep only the vulnerability with highest CVSS score per line
|
|
901
|
+
result.security.vulnerabilities = this.deduplicateVulnerabilities(vulnerabilities);
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Deduplicate vulnerabilities by line number
|
|
905
|
+
* When multiple checks flag the same line, keep only the one with highest CVSS score
|
|
906
|
+
*
|
|
907
|
+
* Common duplicates:
|
|
908
|
+
* - Hardcoded credentials detected by both credentials-crypto.ts and security-misconfiguration.ts
|
|
909
|
+
* - Flask debug mode detected by both flask-security.ts and security-misconfiguration.ts
|
|
910
|
+
* - Random module detected by credentials-crypto.ts, crypto-failures.ts, and authentication-flaws.ts
|
|
911
|
+
* - Pickle detected by deserialization.ts and data-integrity.ts
|
|
912
|
+
* - MD5 detected by crypto-failures.ts and credentials-crypto.ts
|
|
913
|
+
*/
|
|
914
|
+
deduplicateVulnerabilities(vulnerabilities) {
|
|
915
|
+
// Define groups of related vulnerability types that should be deduplicated
|
|
916
|
+
// When multiple vulnerabilities from the same group appear on the same line, keep only the highest CVSS
|
|
917
|
+
const similarityGroups = [
|
|
918
|
+
// Weak random/crypto randomness (all detect random.random(), random.randint(), etc.)
|
|
919
|
+
['weak-random', 'insecure-random', 'insecure-random-number-generator', 'weak-token-generation'],
|
|
920
|
+
// Pickle deserialization (both detect pickle.load/loads)
|
|
921
|
+
['unsafe-pickle', 'insecure-pickle', 'pickle-deserialization', 'insecure-deserialization'],
|
|
922
|
+
// Weak hashing (MD5, SHA1)
|
|
923
|
+
['weak-crypto', 'weak-crypto-algorithm', 'weak-hash-md5', 'weak-hash-sha1', 'md5-sha1-password-hashing', 'weak-cryptographic-hash'],
|
|
924
|
+
// Hardcoded credentials
|
|
925
|
+
['hardcoded-credentials', 'flask-weak-secret-key', 'django-weak-secret-key'],
|
|
926
|
+
// Debug mode
|
|
927
|
+
['flask-debug-mode', 'django-debug-mode'],
|
|
928
|
+
];
|
|
929
|
+
const lineMap = new Map();
|
|
930
|
+
// Group vulnerabilities by line number
|
|
931
|
+
vulnerabilities.forEach(vuln => {
|
|
932
|
+
if (!vuln.line)
|
|
933
|
+
return;
|
|
934
|
+
const existing = lineMap.get(vuln.line) || [];
|
|
935
|
+
existing.push(vuln);
|
|
936
|
+
lineMap.set(vuln.line, existing);
|
|
937
|
+
});
|
|
938
|
+
const deduplicated = [];
|
|
939
|
+
// For each line, check if there are duplicates
|
|
940
|
+
lineMap.forEach((vulns, line) => {
|
|
941
|
+
if (vulns.length === 1) {
|
|
942
|
+
// No duplicates, keep as-is
|
|
943
|
+
deduplicated.push(vulns[0]);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
// Multiple vulnerabilities on same line - group by similarity
|
|
947
|
+
const processed = new Set();
|
|
948
|
+
vulns.forEach(vuln => {
|
|
949
|
+
if (processed.has(vuln))
|
|
950
|
+
return;
|
|
951
|
+
const category = vuln.category || vuln.message || 'unknown';
|
|
952
|
+
// Find which similarity group this vulnerability belongs to
|
|
953
|
+
const similarityGroup = similarityGroups.find(group => group.some(cat => category.toLowerCase().includes(cat.toLowerCase())));
|
|
954
|
+
if (similarityGroup) {
|
|
955
|
+
// Find all vulnerabilities in this line that belong to the same similarity group
|
|
956
|
+
const relatedVulns = vulns.filter(v => {
|
|
957
|
+
const vCat = v.category || v.message || 'unknown';
|
|
958
|
+
return similarityGroup.some(cat => vCat.toLowerCase().includes(cat.toLowerCase()));
|
|
959
|
+
});
|
|
960
|
+
if (relatedVulns.length > 1) {
|
|
961
|
+
// Multiple related vulnerabilities - keep only the highest CVSS
|
|
962
|
+
const highest = relatedVulns.reduce((prev, current) => (current.cvssScore || 0) > (prev.cvssScore || 0) ? current : prev);
|
|
963
|
+
deduplicated.push(highest);
|
|
964
|
+
relatedVulns.forEach(v => processed.add(v));
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
// Only one vulnerability in this group
|
|
968
|
+
deduplicated.push(vuln);
|
|
969
|
+
processed.add(vuln);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
else {
|
|
973
|
+
// Not in any similarity group - keep as unique vulnerability
|
|
974
|
+
deduplicated.push(vuln);
|
|
975
|
+
processed.add(vuln);
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
return; // Skip the old message-based deduplication logic below
|
|
979
|
+
// OLD LOGIC (now skipped by return above):
|
|
980
|
+
// Group by similarity
|
|
981
|
+
const credentialVulns = vulns.filter(v => v.message?.toLowerCase().includes('credential') ||
|
|
982
|
+
v.message?.toLowerCase().includes('password') ||
|
|
983
|
+
v.message?.toLowerCase().includes('secret') ||
|
|
984
|
+
v.message?.toLowerCase().includes('api') && v.message?.toLowerCase().includes('key'));
|
|
985
|
+
const debugVulns = vulns.filter(v => v.message?.toLowerCase().includes('debug') ||
|
|
986
|
+
v.message?.toLowerCase().includes('flask') && v.message?.toLowerCase().includes('debug'));
|
|
987
|
+
// Deduplicate credential-related vulnerabilities (keep highest CVSS)
|
|
988
|
+
if (credentialVulns.length > 1) {
|
|
989
|
+
const highest = credentialVulns.reduce((prev, current) => (current.cvssScore || 0) > (prev.cvssScore || 0) ? current : prev);
|
|
990
|
+
deduplicated.push(highest);
|
|
991
|
+
// Add any non-credential vulnerabilities from this line
|
|
992
|
+
vulns.forEach(v => {
|
|
993
|
+
if (!credentialVulns.includes(v)) {
|
|
994
|
+
deduplicated.push(v);
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
// Deduplicate debug-related vulnerabilities (keep highest CVSS)
|
|
999
|
+
else if (debugVulns.length > 1) {
|
|
1000
|
+
const highest = debugVulns.reduce((prev, current) => (current.cvssScore || 0) > (prev.cvssScore || 0) ? current : prev);
|
|
1001
|
+
deduplicated.push(highest);
|
|
1002
|
+
// Add any non-debug vulnerabilities from this line
|
|
1003
|
+
vulns.forEach(v => {
|
|
1004
|
+
if (!debugVulns.includes(v)) {
|
|
1005
|
+
deduplicated.push(v);
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
// No similar duplicates, keep all
|
|
1010
|
+
else {
|
|
1011
|
+
deduplicated.push(...vulns);
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
return deduplicated;
|
|
1015
|
+
}
|
|
1016
|
+
calculateMetrics(code, result) {
|
|
1017
|
+
const lines = code.split('\n');
|
|
1018
|
+
result.metrics.lines = lines.length;
|
|
1019
|
+
const functions = (code.match(/def\s+\w+/g) || []).length;
|
|
1020
|
+
result.metrics.functions = functions;
|
|
1021
|
+
// Calcular complexidade ciclomática - Python
|
|
1022
|
+
let complexity = 1;
|
|
1023
|
+
const pythonKeywords = ['if', 'elif', 'for', 'while', 'except', 'and', 'or'];
|
|
1024
|
+
pythonKeywords.forEach(keyword => {
|
|
1025
|
+
const matches = code.match(new RegExp(`\\b${keyword}\\b`, 'g'));
|
|
1026
|
+
if (matches)
|
|
1027
|
+
complexity += matches.length;
|
|
1028
|
+
});
|
|
1029
|
+
result.metrics.complexity = complexity;
|
|
1030
|
+
result.metrics.maintainability = Math.max(0, 100 - complexity * 3);
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Check for missing imports - detects usage of common modules without imports
|
|
1034
|
+
*/
|
|
1035
|
+
checkMissingImports(code, lineErrors) {
|
|
1036
|
+
const lines = code.split('\n');
|
|
1037
|
+
const imports = new Set();
|
|
1038
|
+
const usages = new Map();
|
|
1039
|
+
// Common Python modules/functions that require imports
|
|
1040
|
+
const requiresImport = {
|
|
1041
|
+
'defaultdict': 'from collections import defaultdict',
|
|
1042
|
+
'Counter': 'from collections import Counter',
|
|
1043
|
+
'OrderedDict': 'from collections import OrderedDict',
|
|
1044
|
+
'datetime': 'from datetime import datetime',
|
|
1045
|
+
'timedelta': 'from datetime import timedelta',
|
|
1046
|
+
'json': 'import json',
|
|
1047
|
+
'os': 'import os',
|
|
1048
|
+
're': 'import re',
|
|
1049
|
+
'sys': 'import sys',
|
|
1050
|
+
'math': 'import math',
|
|
1051
|
+
'random': 'import random',
|
|
1052
|
+
'pandas': 'import pandas as pd',
|
|
1053
|
+
'numpy': 'import numpy as np'
|
|
1054
|
+
};
|
|
1055
|
+
// First pass: collect all imports
|
|
1056
|
+
lines.forEach((line) => {
|
|
1057
|
+
const trimmed = line.trim();
|
|
1058
|
+
if (trimmed.startsWith('import ') || trimmed.startsWith('from ')) {
|
|
1059
|
+
// Extract module names from imports
|
|
1060
|
+
Object.keys(requiresImport).forEach(moduleName => {
|
|
1061
|
+
if (trimmed.includes(moduleName)) {
|
|
1062
|
+
imports.add(moduleName);
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
// Second pass: find usages
|
|
1068
|
+
lines.forEach((line, index) => {
|
|
1069
|
+
const trimmed = line.trim();
|
|
1070
|
+
if (trimmed.startsWith('#') || trimmed.startsWith('import ') || trimmed.startsWith('from ')) {
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
Object.keys(requiresImport).forEach(moduleName => {
|
|
1074
|
+
// Check for usage (function call, class instantiation, or attribute access)
|
|
1075
|
+
const usagePattern = new RegExp(`\\b${moduleName}\\s*[\\(\\.]`);
|
|
1076
|
+
if (usagePattern.test(line) && !imports.has(moduleName)) {
|
|
1077
|
+
if (!usages.has(moduleName)) {
|
|
1078
|
+
usages.set(moduleName, []);
|
|
1079
|
+
}
|
|
1080
|
+
usages.get(moduleName)?.push(index + 1);
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
});
|
|
1084
|
+
// Report missing imports
|
|
1085
|
+
usages.forEach((lineNumbers, moduleName) => {
|
|
1086
|
+
if (!imports.has(moduleName)) {
|
|
1087
|
+
lineErrors.push({
|
|
1088
|
+
line: lineNumbers[0],
|
|
1089
|
+
error: `'${moduleName}' used without import`,
|
|
1090
|
+
suggestion: `Add: ${requiresImport[moduleName]}`,
|
|
1091
|
+
severity: 'error'
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Check for unbalanced parentheses in function/method calls
|
|
1098
|
+
*/
|
|
1099
|
+
checkUnbalancedParentheses(code, lineErrors) {
|
|
1100
|
+
const lines = code.split('\n');
|
|
1101
|
+
let parenBalance = 0;
|
|
1102
|
+
const unclosedLines = [];
|
|
1103
|
+
let inString = false;
|
|
1104
|
+
let stringChar = '';
|
|
1105
|
+
lines.forEach((line, index) => {
|
|
1106
|
+
const lineNumber = index + 1;
|
|
1107
|
+
const trimmed = line.trim();
|
|
1108
|
+
// Skip comments and empty lines
|
|
1109
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
1110
|
+
return;
|
|
1111
|
+
let lineOpenCount = 0;
|
|
1112
|
+
let lineCloseCount = 0;
|
|
1113
|
+
// Simple string detection (not perfect but catches most cases)
|
|
1114
|
+
for (let i = 0; i < line.length; i++) {
|
|
1115
|
+
const char = line[i];
|
|
1116
|
+
// Toggle string state
|
|
1117
|
+
if ((char === '"' || char === "'") && (i === 0 || line[i - 1] !== '\\')) {
|
|
1118
|
+
if (!inString) {
|
|
1119
|
+
inString = true;
|
|
1120
|
+
stringChar = char;
|
|
1121
|
+
}
|
|
1122
|
+
else if (char === stringChar) {
|
|
1123
|
+
inString = false;
|
|
1124
|
+
stringChar = '';
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
// Count parentheses outside of strings
|
|
1128
|
+
if (!inString) {
|
|
1129
|
+
if (char === '(') {
|
|
1130
|
+
lineOpenCount++;
|
|
1131
|
+
parenBalance++;
|
|
1132
|
+
}
|
|
1133
|
+
else if (char === ')') {
|
|
1134
|
+
lineCloseCount++;
|
|
1135
|
+
parenBalance--;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
// Track lines that open parentheses without closing them
|
|
1140
|
+
if (lineOpenCount > lineCloseCount) {
|
|
1141
|
+
unclosedLines.push(lineNumber);
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
// Report unclosed parentheses
|
|
1145
|
+
if (parenBalance > 0 && unclosedLines.length > 0) {
|
|
1146
|
+
// Report the last line that opened parentheses
|
|
1147
|
+
const lastUnclosedLine = unclosedLines[unclosedLines.length - 1];
|
|
1148
|
+
lineErrors.push({
|
|
1149
|
+
line: lastUnclosedLine,
|
|
1150
|
+
error: `Unclosed parenthesis (${parenBalance} open parenthesis/parentheses)`,
|
|
1151
|
+
suggestion: 'Add ) to close the open parenthesis/parentheses',
|
|
1152
|
+
severity: 'error'
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
else if (parenBalance < 0) {
|
|
1156
|
+
lineErrors.push({
|
|
1157
|
+
line: lines.length,
|
|
1158
|
+
error: `Extra closing parenthesis (${Math.abs(parenBalance)} too many)`,
|
|
1159
|
+
suggestion: 'Remove extra ) or add opening (',
|
|
1160
|
+
severity: 'error'
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Detect AI Hallucinations - Common method name errors
|
|
1166
|
+
* Similar to JavaScript analyzer but for Python-specific methods
|
|
1167
|
+
*/
|
|
1168
|
+
detectAIHallucinations(code, lineErrors) {
|
|
1169
|
+
const lines = code.split('\n');
|
|
1170
|
+
// Python AI hallucination patterns
|
|
1171
|
+
const hallucinationMap = new Map([
|
|
1172
|
+
// String method hallucinations
|
|
1173
|
+
['toUpper', { description: 'Non-existent method in Python', correct: '.upper()' }],
|
|
1174
|
+
['toLower', { description: 'Non-existent method in Python', correct: '.lower()' }],
|
|
1175
|
+
['toUpperCase', { description: 'Non-existent method (JavaScript influence)', correct: '.upper()' }],
|
|
1176
|
+
['toLowerCase', { description: 'Non-existent method (JavaScript influence)', correct: '.lower()' }],
|
|
1177
|
+
['substring', { description: 'Non-existent method (use slicing)', correct: '[start:end]' }],
|
|
1178
|
+
['charAt', { description: 'Non-existent method (use indexing)', correct: '[index]' }],
|
|
1179
|
+
['indexOf', { description: 'Non-existent method', correct: '.find() or .index()' }],
|
|
1180
|
+
// List method hallucinations
|
|
1181
|
+
['push', { description: 'Non-existent method (JavaScript influence)', correct: '.append()' }],
|
|
1182
|
+
['pop_front', { description: 'Non-existent method', correct: '.pop(0)' }],
|
|
1183
|
+
['add', { description: 'Use .append() for lists', correct: '.append()' }],
|
|
1184
|
+
['remove_at', { description: 'Non-existent method', correct: '.pop(index) or del list[index]' }],
|
|
1185
|
+
['size', { description: 'Non-existent method', correct: 'len()' }],
|
|
1186
|
+
['length', { description: 'Non-existent property', correct: 'len()' }],
|
|
1187
|
+
// Dict method hallucinations
|
|
1188
|
+
['hasKey', { description: 'Non-existent method', correct: 'key in dict' }],
|
|
1189
|
+
['contains', { description: 'Non-existent method', correct: 'in operator' }],
|
|
1190
|
+
['containsKey', { description: 'Non-existent method', correct: 'key in dict' }],
|
|
1191
|
+
]);
|
|
1192
|
+
lines.forEach((line, index) => {
|
|
1193
|
+
const lineNumber = index + 1;
|
|
1194
|
+
if (line.trim().startsWith('#'))
|
|
1195
|
+
return;
|
|
1196
|
+
// CRITICAL FIX: Remove comments before pattern matching to prevent false positives
|
|
1197
|
+
// Example: list.append(x) # ERROR: Don't use .push() here
|
|
1198
|
+
// ^^^^^ This would trigger false positive!
|
|
1199
|
+
const lineWithoutComments = code_cleaner_1.CodeCleaner.removeLineComments(line, 'python');
|
|
1200
|
+
// Detect method hallucinations with pattern: .method(
|
|
1201
|
+
const methodMatches = lineWithoutComments.matchAll(/\.(\w+)\s*\(/g);
|
|
1202
|
+
for (const match of methodMatches) {
|
|
1203
|
+
const method = match[1];
|
|
1204
|
+
const details = hallucinationMap.get(method);
|
|
1205
|
+
if (details) {
|
|
1206
|
+
let references = references_1.pythonStandards['ai-hallucination'] || [];
|
|
1207
|
+
// Add specific references based on method type
|
|
1208
|
+
if (method.includes('Upper') || method.includes('Lower')) {
|
|
1209
|
+
references = [
|
|
1210
|
+
{
|
|
1211
|
+
title: 'Python String Methods',
|
|
1212
|
+
url: 'https://docs.python.org/3/library/stdtypes.html#string-methods',
|
|
1213
|
+
description: 'String case conversion: use .upper() and .lower()'
|
|
1214
|
+
},
|
|
1215
|
+
{
|
|
1216
|
+
title: 'Python str.upper()',
|
|
1217
|
+
url: 'https://docs.python.org/3/library/stdtypes.html#str.upper',
|
|
1218
|
+
description: 'Convert string to uppercase'
|
|
1219
|
+
}
|
|
1220
|
+
];
|
|
1221
|
+
}
|
|
1222
|
+
else if (method === 'push' || method === 'add') {
|
|
1223
|
+
references = [
|
|
1224
|
+
{
|
|
1225
|
+
title: 'Python List Methods',
|
|
1226
|
+
url: 'https://docs.python.org/3/tutorial/datastructures.html#more-on-lists',
|
|
1227
|
+
description: 'Use .append() to add elements to a list'
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
title: 'Python list.append()',
|
|
1231
|
+
url: 'https://docs.python.org/3/tutorial/datastructures.html',
|
|
1232
|
+
description: 'Add an item to the end of the list'
|
|
1233
|
+
}
|
|
1234
|
+
];
|
|
1235
|
+
}
|
|
1236
|
+
lineErrors.push({
|
|
1237
|
+
line: lineNumber,
|
|
1238
|
+
error: ` AI Hallucination: ${details.description}`,
|
|
1239
|
+
suggestion: `Replace with: ${details.correct}`,
|
|
1240
|
+
severity: 'error',
|
|
1241
|
+
references
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Detect indentation errors (IndentationError, TabError)
|
|
1249
|
+
*/
|
|
1250
|
+
detectIndentationErrors(code, lineErrors) {
|
|
1251
|
+
const lines = code.split('\n');
|
|
1252
|
+
let hasTabs = false;
|
|
1253
|
+
let hasSpaces = false;
|
|
1254
|
+
lines.forEach((line, index) => {
|
|
1255
|
+
const lineNumber = index + 1;
|
|
1256
|
+
if (!line.trim() || line.trim().startsWith('#'))
|
|
1257
|
+
return;
|
|
1258
|
+
// Check for tab/space mixing
|
|
1259
|
+
if (line.startsWith('\t'))
|
|
1260
|
+
hasTabs = true;
|
|
1261
|
+
if (line.startsWith(' '))
|
|
1262
|
+
hasSpaces = true;
|
|
1263
|
+
// Detect TabError - mixing tabs and spaces
|
|
1264
|
+
if (hasTabs && hasSpaces) {
|
|
1265
|
+
lineErrors.push({
|
|
1266
|
+
line: lineNumber,
|
|
1267
|
+
error: 'TabError: Inconsistent use of tabs and spaces',
|
|
1268
|
+
suggestion: 'Use only spaces (4 spaces per indentation level - PEP 8)',
|
|
1269
|
+
severity: 'error',
|
|
1270
|
+
references: references_1.pythonStandards['indentation'] || []
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Detect TypeError patterns
|
|
1277
|
+
*/
|
|
1278
|
+
detectTypeErrors(code, lineErrors) {
|
|
1279
|
+
const lines = code.split('\n');
|
|
1280
|
+
lines.forEach((line, index) => {
|
|
1281
|
+
const lineNumber = index + 1;
|
|
1282
|
+
if (line.trim().startsWith('#'))
|
|
1283
|
+
return;
|
|
1284
|
+
// Detect string concatenation with non-strings
|
|
1285
|
+
if (line.match(/["'].*["']\s*\+\s*\d+/) || line.match(/\d+\s*\+\s*["'].*["']/)) {
|
|
1286
|
+
lineErrors.push({
|
|
1287
|
+
line: lineNumber,
|
|
1288
|
+
error: 'Potential TypeError: String concatenation with number',
|
|
1289
|
+
suggestion: 'Use str() to convert number to string or use f-string: f"{number}"',
|
|
1290
|
+
severity: 'warning',
|
|
1291
|
+
references: references_1.pythonStandards['type-errors'] || []
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
// Detect calling non-callable objects
|
|
1295
|
+
if (line.match(/\b(str|int|list|dict)\s*=\s*/) && line.match(/\(\)/)) {
|
|
1296
|
+
lineErrors.push({
|
|
1297
|
+
line: lineNumber,
|
|
1298
|
+
error: 'Potential TypeError: Attempting to call non-callable object',
|
|
1299
|
+
suggestion: 'Don\'t override built-in types (str, int, list, dict)',
|
|
1300
|
+
severity: 'error',
|
|
1301
|
+
references: references_1.pythonStandards['type-errors'] || []
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Detect NameError - undefined variables
|
|
1308
|
+
*/
|
|
1309
|
+
detectNameErrors(code, lineErrors) {
|
|
1310
|
+
const lines = code.split('\n');
|
|
1311
|
+
const definedVars = new Set();
|
|
1312
|
+
const imports = new Set();
|
|
1313
|
+
const conditionalVars = new Map();
|
|
1314
|
+
// Helper to get indentation level
|
|
1315
|
+
const getIndent = (line) => {
|
|
1316
|
+
const match = line.match(/^(\s*)/);
|
|
1317
|
+
return match ? match[1].length : 0;
|
|
1318
|
+
};
|
|
1319
|
+
// Track function-level base indentation
|
|
1320
|
+
let functionBaseIndent = 0;
|
|
1321
|
+
let inFunction = false;
|
|
1322
|
+
lines.forEach((line, index) => {
|
|
1323
|
+
const lineNumber = index + 1;
|
|
1324
|
+
const trimmed = line.trim();
|
|
1325
|
+
if (trimmed.startsWith('#'))
|
|
1326
|
+
return;
|
|
1327
|
+
const indent = getIndent(line);
|
|
1328
|
+
// Track function definitions
|
|
1329
|
+
const funcMatch = trimmed.match(/^def\s+(\w+)/);
|
|
1330
|
+
if (funcMatch) {
|
|
1331
|
+
definedVars.add(funcMatch[1]);
|
|
1332
|
+
functionBaseIndent = indent;
|
|
1333
|
+
inFunction = true;
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
// Track imports
|
|
1337
|
+
if (trimmed.startsWith('import ') || trimmed.startsWith('from ')) {
|
|
1338
|
+
const importMatch = trimmed.match(/(?:import|from)\s+(\w+)/);
|
|
1339
|
+
if (importMatch)
|
|
1340
|
+
imports.add(importMatch[1]);
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
// Track variable assignments
|
|
1344
|
+
const assignMatch = trimmed.match(/^(\w+)\s*=/);
|
|
1345
|
+
if (assignMatch && inFunction) {
|
|
1346
|
+
const varName = assignMatch[1];
|
|
1347
|
+
// Check if this assignment is inside a conditional block (higher indentation than function base)
|
|
1348
|
+
if (indent > functionBaseIndent + 4) {
|
|
1349
|
+
// Variable assigned inside conditional block (if/for/while/try)
|
|
1350
|
+
conditionalVars.set(varName, { line: lineNumber, indent });
|
|
1351
|
+
}
|
|
1352
|
+
else {
|
|
1353
|
+
// Variable assigned at function level (safe)
|
|
1354
|
+
definedVars.add(varName);
|
|
1355
|
+
conditionalVars.delete(varName); // Remove from conditional tracking
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
// Check for variable usage (return statements, expressions)
|
|
1359
|
+
if (trimmed.startsWith('return ') && inFunction) {
|
|
1360
|
+
// Match variable name in return statement (handle various patterns)
|
|
1361
|
+
const returnMatch = trimmed.match(/^return\s+(\w+)/);
|
|
1362
|
+
if (returnMatch) {
|
|
1363
|
+
const varName = returnMatch[1];
|
|
1364
|
+
// Check if variable is only defined conditionally
|
|
1365
|
+
if (conditionalVars.has(varName) && !definedVars.has(varName) && !imports.has(varName)) {
|
|
1366
|
+
const defInfo = conditionalVars.get(varName);
|
|
1367
|
+
// Flag if return is at lower indentation than where var was defined (outside the conditional block)
|
|
1368
|
+
// Example: var defined at indent 8 (inside if), return at indent 4 (outside if)
|
|
1369
|
+
if (indent < defInfo.indent) {
|
|
1370
|
+
lineErrors.push({
|
|
1371
|
+
line: lineNumber,
|
|
1372
|
+
error: `NameError: '${varName}' might not be defined`,
|
|
1373
|
+
suggestion: `Variable '${varName}' was assigned inside a conditional block (line ${defInfo.line}) but used outside it. Initialize '${varName}' before the block or ensure it's always defined.`,
|
|
1374
|
+
severity: 'error',
|
|
1375
|
+
references: references_1.pythonStandards['name-errors'] || []
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
// Check for common undefined variable patterns
|
|
1382
|
+
const commonUndefined = ['undefined', 'NULL', 'nil', 'null'];
|
|
1383
|
+
commonUndefined.forEach(undef => {
|
|
1384
|
+
if (line.includes(undef) && !line.includes('#') && !line.includes('"') && !line.includes("'")) {
|
|
1385
|
+
lineErrors.push({
|
|
1386
|
+
line: lineNumber,
|
|
1387
|
+
error: `NameError: '${undef}' does not exist in Python`,
|
|
1388
|
+
suggestion: undef === 'undefined' || undef === 'null' ? 'Use None in Python' : 'Use None',
|
|
1389
|
+
severity: 'error',
|
|
1390
|
+
references: references_1.pythonStandards['name-errors'] || []
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Detect AttributeError - accessing non-existent attributes
|
|
1398
|
+
*/
|
|
1399
|
+
detectAttributeErrors(code, lineErrors) {
|
|
1400
|
+
const lines = code.split('\n');
|
|
1401
|
+
lines.forEach((line, index) => {
|
|
1402
|
+
const lineNumber = index + 1;
|
|
1403
|
+
if (line.trim().startsWith('#'))
|
|
1404
|
+
return;
|
|
1405
|
+
// Detect common attribute errors with string methods
|
|
1406
|
+
const stringAttrErrors = [
|
|
1407
|
+
{ pattern: /\.toUpperCase\(\)/, correct: '.upper()', wrong: 'toUpperCase' },
|
|
1408
|
+
{ pattern: /\.toLowerCase\(\)/, correct: '.lower()', wrong: 'toLowerCase' },
|
|
1409
|
+
{ pattern: /\.trim\(\)/, correct: '.strip()', wrong: 'trim' },
|
|
1410
|
+
{ pattern: /\.substring\(/, correct: '[start:end]', wrong: 'substring' },
|
|
1411
|
+
];
|
|
1412
|
+
stringAttrErrors.forEach(({ pattern, correct, wrong }) => {
|
|
1413
|
+
if (pattern.test(line)) {
|
|
1414
|
+
lineErrors.push({
|
|
1415
|
+
line: lineNumber,
|
|
1416
|
+
error: `AttributeError: Strings don't have method '${wrong}'`,
|
|
1417
|
+
suggestion: `Use ${correct}`,
|
|
1418
|
+
severity: 'error',
|
|
1419
|
+
references: references_1.pythonStandards['attribute-errors'] || []
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
// Detect common list attribute errors
|
|
1424
|
+
if (line.match(/\.push\(/)) {
|
|
1425
|
+
lineErrors.push({
|
|
1426
|
+
line: lineNumber,
|
|
1427
|
+
error: 'AttributeError: Lists don\'t have method push',
|
|
1428
|
+
suggestion: 'Use .append() to add elements',
|
|
1429
|
+
severity: 'error',
|
|
1430
|
+
references: references_1.pythonStandards['attribute-errors'] || []
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Detect IndexError - list index out of range
|
|
1437
|
+
*/
|
|
1438
|
+
detectIndexErrors(code, lineErrors) {
|
|
1439
|
+
const lines = code.split('\n');
|
|
1440
|
+
lines.forEach((line, index) => {
|
|
1441
|
+
const lineNumber = index + 1;
|
|
1442
|
+
if (line.trim().startsWith('#'))
|
|
1443
|
+
return;
|
|
1444
|
+
// Detect potential IndexError: accessing list[len(list)]
|
|
1445
|
+
if (line.match(/\[\s*len\([^)]+\)\s*\]/)) {
|
|
1446
|
+
lineErrors.push({
|
|
1447
|
+
line: lineNumber,
|
|
1448
|
+
error: 'Potential IndexError: index len(list) is out of range',
|
|
1449
|
+
suggestion: 'Use len(list)-1 for the last element or list[-1]',
|
|
1450
|
+
severity: 'warning',
|
|
1451
|
+
references: references_1.pythonStandards['index-errors'] || []
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
// Detect hardcoded index access without length check
|
|
1455
|
+
const indexMatch = line.match(/(\w+)\[(\d+)\]/);
|
|
1456
|
+
if (indexMatch) {
|
|
1457
|
+
const [, varName, indexStr] = indexMatch;
|
|
1458
|
+
const indexNum = parseInt(indexStr);
|
|
1459
|
+
if (indexNum > 10) { // Flag suspiciously high indices
|
|
1460
|
+
lineErrors.push({
|
|
1461
|
+
line: lineNumber,
|
|
1462
|
+
error: `Potential IndexError: index ${indexNum} may be out of range`,
|
|
1463
|
+
suggestion: `Check if the index is within bounds before accessing ${varName}[${indexNum}]`,
|
|
1464
|
+
severity: 'info',
|
|
1465
|
+
references: references_1.pythonStandards['index-errors'] || []
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Detect KeyError - dictionary key access without checking
|
|
1473
|
+
*/
|
|
1474
|
+
detectKeyErrors(code, lineErrors) {
|
|
1475
|
+
const lines = code.split('\n');
|
|
1476
|
+
lines.forEach((line, index) => {
|
|
1477
|
+
const lineNumber = index + 1;
|
|
1478
|
+
if (line.trim().startsWith('#'))
|
|
1479
|
+
return;
|
|
1480
|
+
// Detect dict[key] access pattern (potentially unsafe)
|
|
1481
|
+
const dictAccessMatch = line.match(/(\w+)\[['"](\w+)['"]\]/);
|
|
1482
|
+
if (dictAccessMatch && !line.includes('.get(')) {
|
|
1483
|
+
const [, dictName, key] = dictAccessMatch;
|
|
1484
|
+
// Check if there's a prior 'if key in dict' check
|
|
1485
|
+
const checkExists = lines.slice(Math.max(0, index - 3), index).some(prevLine => prevLine.includes(`${key}`) && prevLine.includes('in') && prevLine.includes(dictName));
|
|
1486
|
+
if (!checkExists) {
|
|
1487
|
+
lineErrors.push({
|
|
1488
|
+
line: lineNumber,
|
|
1489
|
+
error: `Potential KeyError: key '${key}' may not exist in ${dictName}`,
|
|
1490
|
+
suggestion: `Use ${dictName}.get('${key}', default) or check with 'if '${key}' in ${dictName}'`,
|
|
1491
|
+
severity: 'warning',
|
|
1492
|
+
references: references_1.pythonStandards['key-errors'] || []
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Detect mutable default arguments
|
|
1500
|
+
*/
|
|
1501
|
+
detectMutableDefaults(code, lineErrors) {
|
|
1502
|
+
const lines = code.split('\n');
|
|
1503
|
+
lines.forEach((line, index) => {
|
|
1504
|
+
const lineNumber = index + 1;
|
|
1505
|
+
if (!line.trim().startsWith('def '))
|
|
1506
|
+
return;
|
|
1507
|
+
// Detect mutable default arguments: def func(arg=[]) or def func(arg={})
|
|
1508
|
+
if (line.match(/def\s+\w+\s*\([^)]*=\s*[\[{]/)) {
|
|
1509
|
+
lineErrors.push({
|
|
1510
|
+
line: lineNumber,
|
|
1511
|
+
error: 'Mutable default argument detected (list or dictionary)',
|
|
1512
|
+
suggestion: 'Use None as default and initialize inside function: if arg is None: arg = []',
|
|
1513
|
+
severity: 'warning',
|
|
1514
|
+
references: references_1.pythonStandards['mutable-defaults'] || []
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Detect loop modification - modifying list/dict while iterating
|
|
1521
|
+
*/
|
|
1522
|
+
detectLoopModification(code, lineErrors) {
|
|
1523
|
+
const lines = code.split('\n');
|
|
1524
|
+
lines.forEach((line, index) => {
|
|
1525
|
+
const lineNumber = index + 1;
|
|
1526
|
+
const trimmed = line.trim();
|
|
1527
|
+
// Check if we're in a for loop
|
|
1528
|
+
const forMatch = trimmed.match(/for\s+\w+\s+in\s+(\w+)/);
|
|
1529
|
+
if (forMatch) {
|
|
1530
|
+
const iterVar = forMatch[1];
|
|
1531
|
+
// Check next few lines for modifications to the iterated variable
|
|
1532
|
+
const nextLines = lines.slice(index + 1, Math.min(index + 5, lines.length));
|
|
1533
|
+
nextLines.forEach((nextLine) => {
|
|
1534
|
+
// Detect append, remove, pop, del on the same variable
|
|
1535
|
+
if (nextLine.includes(`${iterVar}.append`) ||
|
|
1536
|
+
nextLine.includes(`${iterVar}.remove`) ||
|
|
1537
|
+
nextLine.includes(`${iterVar}.pop`) ||
|
|
1538
|
+
nextLine.includes(`del ${iterVar}`)) {
|
|
1539
|
+
lineErrors.push({
|
|
1540
|
+
line: lineNumber,
|
|
1541
|
+
error: `Modification of '${iterVar}' during iteration`,
|
|
1542
|
+
suggestion: `Create a copy of the list before iterating: for item in ${iterVar}.copy():`,
|
|
1543
|
+
severity: 'error',
|
|
1544
|
+
references: references_1.pythonStandards['loop-modification'] || []
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* Detect scope issues - global/nonlocal misuse
|
|
1553
|
+
*/
|
|
1554
|
+
detectScopeIssues(code, lineErrors) {
|
|
1555
|
+
const lines = code.split('\n');
|
|
1556
|
+
const globalVars = new Set();
|
|
1557
|
+
lines.forEach((line, index) => {
|
|
1558
|
+
const lineNumber = index + 1;
|
|
1559
|
+
const trimmed = line.trim();
|
|
1560
|
+
if (trimmed.startsWith('#'))
|
|
1561
|
+
return;
|
|
1562
|
+
// Track global declarations
|
|
1563
|
+
const globalMatch = trimmed.match(/global\s+(\w+)/);
|
|
1564
|
+
if (globalMatch) {
|
|
1565
|
+
const varName = globalMatch[1];
|
|
1566
|
+
// Warn about excessive global usage
|
|
1567
|
+
if (globalVars.has(varName)) {
|
|
1568
|
+
lineErrors.push({
|
|
1569
|
+
line: lineNumber,
|
|
1570
|
+
error: `Excessive use of 'global ${varName}'`,
|
|
1571
|
+
suggestion: 'Consider using classes or passing variables as parameters',
|
|
1572
|
+
severity: 'warning',
|
|
1573
|
+
references: references_1.pythonStandards['scope-issues'] || []
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
globalVars.add(varName);
|
|
1577
|
+
}
|
|
1578
|
+
// Detect assignment to variable that might need global/nonlocal
|
|
1579
|
+
const funcMatch = lines.slice(Math.max(0, index - 10), index).some(l => l.trim().startsWith('def '));
|
|
1580
|
+
if (funcMatch) {
|
|
1581
|
+
const assignMatch = trimmed.match(/^(\w+)\s*=/);
|
|
1582
|
+
if (assignMatch && globalVars.has(assignMatch[1])) {
|
|
1583
|
+
// Check if global declaration exists
|
|
1584
|
+
const hasGlobal = lines.slice(Math.max(0, index - 5), index).some(l => l.includes(`global ${assignMatch[1]}`));
|
|
1585
|
+
if (!hasGlobal) {
|
|
1586
|
+
lineErrors.push({
|
|
1587
|
+
line: lineNumber,
|
|
1588
|
+
error: `Possible scope error: assignment to '${assignMatch[1]}' without 'global'`,
|
|
1589
|
+
suggestion: `Add 'global ${assignMatch[1]}' or use function parameter`,
|
|
1590
|
+
severity: 'info',
|
|
1591
|
+
references: references_1.pythonStandards['scope-issues'] || []
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
exports.PythonAnalyzer = PythonAnalyzer;
|
|
1600
|
+
//# sourceMappingURL=python-analyzer.js.map
|