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,141 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ApiFuzzerAgent §5.3.1 — EvoMaster-style REST/GraphQL fuzz generation.
|
|
3
|
+
|
|
4
|
+
From RouteMapper 18fw routes → generates malformed JSON, massive payloads,
|
|
5
|
+
wrong Content-Types, injection strings via InputFuzzer. Optionally shells out
|
|
6
|
+
to EvoMaster (WebFuzzing/EvoMaster) if `evomaster.jar` or docker is available,
|
|
7
|
+
else falls back to local InputFuzzer + in-process HTTP smoke.
|
|
8
|
+
|
|
9
|
+
Does NOT require a running server for generation; fuzz payloads are persisted
|
|
10
|
+
to .patchi/fuzz/<route>.json for DAST replay via --with-fuzz.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import shutil
|
|
18
|
+
import subprocess
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from patchi.core.agents.base import (
|
|
22
|
+
AgentGroup,
|
|
23
|
+
AgentInput,
|
|
24
|
+
AgentResult,
|
|
25
|
+
AgentStatus,
|
|
26
|
+
BaseAgent,
|
|
27
|
+
Severity,
|
|
28
|
+
make_finding,
|
|
29
|
+
register,
|
|
30
|
+
)
|
|
31
|
+
from patchi.core.brain.route_mapper import RouteInfo
|
|
32
|
+
from patchi.core.fuzz.input_fuzzer import InputFuzzer
|
|
33
|
+
|
|
34
|
+
_log = logging.getLogger("patchi.agents.api_fuzzer")
|
|
35
|
+
|
|
36
|
+
_EVO_DOCKER = "evomaster/evomaster:latest"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _try_evomaster(routes: list[RouteInfo], out_dir: Path) -> int:
|
|
40
|
+
"""Try EvoMaster jar/docker — returns count or 0 if not available."""
|
|
41
|
+
jar = shutil.which("evomaster.jar") or shutil.which("java")
|
|
42
|
+
docker = shutil.which("docker")
|
|
43
|
+
if not jar and not docker:
|
|
44
|
+
return 0
|
|
45
|
+
# Probe only if OpenAPI spec exists
|
|
46
|
+
if not (out_dir.parent / "openapi.json").exists() and not (out_dir.parent / "swagger.json").exists():
|
|
47
|
+
return 0
|
|
48
|
+
try:
|
|
49
|
+
# Use docker EvoMaster in blackbox mode against openapi.json if present
|
|
50
|
+
spec = out_dir.parent / "openapi.json"
|
|
51
|
+
if not spec.exists():
|
|
52
|
+
spec = out_dir.parent / "swagger.json"
|
|
53
|
+
cmd = ["docker", "run", "--rm", "-v", f"{spec}:/tmp/spec.json", _EVO_DOCKER, "--blackBox", "true", "--bbSwaggerUrl", "file:///tmp/spec.json", "--outputFolder", "/tmp/out", "--maxTime", "30s"] if docker else None
|
|
54
|
+
if not cmd:
|
|
55
|
+
return 0
|
|
56
|
+
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
|
57
|
+
return 1 if proc.returncode in (0, 1) else 0
|
|
58
|
+
except Exception as exc: # noqa: BLE001
|
|
59
|
+
_log.debug("evomaster probe failed: %s", exc)
|
|
60
|
+
return 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@register
|
|
64
|
+
class ApiFuzzerAgent(BaseAgent):
|
|
65
|
+
group = AgentGroup.SECURITY
|
|
66
|
+
name = "ApiFuzzerAgent"
|
|
67
|
+
description = "EvoMaster-style API fuzz payloads from RouteMapper routes (REST/GraphQL) → .patchi/fuzz/"
|
|
68
|
+
timeout = 90
|
|
69
|
+
|
|
70
|
+
def _run(self, inp: AgentInput, result: AgentResult) -> None:
|
|
71
|
+
# Routes from brain (RouteMapper) or live scan
|
|
72
|
+
routes: list[RouteInfo] = []
|
|
73
|
+
try:
|
|
74
|
+
from patchi.core.brain.file_corpus import FileCorpus
|
|
75
|
+
from patchi.core.brain.framework import FrameworkDetector
|
|
76
|
+
from patchi.core.brain.route_mapper import RouteMapper
|
|
77
|
+
|
|
78
|
+
corpus = FileCorpus(inp.root)
|
|
79
|
+
stack = FrameworkDetector(inp.root, corpus=corpus).detect()
|
|
80
|
+
mapper = RouteMapper(inp.root, stack)
|
|
81
|
+
# Use inp.brain routes if present to avoid re-parse
|
|
82
|
+
if inp.brain.get("routes"):
|
|
83
|
+
routes = [RouteInfo(**r) if isinstance(r, dict) else r for r in inp.brain.get("routes", [])]
|
|
84
|
+
else:
|
|
85
|
+
from patchi.core.brain.scanner import FileScanner
|
|
86
|
+
routes = mapper.extract(FileScanner(inp.root).scan())
|
|
87
|
+
except Exception as exc: # noqa: BLE001
|
|
88
|
+
_log.debug("route extract failed: %s", exc)
|
|
89
|
+
routes = []
|
|
90
|
+
|
|
91
|
+
if not routes:
|
|
92
|
+
result.status = AgentStatus.SUCCEEDED
|
|
93
|
+
result.data["fuzz_count"] = 0
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
fuzzer = InputFuzzer(seed=0xF22A)
|
|
97
|
+
out_dir = inp.root / ".patchi" / "fuzz"
|
|
98
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
99
|
+
|
|
100
|
+
# Try EvoMaster first
|
|
101
|
+
evo = _try_evomaster(routes, out_dir)
|
|
102
|
+
|
|
103
|
+
fuzz_count = 0
|
|
104
|
+
for r in routes[:50]: # cap 50 routes
|
|
105
|
+
# Skip health/static
|
|
106
|
+
if r.path in ("/health", "/healthz", "/metrics") or r.path.startswith("/static/"):
|
|
107
|
+
continue
|
|
108
|
+
payloads = []
|
|
109
|
+
# 1. Boundary JSON
|
|
110
|
+
payloads.extend([f.to_dict() for f in fuzzer.fuzz_dict({"id": 1, "name": "test"}, count=5)])
|
|
111
|
+
# 2. Injection strings as query/body values
|
|
112
|
+
for fi in fuzzer.fuzz_string(r.path, count=6):
|
|
113
|
+
payloads.append({"strategy": fi.strategy, "value": fi.value, "label": fi.label})
|
|
114
|
+
# 3. Method fuzz: wrong method
|
|
115
|
+
payloads.append({"strategy": "method", "value": "WRONG_METHOD", "label": "method_fuzz"})
|
|
116
|
+
# Persist
|
|
117
|
+
fname = f"{r.method.lower()}_{r.path.strip('/').replace('/','_') or 'root'}.json"
|
|
118
|
+
fname = "".join(c if c.isalnum() or c in "._-" else "_" for c in fname)[:120]
|
|
119
|
+
try:
|
|
120
|
+
(out_dir / fname).write_text(json.dumps({"route": r.to_dict(), "payloads": payloads[:12]}, indent=2), encoding="utf-8")
|
|
121
|
+
fuzz_count += 1
|
|
122
|
+
except OSError as exc:
|
|
123
|
+
_log.warning("ApiFuzzer write failed: %s", exc)
|
|
124
|
+
|
|
125
|
+
# Finding per route with high-risk payload count
|
|
126
|
+
result.findings.append(
|
|
127
|
+
make_finding(
|
|
128
|
+
severity=Severity.INFO,
|
|
129
|
+
file=r.file,
|
|
130
|
+
line_start=r.line,
|
|
131
|
+
title=f"Fuzz payloads for {r.method} {r.path}",
|
|
132
|
+
description=f"Generated {len(payloads[:12])} EvoMaster/InputFuzzer payloads (boundary/injection/encoding) for DAST replay --with-fuzz. EvoMaster: {'yes' if evo else 'local'}",
|
|
133
|
+
evidence=r.path,
|
|
134
|
+
finding_type="fuzz_payload",
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
result.status = AgentStatus.SUCCEEDED
|
|
139
|
+
result.data["fuzz_count"] = fuzz_count
|
|
140
|
+
result.data["evomaster"] = bool(evo)
|
|
141
|
+
result.files_scanned = len(routes)
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AttackAgent — Metasploit auxiliary/scanner probe integration.
|
|
3
|
+
|
|
4
|
+
Uses pymetasploit3 bridge to msfrpcd to run auxiliary/scanner modules
|
|
5
|
+
against the local project. Never targets external hosts — localhost only.
|
|
6
|
+
|
|
7
|
+
Flow:
|
|
8
|
+
1. Read project brain data (framework, ports, routes)
|
|
9
|
+
2. Pick relevant auxiliary/scanner modules based on framework
|
|
10
|
+
3. Connect to msfrpcd on localhost:55553
|
|
11
|
+
4. Run each module against 127.0.0.1
|
|
12
|
+
5. Return findings as structured JSON
|
|
13
|
+
|
|
14
|
+
Optional dependency: pymetasploit3 (pip install patchi[msf])
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
21
|
+
from concurrent.futures import TimeoutError as FutureTimeout
|
|
22
|
+
|
|
23
|
+
from .base import (
|
|
24
|
+
AgentGroup,
|
|
25
|
+
AgentInput,
|
|
26
|
+
AgentResult,
|
|
27
|
+
BaseAgent,
|
|
28
|
+
Finding,
|
|
29
|
+
Severity,
|
|
30
|
+
register,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# ── Framework → auxiliary/scanner module mapping ──────────────────────────────
|
|
34
|
+
|
|
35
|
+
# Shared module groups to avoid duplication
|
|
36
|
+
_HTTP_COMMON = [
|
|
37
|
+
"scanner/http/http_header_xss",
|
|
38
|
+
"scanner/http/http_version",
|
|
39
|
+
"scanner/http/options",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
_FRAMEWORK_MODULES: dict[str, list[str]] = {
|
|
43
|
+
"flask": [*_HTTP_COMMON, "scanner/http/http_methods"],
|
|
44
|
+
"django": [*_HTTP_COMMON, "scanner/http/http_methods"],
|
|
45
|
+
"fastapi": _HTTP_COMMON,
|
|
46
|
+
"express": _HTTP_COMMON,
|
|
47
|
+
"react": [
|
|
48
|
+
"scanner/http/http_header_xss",
|
|
49
|
+
"scanner/http/http_version",
|
|
50
|
+
],
|
|
51
|
+
"spring": [*_HTTP_COMMON, "scanner/http/http_methods"],
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_FALLBACK_MODULES = _HTTP_COMMON
|
|
55
|
+
|
|
56
|
+
_DEFAULT_PORT = 8000
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@register
|
|
60
|
+
class AttackAgent(BaseAgent):
|
|
61
|
+
"""Metasploit auxiliary/scanner probe integration (localhost only)."""
|
|
62
|
+
|
|
63
|
+
group = AgentGroup.TEST
|
|
64
|
+
name = "AttackAgent"
|
|
65
|
+
timeout = 120 # Metasploit modules can take a while
|
|
66
|
+
|
|
67
|
+
def _run(self, inp: AgentInput, result: AgentResult) -> None:
|
|
68
|
+
# ── 1. Check if pymetasploit3 is available ────────────────────────────
|
|
69
|
+
try:
|
|
70
|
+
import pymetasploit3 # noqa: F401
|
|
71
|
+
except ImportError:
|
|
72
|
+
# Optional dependency — skip cleanly, never FAIL the agent run.
|
|
73
|
+
self.skip(result, "pymetasploit3 not installed. Run: pip install patchi[msf]")
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# ── 2. Read brain data for framework / port hints ─────────────────────
|
|
77
|
+
brain = inp.brain
|
|
78
|
+
frameworks = _get_frameworks(brain)
|
|
79
|
+
routes = brain.get("routes", [])
|
|
80
|
+
|
|
81
|
+
# Determine target port from config, routes, or default
|
|
82
|
+
target_port = _pick_port(brain, routes)
|
|
83
|
+
target_ip = "127.0.0.1"
|
|
84
|
+
result.data["target"] = f"{target_ip}:{target_port}"
|
|
85
|
+
result.data["frameworks"] = frameworks
|
|
86
|
+
|
|
87
|
+
# ── 3. Pick modules ──────────────────────────────────────────────────
|
|
88
|
+
modules = _pick_modules(frameworks)
|
|
89
|
+
|
|
90
|
+
result.data["modules_selected"] = modules
|
|
91
|
+
|
|
92
|
+
# ── 4. Connect to msfrpcd and run modules ────────────────────────────
|
|
93
|
+
from pymetasploit3.msfrpc import MsfRpcClient
|
|
94
|
+
|
|
95
|
+
password = inp.extra.get("msf_password", "")
|
|
96
|
+
msf_port = inp.extra.get("msf_port", 55553)
|
|
97
|
+
msf_ssl = inp.extra.get("msf_ssl", True)
|
|
98
|
+
msf_module_timeout = inp.extra.get("msf_module_timeout", 30)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
client = MsfRpcClient(password, port=msf_port, ssl=msf_ssl)
|
|
102
|
+
except Exception as exc:
|
|
103
|
+
result.add_error(
|
|
104
|
+
f"Cannot connect to msfrpcd at 127.0.0.1:{msf_port} — {exc}. "
|
|
105
|
+
f"Make sure msfrpcd is running (msfrpcd -P your_password -S -a 127.0.0.1 -p {msf_port})"
|
|
106
|
+
)
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
with ThreadPoolExecutor(max_workers=1) as pool:
|
|
110
|
+
for module_name in modules:
|
|
111
|
+
future = pool.submit(
|
|
112
|
+
self._run_module, client, module_name, target_ip, target_port, modules, result
|
|
113
|
+
)
|
|
114
|
+
try:
|
|
115
|
+
finding = future.result(timeout=msf_module_timeout)
|
|
116
|
+
if finding:
|
|
117
|
+
result.findings.append(finding)
|
|
118
|
+
except FutureTimeout:
|
|
119
|
+
result.errors.append(f"{module_name}: timed out after {msf_module_timeout}s")
|
|
120
|
+
except Exception as exc:
|
|
121
|
+
result.errors.append(f"{module_name}: {exc}")
|
|
122
|
+
|
|
123
|
+
def _run_module(
|
|
124
|
+
self,
|
|
125
|
+
client,
|
|
126
|
+
module_name: str,
|
|
127
|
+
target_ip: str,
|
|
128
|
+
target_port: int,
|
|
129
|
+
modules: list[str],
|
|
130
|
+
result: AgentResult,
|
|
131
|
+
) -> Finding | None:
|
|
132
|
+
"""Run a single auxiliary/scanner module and return a Finding."""
|
|
133
|
+
try:
|
|
134
|
+
aux = client.modules.use("auxiliary", module_name)
|
|
135
|
+
aux["RHOSTS"] = target_ip
|
|
136
|
+
aux["RPORT"] = target_port
|
|
137
|
+
raw = aux.execute()
|
|
138
|
+
except Exception as exc:
|
|
139
|
+
result.errors.append(f"{module_name}: {exc}")
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
if not raw:
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
# Normalise result to dict
|
|
146
|
+
if isinstance(raw, str):
|
|
147
|
+
try:
|
|
148
|
+
parsed = json.loads(raw)
|
|
149
|
+
except (json.JSONDecodeError, ValueError):
|
|
150
|
+
parsed = {"raw_output": raw[:500]}
|
|
151
|
+
else:
|
|
152
|
+
parsed = raw if isinstance(raw, dict) else {"data": str(raw)}
|
|
153
|
+
|
|
154
|
+
# Determine severity based on module output
|
|
155
|
+
severity = _classify_severity(module_name, parsed)
|
|
156
|
+
|
|
157
|
+
# Store full result in data
|
|
158
|
+
result.data.setdefault("module_results", {})[module_name] = parsed
|
|
159
|
+
|
|
160
|
+
# Build a Finding for display
|
|
161
|
+
message = f"{module_name} — {severity.value}"
|
|
162
|
+
detail_lines = []
|
|
163
|
+
if isinstance(parsed, dict):
|
|
164
|
+
for k, v in list(parsed.items())[:5]:
|
|
165
|
+
detail_lines.append(f"{k}: {v}")
|
|
166
|
+
detail = "\n".join(detail_lines)
|
|
167
|
+
|
|
168
|
+
return Finding(
|
|
169
|
+
agent=self.name,
|
|
170
|
+
type="metasploit_aux",
|
|
171
|
+
severity=severity,
|
|
172
|
+
file="metasploit",
|
|
173
|
+
line=0,
|
|
174
|
+
message=message[:120],
|
|
175
|
+
detail=detail[:1000] or "No detail returned",
|
|
176
|
+
extra={
|
|
177
|
+
"module": module_name,
|
|
178
|
+
"target": f"{target_ip}:{target_port}",
|
|
179
|
+
"raw": parsed if isinstance(parsed, dict) else {"output": str(parsed)[:500]},
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _get_frameworks(brain: dict) -> list[str]:
|
|
188
|
+
"""Extract framework names from brain data."""
|
|
189
|
+
frameworks = []
|
|
190
|
+
fw_raw = brain.get("framework", "")
|
|
191
|
+
if fw_raw and fw_raw != "Unknown":
|
|
192
|
+
frameworks.append(fw_raw.lower())
|
|
193
|
+
|
|
194
|
+
fws_raw = brain.get("frameworks", [])
|
|
195
|
+
for fw in fws_raw:
|
|
196
|
+
name = ""
|
|
197
|
+
if isinstance(fw, dict):
|
|
198
|
+
name = fw.get("name", "")
|
|
199
|
+
elif isinstance(fw, str):
|
|
200
|
+
name = fw
|
|
201
|
+
if name and name.lower() not in frameworks:
|
|
202
|
+
frameworks.append(name.lower())
|
|
203
|
+
return frameworks
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _pick_port(brain: dict, routes: list) -> int:
|
|
207
|
+
"""Pick the most likely target port from brain/routes data."""
|
|
208
|
+
config_fallback = brain.get("config", {}).get("port", None)
|
|
209
|
+
if config_fallback:
|
|
210
|
+
return int(config_fallback)
|
|
211
|
+
return _DEFAULT_PORT
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _pick_modules(frameworks: list[str]) -> list[str]:
|
|
215
|
+
"""Select relevant auxiliary/scanner modules based on detected frameworks."""
|
|
216
|
+
selected: list[str] = []
|
|
217
|
+
seen: set[str] = set()
|
|
218
|
+
for fw in frameworks:
|
|
219
|
+
for mod in _FRAMEWORK_MODULES.get(fw, []):
|
|
220
|
+
if mod not in seen:
|
|
221
|
+
selected.append(mod)
|
|
222
|
+
seen.add(mod)
|
|
223
|
+
if not selected:
|
|
224
|
+
selected = list(_FALLBACK_MODULES)
|
|
225
|
+
return selected
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _classify_severity(module_name: str, result: dict) -> Severity:
|
|
229
|
+
"""Heuristic to map module results to a severity level."""
|
|
230
|
+
job_id = result.get("job_id")
|
|
231
|
+
if job_id is not None:
|
|
232
|
+
return Severity.INFO
|
|
233
|
+
|
|
234
|
+
raw_out = str(result).lower()
|
|
235
|
+
vuln_keywords = ["vulnerable", "found", "detected", "xss", "cve", "open", "leaked"]
|
|
236
|
+
if any(kw in raw_out for kw in vuln_keywords):
|
|
237
|
+
return Severity.MEDIUM
|
|
238
|
+
|
|
239
|
+
if result.get("error"):
|
|
240
|
+
return Severity.LOW
|
|
241
|
+
|
|
242
|
+
return Severity.INFO
|