wpsecscan 2.4.0__tar.gz → 2.6.0__tar.gz
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.
- {wpsecscan-2.4.0/wpsecscan.egg-info → wpsecscan-2.6.0}/PKG-INFO +42 -12
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/README.md +36 -11
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/pyproject.toml +16 -2
- wpsecscan-2.6.0/tests/test_board_one_pager.py +69 -0
- wpsecscan-2.6.0/tests/test_burp_zap_import.py +117 -0
- wpsecscan-2.6.0/tests/test_creds_vault_cli.py +87 -0
- wpsecscan-2.6.0/tests/test_diff_agency.py +104 -0
- wpsecscan-2.6.0/tests/test_gh_check_run.py +100 -0
- wpsecscan-2.6.0/tests/test_mobile_api_traversal.py +87 -0
- wpsecscan-2.6.0/tests/test_reference_diff.py +72 -0
- wpsecscan-2.6.0/tests/test_reference_diff_traversal.py +78 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_63.py +6 -6
- wpsecscan-2.6.0/tests/test_scan_zip.py +85 -0
- wpsecscan-2.6.0/tests/test_scheduler.py +102 -0
- wpsecscan-2.6.0/tests/test_scheduler_cron_dow.py +62 -0
- wpsecscan-2.6.0/tests/test_siem.py +162 -0
- wpsecscan-2.6.0/tests/test_sla.py +98 -0
- wpsecscan-2.6.0/tests/test_slack_app.py +59 -0
- wpsecscan-2.6.0/tests/test_user_template.py +72 -0
- wpsecscan-2.6.0/wpsecscan/__init__.py +1 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/__main__.py +1788 -30
- wpsecscan-2.6.0/wpsecscan/_util.py +158 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/ai_assist.py +29 -0
- wpsecscan-2.6.0/wpsecscan/ai_fp_predictor.py +135 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/ai_triage.py +95 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/__init__.py +112 -13
- wpsecscan-2.6.0/wpsecscan/checks/admin_invite_link_scan.py +64 -0
- wpsecscan-2.6.0/wpsecscan/checks/ai_agent_webhook_leak.py +93 -0
- wpsecscan-2.6.0/wpsecscan/checks/ai_plugin_prompt_storage.py +100 -0
- wpsecscan-2.6.0/wpsecscan/checks/algolia_elastic_frontend_keys.py +85 -0
- wpsecscan-2.6.0/wpsecscan/checks/amp_transitional_redirect.py +60 -0
- wpsecscan-2.6.0/wpsecscan/checks/app_passwords_stale_audit.py +122 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/authenticated.py +416 -30
- wpsecscan-2.6.0/wpsecscan/checks/block_bindings_exposure.py +81 -0
- wpsecscan-2.6.0/wpsecscan/checks/block_style_variations_url.py +64 -0
- wpsecscan-2.6.0/wpsecscan/checks/bucket_shadow_takeover.py +115 -0
- wpsecscan-2.6.0/wpsecscan/checks/companion_advanced.py +575 -0
- wpsecscan-2.6.0/wpsecscan/checks/companion_v13.py +194 -0
- wpsecscan-2.6.0/wpsecscan/checks/composer_npm_typosquat.py +99 -0
- wpsecscan-2.6.0/wpsecscan/checks/cookie_consent_desync.py +72 -0
- wpsecscan-2.6.0/wpsecscan/checks/ct_log_shadow_cert.py +105 -0
- wpsecscan-2.6.0/wpsecscan/checks/font_library_api_ssrf.py +58 -0
- wpsecscan-2.6.0/wpsecscan/checks/form_builder_upload_bypass.py +62 -0
- wpsecscan-2.6.0/wpsecscan/checks/gdpr_dsr_endpoint_enum.py +64 -0
- wpsecscan-2.6.0/wpsecscan/checks/github_actions_workflow_leak.py +79 -0
- wpsecscan-2.6.0/wpsecscan/checks/host_platform_detect.py +150 -0
- wpsecscan-2.6.0/wpsecscan/checks/hsts_preload_mismatch.py +88 -0
- wpsecscan-2.6.0/wpsecscan/checks/html_api_csp_nonce.py +82 -0
- wpsecscan-2.6.0/wpsecscan/checks/interactivity_api_state_leak.py +113 -0
- wpsecscan-2.6.0/wpsecscan/checks/jwt_auth_plugin_audit.py +77 -0
- wpsecscan-2.6.0/wpsecscan/checks/lead_gen_list_id_enum.py +68 -0
- wpsecscan-2.6.0/wpsecscan/checks/mcp_endpoint_exposure.py +104 -0
- wpsecscan-2.6.0/wpsecscan/checks/multisite_sso_key_reuse.py +88 -0
- wpsecscan-2.6.0/wpsecscan/checks/plugin_install_rest_race.py +71 -0
- wpsecscan-2.6.0/wpsecscan/checks/pwa_service_worker_cache.py +57 -0
- wpsecscan-2.6.0/wpsecscan/checks/rest_schema_field_leak.py +67 -0
- wpsecscan-2.6.0/wpsecscan/checks/search_highlight_xss.py +54 -0
- wpsecscan-2.6.0/wpsecscan/checks/service_worker_scope_hijack.py +69 -0
- wpsecscan-2.6.0/wpsecscan/checks/speculation_rules_audit.py +75 -0
- wpsecscan-2.6.0/wpsecscan/checks/stripe_webhook_audit.py +92 -0
- wpsecscan-2.6.0/wpsecscan/checks/theme_json_font_ssrf.py +76 -0
- wpsecscan-2.6.0/wpsecscan/checks/translation_plugin_key_leak.py +70 -0
- wpsecscan-2.6.0/wpsecscan/checks/turnstile_sitekey_reuse.py +81 -0
- wpsecscan-2.6.0/wpsecscan/checks/vercel_preview_url_leak.py +75 -0
- wpsecscan-2.6.0/wpsecscan/checks/wc_api_key_escalation.py +54 -0
- wpsecscan-2.6.0/wpsecscan/checks/woo_blocks_checkout_drift.py +70 -0
- wpsecscan-2.6.0/wpsecscan/checks/woo_subscriptions_renewal_race.py +71 -0
- wpsecscan-2.6.0/wpsecscan/checks/wp_cli_http_exposure.py +92 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_fork_detection.py +1 -4
- wpsecscan-2.6.0/wpsecscan/checks/wp_mail_smtp_site_health_leak.py +57 -0
- wpsecscan-2.6.0/wpsecscan/checks/wp_playground_sqlite.py +116 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/completion.py +23 -1
- wpsecscan-2.6.0/wpsecscan/creds_vault.py +249 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/check_tags.json +44 -1
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/compliance_map.json +210 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/dashboard.html.j2 +3 -0
- wpsecscan-2.6.0/wpsecscan/data/datadog-dashboard.json +66 -0
- wpsecscan-2.6.0/wpsecscan/data/newrelic-dashboard.json +57 -0
- wpsecscan-2.6.0/wpsecscan/data/openapi-scan-report.json +115 -0
- wpsecscan-2.6.0/wpsecscan/gh_check_run.py +89 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/gui.py +179 -1
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/gui_windows.py +132 -0
- wpsecscan-2.6.0/wpsecscan/importers/__init__.py +1 -0
- wpsecscan-2.6.0/wpsecscan/importers/burp_zap.py +158 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/webhooks_chat.py +56 -16
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/issue_push.py +138 -0
- wpsecscan-2.6.0/wpsecscan/kev.py +101 -0
- wpsecscan-2.6.0/wpsecscan/mobile_api.py +298 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/playbook.py +43 -11
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/policy.py +74 -0
- wpsecscan-2.6.0/wpsecscan/reference_diff.py +175 -0
- wpsecscan-2.6.0/wpsecscan/reporters/auditor_pdf.py +255 -0
- wpsecscan-2.6.0/wpsecscan/reporters/board_one_pager.py +155 -0
- wpsecscan-2.6.0/wpsecscan/reporters/compliance_attestation.py +183 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/dashboard.py +24 -1
- wpsecscan-2.6.0/wpsecscan/reporters/diff_agency.py +163 -0
- wpsecscan-2.6.0/wpsecscan/reporters/user_template.py +63 -0
- wpsecscan-2.6.0/wpsecscan/risk.py +163 -0
- wpsecscan-2.6.0/wpsecscan/scan_zip.py +189 -0
- wpsecscan-2.6.0/wpsecscan/scheduler.py +221 -0
- wpsecscan-2.6.0/wpsecscan/siem.py +232 -0
- wpsecscan-2.6.0/wpsecscan/sla.py +131 -0
- wpsecscan-2.6.0/wpsecscan/slack_app.py +132 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0/wpsecscan.egg-info}/PKG-INFO +42 -12
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan.egg-info/SOURCES.txt +79 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan.egg-info/requires.txt +6 -0
- wpsecscan-2.4.0/wpsecscan/__init__.py +0 -1
- wpsecscan-2.4.0/wpsecscan/checks/companion_advanced.py +0 -277
- wpsecscan-2.4.0/wpsecscan/risk.py +0 -88
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/LICENSE +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/NOTICE +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/setup.cfg +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_audit_fixes.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_audit_round_r.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_cache.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_checks.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_confidence_eta_tags.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_default_creds.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_global_sigs_regression.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_login_throttle.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_login_throttle_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_new_check_inventory.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_new_checks.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_new_checks_aggressive.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_new_checks_quality.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_notify.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_password_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_payloads.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_phase5.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_playbook.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_prove.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_risk_score.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_54.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_55.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_56_activity.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_57.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_58.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_59.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_60.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_61.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_62.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_64.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_65.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_round_q.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/tests/test_ssh_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/activity.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/ai_safety.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/ai_triage_ui.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/analytics.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/api_server.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/attack_checkpoint.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/attack_scripts.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/auth/__init__.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/auth/approval_workflow.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/auth/audit_log.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/auth/rbac.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/auth/sso_oidc.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/auth/sso_saml.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/auto_pr.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/auto_update.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/baseline.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/branding.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/bug_report.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/burp_import.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/cache.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/check_health.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/_template.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/a11y_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/a11y_lite.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/a11y_wcag_aaa.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/abuseipdb_lookup.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/admin_ajax_brute_surface.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/ai_chatbot_endpoint_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/ai_prompt_injection_passive.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/ajax_surface.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/app_passwords.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/auth_modernisation.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/backup_exposure.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/backup_file_fuzz.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/brand_monitor.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cache_headers.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cache_poisoning.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cache_poisoning_v2.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cdn_edge_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cloud_metadata_ssrf.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cloudflare_origin_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/compliance_frameworks.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/composer_lock_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cookie_consent.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cookies.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/core_checksums.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/core_cves.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/core_tampering.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/core_version.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cors.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/crlf_location_injection.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/crypto_agility.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/crypto_payment_callback_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/cryptominer_js_injection.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/csp.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/csp_report_endpoint.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/csrf_entropy.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/csrf_nonce.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/csv_export_csp.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/ct_log_recent_certs.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/db_admin_login_probe.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/db_trigger_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/debug_leaks.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/debug_log_pii_sniff.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/default_creds.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/dev_params.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/directory_listing.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/dns_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/dns_rebinding.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/dns_security.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/dns_templates.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/dom_xss_headless.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/email_obfuscation_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/email_security_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/env_file_enum.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/error_pages.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/exposed_files.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/favicon_fingerprint.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/file_upload.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/forced_browse.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/gdpr_dsr.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/git_dir_deep_scan.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/github_leak_search.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/graphql_dos.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/graphql_field_authz_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/gtm_inventory.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/gutenberg_blocks.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/header_smuggling_case.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/headless_templates.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/headless_wp_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/heartbeat_abuse.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/heartbeat_frontend.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/helm_compose_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/hibp.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/honeypot_admin.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/host_header_validation.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/host_recon.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/hosting_platform_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/hostname_collision.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/hpp.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/hsts_preload_eligibility.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/http2_settings.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/http2_smuggling.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/http3_fingerprint.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/http_methods.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/js_framework_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/js_libraries.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/js_supply_chain.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/jwt_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/login.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/login_redirect_http_hop.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/login_throttle.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/login_throttle_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/login_timing.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/magecart_skimmer_patterns.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/mfa_priv_account_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/misc_injection_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/mixed_content.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/mobile_app_endpoints.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/multisite.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/nft_mint_pubapi.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/nonce_freshness.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/nosql_injection.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/oauth_oidc.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/oauth_redirect.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/oauth_redirect_misconfig.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/object_cache_dropin.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/open_redirect.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/open_registration.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/openapi_scanner.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/origin_ip_discovery.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/osint_enrich.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/package_lock_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/page_builder_cve.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/path_bypass.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/path_traversal.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/payment_commerce_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/payment_gateway_test_keys.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/perf_budget.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/permissions_policy.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/php_eol.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/phpinfo_dangerous_directives.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/plugin_archive_fuzz.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/plugin_cemetery.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/plugin_cves.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/plugin_hash_fingerprint.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/plugin_route_fuzz.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/plugin_specific_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/plugin_typosquat_detection.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/plugins.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/postmeta_stored_xss_scan.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/premium_license_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/privacy_inventory.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/prototype_pollution.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/race_condition.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/redirect_chain.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/referenced_buckets.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/rest_api.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/rest_app_passwords_enum.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/rest_fields_dos.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/rest_link_header.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/rest_namespace_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/rest_permission_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/robots_sitemap.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/rum_beacons.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/s3_bucket_discovery.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/saml_xsw.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/secret_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/security_txt.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/sendmail_injection.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/server_stack_reveal.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/server_timing.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/service_exposure.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/session_fixation.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/sitemap_cve_probe.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/smuggling_probe.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/solidity_abi_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/source_maps.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/spider_crawl.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/sqli.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/sri_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/sri_pwa_misc.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/ssrf.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/ssti.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/subdomains.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/tailwind_css_comment_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/theme_cves.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/themes.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/timthumb.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/tls_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/tls_headers.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/tls_modern.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/tls_reneg_dos.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/upload_bypass_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/upload_path_predictable.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/uploads_year_listing.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/users.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/users_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/users_me_capability_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/vendor_backdoor_patterns.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/waf.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/waf_brand_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/waf_bypass_probe.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/waf_lockout_guard.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/waf_ruleset.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wallet_seed_phrase_leak.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/web3_wallet_connector_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/webdav.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/webhook_signing_secrets.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/webhook_url_fingerprint.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/webhooks.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/websocket_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/websocket_fuzz.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/well_known.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/woocommerce_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/woocommerce_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/woocommerce_order_idor.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/woocommerce_storefront.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_builder_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_cli_inject.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_commerce_alt_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_cron_cpu.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_cron_disabled.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_cron_dos.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_debug_display_via_rest.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_engine_misconfig.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_form_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_membership_lms_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_multisite_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_plugin_ecosystem_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_query_sqli.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_rest_methods.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wp_salts_age.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wpconfig_hardening_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wpcron_suspicious_jobs.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/wpgraphql.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/xmlrpc_amplification.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/xmlrpc_deep.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/xmlrpc_method_brute.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/xss_dom_sinks.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/xss_reflected.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/xxe_upload.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/yaml_templates.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/yaml_workflows.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/checks/yarn_pnpm_lock_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/confidence.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/config.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/console_live.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/continuous_monitor.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/crash_submit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/daemon/__init__.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/daemon/_legacy.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/daemon/webhook_v2.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/common_paths.txt +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/compliance_extra.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/compliance_v2.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/exploit_playbook.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/exploit_signatures.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/known_paths.txt +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/marketplace.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/payloads.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/plugin_cves.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/plugin_file_hashes.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/quick_fixes.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/references.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/remediation_videos.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/report.html.j2 +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/report.schema.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/data/security_tutorial.json +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/db.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/demo.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/diff.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/education.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/enterprise/__init__.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/enterprise/billing_stub.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/enterprise/multi_tenant.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/enterprise/quota.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/eta.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/fun/__init__.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/fun/bingo_card.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/gui_payloads.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/har_replay.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/hardware_keys.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/heatmap.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/history.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/http.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/i18n.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/incremental/__init__.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/incremental/_legacy.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/incremental/diff_scan.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/incremental/smart_skip.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/__init__.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/cisa_kev.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/epss.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/github_issues.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/osint.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/sucuri_sitecheck.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/threat_intel.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/ticketing.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/tor_proxy.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/integrations/virustotal.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/interactsh.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/js_plugin.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/licensing.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/log.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/marketplace.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/mobile_app_discovery.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/models.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/monitors.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/notify.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/observability.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/password_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/payloads.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/perf/__init__.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/perf/_legacy.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/perf/connection_pool.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/perf/parallel_sites.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/pr_inspector.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/prove.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/py.typed +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/recommend.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/region_egress.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/remediation_videos.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/report_query.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/__init__.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/attestation.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/badge_svg.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/bounty_format.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/burp_export.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/comparison_two_sites.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/console.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/csv_out.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/diff_viewer.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/docx_report.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/eli5_toggle.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/exec_pdf.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/executive_pack.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/html.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/issue_export.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/json_out.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/markdown.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/org_dashboard.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/pdf_custom_branding.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/public_page.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/sarif.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/snapshot_compare.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/translated_summary.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/trend_over_time.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/reporters/xlsx_out.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/risk_weights.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/sbom.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/scanner.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/sites.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/spider.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/ssh_audit.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/tags.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/template_engine.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/template_signature.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/threat_intel_v2.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/tray.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/turbo_engine.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/ua_rotation.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/ux_extras.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/waf_rules.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/watchers.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan/workflow.py +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan.egg-info/dependency_links.txt +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan.egg-info/entry_points.txt +0 -0
- {wpsecscan-2.4.0 → wpsecscan-2.6.0}/wpsecscan.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wpsecscan
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Defensive WordPress security scanner. 200+ checks, 8-source nightly CVE aggregator, AI-assisted remediation (BYO key), 15 compliance frameworks (OWASP/PCI/NIST/ISO/HIPAA/SOC2/HITRUST/CMMC), Sigstore-signed releases with SLSA L3 provenance, 10 threat-intel feeds (KEV/EPSS/Exploit-DB/ATT&CK/STIX/MISP/OTX/GreyNoise), continuous monitors, consent-gated exploit verification, multi-tenant RBAC/SSO/audit-log enterprise mode, 12 report formats. Authorized testing only.
|
|
5
5
|
Author-email: Bryan <bryaninbangkok@gmail.com>
|
|
6
6
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
@@ -693,6 +693,7 @@ Requires-Dist: httpx[http2]<0.29,>=0.27
|
|
|
693
693
|
Requires-Dist: jinja2<4,>=3.1
|
|
694
694
|
Requires-Dist: rich<14,>=13.7
|
|
695
695
|
Requires-Dist: openpyxl<4,>=3.1
|
|
696
|
+
Requires-Dist: keyring>=24
|
|
696
697
|
Provides-Extra: pdf
|
|
697
698
|
Requires-Dist: reportlab>=4.0; extra == "pdf"
|
|
698
699
|
Provides-Extra: browser
|
|
@@ -705,6 +706,8 @@ Requires-Dist: bcrypt>=4.0; extra == "ops"
|
|
|
705
706
|
Provides-Extra: ui
|
|
706
707
|
Requires-Dist: Pillow>=10; extra == "ui"
|
|
707
708
|
Requires-Dist: pystray>=0.19; extra == "ui"
|
|
709
|
+
Provides-Extra: security
|
|
710
|
+
Requires-Dist: defusedxml>=0.7; extra == "security"
|
|
708
711
|
Provides-Extra: all
|
|
709
712
|
Requires-Dist: reportlab>=4.0; extra == "all"
|
|
710
713
|
Requires-Dist: playwright>=1.40; extra == "all"
|
|
@@ -713,6 +716,8 @@ Requires-Dist: redis>=5.0; extra == "all"
|
|
|
713
716
|
Requires-Dist: bcrypt>=4.0; extra == "all"
|
|
714
717
|
Requires-Dist: Pillow>=10; extra == "all"
|
|
715
718
|
Requires-Dist: pystray>=0.19; extra == "all"
|
|
719
|
+
Requires-Dist: keyring>=24; extra == "all"
|
|
720
|
+
Requires-Dist: defusedxml>=0.7; extra == "all"
|
|
716
721
|
Provides-Extra: test
|
|
717
722
|
Requires-Dist: pytest>=7.4; extra == "test"
|
|
718
723
|
Dynamic: license-file
|
|
@@ -725,9 +730,10 @@ Dynamic: license-file
|
|
|
725
730
|
[](https://github.com/bryanflowers/wpsecscan/releases/latest)
|
|
726
731
|
[](https://github.com/bryanflowers/wpsecscan/releases)
|
|
727
732
|
[](https://www.python.org/)
|
|
728
|
-
[](FEATURES.md)
|
|
729
734
|
[](docs/data-sources.md)
|
|
730
|
-
[](tests/)
|
|
736
|
+
[](https://pypi.org/project/wpsecscan/)
|
|
731
737
|
[](docs/verify-release.md)
|
|
732
738
|
[](docs/verify-release.md)
|
|
733
739
|
[](FEATURES.md)
|
|
@@ -744,7 +750,7 @@ Dynamic: license-file
|
|
|
744
750
|
|
|
745
751
|
**The most thoroughly-sourced WordPress vulnerability scanner — open source, AGPLv3, runs locally.**
|
|
746
752
|
|
|
747
|
-
|
|
753
|
+
226 checks across 18 categories. **8-source nightly CVE aggregator**
|
|
748
754
|
(NVD + GHSA + Mitre + OSV + Wordfence + WPVulnerability + CIRCL +
|
|
749
755
|
Patchstack). **SLSA L3 + Sigstore-signed releases**. **10-provider
|
|
750
756
|
threat-intel federation** (CISA KEV, EPSS, Exploit-DB, Metasploit,
|
|
@@ -802,7 +808,31 @@ Ships as two standalone Windows binaries — no Python required on the machine y
|
|
|
802
808
|
|
|
803
809
|
## Quick install
|
|
804
810
|
|
|
805
|
-
### Option A —
|
|
811
|
+
### Option A — `pip install wpsecscan` (any platform)
|
|
812
|
+
|
|
813
|
+
The simplest path on Linux, macOS, or Windows-with-Python:
|
|
814
|
+
|
|
815
|
+
```bash
|
|
816
|
+
pip install wpsecscan
|
|
817
|
+
wpsecscan --version
|
|
818
|
+
wpsecscan https://example.com --json-only
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
PyPI: <https://pypi.org/project/wpsecscan/>
|
|
822
|
+
|
|
823
|
+
For the optional GUI minimize-to-tray feature, install with the
|
|
824
|
+
`[ui]` extra (pulls in Pillow + pystray):
|
|
825
|
+
|
|
826
|
+
```bash
|
|
827
|
+
pip install "wpsecscan[ui]"
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
Other optional extras: `[pdf]` (reportlab for true PDF exec reports),
|
|
831
|
+
`[browser]` (playwright for headless DOM-XSS), `[yaml]` (pyyaml for
|
|
832
|
+
daemon config), `[ops]` (redis + bcrypt for enterprise mode), or
|
|
833
|
+
`[all]` to install everything.
|
|
834
|
+
|
|
835
|
+
### Option B — pre-built Windows binaries
|
|
806
836
|
|
|
807
837
|
1. Open `dist\` in this folder.
|
|
808
838
|
2. Copy `wpsecscan.exe` and `wpsecscan-gui.exe` somewhere on your PATH
|
|
@@ -812,7 +842,7 @@ Ships as two standalone Windows binaries — no Python required on the machine y
|
|
|
812
842
|
The first time you run them, Windows SmartScreen may warn that the publisher is unknown
|
|
813
843
|
(the binaries aren't code-signed). Click **More info → Run anyway**.
|
|
814
844
|
|
|
815
|
-
### Option
|
|
845
|
+
### Option C — build from source
|
|
816
846
|
|
|
817
847
|
Requires Python 3.10+ and PyInstaller. Open PowerShell in the project root:
|
|
818
848
|
|
|
@@ -830,15 +860,15 @@ dist\wpsecscan-gui.exe # GUI (~17 MB)
|
|
|
830
860
|
|
|
831
861
|
Both are single-file executables — copy them anywhere and they run on their own.
|
|
832
862
|
|
|
833
|
-
### Option
|
|
863
|
+
### Option D — run from source directly (development)
|
|
834
864
|
|
|
835
865
|
```powershell
|
|
836
866
|
python -m venv .venv
|
|
837
867
|
.\.venv\Scripts\Activate.ps1
|
|
838
|
-
pip install
|
|
868
|
+
pip install .
|
|
839
869
|
python run.py https://example.com # CLI
|
|
840
870
|
python run_gui.py # GUI
|
|
841
|
-
pytest #
|
|
871
|
+
pytest # 780 tests
|
|
842
872
|
```
|
|
843
873
|
|
|
844
874
|
---
|
|
@@ -883,7 +913,7 @@ wpsecscan.exe https://your-wp-site.com --wpscan-token <KEY>
|
|
|
883
913
|
|
|
884
914
|
---
|
|
885
915
|
|
|
886
|
-
## What it checks (
|
|
916
|
+
## What it checks (226 checks)
|
|
887
917
|
|
|
888
918
|
Passive checks always run; aggressive checks need `--aggressive`.
|
|
889
919
|
|
|
@@ -1166,12 +1196,12 @@ to see errors).
|
|
|
1166
1196
|
|
|
1167
1197
|
| Category | Count |
|
|
1168
1198
|
|---------------------------|-------|
|
|
1169
|
-
| Checks | **
|
|
1199
|
+
| Checks | **226** |
|
|
1170
1200
|
| Payloads | **224** |
|
|
1171
1201
|
| Exploit signatures | **307** |
|
|
1172
1202
|
| Plugin CVE database | ~7,000 (Wordfence) + 7 other sources via the nightly aggregator |
|
|
1173
1203
|
| Exploit-playbook entries | **25** |
|
|
1174
|
-
| Tests | **
|
|
1204
|
+
| Tests | **667** |
|
|
1175
1205
|
|
|
1176
1206
|
---
|
|
1177
1207
|
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
[](https://github.com/bryanflowers/wpsecscan/releases/latest)
|
|
7
7
|
[](https://github.com/bryanflowers/wpsecscan/releases)
|
|
8
8
|
[](https://www.python.org/)
|
|
9
|
-
[](FEATURES.md)
|
|
10
10
|
[](docs/data-sources.md)
|
|
11
|
-
[](tests/)
|
|
12
|
+
[](https://pypi.org/project/wpsecscan/)
|
|
12
13
|
[](docs/verify-release.md)
|
|
13
14
|
[](docs/verify-release.md)
|
|
14
15
|
[](FEATURES.md)
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
|
|
26
27
|
**The most thoroughly-sourced WordPress vulnerability scanner — open source, AGPLv3, runs locally.**
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
226 checks across 18 categories. **8-source nightly CVE aggregator**
|
|
29
30
|
(NVD + GHSA + Mitre + OSV + Wordfence + WPVulnerability + CIRCL +
|
|
30
31
|
Patchstack). **SLSA L3 + Sigstore-signed releases**. **10-provider
|
|
31
32
|
threat-intel federation** (CISA KEV, EPSS, Exploit-DB, Metasploit,
|
|
@@ -83,7 +84,31 @@ Ships as two standalone Windows binaries — no Python required on the machine y
|
|
|
83
84
|
|
|
84
85
|
## Quick install
|
|
85
86
|
|
|
86
|
-
### Option A —
|
|
87
|
+
### Option A — `pip install wpsecscan` (any platform)
|
|
88
|
+
|
|
89
|
+
The simplest path on Linux, macOS, or Windows-with-Python:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install wpsecscan
|
|
93
|
+
wpsecscan --version
|
|
94
|
+
wpsecscan https://example.com --json-only
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
PyPI: <https://pypi.org/project/wpsecscan/>
|
|
98
|
+
|
|
99
|
+
For the optional GUI minimize-to-tray feature, install with the
|
|
100
|
+
`[ui]` extra (pulls in Pillow + pystray):
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pip install "wpsecscan[ui]"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Other optional extras: `[pdf]` (reportlab for true PDF exec reports),
|
|
107
|
+
`[browser]` (playwright for headless DOM-XSS), `[yaml]` (pyyaml for
|
|
108
|
+
daemon config), `[ops]` (redis + bcrypt for enterprise mode), or
|
|
109
|
+
`[all]` to install everything.
|
|
110
|
+
|
|
111
|
+
### Option B — pre-built Windows binaries
|
|
87
112
|
|
|
88
113
|
1. Open `dist\` in this folder.
|
|
89
114
|
2. Copy `wpsecscan.exe` and `wpsecscan-gui.exe` somewhere on your PATH
|
|
@@ -93,7 +118,7 @@ Ships as two standalone Windows binaries — no Python required on the machine y
|
|
|
93
118
|
The first time you run them, Windows SmartScreen may warn that the publisher is unknown
|
|
94
119
|
(the binaries aren't code-signed). Click **More info → Run anyway**.
|
|
95
120
|
|
|
96
|
-
### Option
|
|
121
|
+
### Option C — build from source
|
|
97
122
|
|
|
98
123
|
Requires Python 3.10+ and PyInstaller. Open PowerShell in the project root:
|
|
99
124
|
|
|
@@ -111,15 +136,15 @@ dist\wpsecscan-gui.exe # GUI (~17 MB)
|
|
|
111
136
|
|
|
112
137
|
Both are single-file executables — copy them anywhere and they run on their own.
|
|
113
138
|
|
|
114
|
-
### Option
|
|
139
|
+
### Option D — run from source directly (development)
|
|
115
140
|
|
|
116
141
|
```powershell
|
|
117
142
|
python -m venv .venv
|
|
118
143
|
.\.venv\Scripts\Activate.ps1
|
|
119
|
-
pip install
|
|
144
|
+
pip install .
|
|
120
145
|
python run.py https://example.com # CLI
|
|
121
146
|
python run_gui.py # GUI
|
|
122
|
-
pytest #
|
|
147
|
+
pytest # 780 tests
|
|
123
148
|
```
|
|
124
149
|
|
|
125
150
|
---
|
|
@@ -164,7 +189,7 @@ wpsecscan.exe https://your-wp-site.com --wpscan-token <KEY>
|
|
|
164
189
|
|
|
165
190
|
---
|
|
166
191
|
|
|
167
|
-
## What it checks (
|
|
192
|
+
## What it checks (226 checks)
|
|
168
193
|
|
|
169
194
|
Passive checks always run; aggressive checks need `--aggressive`.
|
|
170
195
|
|
|
@@ -447,12 +472,12 @@ to see errors).
|
|
|
447
472
|
|
|
448
473
|
| Category | Count |
|
|
449
474
|
|---------------------------|-------|
|
|
450
|
-
| Checks | **
|
|
475
|
+
| Checks | **226** |
|
|
451
476
|
| Payloads | **224** |
|
|
452
477
|
| Exploit signatures | **307** |
|
|
453
478
|
| Plugin CVE database | ~7,000 (Wordfence) + 7 other sources via the nightly aggregator |
|
|
454
479
|
| Exploit-playbook entries | **25** |
|
|
455
|
-
| Tests | **
|
|
480
|
+
| Tests | **667** |
|
|
456
481
|
|
|
457
482
|
---
|
|
458
483
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "wpsecscan"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.6.0"
|
|
8
8
|
description = "Defensive WordPress security scanner. 200+ checks, 8-source nightly CVE aggregator, AI-assisted remediation (BYO key), 15 compliance frameworks (OWASP/PCI/NIST/ISO/HIPAA/SOC2/HITRUST/CMMC), Sigstore-signed releases with SLSA L3 provenance, 10 threat-intel feeds (KEV/EPSS/Exploit-DB/ATT&CK/STIX/MISP/OTX/GreyNoise), continuous monitors, consent-gated exploit verification, multi-tenant RBAC/SSO/audit-log enterprise mode, 12 report formats. Authorized testing only."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -36,6 +36,12 @@ dependencies = [
|
|
|
36
36
|
"jinja2>=3.1,<4",
|
|
37
37
|
"rich>=13.7,<14",
|
|
38
38
|
"openpyxl>=3.1,<4",
|
|
39
|
+
# Q8: keyring is needed by the `wpsecscan creds` default-CLI surface
|
|
40
|
+
# (post-v2.5.0). Previously in [ui] extra; default install silently
|
|
41
|
+
# fell back to plain JSON. creds_vault still gracefully handles the
|
|
42
|
+
# missing-keyring case so headless installs without a backend keep
|
|
43
|
+
# working (the FailBackend check in creds_vault._have_keyring).
|
|
44
|
+
"keyring>=24",
|
|
39
45
|
]
|
|
40
46
|
|
|
41
47
|
[project.optional-dependencies]
|
|
@@ -47,8 +53,14 @@ browser = ["playwright>=1.40"]
|
|
|
47
53
|
yaml = ["pyyaml>=6.0"]
|
|
48
54
|
# Distributed CVE-DB cache + RBAC bcrypt hashing
|
|
49
55
|
ops = ["redis>=5.0", "bcrypt>=4.0"]
|
|
50
|
-
# Optional Pillow for GUI image rendering + pystray for minimize-to-tray (#56)
|
|
56
|
+
# Optional Pillow for GUI image rendering + pystray for minimize-to-tray (#56).
|
|
57
|
+
# (keyring moved to default `dependencies` in v2.5.1 because the `wpsecscan
|
|
58
|
+
# creds` CLI surface is no longer GUI-only.)
|
|
51
59
|
ui = ["Pillow>=10", "pystray>=0.19"]
|
|
60
|
+
# S5 — defusedxml protects the burp/zap importer against billion-laughs
|
|
61
|
+
# entity-expansion DoS on hostile XML reports. Stdlib ElementTree falls
|
|
62
|
+
# back with a stderr warning when this isn't installed.
|
|
63
|
+
security = ["defusedxml>=0.7"]
|
|
52
64
|
# Everything optional in one install
|
|
53
65
|
all = [
|
|
54
66
|
"reportlab>=4.0",
|
|
@@ -58,6 +70,8 @@ all = [
|
|
|
58
70
|
"bcrypt>=4.0",
|
|
59
71
|
"Pillow>=10",
|
|
60
72
|
"pystray>=0.19",
|
|
73
|
+
"keyring>=24",
|
|
74
|
+
"defusedxml>=0.7",
|
|
61
75
|
]
|
|
62
76
|
# Test deps (also installed by CI)
|
|
63
77
|
test = ["pytest>=7.4"]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Wave 3 — tests for wpsecscan/reporters/board_one_pager.py."""
|
|
2
|
+
from wpsecscan.models import CheckResult, Finding, ScanReport
|
|
3
|
+
from wpsecscan.reporters import board_one_pager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _make(*sevs):
|
|
7
|
+
return ScanReport(
|
|
8
|
+
target="https://example.com", scanned_at="2026-05-27T00:00:00",
|
|
9
|
+
duration_ms=0,
|
|
10
|
+
results=[CheckResult(check_id="x", check_name="X",
|
|
11
|
+
findings=[Finding(severity=s, title=f"{s} finding")
|
|
12
|
+
for s in sevs])],
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_render_returns_html():
|
|
17
|
+
rep = _make("high", "high", "medium")
|
|
18
|
+
html = board_one_pager.render(rep)
|
|
19
|
+
assert html.startswith("<!doctype html>")
|
|
20
|
+
assert "</html>" in html.rstrip()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_render_includes_three_big_numbers():
|
|
24
|
+
rep = _make("critical", "high")
|
|
25
|
+
html = board_one_pager.render(rep)
|
|
26
|
+
# The risk-score, delta, and open-crit+high blocks all appear
|
|
27
|
+
assert "Risk score / 100" in html
|
|
28
|
+
assert "vs prior scan" in html
|
|
29
|
+
assert "Critical & High open" in html
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_render_includes_target_and_timestamp():
|
|
33
|
+
rep = _make("low")
|
|
34
|
+
html = board_one_pager.render(rep)
|
|
35
|
+
assert "https://example.com" in html
|
|
36
|
+
assert "2026-05-27T00:00:00" in html
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_bucket_message_score_brackets():
|
|
40
|
+
"""The headline text changes per score band."""
|
|
41
|
+
_, h_90, _ = board_one_pager._bucket_message(95)
|
|
42
|
+
_, h_70, _ = board_one_pager._bucket_message(80)
|
|
43
|
+
_, h_50, _ = board_one_pager._bucket_message(60)
|
|
44
|
+
_, h_low, _ = board_one_pager._bucket_message(20)
|
|
45
|
+
assert "strong" in h_90.lower()
|
|
46
|
+
assert "acceptable" in h_70.lower()
|
|
47
|
+
assert "material" in h_50.lower()
|
|
48
|
+
assert "significant" in h_low.lower()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_render_three_action_bullets_present():
|
|
52
|
+
rep = _make("critical")
|
|
53
|
+
html = board_one_pager.render(rep)
|
|
54
|
+
# The render emits <li> per action; bucketed message ships three actions.
|
|
55
|
+
assert html.count("<li>") == 3
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_render_handles_empty_report():
|
|
59
|
+
rep = ScanReport(target="https://x", scanned_at="t", duration_ms=0, results=[])
|
|
60
|
+
html = board_one_pager.render(rep)
|
|
61
|
+
assert "0</div><div class=\"lab\">Critical & High open" in html
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_write_creates_file(tmp_path):
|
|
65
|
+
rep = _make("high")
|
|
66
|
+
p = tmp_path / "board.html"
|
|
67
|
+
board_one_pager.write(rep, p)
|
|
68
|
+
assert p.exists()
|
|
69
|
+
assert "Board summary" in p.read_text(encoding="utf-8") or "board" in p.read_text(encoding="utf-8").lower()
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Wave 3 — tests for wpsecscan/importers/burp_zap.py."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from wpsecscan.importers import burp_zap
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_BURP_XML = """<?xml version="1.0"?>
|
|
10
|
+
<issues>
|
|
11
|
+
<issue>
|
|
12
|
+
<name>SQL injection</name>
|
|
13
|
+
<severity>High</severity>
|
|
14
|
+
<host>https://target.example/</host>
|
|
15
|
+
<path>/?id=</path>
|
|
16
|
+
<issueDetail>POST id=1' AND 1=1-- returned 500</issueDetail>
|
|
17
|
+
<issueBackground>Classic time-based blind</issueBackground>
|
|
18
|
+
<remediationBackground>Use prepared statements</remediationBackground>
|
|
19
|
+
</issue>
|
|
20
|
+
<issue>
|
|
21
|
+
<name>Mixed content</name>
|
|
22
|
+
<severity>Information</severity>
|
|
23
|
+
<host>https://target.example/</host>
|
|
24
|
+
<path>/about</path>
|
|
25
|
+
<issueDetail>http:// image referenced</issueDetail>
|
|
26
|
+
</issue>
|
|
27
|
+
</issues>
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
_ZAP_XML = """<?xml version="1.0"?>
|
|
31
|
+
<OWASPZAPReport version="2.14">
|
|
32
|
+
<site name="https://target.example">
|
|
33
|
+
<alerts>
|
|
34
|
+
<alertitem>
|
|
35
|
+
<alert>X-Frame-Options Header Not Set</alert>
|
|
36
|
+
<riskcode>2</riskcode>
|
|
37
|
+
<desc>Header missing</desc>
|
|
38
|
+
<solution>Add X-Frame-Options: DENY</solution>
|
|
39
|
+
<instances><instance><uri>https://target.example/</uri></instance></instances>
|
|
40
|
+
</alertitem>
|
|
41
|
+
<alertitem>
|
|
42
|
+
<alert>SQL Injection</alert>
|
|
43
|
+
<riskcode>3</riskcode>
|
|
44
|
+
<desc>Param 'id' is vulnerable</desc>
|
|
45
|
+
<solution>Prepared statements</solution>
|
|
46
|
+
<instances><instance><uri>https://target.example/?id=</uri></instance></instances>
|
|
47
|
+
</alertitem>
|
|
48
|
+
</alerts>
|
|
49
|
+
</site>
|
|
50
|
+
</OWASPZAPReport>
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_burp_import_basic(tmp_path):
|
|
55
|
+
p = tmp_path / "burp.xml"
|
|
56
|
+
p.write_text(_BURP_XML, encoding="utf-8")
|
|
57
|
+
rep = burp_zap.import_burp(p)
|
|
58
|
+
# 2 findings, one synthetic check_id
|
|
59
|
+
findings = rep.all_findings
|
|
60
|
+
assert len(findings) == 2
|
|
61
|
+
assert {f.severity for f in findings} == {"high", "info"}
|
|
62
|
+
assert any(f.title == "SQL injection" for f in findings)
|
|
63
|
+
assert all(f.extra.get("source") == "burp" for f in findings)
|
|
64
|
+
assert rep.target == "https://target.example/"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_zap_import_basic(tmp_path):
|
|
68
|
+
p = tmp_path / "zap.xml"
|
|
69
|
+
p.write_text(_ZAP_XML, encoding="utf-8")
|
|
70
|
+
rep = burp_zap.import_zap(p)
|
|
71
|
+
findings = rep.all_findings
|
|
72
|
+
assert len(findings) == 2
|
|
73
|
+
assert {f.severity for f in findings} == {"medium", "high"}
|
|
74
|
+
assert all(f.extra.get("source") == "zap" for f in findings)
|
|
75
|
+
assert "target.example" in rep.target
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_autoimport_sniffs_zap(tmp_path):
|
|
79
|
+
p = tmp_path / "z.xml"
|
|
80
|
+
p.write_text(_ZAP_XML, encoding="utf-8")
|
|
81
|
+
rep = burp_zap.autoimport(p)
|
|
82
|
+
assert rep.results[0].check_id == "imported_zap"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_autoimport_falls_back_to_burp(tmp_path):
|
|
86
|
+
p = tmp_path / "b.xml"
|
|
87
|
+
p.write_text(_BURP_XML, encoding="utf-8")
|
|
88
|
+
rep = burp_zap.autoimport(p)
|
|
89
|
+
assert rep.results[0].check_id == "imported_burp"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_target_override(tmp_path):
|
|
93
|
+
p = tmp_path / "b.xml"
|
|
94
|
+
p.write_text(_BURP_XML, encoding="utf-8")
|
|
95
|
+
rep = burp_zap.import_burp(p, target_override="https://override.example")
|
|
96
|
+
assert rep.target == "https://override.example"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_burp_severity_mapping(tmp_path):
|
|
100
|
+
"""Burp 'Information' must map to wpsecscan 'info'."""
|
|
101
|
+
xml = _BURP_XML.replace("<severity>High</severity>",
|
|
102
|
+
"<severity>Information</severity>")
|
|
103
|
+
p = tmp_path / "b.xml"
|
|
104
|
+
p.write_text(xml, encoding="utf-8")
|
|
105
|
+
rep = burp_zap.import_burp(p)
|
|
106
|
+
sevs = [f.severity for f in rep.all_findings]
|
|
107
|
+
assert sevs.count("info") == 2 # both findings now map to info
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_zap_unknown_riskcode_defaults_medium(tmp_path):
|
|
111
|
+
"""An unknown ZAP riskcode falls back to medium per _SEV_MAP default."""
|
|
112
|
+
xml = _ZAP_XML.replace("<riskcode>2</riskcode>", "<riskcode>99</riskcode>")
|
|
113
|
+
p = tmp_path / "z.xml"
|
|
114
|
+
p.write_text(xml, encoding="utf-8")
|
|
115
|
+
rep = burp_zap.import_zap(p)
|
|
116
|
+
sevs = [f.severity for f in rep.all_findings]
|
|
117
|
+
assert "medium" in sevs # the fallback severity
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Wave 3 — tests for the creds_vault module's public API.
|
|
2
|
+
|
|
3
|
+
The fallback storage path runs whenever keyring is unavailable or its
|
|
4
|
+
backend is FailBackend. We monkeypatch `_have_keyring` to force the
|
|
5
|
+
fallback path so the test is hermetic on any host.
|
|
6
|
+
"""
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from wpsecscan import creds_vault
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture(autouse=True)
|
|
16
|
+
def force_fallback(monkeypatch, tmp_path):
|
|
17
|
+
monkeypatch.setenv("WPSECSCAN_HOME", str(tmp_path))
|
|
18
|
+
monkeypatch.setattr(creds_vault, "_have_keyring", lambda: False)
|
|
19
|
+
yield
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_set_get_round_trip():
|
|
23
|
+
creds_vault.set_secret("https://example.com", "username", "admin")
|
|
24
|
+
creds_vault.set_secret("https://example.com", "password", "s3cret!")
|
|
25
|
+
assert creds_vault.get_secret("https://example.com", "username") == "admin"
|
|
26
|
+
assert creds_vault.get_secret("https://example.com", "password") == "s3cret!"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_missing_secret_returns_none():
|
|
30
|
+
assert creds_vault.get_secret("https://nope", "username") is None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_delete_secret():
|
|
34
|
+
creds_vault.set_secret("https://x", "field", "value")
|
|
35
|
+
assert creds_vault.delete_secret("https://x", "field") is True
|
|
36
|
+
assert creds_vault.get_secret("https://x", "field") is None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_list_sites_after_set():
|
|
40
|
+
creds_vault.set_secret("https://a", "username", "u")
|
|
41
|
+
creds_vault.set_secret("https://b", "password", "p")
|
|
42
|
+
sites = dict(creds_vault.list_sites())
|
|
43
|
+
assert sites["https://a"] == ["username"]
|
|
44
|
+
assert sites["https://b"] == ["password"]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_list_fields_for():
|
|
48
|
+
creds_vault.set_secret("https://x", "username", "u")
|
|
49
|
+
creds_vault.set_secret("https://x", "password", "p")
|
|
50
|
+
fields = creds_vault.list_fields_for("https://x")
|
|
51
|
+
assert set(fields) == {"username", "password"}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_rotate_overwrites():
|
|
55
|
+
creds_vault.set_secret("https://x", "password", "old")
|
|
56
|
+
creds_vault.rotate_secret("https://x", "password", "new")
|
|
57
|
+
assert creds_vault.get_secret("https://x", "password") == "new"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_backend_in_use_reports_fallback():
|
|
61
|
+
assert creds_vault.backend_in_use() == "fallback"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_fallback_file_mode_posix(tmp_path):
|
|
65
|
+
"""On POSIX, the fallback vault file should be 0600 from creation."""
|
|
66
|
+
import os
|
|
67
|
+
creds_vault.set_secret("https://x", "username", "u")
|
|
68
|
+
p = tmp_path / "creds-vault.json"
|
|
69
|
+
assert p.exists()
|
|
70
|
+
if os.name != "nt":
|
|
71
|
+
mode = p.stat().st_mode & 0o777
|
|
72
|
+
assert mode == 0o600, f"expected 0600, got {oct(mode)}"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_multi_account_via_field_suffix():
|
|
76
|
+
"""The CLI encodes multi-account as field@account; vault stores it as-is."""
|
|
77
|
+
creds_vault.set_secret("https://x", "username@admin1", "alice")
|
|
78
|
+
creds_vault.set_secret("https://x", "username@admin2", "bob")
|
|
79
|
+
assert creds_vault.get_secret("https://x", "username@admin1") == "alice"
|
|
80
|
+
assert creds_vault.get_secret("https://x", "username@admin2") == "bob"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_delete_removes_from_index():
|
|
84
|
+
creds_vault.set_secret("https://x", "f", "v")
|
|
85
|
+
creds_vault.delete_secret("https://x", "f")
|
|
86
|
+
sites = dict(creds_vault.list_sites())
|
|
87
|
+
assert "https://x" not in sites
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Wave 3 — tests for wpsecscan/reporters/diff_agency.py."""
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from wpsecscan.reporters import diff_agency
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _dashboard_html(manifest: dict) -> str:
|
|
11
|
+
"""Synthesize the minimum dashboard HTML diff_agency reads from."""
|
|
12
|
+
return (
|
|
13
|
+
'<html><body><table>...</table>'
|
|
14
|
+
f'<script type="application/json" id="wpsecscan-dashboard-data">'
|
|
15
|
+
f'{json.dumps(manifest)}</script></body></html>'
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_extract_manifest_from_script(tmp_path):
|
|
20
|
+
p = tmp_path / "d.html"
|
|
21
|
+
p.write_text(_dashboard_html({"generated_at": "t", "totals": {},
|
|
22
|
+
"sites": [{"target": "https://x",
|
|
23
|
+
"risk_score": 90,
|
|
24
|
+
"worst": "low",
|
|
25
|
+
"summary": {}}]}),
|
|
26
|
+
encoding="utf-8")
|
|
27
|
+
out = diff_agency._extract_manifest(p.read_text(encoding="utf-8"))
|
|
28
|
+
assert out["sites"][0]["target"] == "https://x"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_extract_manifest_fallback_html_scrape(tmp_path):
|
|
32
|
+
"""Pre-v2.5.0 dashboards have no embedded manifest — table-scrape works."""
|
|
33
|
+
html = (
|
|
34
|
+
"<html><body><table>"
|
|
35
|
+
"<tr><td>https://a.example</td><td>x</td><td>87/100</td></tr>"
|
|
36
|
+
"<tr><td>https://b.example</td><td>x</td><td>42/100</td></tr>"
|
|
37
|
+
"</table></body></html>"
|
|
38
|
+
)
|
|
39
|
+
out = diff_agency._extract_manifest(html)
|
|
40
|
+
assert len(out["sites"]) == 2
|
|
41
|
+
assert out["sites"][0]["target"] == "https://a.example"
|
|
42
|
+
assert out["sites"][0]["risk_score"] == 87
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_diff_added_removed_changed():
|
|
46
|
+
old = {"sites": [
|
|
47
|
+
{"target": "https://a", "risk_score": 90, "worst": "low", "summary": {}},
|
|
48
|
+
{"target": "https://b", "risk_score": 70, "worst": "high", "summary": {}},
|
|
49
|
+
], "totals": {"critical": 0, "high": 1, "medium": 0, "low": 0, "info": 0}}
|
|
50
|
+
new = {"sites": [
|
|
51
|
+
{"target": "https://a", "risk_score": 95, "worst": "low", "summary": {}}, # improved
|
|
52
|
+
{"target": "https://c", "risk_score": 100, "worst": None, "summary": {}}, # new
|
|
53
|
+
], "totals": {"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0}}
|
|
54
|
+
d = diff_agency.diff(old, new)
|
|
55
|
+
assert [s["target"] for s in d["added"]] == ["https://c"]
|
|
56
|
+
assert [s["target"] for s in d["removed"]] == ["https://b"]
|
|
57
|
+
assert len(d["changed"]) == 1
|
|
58
|
+
assert d["changed"][0]["target"] == "https://a"
|
|
59
|
+
assert d["changed"][0]["delta"] == 5
|
|
60
|
+
assert d["totals_delta"]["high"] == -1
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_diff_empty_inputs():
|
|
64
|
+
d = diff_agency.diff({"sites": [], "totals": {}}, {"sites": [], "totals": {}})
|
|
65
|
+
assert d["added"] == []
|
|
66
|
+
assert d["removed"] == []
|
|
67
|
+
assert d["changed"] == []
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_render_html_contains_section_headings():
|
|
71
|
+
d = {
|
|
72
|
+
"old_at": "t1", "new_at": "t2",
|
|
73
|
+
"site_count_old": 1, "site_count_new": 1,
|
|
74
|
+
"totals_delta": {"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0},
|
|
75
|
+
"added": [],
|
|
76
|
+
"removed": [],
|
|
77
|
+
"changed": [{"target": "https://x", "old_score": 80, "new_score": 85,
|
|
78
|
+
"delta": 5, "old_worst": "high", "new_worst": "medium"}],
|
|
79
|
+
}
|
|
80
|
+
html = diff_agency.render_html(d)
|
|
81
|
+
assert "Score / severity changes (1)" in html
|
|
82
|
+
assert "https://x" in html
|
|
83
|
+
assert "+5" in html
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_write_round_trip(tmp_path):
|
|
87
|
+
old = tmp_path / "old.html"
|
|
88
|
+
new = tmp_path / "new.html"
|
|
89
|
+
old.write_text(_dashboard_html({"generated_at": "t1", "totals": {},
|
|
90
|
+
"sites": [{"target": "https://x",
|
|
91
|
+
"risk_score": 80,
|
|
92
|
+
"worst": "high",
|
|
93
|
+
"summary": {}}]}),
|
|
94
|
+
encoding="utf-8")
|
|
95
|
+
new.write_text(_dashboard_html({"generated_at": "t2", "totals": {},
|
|
96
|
+
"sites": [{"target": "https://x",
|
|
97
|
+
"risk_score": 90,
|
|
98
|
+
"worst": "medium",
|
|
99
|
+
"summary": {}}]}),
|
|
100
|
+
encoding="utf-8")
|
|
101
|
+
out = tmp_path / "diff.html"
|
|
102
|
+
d = diff_agency.write(old, new, out)
|
|
103
|
+
assert out.exists()
|
|
104
|
+
assert d["changed"][0]["delta"] == 10
|