patchi 0.7.5__py3-none-any.whl
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.
- patchi/__init__.py +1 -0
- patchi/__main__.py +13 -0
- patchi/cli/__init__.py +0 -0
- patchi/cli/command_families.py +235 -0
- patchi/cli/commands/__init__.py +0 -0
- patchi/cli/commands/access_cmd.py +65 -0
- patchi/cli/commands/agent_stats_cmd.py +273 -0
- patchi/cli/commands/agents_cmd.py +187 -0
- patchi/cli/commands/ai_cmd.py +308 -0
- patchi/cli/commands/assure_cmd.py +538 -0
- patchi/cli/commands/audit_cmd.py +286 -0
- patchi/cli/commands/auto_cmd.py +110 -0
- patchi/cli/commands/blame_cmd.py +53 -0
- patchi/cli/commands/chains_cmd.py +327 -0
- patchi/cli/commands/charter_cmd.py +173 -0
- patchi/cli/commands/chat_cmd.py +597 -0
- patchi/cli/commands/check_cmd.py +232 -0
- patchi/cli/commands/cleanup_cmd.py +328 -0
- patchi/cli/commands/cockpit_cmd.py +208 -0
- patchi/cli/commands/commands_cmd.py +160 -0
- patchi/cli/commands/cross_repo_cmd.py +113 -0
- patchi/cli/commands/deps_cmd.py +110 -0
- patchi/cli/commands/dev_check_cmd.py +342 -0
- patchi/cli/commands/dev_cmd.py +445 -0
- patchi/cli/commands/doctor_cmd.py +457 -0
- patchi/cli/commands/findings_cmd.py +187 -0
- patchi/cli/commands/fix_cmd.py +648 -0
- patchi/cli/commands/fix_review_cmd.py +273 -0
- patchi/cli/commands/heatmap_cmd.py +62 -0
- patchi/cli/commands/help_cmd.py +159 -0
- patchi/cli/commands/hosted_cmd.py +980 -0
- patchi/cli/commands/init.py +456 -0
- patchi/cli/commands/key_cmd.py +571 -0
- patchi/cli/commands/learn_cmd.py +140 -0
- patchi/cli/commands/link_cmd.py +104 -0
- patchi/cli/commands/log_cmd.py +57 -0
- patchi/cli/commands/memory_cmd.py +215 -0
- patchi/cli/commands/model_cmd.py +207 -0
- patchi/cli/commands/notify_cmd.py +213 -0
- patchi/cli/commands/notify_prompts.py +29 -0
- patchi/cli/commands/patch_cmd.py +231 -0
- patchi/cli/commands/plan_cmd.py +128 -0
- patchi/cli/commands/plugins_cmd.py +307 -0
- patchi/cli/commands/queue_cmd.py +152 -0
- patchi/cli/commands/quick_cmd.py +159 -0
- patchi/cli/commands/ready_cmd.py +292 -0
- patchi/cli/commands/report_cmd.py +416 -0
- patchi/cli/commands/restrict_cmd.py +127 -0
- patchi/cli/commands/review_cmd.py +225 -0
- patchi/cli/commands/rules_cmd.py +233 -0
- patchi/cli/commands/scan_cmd.py +1908 -0
- patchi/cli/commands/settings_cmd.py +45 -0
- patchi/cli/commands/status_cmd.py +679 -0
- patchi/cli/commands/test_cmd.py +1052 -0
- patchi/cli/commands/trend_cmd.py +296 -0
- patchi/cli/commands/undo_cmd.py +235 -0
- patchi/cli/commands/update_cmd.py +345 -0
- patchi/cli/commands/verify_cmd.py +56 -0
- patchi/cli/commands/vr_cmd.py +440 -0
- patchi/cli/commands/watch_cmd.py +304 -0
- patchi/cli/commands/web_cmd.py +114 -0
- patchi/cli/console.py +101 -0
- patchi/cli/display/__init__.py +1 -0
- patchi/cli/display/live_audit.py +269 -0
- patchi/cli/display/live_progress.py +169 -0
- patchi/cli/framework.py +241 -0
- patchi/cli/logo.py +128 -0
- patchi/cli/main.py +222 -0
- patchi/cli/registry.py +1171 -0
- patchi/cli/style.py +68 -0
- patchi/cli/themes.py +187 -0
- patchi/cli/ux.py +444 -0
- patchi/core/__init__.py +0 -0
- patchi/core/agents/__init__.py +0 -0
- patchi/core/agents/api_fuzzer_agent.py +141 -0
- patchi/core/agents/attack_agent.py +242 -0
- patchi/core/agents/base.py +1012 -0
- patchi/core/agents/bug_predictor_agent.py +58 -0
- patchi/core/agents/build_tool_validator.py +150 -0
- patchi/core/agents/cache.py +192 -0
- patchi/core/agents/chaos_agent.py +133 -0
- patchi/core/agents/cicd_generator.py +240 -0
- patchi/core/agents/comment_scanner.py +280 -0
- patchi/core/agents/console_logging_agent.py +156 -0
- patchi/core/agents/coordinator.py +858 -0
- patchi/core/agents/core_scanner.py +269 -0
- patchi/core/agents/coverage_prioritizer.py +181 -0
- patchi/core/agents/dead_code_hygiene.py +296 -0
- patchi/core/agents/dead_code_scanner.py +692 -0
- patchi/core/agents/dependency_scanner.py +725 -0
- patchi/core/agents/doc_claim_agent.py +573 -0
- patchi/core/agents/duplicate_scanner.py +568 -0
- patchi/core/agents/env_scanner.py +410 -0
- patchi/core/agents/feature_flag_agent.py +89 -0
- patchi/core/agents/frontend_framework_agent.py +133 -0
- patchi/core/agents/goroutine_agent.py +82 -0
- patchi/core/agents/governor.py +1972 -0
- patchi/core/agents/i18n_agent.py +95 -0
- patchi/core/agents/license_compliance.py +247 -0
- patchi/core/agents/linking_agent.py +251 -0
- patchi/core/agents/memory_profiler_agent.py +102 -0
- patchi/core/agents/modernization_agent.py +84 -0
- patchi/core/agents/mutation_agent.py +174 -0
- patchi/core/agents/nl_query_agent.py +74 -0
- patchi/core/agents/promise_rejection_agent.py +100 -0
- patchi/core/agents/refactoring_agent.py +286 -0
- patchi/core/agents/resource_leak_agent.py +91 -0
- patchi/core/agents/route_graph_scanner.py +951 -0
- patchi/core/agents/run_agent.py +106 -0
- patchi/core/agents/rust_unwrap_agent.py +100 -0
- patchi/core/agents/sbom_generator.py +284 -0
- patchi/core/agents/scanners.py +67 -0
- patchi/core/agents/side/__init__.py +1 -0
- patchi/core/agents/side/build_agent.py +65 -0
- patchi/core/agents/side/format_agent.py +50 -0
- patchi/core/agents/side/install_agent.py +63 -0
- patchi/core/agents/side_file_scanner.py +549 -0
- patchi/core/agents/snapshot_drift_detector.py +146 -0
- patchi/core/agents/spa_route_inventory.py +203 -0
- patchi/core/agents/test_scanner.py +442 -0
- patchi/core/agents/tool_health.py +109 -0
- patchi/core/agents/tool_runner.py +162 -0
- patchi/core/agents/type_scanner.py +168 -0
- patchi/core/agents/ui_scanner.py +558 -0
- patchi/core/ai/__init__.py +20 -0
- patchi/core/ai/agent_audit.py +153 -0
- patchi/core/ai/agent_profiler.py +277 -0
- patchi/core/ai/client.py +499 -0
- patchi/core/ai/cost_tracker.py +107 -0
- patchi/core/ai/model_router.py +318 -0
- patchi/core/ai/orchestrator.py +625 -0
- patchi/core/ai/prompts.py +681 -0
- patchi/core/ai/smart.py +302 -0
- patchi/core/ai/tool_executor.py +24 -0
- patchi/core/ai/tools/__init__.py +10 -0
- patchi/core/ai/tools/executor.py +621 -0
- patchi/core/ai/tools/realize.py +1160 -0
- patchi/core/ai/tools/registry.py +1577 -0
- patchi/core/assurance/__init__.py +33 -0
- patchi/core/assurance/builder.py +211 -0
- patchi/core/assurance/graph.py +342 -0
- patchi/core/assurance/invariants.py +168 -0
- patchi/core/atomic.py +61 -0
- patchi/core/attackers/__init__.py +17 -0
- patchi/core/attackers/api_abuse.py +89 -0
- patchi/core/attackers/auth_bypass.py +73 -0
- patchi/core/attackers/base.py +91 -0
- patchi/core/attackers/business_logic.py +88 -0
- patchi/core/attackers/planner.py +108 -0
- patchi/core/attackers/priv_escalation.py +88 -0
- patchi/core/attackers/recon.py +87 -0
- patchi/core/brain/__init__.py +0 -0
- patchi/core/brain/ast_utils/__init__.py +81 -0
- patchi/core/brain/ast_utils/assignments.py +219 -0
- patchi/core/brain/ast_utils/calls.py +139 -0
- patchi/core/brain/ast_utils/config.py +132 -0
- patchi/core/brain/ast_utils/control_flow.py +74 -0
- patchi/core/brain/ast_utils/dead_symbols.py +205 -0
- patchi/core/brain/ast_utils/decorators.py +108 -0
- patchi/core/brain/ast_utils/helpers.py +144 -0
- patchi/core/brain/ast_utils/imports.py +169 -0
- patchi/core/brain/ast_utils/scan.py +143 -0
- patchi/core/brain/ast_utils/taint.py +310 -0
- patchi/core/brain/audit.py +379 -0
- patchi/core/brain/baseline.py +195 -0
- patchi/core/brain/blast_radius.py +312 -0
- patchi/core/brain/body_tags.py +144 -0
- patchi/core/brain/brain.py +1161 -0
- patchi/core/brain/brain_context.py +393 -0
- patchi/core/brain/brain_watcher.py +340 -0
- patchi/core/brain/charter.py +473 -0
- patchi/core/brain/classifier.py +214 -0
- patchi/core/brain/cockpit.py +350 -0
- patchi/core/brain/code_query.py +710 -0
- patchi/core/brain/contract.py +889 -0
- patchi/core/brain/contract_diff.py +155 -0
- patchi/core/brain/council.py +800 -0
- patchi/core/brain/cross_repo.py +104 -0
- patchi/core/brain/dead_code_tools.py +157 -0
- patchi/core/brain/doc_validator.py +197 -0
- patchi/core/brain/domain_activator.py +2183 -0
- patchi/core/brain/enriched_context.py +220 -0
- patchi/core/brain/file_corpus.py +220 -0
- patchi/core/brain/framework.py +953 -0
- patchi/core/brain/freshness.py +273 -0
- patchi/core/brain/git_aware.py +206 -0
- patchi/core/brain/heatmap.py +104 -0
- patchi/core/brain/ignore_parser.py +153 -0
- patchi/core/brain/import_graph.py +486 -0
- patchi/core/brain/languages.py +397 -0
- patchi/core/brain/layered_brain.py +419 -0
- patchi/core/brain/learning.py +229 -0
- patchi/core/brain/orphaned_endpoints.py +313 -0
- patchi/core/brain/personas/__init__.py +648 -0
- patchi/core/brain/personas/bad_user.py +165 -0
- patchi/core/brain/personas/base.py +349 -0
- patchi/core/brain/proactive.py +807 -0
- patchi/core/brain/project_context.py +121 -0
- patchi/core/brain/project_reader.py +521 -0
- patchi/core/brain/reasoning.py +269 -0
- patchi/core/brain/route_detector/__init__.py +15 -0
- patchi/core/brain/route_detector/base.py +31 -0
- patchi/core/brain/route_detector/csharp.py +209 -0
- patchi/core/brain/route_detector/file_based.py +81 -0
- patchi/core/brain/route_detector/go.py +152 -0
- patchi/core/brain/route_detector/java.py +195 -0
- patchi/core/brain/route_detector/javascript.py +197 -0
- patchi/core/brain/route_detector/php.py +158 -0
- patchi/core/brain/route_detector/python.py +193 -0
- patchi/core/brain/route_detector/registry.py +25 -0
- patchi/core/brain/route_detector/ruby.py +134 -0
- patchi/core/brain/route_detector/rust.py +216 -0
- patchi/core/brain/route_detector/swift.py +118 -0
- patchi/core/brain/route_mapper.py +265 -0
- patchi/core/brain/scanner.py +2136 -0
- patchi/core/brain/secrets.py +280 -0
- patchi/core/brain/symbol_graph.py +1955 -0
- patchi/core/brain/team.py +28 -0
- patchi/core/brain/trace_log.py +132 -0
- patchi/core/brain/type_checker/__init__.py +65 -0
- patchi/core/brain/type_checker/base.py +60 -0
- patchi/core/brain/type_checker/csharp.py +59 -0
- patchi/core/brain/type_checker/dart.py +59 -0
- patchi/core/brain/type_checker/go.py +81 -0
- patchi/core/brain/type_checker/java.py +75 -0
- patchi/core/brain/type_checker/kotlin.py +75 -0
- patchi/core/brain/type_checker/php.py +88 -0
- patchi/core/brain/type_checker/python.py +87 -0
- patchi/core/brain/type_checker/rust.py +81 -0
- patchi/core/brain/type_checker/swift.py +59 -0
- patchi/core/brain/type_checker/typescript.py +298 -0
- patchi/core/brain/understander.py +130 -0
- patchi/core/brain/verify.py +379 -0
- patchi/core/campaigns/__init__.py +14 -0
- patchi/core/campaigns/base.py +62 -0
- patchi/core/campaigns/data_flow_abuse.py +153 -0
- patchi/core/campaigns/orchestrator.py +140 -0
- patchi/core/campaigns/state_transitions.py +178 -0
- patchi/core/ci_bundle.py +104 -0
- patchi/core/config.py +311 -0
- patchi/core/constants.py +245 -0
- patchi/core/debug/__init__.py +25 -0
- patchi/core/debug/adapters/__init__.py +11 -0
- patchi/core/debug/adapters/codelldb.py +379 -0
- patchi/core/debug/adapters/node.py +222 -0
- patchi/core/debug/adapters/powershell.py +210 -0
- patchi/core/debug/adapters/python.py +244 -0
- patchi/core/debug/capture.py +210 -0
- patchi/core/debug/dap_client.py +268 -0
- patchi/core/detector/__init__.py +0 -0
- patchi/core/detector/audit.py +194 -0
- patchi/core/detector/bus.py +226 -0
- patchi/core/detector/dispatcher.py +275 -0
- patchi/core/detector/event.py +254 -0
- patchi/core/detector/sigma_engine.py +378 -0
- patchi/core/detector/triage.py +289 -0
- patchi/core/export/sarif.py +113 -0
- patchi/core/fix/__init__.py +17 -0
- patchi/core/fix/applier.py +574 -0
- patchi/core/fix/base.py +510 -0
- patchi/core/fix/code_fixer.py +169 -0
- patchi/core/fix/dead_code_remover.py +131 -0
- patchi/core/fix/fix_agents.py +775 -0
- patchi/core/fix/patch.py +444 -0
- patchi/core/fix/risk_gate.py +383 -0
- patchi/core/fix/security_fixer.py +93 -0
- patchi/core/fix/verify_loop.py +204 -0
- patchi/core/fuzz/__init__.py +8 -0
- patchi/core/fuzz/corpus.py +137 -0
- patchi/core/fuzz/input_fuzzer.py +256 -0
- patchi/core/fuzz/sequence_fuzzer.py +162 -0
- patchi/core/fuzz/state_fuzzer.py +236 -0
- patchi/core/health.py +407 -0
- patchi/core/hosted/__init__.py +1 -0
- patchi/core/hosted/anomaly.py +298 -0
- patchi/core/hosted/audit_log.py +102 -0
- patchi/core/hosted/compliance_report.py +148 -0
- patchi/core/hosted/ip_reputation.py +169 -0
- patchi/core/hosted/log_parsers.py +251 -0
- patchi/core/hosted/tokens.py +127 -0
- patchi/core/hosted/watchlist.py +164 -0
- patchi/core/hosted/webhooks.py +156 -0
- patchi/core/memory.py +437 -0
- patchi/core/notifications/__init__.py +18 -0
- patchi/core/notifications/channels.py +192 -0
- patchi/core/notifications/digest.py +141 -0
- patchi/core/notifications/escalation.py +158 -0
- patchi/core/notifications/notifier.py +182 -0
- patchi/core/notifications/quiet_hours.py +69 -0
- patchi/core/plugins/__init__.py +48 -0
- patchi/core/plugins/analyzer.py +326 -0
- patchi/core/plugins/analyzers/__init__.py +6 -0
- patchi/core/plugins/analyzers/dead_code_analyzer.py +120 -0
- patchi/core/plugins/analyzers/secret_scanner.py +158 -0
- patchi/core/plugins/analyzers/todo_scanner.py +115 -0
- patchi/core/plugins/registry.py +335 -0
- patchi/core/queue.py +375 -0
- patchi/core/reliability/__init__.py +13 -0
- patchi/core/reliability/chaos.py +163 -0
- patchi/core/reliability/fault_injector.py +172 -0
- patchi/core/reliability/idempotency.py +128 -0
- patchi/core/reliability/recovery.py +147 -0
- patchi/core/runtime/__init__.py +5 -0
- patchi/core/runtime/tracer.py +230 -0
- patchi/core/scan_bus.py +156 -0
- patchi/core/security/__init__.py +1 -0
- patchi/core/security/ai_validator.py +462 -0
- patchi/core/security/app_mapper.py +216 -0
- patchi/core/security/app_profile.py +464 -0
- patchi/core/security/attack_feedback.py +141 -0
- patchi/core/security/attack_scenarios/__init__.py +1 -0
- patchi/core/security/attack_tree.py +176 -0
- patchi/core/security/auth_audit_agent.py +344 -0
- patchi/core/security/authz_agent.py +602 -0
- patchi/core/security/auto_fix_proactive.py +620 -0
- patchi/core/security/auto_fixer.py +660 -0
- patchi/core/security/auto_ticket.py +53 -0
- patchi/core/security/bandit_agent.py +152 -0
- patchi/core/security/blast_radius.py +226 -0
- patchi/core/security/browser_tester.py +333 -0
- patchi/core/security/business_logic_agent.py +263 -0
- patchi/core/security/catch_block_auditor.py +395 -0
- patchi/core/security/cdn_cache_agent.py +357 -0
- patchi/core/security/chain_analyzer.py +321 -0
- patchi/core/security/chain_to_assurance.py +168 -0
- patchi/core/security/charter.py +510 -0
- patchi/core/security/cloud_waf_detector.py +295 -0
- patchi/core/security/codeql_agent.py +233 -0
- patchi/core/security/compliance_agent.py +348 -0
- patchi/core/security/confidence_gate.py +423 -0
- patchi/core/security/container_scanner.py +282 -0
- patchi/core/security/crypto_agent.py +378 -0
- patchi/core/security/cve_monitor.py +262 -0
- patchi/core/security/dast_agent.py +865 -0
- patchi/core/security/defectdojo.py +238 -0
- patchi/core/security/defenders/__init__.py +75 -0
- patchi/core/security/defenders/auth_middleware.py +115 -0
- patchi/core/security/defenders/base.py +97 -0
- patchi/core/security/defenders/block_ip.py +71 -0
- patchi/core/security/defenders/block_ws_origin.py +36 -0
- patchi/core/security/defenders/crypto_fix.py +118 -0
- patchi/core/security/defenders/enforce_rate_limit.py +108 -0
- patchi/core/security/defenders/fix_code.py +93 -0
- patchi/core/security/defenders/invalidate_session.py +40 -0
- patchi/core/security/defenders/patch_config.py +70 -0
- patchi/core/security/defenders/rotate_secret.py +54 -0
- patchi/core/security/defenders/suspend_account.py +41 -0
- patchi/core/security/defenders/update_dependency.py +160 -0
- patchi/core/security/defense_layer.py +217 -0
- patchi/core/security/dependency_vulnerability_agent.py +616 -0
- patchi/core/security/detection_pipeline.py +289 -0
- patchi/core/security/dns_security_agent.py +497 -0
- patchi/core/security/domain_activator_v2.py +913 -0
- patchi/core/security/domain_loader.py +1004 -0
- patchi/core/security/domains/__init__.py +0 -0
- patchi/core/security/email_authentication_agent.py +417 -0
- patchi/core/security/env_var_validator.py +304 -0
- patchi/core/security/evidence.py +100 -0
- patchi/core/security/falco_runtime_agent.py +387 -0
- patchi/core/security/fix-playbooks/__init__.py +0 -0
- patchi/core/security/gated_finding.py +85 -0
- patchi/core/security/git_diff_activator.py +367 -0
- patchi/core/security/governance.py +208 -0
- patchi/core/security/gradual_gate.py +34 -0
- patchi/core/security/history.py +315 -0
- patchi/core/security/iac_scanner.py +250 -0
- patchi/core/security/ignore_expiry.py +111 -0
- patchi/core/security/ignore_learner.py +416 -0
- patchi/core/security/injection_agent.py +319 -0
- patchi/core/security/insecure_randomness_agent.py +288 -0
- patchi/core/security/intent_analyzer.py +326 -0
- patchi/core/security/jwt_agent.py +453 -0
- patchi/core/security/kubernetes_agent.py +638 -0
- patchi/core/security/layer2_orchestrator.py +341 -0
- patchi/core/security/llm_security_agent.py +252 -0
- patchi/core/security/misconfig_agent.py +600 -0
- patchi/core/security/mobile_security_agent.py +474 -0
- patchi/core/security/network_agent.py +376 -0
- patchi/core/security/noise_filter.py +274 -0
- patchi/core/security/orchestrator.py +313 -0
- patchi/core/security/pattern_context.py +125 -0
- patchi/core/security/pattern_loader.py +101 -0
- patchi/core/security/pentest/__init__.py +4 -0
- patchi/core/security/pentest/base.py +58 -0
- patchi/core/security/pentest/dalfox_adapter.py +40 -0
- patchi/core/security/pentest/ffuf_adapter.py +48 -0
- patchi/core/security/pentest/nuclei_adapter.py +50 -0
- patchi/core/security/pentest/registry.py +80 -0
- patchi/core/security/pentest/shannon_adapter.py +107 -0
- patchi/core/security/pentest/sqlmap_adapter.py +56 -0
- patchi/core/security/pentest/zap_adapter.py +52 -0
- patchi/core/security/plan_auditor.py +276 -0
- patchi/core/security/policy_engine.py +272 -0
- patchi/core/security/prechecks.py +172 -0
- patchi/core/security/precommit_hook.py +247 -0
- patchi/core/security/privacy_agent.py +361 -0
- patchi/core/security/push_notification_agent.py +417 -0
- patchi/core/security/pysa_agent.py +143 -0
- patchi/core/security/reasoning.py +442 -0
- patchi/core/security/red_team_agent.py +184 -0
- patchi/core/security/red_team_engine.py +1142 -0
- patchi/core/security/remediation.py +360 -0
- patchi/core/security/request_interceptor.py +294 -0
- patchi/core/security/runtime_validator.py +245 -0
- patchi/core/security/saml_sso_agent.py +401 -0
- patchi/core/security/sast_agent.py +213 -0
- patchi/core/security/sast_gate.py +95 -0
- patchi/core/security/scheduler.py +287 -0
- patchi/core/security/secrets_runtime_agent.py +476 -0
- patchi/core/security/security_agents.py +175 -0
- patchi/core/security/security_config.py +551 -0
- patchi/core/security/security_probe.py +762 -0
- patchi/core/security/security_taint.py +1005 -0
- patchi/core/security/sensitive_data_agent.py +271 -0
- patchi/core/security/service_mesh_agent.py +412 -0
- patchi/core/security/session_management_agent.py +245 -0
- patchi/core/security/ssrf_agent.py +422 -0
- patchi/core/security/supply_chain.py +449 -0
- patchi/core/security/threat_model_generator.py +378 -0
- patchi/core/security/threat_model_updater.py +196 -0
- patchi/core/security/tool_adapters.py +135 -0
- patchi/core/security/tool_verify.py +173 -0
- patchi/core/security/websocket_security_agent.py +188 -0
- patchi/core/snapshot.py +242 -0
- patchi/core/tenant.py +386 -0
- patchi/core/testing/__init__.py +17 -0
- patchi/core/testing/_browser.py +147 -0
- patchi/core/testing/ads_agent.py +141 -0
- patchi/core/testing/api_contract_agent.py +450 -0
- patchi/core/testing/app_discovery_agent.py +641 -0
- patchi/core/testing/app_launcher.py +184 -0
- patchi/core/testing/browser_test_agent.py +516 -0
- patchi/core/testing/e2e_flow_agent.py +354 -0
- patchi/core/testing/flake_detector_agent.py +292 -0
- patchi/core/testing/gate.py +31 -0
- patchi/core/testing/live_test_runner.py +304 -0
- patchi/core/testing/live_v2/__init__.py +1 -0
- patchi/core/testing/live_v2/browser_pool.py +349 -0
- patchi/core/testing/live_v2/browser_test_runner.py +313 -0
- patchi/core/testing/live_v2/runner.py +626 -0
- patchi/core/testing/live_v2/screenshot_manager.py +474 -0
- patchi/core/testing/live_v2/stress_orchestrator.py +523 -0
- patchi/core/testing/live_v2/video_recorder.py +303 -0
- patchi/core/testing/regression_agent.py +302 -0
- patchi/core/testing/security_test_agent.py +253 -0
- patchi/core/testing/stress_test_agent.py +203 -0
- patchi/core/testing/test_agents.py +50 -0
- patchi/core/testing/test_config.py +194 -0
- patchi/core/testing/ui_accessibility_agent.py +324 -0
- patchi/core/testing/ui_button_agent.py +294 -0
- patchi/core/testing/ui_layout_agent.py +330 -0
- patchi/core/testing/unit_test_agent.py +1149 -0
- patchi/core/testing/visual_regression_agent.py +260 -0
- patchi/web/__init__.py +1 -0
- patchi/web/api/__init__.py +1 -0
- patchi/web/api/brain_map.py +114 -0
- patchi/web/api/charts.py +47 -0
- patchi/web/api/chat.py +224 -0
- patchi/web/api/cicd.py +576 -0
- patchi/web/api/dast_evidence.py +49 -0
- patchi/web/api/dev_check.py +320 -0
- patchi/web/api/fix.py +134 -0
- patchi/web/api/guard.py +65 -0
- patchi/web/api/hosted.py +128 -0
- patchi/web/api/hosted_v2.py +234 -0
- patchi/web/api/live_testing.py +699 -0
- patchi/web/api/scan.py +582 -0
- patchi/web/api/smart.py +111 -0
- patchi/web/api/tenant.py +117 -0
- patchi/web/api/tools.py +85 -0
- patchi/web/api_legacy.py +2401 -0
- patchi/web/app.py +310 -0
- patchi/web/events.py +483 -0
- patchi/web/routes/__init__.py +1 -0
- patchi/web/routes/assurance.py +541 -0
- patchi/web/routes/brain.py +36 -0
- patchi/web/routes/charter.py +227 -0
- patchi/web/routes/chat.py +23 -0
- patchi/web/routes/dashboard.py +558 -0
- patchi/web/routes/dashboard_v2.py +402 -0
- patchi/web/routes/findings.py +330 -0
- patchi/web/routes/guard.py +35 -0
- patchi/web/routes/history.py +126 -0
- patchi/web/routes/landing.py +52 -0
- patchi/web/routes/live_testing.py +24 -0
- patchi/web/routes/review.py +57 -0
- patchi/web/routes/self_improvement.py +110 -0
- patchi/web/routes/settings.py +49 -0
- patchi/web/routes/tokens.py +29 -0
- patchi/web/spawn.py +139 -0
- patchi/web/ws.py +141 -0
- patchi-0.7.5.dist-info/METADATA +315 -0
- patchi-0.7.5.dist-info/RECORD +496 -0
- patchi-0.7.5.dist-info/WHEEL +5 -0
- patchi-0.7.5.dist-info/licenses/LICENSE +60 -0
- patchi-0.7.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MisconfigAgent — security misconfigurations.
|
|
3
|
+
|
|
4
|
+
Detects security misconfigurations:
|
|
5
|
+
- Debug enabled in production
|
|
6
|
+
- Insecure CORS settings
|
|
7
|
+
- Missing security headers
|
|
8
|
+
- Weak TLS/SSL configurations
|
|
9
|
+
- Improper error handling
|
|
10
|
+
- Verbose error messages
|
|
11
|
+
- Unrestricted file uploads
|
|
12
|
+
- Missing rate limiting
|
|
13
|
+
- Default credentials
|
|
14
|
+
- Unnecessary services/features enabled
|
|
15
|
+
|
|
16
|
+
Uses pattern matching and configuration analysis.
|
|
17
|
+
Does NOT call AI.
|
|
18
|
+
Does NOT write to disk.
|
|
19
|
+
Does NOT touch the queue.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
import yaml
|
|
28
|
+
|
|
29
|
+
from ..agents.base import (
|
|
30
|
+
AgentDomain,
|
|
31
|
+
AgentGroup,
|
|
32
|
+
AgentInput,
|
|
33
|
+
AgentResult,
|
|
34
|
+
AgentStatus,
|
|
35
|
+
BaseAgent,
|
|
36
|
+
Finding,
|
|
37
|
+
Severity,
|
|
38
|
+
make_finding,
|
|
39
|
+
register,
|
|
40
|
+
safe_rglob,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@register
|
|
45
|
+
class MisconfigAgent(BaseAgent):
|
|
46
|
+
"""Agent for detecting security misconfigurations."""
|
|
47
|
+
|
|
48
|
+
group = AgentGroup.SECURITY
|
|
49
|
+
domain = AgentDomain.SECURITY
|
|
50
|
+
name = "MisconfigAgent"
|
|
51
|
+
description = (
|
|
52
|
+
"Security misconfig: debug, CORS, headers, TLS, error handling, file upload, rate limiting"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def _run(self, inp: AgentInput, result: AgentResult) -> None:
|
|
56
|
+
"""Run security misconfiguration detection."""
|
|
57
|
+
findings = []
|
|
58
|
+
|
|
59
|
+
# Define configuration file patterns to scan
|
|
60
|
+
config_patterns = [
|
|
61
|
+
"*.json",
|
|
62
|
+
"*.yml",
|
|
63
|
+
"*.yaml",
|
|
64
|
+
"*.toml",
|
|
65
|
+
"*.xml",
|
|
66
|
+
"*.ini",
|
|
67
|
+
"*.cfg",
|
|
68
|
+
"*.conf",
|
|
69
|
+
".env*",
|
|
70
|
+
"*rc",
|
|
71
|
+
".gitignore",
|
|
72
|
+
".dockerignore",
|
|
73
|
+
"Dockerfile*",
|
|
74
|
+
"package.json",
|
|
75
|
+
"requirements.txt",
|
|
76
|
+
"Cargo.toml",
|
|
77
|
+
"go.mod",
|
|
78
|
+
"composer.json",
|
|
79
|
+
"Gemfile",
|
|
80
|
+
"Pipfile",
|
|
81
|
+
"poetry.lock",
|
|
82
|
+
"yarn.lock",
|
|
83
|
+
"pnpm-lock.yaml",
|
|
84
|
+
"**/config/**",
|
|
85
|
+
"**/configs/**",
|
|
86
|
+
"**/conf/**",
|
|
87
|
+
"**/settings/**",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
# Search for configuration files
|
|
91
|
+
for pattern in config_patterns:
|
|
92
|
+
for file_path in safe_rglob(inp.root, pattern):
|
|
93
|
+
if file_path.is_file():
|
|
94
|
+
rel_path = file_path.relative_to(inp.root).as_posix()
|
|
95
|
+
if not self._should_skip_file(rel_path, inp):
|
|
96
|
+
findings.extend(self._scan_config_file_misconfig(file_path, rel_path))
|
|
97
|
+
|
|
98
|
+
# Also scan source files for misconfigurations
|
|
99
|
+
source_patterns = [
|
|
100
|
+
"*.py",
|
|
101
|
+
"*.js",
|
|
102
|
+
"*.jsx",
|
|
103
|
+
"*.ts",
|
|
104
|
+
"*.tsx",
|
|
105
|
+
"*.java",
|
|
106
|
+
"*.php",
|
|
107
|
+
"*.rb",
|
|
108
|
+
"*.go",
|
|
109
|
+
"*.rs",
|
|
110
|
+
"*.cpp",
|
|
111
|
+
"*.cxx",
|
|
112
|
+
"*.cc",
|
|
113
|
+
"*.c",
|
|
114
|
+
"*.h",
|
|
115
|
+
"*.hpp",
|
|
116
|
+
"*.cs",
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
for pattern in source_patterns:
|
|
120
|
+
for file_path in safe_rglob(inp.root, pattern):
|
|
121
|
+
if file_path.is_file():
|
|
122
|
+
rel_path = file_path.relative_to(inp.root).as_posix()
|
|
123
|
+
if not self._should_skip_file(rel_path, inp):
|
|
124
|
+
findings.extend(self._scan_source_file_misconfig(file_path, rel_path))
|
|
125
|
+
|
|
126
|
+
result.status = AgentStatus.SUCCEEDED
|
|
127
|
+
result.findings = findings
|
|
128
|
+
result.data.update(
|
|
129
|
+
{
|
|
130
|
+
"misconfig_findings": len(
|
|
131
|
+
[
|
|
132
|
+
f
|
|
133
|
+
for f in findings
|
|
134
|
+
if any(
|
|
135
|
+
word in f.title.lower()
|
|
136
|
+
for word in [
|
|
137
|
+
"misconfig",
|
|
138
|
+
"debug",
|
|
139
|
+
"cors",
|
|
140
|
+
"header",
|
|
141
|
+
"tls",
|
|
142
|
+
"error",
|
|
143
|
+
"upload",
|
|
144
|
+
"rate",
|
|
145
|
+
]
|
|
146
|
+
)
|
|
147
|
+
]
|
|
148
|
+
),
|
|
149
|
+
"needs_ai": False,
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
def _should_skip_file(self, file_path: str, inp: AgentInput) -> bool:
|
|
155
|
+
"""Check if file should be skipped based on restrictions."""
|
|
156
|
+
from pathlib import PurePosixPath
|
|
157
|
+
|
|
158
|
+
# Check restrictions
|
|
159
|
+
restrictions = inp.config.get("restrictions", [])
|
|
160
|
+
for r in restrictions:
|
|
161
|
+
if r.get("enabled", True):
|
|
162
|
+
path = r["path"]
|
|
163
|
+
if file_path.startswith(path) or PurePosixPath(file_path).match(path):
|
|
164
|
+
if r["type"] == "NO_TOUCH":
|
|
165
|
+
return True
|
|
166
|
+
elif r["type"] == "SCAN_ONLY" and self.__class__.__name__ == "FixAgent":
|
|
167
|
+
return True
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
def _scan_config_file_misconfig(self, file_path: Path, rel_path: str) -> list[Finding]:
|
|
171
|
+
"""Scan a configuration file for security misconfigurations."""
|
|
172
|
+
findings = []
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
content = file_path.read_text(encoding="utf-8")
|
|
176
|
+
|
|
177
|
+
# Determine file type and scan accordingly
|
|
178
|
+
if file_path.suffix.lower() in [".json"]:
|
|
179
|
+
findings.extend(self._scan_json_config_misconfig(content, rel_path))
|
|
180
|
+
elif file_path.suffix.lower() in [".yml", ".yaml"]:
|
|
181
|
+
findings.extend(self._scan_yaml_config_misconfig(content, rel_path))
|
|
182
|
+
elif file_path.name in ["package.json"]:
|
|
183
|
+
findings.extend(self._scan_package_json_misconfig(content, rel_path))
|
|
184
|
+
elif file_path.name in ["requirements.txt"]:
|
|
185
|
+
findings.extend(self._scan_requirements_txt_misconfig(content, rel_path))
|
|
186
|
+
elif file_path.suffix.lower() in [".toml"]:
|
|
187
|
+
findings.extend(self._scan_toml_config_misconfig(content, rel_path))
|
|
188
|
+
elif ".env" in file_path.name.lower():
|
|
189
|
+
findings.extend(self._scan_env_file_misconfig(content, rel_path))
|
|
190
|
+
else:
|
|
191
|
+
# General configuration file scan
|
|
192
|
+
findings.extend(self._scan_general_config_misconfig(content, rel_path))
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
findings.append(
|
|
196
|
+
make_finding(
|
|
197
|
+
severity=Severity.LOW,
|
|
198
|
+
file=rel_path,
|
|
199
|
+
line_start=0,
|
|
200
|
+
title="Misconfig scanner file read error",
|
|
201
|
+
description=f"Could not analyze {file_path.name} for misconfigurations: {str(e)}",
|
|
202
|
+
evidence=str(e),
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return findings
|
|
207
|
+
|
|
208
|
+
def _scan_json_config_misconfig(self, content: str, rel_path: str) -> list[Finding]:
|
|
209
|
+
"""Scan JSON configuration files for misconfigurations."""
|
|
210
|
+
findings = []
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
data = json.loads(content)
|
|
214
|
+
|
|
215
|
+
# Check for debug enabled
|
|
216
|
+
if self._find_nested_key(data, ["debug", "DEBUG", "enable_debug", "debug_mode"]):
|
|
217
|
+
findings.append(
|
|
218
|
+
make_finding(
|
|
219
|
+
severity=Severity.HIGH,
|
|
220
|
+
file=rel_path,
|
|
221
|
+
line_start=0,
|
|
222
|
+
title="Debug Enabled in Configuration",
|
|
223
|
+
description="Debug mode appears to be enabled in configuration file",
|
|
224
|
+
evidence="Debug setting found in JSON config",
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Check for insecure settings
|
|
229
|
+
if self._find_nested_key(
|
|
230
|
+
data, ["allow_origin", "origins"], lambda v: v == "*" or v == ["*"]
|
|
231
|
+
):
|
|
232
|
+
findings.append(
|
|
233
|
+
make_finding(
|
|
234
|
+
severity=Severity.HIGH,
|
|
235
|
+
file=rel_path,
|
|
236
|
+
line_start=0,
|
|
237
|
+
title="Insecure CORS Configuration",
|
|
238
|
+
description="CORS policy allows all origins (*)",
|
|
239
|
+
evidence="Wildcard CORS origin found in config",
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
except json.JSONDecodeError as e:
|
|
244
|
+
findings.append(
|
|
245
|
+
make_finding(
|
|
246
|
+
severity=Severity.LOW,
|
|
247
|
+
file=rel_path,
|
|
248
|
+
line_start=e.lineno,
|
|
249
|
+
title="Invalid JSON",
|
|
250
|
+
description=f"JSON syntax error: {str(e)}",
|
|
251
|
+
evidence=e.msg,
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
return findings
|
|
256
|
+
|
|
257
|
+
def _scan_yaml_config_misconfig(self, content: str, rel_path: str) -> list[Finding]:
|
|
258
|
+
"""Scan YAML configuration files for misconfigurations."""
|
|
259
|
+
findings = []
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
data = yaml.safe_load(content)
|
|
263
|
+
|
|
264
|
+
if isinstance(data, dict):
|
|
265
|
+
# Check for debug enabled
|
|
266
|
+
if self._find_nested_key(data, ["debug", "DEBUG", "enable_debug", "debug_mode"]):
|
|
267
|
+
findings.append(
|
|
268
|
+
make_finding(
|
|
269
|
+
severity=Severity.HIGH,
|
|
270
|
+
file=rel_path,
|
|
271
|
+
line_start=0,
|
|
272
|
+
title="Debug Enabled in Configuration",
|
|
273
|
+
description="Debug mode appears to be enabled in configuration file",
|
|
274
|
+
evidence="Debug setting found in YAML config",
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# Check for insecure settings
|
|
279
|
+
if self._find_nested_key(
|
|
280
|
+
data, ["allow_origin", "origins"], lambda v: v == "*" or v == ["*"]
|
|
281
|
+
):
|
|
282
|
+
findings.append(
|
|
283
|
+
make_finding(
|
|
284
|
+
severity=Severity.HIGH,
|
|
285
|
+
file=rel_path,
|
|
286
|
+
line_start=0,
|
|
287
|
+
title="Insecure CORS Configuration",
|
|
288
|
+
description="CORS policy allows all origins (*)",
|
|
289
|
+
evidence="Wildcard CORS origin found in config",
|
|
290
|
+
)
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
except yaml.YAMLError as e:
|
|
294
|
+
findings.append(
|
|
295
|
+
make_finding(
|
|
296
|
+
severity=Severity.HIGH,
|
|
297
|
+
file=rel_path,
|
|
298
|
+
line_start=0, # YAML errors don't typically include line numbers
|
|
299
|
+
title="Invalid YAML",
|
|
300
|
+
description=f"YAML syntax error: {str(e)}",
|
|
301
|
+
evidence=str(e),
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
return findings
|
|
306
|
+
|
|
307
|
+
def _scan_package_json_misconfig(self, content: str, rel_path: str) -> list[Finding]:
|
|
308
|
+
"""Scan package.json for misconfigurations."""
|
|
309
|
+
findings = []
|
|
310
|
+
|
|
311
|
+
try:
|
|
312
|
+
data = json.loads(content)
|
|
313
|
+
|
|
314
|
+
# Check for development dependencies in production
|
|
315
|
+
if "devDependencies" in data and "NODE_ENV" not in content:
|
|
316
|
+
findings.append(
|
|
317
|
+
make_finding(
|
|
318
|
+
severity=Severity.MEDIUM,
|
|
319
|
+
file=rel_path,
|
|
320
|
+
line_start=0,
|
|
321
|
+
title="Potential Development Dependencies in Production",
|
|
322
|
+
description="package.json includes devDependencies without NODE_ENV check",
|
|
323
|
+
evidence="devDependencies found without environment check",
|
|
324
|
+
)
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# Check for scripts that might expose vulnerabilities
|
|
328
|
+
scripts = data.get("scripts", {})
|
|
329
|
+
for script_name, script_content in scripts.items():
|
|
330
|
+
if any(
|
|
331
|
+
keyword in script_content.lower() for keyword in ["inspect", "--debug", "debug"]
|
|
332
|
+
):
|
|
333
|
+
findings.append(
|
|
334
|
+
make_finding(
|
|
335
|
+
severity=Severity.HIGH,
|
|
336
|
+
file=rel_path,
|
|
337
|
+
line_start=0,
|
|
338
|
+
title="Debug Flag in Scripts",
|
|
339
|
+
description=f"Script '{script_name}' contains debugging flags",
|
|
340
|
+
evidence=script_content,
|
|
341
|
+
)
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
except json.JSONDecodeError as e:
|
|
345
|
+
findings.append(
|
|
346
|
+
make_finding(
|
|
347
|
+
severity=Severity.HIGH,
|
|
348
|
+
file=rel_path,
|
|
349
|
+
line_start=e.lineno,
|
|
350
|
+
title="Invalid package.json",
|
|
351
|
+
description=f"JSON syntax error in package.json: {str(e)}",
|
|
352
|
+
evidence=e.msg,
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
return findings
|
|
357
|
+
|
|
358
|
+
def _scan_requirements_txt_misconfig(self, content: str, rel_path: str) -> list[Finding]:
|
|
359
|
+
"""Scan requirements.txt for misconfigurations."""
|
|
360
|
+
findings = []
|
|
361
|
+
|
|
362
|
+
# Check for development packages in production requirements
|
|
363
|
+
dev_packages = ["pytest", "mock", "faker", "factory_boy", "django-debug-toolbar"]
|
|
364
|
+
lines = content.splitlines()
|
|
365
|
+
for i, line in enumerate(lines, 1):
|
|
366
|
+
for pkg in dev_packages:
|
|
367
|
+
if pkg in line.lower():
|
|
368
|
+
findings.append(
|
|
369
|
+
make_finding(
|
|
370
|
+
severity=Severity.MEDIUM,
|
|
371
|
+
file=rel_path,
|
|
372
|
+
line_start=i,
|
|
373
|
+
title="Development Package in Requirements",
|
|
374
|
+
description=f"Development package '{pkg}' found in requirements.txt",
|
|
375
|
+
evidence=line.strip(),
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
return findings
|
|
380
|
+
|
|
381
|
+
def _scan_toml_config_misconfig(self, content: str, rel_path: str) -> list[Finding]:
|
|
382
|
+
"""Scan TOML configuration files for misconfigurations."""
|
|
383
|
+
findings = []
|
|
384
|
+
|
|
385
|
+
# Since Python < 3.11 doesn't have built-in TOML support, we'll just do basic scanning
|
|
386
|
+
lines = content.splitlines()
|
|
387
|
+
|
|
388
|
+
for i, line in enumerate(lines, 1):
|
|
389
|
+
# Look for common misconfiguration patterns
|
|
390
|
+
if any(
|
|
391
|
+
debug_setting in line.lower()
|
|
392
|
+
for debug_setting in ["debug =", "debug =", "enable_debug =", "debug_mode ="]
|
|
393
|
+
):
|
|
394
|
+
if "true" in line.lower() or "1" in line:
|
|
395
|
+
findings.append(
|
|
396
|
+
make_finding(
|
|
397
|
+
severity=Severity.HIGH,
|
|
398
|
+
file=rel_path,
|
|
399
|
+
line_start=i,
|
|
400
|
+
title="Debug Enabled in Configuration",
|
|
401
|
+
description="Debug mode appears to be enabled in TOML configuration",
|
|
402
|
+
evidence=line.strip(),
|
|
403
|
+
)
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
return findings
|
|
407
|
+
|
|
408
|
+
def _scan_env_file_misconfig(self, content: str, rel_path: str) -> list[Finding]:
|
|
409
|
+
"""Scan environment files for misconfigurations."""
|
|
410
|
+
findings = []
|
|
411
|
+
|
|
412
|
+
lines = content.splitlines()
|
|
413
|
+
|
|
414
|
+
for i, line in enumerate(lines, 1):
|
|
415
|
+
# Skip comments and empty lines
|
|
416
|
+
if line.strip().startswith("#") or not line.strip():
|
|
417
|
+
continue
|
|
418
|
+
|
|
419
|
+
# Check for debug enabled
|
|
420
|
+
if any(
|
|
421
|
+
debug_var in line.upper()
|
|
422
|
+
for debug_var in ["DEBUG=", "FLASK_DEBUG=", "DJANGO_DEBUG="]
|
|
423
|
+
):
|
|
424
|
+
if "TRUE" in line.upper() or "1" in line or "YES" in line.upper():
|
|
425
|
+
findings.append(
|
|
426
|
+
make_finding(
|
|
427
|
+
severity=Severity.HIGH,
|
|
428
|
+
file=rel_path,
|
|
429
|
+
line_start=i,
|
|
430
|
+
title="Debug Enabled in Environment",
|
|
431
|
+
description="Debug mode enabled in environment file",
|
|
432
|
+
evidence=line.strip(),
|
|
433
|
+
)
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
# Check for insecure settings
|
|
437
|
+
if any(
|
|
438
|
+
cors_var in line.upper()
|
|
439
|
+
for cors_var in ["CORS_ORIGIN_ALLOW_ALL=", "ALLOWED_HOSTS="]
|
|
440
|
+
):
|
|
441
|
+
if "*" in line or '"*"' in line or "'*'" in line:
|
|
442
|
+
findings.append(
|
|
443
|
+
make_finding(
|
|
444
|
+
severity=Severity.HIGH,
|
|
445
|
+
file=rel_path,
|
|
446
|
+
line_start=i,
|
|
447
|
+
title="Insecure CORS Configuration",
|
|
448
|
+
description="Wildcard allowed in CORS settings",
|
|
449
|
+
evidence=line.strip(),
|
|
450
|
+
)
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
return findings
|
|
454
|
+
|
|
455
|
+
def _scan_general_config_misconfig(self, content: str, rel_path: str) -> list[Finding]:
|
|
456
|
+
"""General configuration file scanning."""
|
|
457
|
+
findings = []
|
|
458
|
+
|
|
459
|
+
lines = content.splitlines()
|
|
460
|
+
|
|
461
|
+
for i, line in enumerate(lines, 1):
|
|
462
|
+
# Look for common misconfiguration patterns
|
|
463
|
+
if any(
|
|
464
|
+
pattern in line.lower()
|
|
465
|
+
for pattern in ["debug=true", "debug: true", "enable_debug=1", "debug_mode=yes"]
|
|
466
|
+
):
|
|
467
|
+
findings.append(
|
|
468
|
+
make_finding(
|
|
469
|
+
severity=Severity.HIGH,
|
|
470
|
+
file=rel_path,
|
|
471
|
+
line_start=i,
|
|
472
|
+
title="Debug Enabled in Configuration",
|
|
473
|
+
description="Debug mode appears to be enabled in configuration",
|
|
474
|
+
evidence=line.strip(),
|
|
475
|
+
)
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
# Check for insecure CORS
|
|
479
|
+
if any(
|
|
480
|
+
cors_pattern in line.lower()
|
|
481
|
+
for cors_pattern in ["*.*", "allowed_origins=*", "origin_allow_all=true"]
|
|
482
|
+
):
|
|
483
|
+
findings.append(
|
|
484
|
+
make_finding(
|
|
485
|
+
severity=Severity.HIGH,
|
|
486
|
+
file=rel_path,
|
|
487
|
+
line_start=i,
|
|
488
|
+
title="Insecure CORS Configuration",
|
|
489
|
+
description="Insecure CORS configuration detected",
|
|
490
|
+
evidence=line.strip(),
|
|
491
|
+
)
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
return findings
|
|
495
|
+
|
|
496
|
+
def _scan_source_file_misconfig(self, file_path: Path, rel_path: str) -> list[Finding]:
|
|
497
|
+
"""Scan source files for misconfigurations."""
|
|
498
|
+
findings = []
|
|
499
|
+
|
|
500
|
+
try:
|
|
501
|
+
content = file_path.read_text(encoding="utf-8")
|
|
502
|
+
lines = content.splitlines()
|
|
503
|
+
|
|
504
|
+
for i, line in enumerate(lines, 1):
|
|
505
|
+
# Look for misconfigurations in source code
|
|
506
|
+
if any(
|
|
507
|
+
debug_pattern in line.lower()
|
|
508
|
+
for debug_pattern in ["debug=true", "debug: true", "enable_debug", "app.debug"]
|
|
509
|
+
):
|
|
510
|
+
if any(on_val in line.lower() for on_val in ["true", "1", "yes", "on"]):
|
|
511
|
+
findings.append(
|
|
512
|
+
make_finding(
|
|
513
|
+
severity=Severity.HIGH,
|
|
514
|
+
file=rel_path,
|
|
515
|
+
line_start=i,
|
|
516
|
+
title="Debug Enabled in Source Code",
|
|
517
|
+
description="Debug mode enabled in source code",
|
|
518
|
+
evidence=line.strip(),
|
|
519
|
+
)
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
# Look for insecure CORS in source code
|
|
523
|
+
if any(
|
|
524
|
+
cors_pattern in line.lower()
|
|
525
|
+
for cors_pattern in ["cors.*[*]", "allow_all_origins", "origins=[*]"]
|
|
526
|
+
):
|
|
527
|
+
findings.append(
|
|
528
|
+
make_finding(
|
|
529
|
+
severity=Severity.HIGH,
|
|
530
|
+
file=rel_path,
|
|
531
|
+
line_start=i,
|
|
532
|
+
title="Insecure CORS Configuration",
|
|
533
|
+
description="Insecure CORS configuration in source code",
|
|
534
|
+
evidence=line.strip(),
|
|
535
|
+
)
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
# Look for missing security headers
|
|
539
|
+
if any(
|
|
540
|
+
header_pattern in line.lower()
|
|
541
|
+
for header_pattern in [
|
|
542
|
+
"x-frame-options",
|
|
543
|
+
"frame_options",
|
|
544
|
+
"content-security-policy",
|
|
545
|
+
]
|
|
546
|
+
):
|
|
547
|
+
if any(
|
|
548
|
+
off_pattern in line.lower()
|
|
549
|
+
for off_pattern in ["sameorigin", "deny", "default-src"]
|
|
550
|
+
):
|
|
551
|
+
# These are generally secure settings
|
|
552
|
+
pass
|
|
553
|
+
else:
|
|
554
|
+
# Check if the header is disabled
|
|
555
|
+
if any(
|
|
556
|
+
disable_pattern in line.lower()
|
|
557
|
+
for disable_pattern in ["none", "null", "empty", "unset"]
|
|
558
|
+
):
|
|
559
|
+
findings.append(
|
|
560
|
+
make_finding(
|
|
561
|
+
severity=Severity.MEDIUM,
|
|
562
|
+
file=rel_path,
|
|
563
|
+
line_start=i,
|
|
564
|
+
title="Missing Security Header",
|
|
565
|
+
description="Security header may not be properly set",
|
|
566
|
+
evidence=line.strip(),
|
|
567
|
+
)
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
except Exception as e:
|
|
571
|
+
findings.append(
|
|
572
|
+
make_finding(
|
|
573
|
+
severity=Severity.LOW,
|
|
574
|
+
file=rel_path,
|
|
575
|
+
line_start=0,
|
|
576
|
+
title="Misconfig scanner source file read error",
|
|
577
|
+
description=f"Could not analyze {file_path.name} for misconfigurations: {str(e)}",
|
|
578
|
+
evidence=str(e),
|
|
579
|
+
)
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
return findings
|
|
583
|
+
|
|
584
|
+
def _find_nested_key(self, data, keys, condition=None):
|
|
585
|
+
"""Helper to find nested keys in config data."""
|
|
586
|
+
if isinstance(data, dict):
|
|
587
|
+
for key, value in data.items():
|
|
588
|
+
if key in keys:
|
|
589
|
+
if condition is None or condition(value):
|
|
590
|
+
return True
|
|
591
|
+
# Recursively search nested dicts
|
|
592
|
+
if isinstance(value, (dict, list)):
|
|
593
|
+
if self._find_nested_key(value, keys, condition):
|
|
594
|
+
return True
|
|
595
|
+
elif isinstance(data, list):
|
|
596
|
+
for item in data:
|
|
597
|
+
if isinstance(item, (dict, list)):
|
|
598
|
+
if self._find_nested_key(item, keys, condition):
|
|
599
|
+
return True
|
|
600
|
+
return False
|