wpsecscan 2.7.1__tar.gz → 2.7.3__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.7.1/wpsecscan.egg-info → wpsecscan-2.7.3}/PKG-INFO +19 -19
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/pyproject.toml +25 -17
- wpsecscan-2.7.3/tests/test_v272_audit_log_constant_time.py +49 -0
- wpsecscan-2.7.3/tests/test_v272_marketplace_sigstore_identity.py +134 -0
- wpsecscan-2.7.3/tests/test_v272_wave2_regressions.py +199 -0
- wpsecscan-2.7.3/tests/test_v272_wave3_regressions.py +143 -0
- wpsecscan-2.7.3/tests/test_v272_wave4_regressions.py +85 -0
- wpsecscan-2.7.3/tests/test_v273_critical_regressions.py +199 -0
- wpsecscan-2.7.3/tests/test_v273_wave2_regressions.py +296 -0
- wpsecscan-2.7.3/tests/test_v273_wave3_companion.py +79 -0
- wpsecscan-2.7.3/tests/test_v273_wave5_audit_log_wired.py +102 -0
- wpsecscan-2.7.3/wpsecscan/__init__.py +1 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/__main__.py +8 -3
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_assist.py +37 -13
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_safety.py +47 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_triage.py +63 -5
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/api_server.py +3 -1
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/audit_log.py +59 -5
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/authenticated.py +7 -1
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login_redirect_http_hop.py +8 -1
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/continuous_monitor.py +7 -1
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/creds_vault.py +32 -4
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gui.py +108 -25
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gui_v27_extras.py +12 -4
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/hardware_keys.py +9 -2
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/history.py +63 -8
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations_v27.py +19 -3
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/interactsh.py +16 -6
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/marketplace_v27.py +101 -3
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/observability.py +14 -2
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf/_legacy.py +8 -3
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf_v27.py +5 -1
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/share_link.py +49 -6
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/trust_v27.py +23 -3
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ua_rotation.py +6 -2
- {wpsecscan-2.7.1 → wpsecscan-2.7.3/wpsecscan.egg-info}/PKG-INFO +19 -19
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan.egg-info/SOURCES.txt +9 -0
- wpsecscan-2.7.3/wpsecscan.egg-info/requires.txt +39 -0
- wpsecscan-2.7.1/wpsecscan/__init__.py +0 -1
- wpsecscan-2.7.1/wpsecscan.egg-info/requires.txt +0 -39
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/LICENSE +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/NOTICE +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/README.md +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/setup.cfg +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_audit_fixes.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_audit_round_r.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_board_one_pager.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_burp_zap_import.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_cache.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_checks.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_confidence_eta_tags.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_creds_vault_cli.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_default_creds.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_diff_agency.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_gh_check_run.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_global_sigs_regression.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_integrations_graphql_escape.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_login_throttle.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_login_throttle_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_marketplace_install_url_guard.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_mobile_api_traversal.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_new_check_inventory.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_new_checks.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_new_checks_aggressive.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_new_checks_quality.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_notify.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_password_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_payloads.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_phase5.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_playbook.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_prove.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_reference_diff.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_reference_diff_traversal.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_risk_score.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_54.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_55.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_56_activity.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_57.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_58.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_59.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_60.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_61.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_62.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_63.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_64.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_65.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_q.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_scan_zip.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_scheduler.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_scheduler_cron_dow.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_siem.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_sla.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_slack_app.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_ssh_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_user_template.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/_util.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/activity.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_fp_predictor.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_triage_ui.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/analytics.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/attack_checkpoint.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/attack_scripts.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/approval_workflow.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/rbac.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/sso_oidc.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/sso_saml.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auto_pr.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auto_update.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/baseline.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/branding.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/bug_report.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/burp_import.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/cache.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/check_health.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/_template.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/a11y_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/a11y_lite.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/a11y_wcag_aaa.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/abuseipdb_lookup.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/admin_ajax_brute_surface.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/admin_invite_link_scan.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ai_agent_webhook_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ai_chatbot_endpoint_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ai_plugin_prompt_storage.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ai_prompt_injection_passive.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ajax_surface.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/algolia_elastic_frontend_keys.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/amp_transitional_redirect.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/app_passwords.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/app_passwords_stale_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/auth_modernisation.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/backup_exposure.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/backup_file_fuzz.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/block_bindings_exposure.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/block_style_variations_url.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/brand_monitor.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/bucket_shadow_takeover.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cache_headers.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cache_poisoning.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cache_poisoning_v2.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cdn_edge_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cloud_metadata_ssrf.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cloudflare_origin_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/companion_advanced.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/companion_v13.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/companion_v14.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/compliance_frameworks.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/composer_lock_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/composer_npm_typosquat.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cookie_consent.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cookie_consent_desync.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cookies.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/core_checksums.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/core_cves.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/core_tampering.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/core_version.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cors.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/crlf_location_injection.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/crypto_agility.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/crypto_payment_callback_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cryptominer_js_injection.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csp.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csp_report_endpoint.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csrf_entropy.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csrf_nonce.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csv_export_csp.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ct_log_recent_certs.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ct_log_shadow_cert.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/db_admin_login_probe.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/db_trigger_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/debug_leaks.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/debug_log_pii_sniff.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/default_creds.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dev_params.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/directory_listing.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dns_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dns_rebinding.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dns_security.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dns_templates.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dom_xss_headless.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/email_obfuscation_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/email_security_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/env_file_enum.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/error_pages.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/exposed_files.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/favicon_fingerprint.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/file_upload.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/font_library_api_ssrf.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/forced_browse.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/form_builder_upload_bypass.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/gdpr_dsr.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/gdpr_dsr_endpoint_enum.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/git_dir_deep_scan.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/github_actions_workflow_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/github_leak_search.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/graphql_dos.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/graphql_field_authz_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/gtm_inventory.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/gutenberg_blocks.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/header_smuggling_case.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/headless_templates.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/headless_vercel_netlify_detect.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/headless_wp_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/heartbeat_abuse.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/heartbeat_frontend.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/helm_compose_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hibp.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/honeypot_admin.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/host_header_validation.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/host_platform_detect.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/host_recon.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hosting_platform_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hostname_collision.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hpp.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hsts_preload_eligibility.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hsts_preload_mismatch.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/html_api_csp_nonce.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/http2_settings.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/http2_smuggling.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/http3_fingerprint.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/http_methods.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/interactivity_api_state_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/js_framework_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/js_libraries.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/js_supply_chain.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/jwt_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/jwt_auth_plugin_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/lead_gen_list_id_enum.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login_throttle.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login_throttle_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login_timing.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/magecart_skimmer_patterns.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/mcp_endpoint_exposure.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/mfa_priv_account_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/misc_injection_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/mixed_content.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/mobile_app_endpoints.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/multisite.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/multisite_sso_key_reuse.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/nft_mint_pubapi.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/nonce_freshness.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/nosql_injection.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/oauth_oidc.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/oauth_redirect.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/oauth_redirect_misconfig.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/object_cache_dropin.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/open_redirect.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/open_registration.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/openapi_scanner.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/origin_ip_discovery.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/osint_enrich.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/package_lock_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/page_builder_cve.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/path_bypass.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/path_traversal.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/payment_commerce_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/payment_gateway_test_keys.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/perf_budget.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/perf_of_target.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/permissions_policy.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/php_eol.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/phpinfo_dangerous_directives.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_archive_fuzz.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_cemetery.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_cves.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_hash_fingerprint.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_install_rest_race.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_route_fuzz.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_specific_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_typosquat_detection.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugins.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/postmeta_stored_xss_scan.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/premium_license_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/privacy_inventory.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/prototype_pollution.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/pwa_service_worker_cache.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/race_condition.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/redirect_chain.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/referenced_buckets.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_api.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_app_passwords_enum.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_fields_dos.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_link_header.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_namespace_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_permission_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_schema_field_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/robots_sitemap.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rum_beacons.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/s3_bucket_discovery.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/saml_xsw.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/search_highlight_xss.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/secret_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/security_txt.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sendmail_injection.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/server_stack_reveal.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/server_timing.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/service_exposure.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/service_worker_scope_hijack.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/session_fixation.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sitemap_cve_probe.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/smuggling_probe.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/solidity_abi_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/source_maps.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/speculation_rules_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/spider_crawl.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sqli.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sri_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sri_pwa_misc.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ssrf.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ssti.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/stripe_webhook_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/subdomains.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tailwind_css_comment_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/theme_cves.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/theme_json_font_ssrf.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/themes.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/timthumb.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tls_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tls_headers.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tls_modern.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tls_reneg_dos.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/translation_plugin_key_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/trellis_yaml_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/turnstile_sitekey_reuse.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/upload_bypass_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/upload_path_predictable.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/uploads_year_listing.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/users.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/users_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/users_me_capability_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/vendor_backdoor_patterns.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/vercel_preview_url_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf_brand_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf_bypass_probe.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf_lockout_guard.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf_ruleset.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wallet_seed_phrase_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wc_api_key_escalation.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/web3_wallet_connector_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/webdav.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/webhook_signing_secrets.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/webhook_url_fingerprint.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/webhooks.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/websocket_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/websocket_fuzz.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/well_known.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woo_blocks_checkout_drift.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woo_subscriptions_renewal_race.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woocommerce_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woocommerce_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woocommerce_order_idor.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woocommerce_storefront.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_builder_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cli_http_exposure.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cli_inject.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_commerce_alt_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cron_cpu.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cron_disabled.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cron_dos.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_debug_display_via_rest.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_engine_misconfig.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_fork_detection.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_form_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_mail_smtp_site_health_leak.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_membership_lms_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_multisite_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_playground_sqlite.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_plugin_ecosystem_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_query_sqli.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_rest_methods.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_salts_age.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wpconfig_hardening_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wpcron_suspicious_jobs.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wpgraphql.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xmlrpc_amplification.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xmlrpc_deep.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xmlrpc_method_brute.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xss_dom_sinks.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xss_reflected.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xxe_upload.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/yaml_templates.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/yaml_workflows.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/yarn_pnpm_lock_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/cli_extras.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/completion.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/confidence.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/config.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/console_live.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/crash_submit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/daemon/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/daemon/_legacy.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/daemon/webhook_v2.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/check_tags.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/common_paths.txt +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/compliance_extra.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/compliance_map.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/compliance_v2.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/dashboard.html.j2 +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/datadog-dashboard.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/exploit_playbook.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/exploit_signatures.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/known_paths.txt +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/marketplace.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/newrelic-dashboard.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/openapi-scan-report.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/payloads.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/plugin_cves.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/plugin_file_hashes.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/quick_fixes.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/references.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/remediation_videos.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/report.html.j2 +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/report.schema.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/security_tutorial.json +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/db.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/demo.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/diff.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/edu_v27.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/education.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/enterprise/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/enterprise/billing_stub.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/enterprise/multi_tenant.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/enterprise/quota.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/eta.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/fun/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/fun/bingo_card.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gh_check_run.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gui_payloads.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gui_windows.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/har_replay.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/heatmap.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/http.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/i18n.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/importers/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/importers/burp_zap.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/incremental/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/incremental/_legacy.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/incremental/diff_scan.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/incremental/smart_skip.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/cisa_kev.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/epss.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/github_issues.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/osint.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/sucuri_sitecheck.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/threat_intel.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/ticketing.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/tor_proxy.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/virustotal.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/webhooks_chat.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/issue_push.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/js_plugin.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/kev.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/licensing.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/log.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/marketplace.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/mobile_api.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/mobile_app_discovery.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/mobile_v27.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/models.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/monitors.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/notify.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/password_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/payloads.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf/connection_pool.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf/parallel_sites.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/playbook.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/policy.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/pr_inspector.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/prove.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/py.typed +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/recommend.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reference_diff.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/region_egress.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/remediation_videos.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/report_query.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/__init__.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/attestation.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/auditor_pdf.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/badge_svg.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/board_one_pager.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/bounty_format.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/burp_export.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/comparison_two_sites.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/compliance_attestation.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/console.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/csv_out.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/d3fend_mapping.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/dashboard.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/diff_agency.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/diff_viewer.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/docx_report.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/eli5_toggle.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/exec_pdf.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/executive_pack.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/executive_tldr.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/finding_heatmap.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/gdpr_dsr_report.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/html.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/issue_export.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/json_out.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/live_sync.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/markdown.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/org_dashboard.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/pdf_custom_branding.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/public_page.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/risk_forecast.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/sarif.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/snapshot_compare.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/translated_summary.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/trend_over_time.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/user_template.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/vex_export.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/xlsx_out.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/xlsx_pivot.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/risk.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/risk_weights.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/sbom.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/scan_zip.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/scanner.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/scheduler.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/siem.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/sites.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/sla.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/slack_app.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/spider.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ssh_audit.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/tags.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/template_engine.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/template_signature.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/threat_intel_v2.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/tray.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/turbo_engine.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ux_extras.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/waf_rules.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/watchers.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/workflow.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/workflow_cmds.py +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan.egg-info/dependency_links.txt +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan.egg-info/entry_points.txt +0 -0
- {wpsecscan-2.7.1 → wpsecscan-2.7.3}/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.7.
|
|
3
|
+
Version: 2.7.3
|
|
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,31 +693,31 @@ 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
|
|
696
|
+
Requires-Dist: keyring<26,>=24
|
|
697
697
|
Provides-Extra: pdf
|
|
698
|
-
Requires-Dist: reportlab
|
|
698
|
+
Requires-Dist: reportlab<5,>=4.0; extra == "pdf"
|
|
699
699
|
Provides-Extra: browser
|
|
700
|
-
Requires-Dist: playwright
|
|
700
|
+
Requires-Dist: playwright<2,>=1.40; extra == "browser"
|
|
701
701
|
Provides-Extra: yaml
|
|
702
|
-
Requires-Dist: pyyaml
|
|
702
|
+
Requires-Dist: pyyaml<7,>=6.0; extra == "yaml"
|
|
703
703
|
Provides-Extra: ops
|
|
704
|
-
Requires-Dist: redis
|
|
705
|
-
Requires-Dist: bcrypt
|
|
704
|
+
Requires-Dist: redis<7,>=5.0; extra == "ops"
|
|
705
|
+
Requires-Dist: bcrypt<5,>=4.0; extra == "ops"
|
|
706
706
|
Provides-Extra: ui
|
|
707
|
-
Requires-Dist: Pillow
|
|
708
|
-
Requires-Dist: pystray
|
|
707
|
+
Requires-Dist: Pillow<12,>=10; extra == "ui"
|
|
708
|
+
Requires-Dist: pystray<0.20,>=0.19; extra == "ui"
|
|
709
709
|
Provides-Extra: security
|
|
710
|
-
Requires-Dist: defusedxml
|
|
710
|
+
Requires-Dist: defusedxml<0.8,>=0.7; extra == "security"
|
|
711
711
|
Provides-Extra: all
|
|
712
|
-
Requires-Dist: reportlab
|
|
713
|
-
Requires-Dist: playwright
|
|
714
|
-
Requires-Dist: pyyaml
|
|
715
|
-
Requires-Dist: redis
|
|
716
|
-
Requires-Dist: bcrypt
|
|
717
|
-
Requires-Dist: Pillow
|
|
718
|
-
Requires-Dist: pystray
|
|
719
|
-
Requires-Dist: keyring
|
|
720
|
-
Requires-Dist: defusedxml
|
|
712
|
+
Requires-Dist: reportlab<5,>=4.0; extra == "all"
|
|
713
|
+
Requires-Dist: playwright<2,>=1.40; extra == "all"
|
|
714
|
+
Requires-Dist: pyyaml<7,>=6.0; extra == "all"
|
|
715
|
+
Requires-Dist: redis<7,>=5.0; extra == "all"
|
|
716
|
+
Requires-Dist: bcrypt<5,>=4.0; extra == "all"
|
|
717
|
+
Requires-Dist: Pillow<12,>=10; extra == "all"
|
|
718
|
+
Requires-Dist: pystray<0.20,>=0.19; extra == "all"
|
|
719
|
+
Requires-Dist: keyring<26,>=24; extra == "all"
|
|
720
|
+
Requires-Dist: defusedxml<0.8,>=0.7; extra == "all"
|
|
721
721
|
Provides-Extra: test
|
|
722
722
|
Requires-Dist: pytest>=7.4; extra == "test"
|
|
723
723
|
Dynamic: license-file
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "wpsecscan"
|
|
7
|
-
version = "2.7.
|
|
7
|
+
version = "2.7.3"
|
|
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"
|
|
@@ -41,37 +41,45 @@ dependencies = [
|
|
|
41
41
|
# fell back to plain JSON. creds_vault still gracefully handles the
|
|
42
42
|
# missing-keyring case so headless installs without a backend keep
|
|
43
43
|
# working (the FailBackend check in creds_vault._have_keyring).
|
|
44
|
-
|
|
44
|
+
# C26 (v2.7.2) — upper bound on keyring; v25+ tends to break the
|
|
45
|
+
# backend-detection API. Without an upper bound `pip install` will
|
|
46
|
+
# pull majors that change behaviour silently.
|
|
47
|
+
"keyring>=24,<26",
|
|
45
48
|
]
|
|
46
49
|
|
|
47
50
|
[project.optional-dependencies]
|
|
51
|
+
# C26 (v2.7.2) — every optional dep now has an upper-bound. Without one
|
|
52
|
+
# `pip install 'wpsecscan[ui]'` would silently pull the next major of
|
|
53
|
+
# Pillow / reportlab / etc., any of which can introduce a breaking
|
|
54
|
+
# change OR (in Pillow's case) a fresh CVE. Bumps are still possible
|
|
55
|
+
# but become a deliberate code review.
|
|
48
56
|
# Real PDF executive / attestation reports
|
|
49
|
-
pdf = ["reportlab>=4.0"]
|
|
57
|
+
pdf = ["reportlab>=4.0,<5"]
|
|
50
58
|
# Headless DOM-XSS check + per-finding screenshots
|
|
51
|
-
browser = ["playwright>=1.40"]
|
|
59
|
+
browser = ["playwright>=1.40,<2"]
|
|
52
60
|
# Daemon mode YAML config
|
|
53
|
-
yaml = ["pyyaml>=6.0"]
|
|
61
|
+
yaml = ["pyyaml>=6.0,<7"]
|
|
54
62
|
# Distributed CVE-DB cache + RBAC bcrypt hashing
|
|
55
|
-
ops = ["redis>=5.0", "bcrypt>=4.0"]
|
|
63
|
+
ops = ["redis>=5.0,<7", "bcrypt>=4.0,<5"]
|
|
56
64
|
# Optional Pillow for GUI image rendering + pystray for minimize-to-tray (#56).
|
|
57
65
|
# (keyring moved to default `dependencies` in v2.5.1 because the `wpsecscan
|
|
58
66
|
# creds` CLI surface is no longer GUI-only.)
|
|
59
|
-
ui = ["Pillow>=10", "pystray>=0.19"]
|
|
67
|
+
ui = ["Pillow>=10,<12", "pystray>=0.19,<0.20"]
|
|
60
68
|
# S5 — defusedxml protects the burp/zap importer against billion-laughs
|
|
61
69
|
# entity-expansion DoS on hostile XML reports. Stdlib ElementTree falls
|
|
62
70
|
# back with a stderr warning when this isn't installed.
|
|
63
|
-
security = ["defusedxml>=0.7"]
|
|
71
|
+
security = ["defusedxml>=0.7,<0.8"]
|
|
64
72
|
# Everything optional in one install
|
|
65
73
|
all = [
|
|
66
|
-
"reportlab>=4.0",
|
|
67
|
-
"playwright>=1.40",
|
|
68
|
-
"pyyaml>=6.0",
|
|
69
|
-
"redis>=5.0",
|
|
70
|
-
"bcrypt>=4.0",
|
|
71
|
-
"Pillow>=10",
|
|
72
|
-
"pystray>=0.19",
|
|
73
|
-
"keyring>=24",
|
|
74
|
-
"defusedxml>=0.7",
|
|
74
|
+
"reportlab>=4.0,<5",
|
|
75
|
+
"playwright>=1.40,<2",
|
|
76
|
+
"pyyaml>=6.0,<7",
|
|
77
|
+
"redis>=5.0,<7",
|
|
78
|
+
"bcrypt>=4.0,<5",
|
|
79
|
+
"Pillow>=10,<12",
|
|
80
|
+
"pystray>=0.19,<0.20",
|
|
81
|
+
"keyring>=24,<26",
|
|
82
|
+
"defusedxml>=0.7,<0.8",
|
|
75
83
|
]
|
|
76
84
|
# Test deps (also installed by CI)
|
|
77
85
|
test = ["pytest>=7.4"]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Regression test for v2.7.2 C2 — audit log HMAC compare must be
|
|
2
|
+
constant-time.
|
|
3
|
+
|
|
4
|
+
verify_chain() walked each entry and compared `expected != stored_hmac`
|
|
5
|
+
with Python's `!=` operator, which short-circuits on the first differing
|
|
6
|
+
byte. An attacker who can append entries to the log and call verify_chain
|
|
7
|
+
(directly or via a CLI subcommand) can extract one byte at a time of a
|
|
8
|
+
valid HMAC for a tampered prior entry.
|
|
9
|
+
|
|
10
|
+
The fix is `not hmac.compare_digest(expected, stored_hmac)`.
|
|
11
|
+
"""
|
|
12
|
+
import inspect
|
|
13
|
+
|
|
14
|
+
from wpsecscan.auth import audit_log
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_verify_chain_uses_compare_digest():
|
|
18
|
+
"""The verifier must invoke `hmac.compare_digest`, not raw `!=`,
|
|
19
|
+
when comparing the stored HMAC against the recomputed one."""
|
|
20
|
+
src = inspect.getsource(audit_log.verify_chain)
|
|
21
|
+
assert "compare_digest" in src, (
|
|
22
|
+
"verify_chain must compare HMACs via hmac.compare_digest to avoid "
|
|
23
|
+
"a timing side-channel; bare !=/== leaks one byte per comparison."
|
|
24
|
+
)
|
|
25
|
+
# Ensure the old bug-pattern is gone.
|
|
26
|
+
assert "expected != stored_hmac" not in src
|
|
27
|
+
assert "stored_hmac != expected" not in src
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_verify_chain_still_catches_tampering(tmp_path, monkeypatch):
|
|
31
|
+
"""Functional check — after the fix, tampering still surfaces as
|
|
32
|
+
`is_valid == False`."""
|
|
33
|
+
monkeypatch.setenv("WPSECSCAN_HOME", str(tmp_path))
|
|
34
|
+
audit_log.append("alice", "scan_started", "https://example.com")
|
|
35
|
+
audit_log.append("alice", "scan_finished", "https://example.com")
|
|
36
|
+
ok, n, err = audit_log.verify_chain()
|
|
37
|
+
assert ok is True
|
|
38
|
+
assert n == 2
|
|
39
|
+
assert err == ""
|
|
40
|
+
|
|
41
|
+
# Tamper with the first entry's action field.
|
|
42
|
+
log = audit_log._log_path()
|
|
43
|
+
raw = log.read_text(encoding="utf-8")
|
|
44
|
+
tampered = raw.replace('"scan_started"', '"scan_HACKED__"', 1)
|
|
45
|
+
log.write_text(tampered, encoding="utf-8")
|
|
46
|
+
|
|
47
|
+
ok, n, err = audit_log.verify_chain()
|
|
48
|
+
assert ok is False
|
|
49
|
+
assert "HMAC mismatch" in err or "mismatch" in err
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Regression tests for v2.7.2 C1 — marketplace sigstore identity binding.
|
|
2
|
+
|
|
3
|
+
v2.7.1 S1 closed the source_url scheme+host bypass, but left two holes:
|
|
4
|
+
|
|
5
|
+
1. The sigstore_sig_url and sigstore_pem_url were fetched from any host
|
|
6
|
+
and any scheme. A malicious or MITM'd index could point them at an
|
|
7
|
+
attacker-controlled cert+sig pair.
|
|
8
|
+
|
|
9
|
+
2. cosign was invoked with `--certificate-identity-regexp '.+'` and
|
|
10
|
+
`--certificate-oidc-issuer-regexp '.+'`, so ANY Sigstore-signed
|
|
11
|
+
blob passed verification regardless of who signed it.
|
|
12
|
+
|
|
13
|
+
Combined: attacker controls the index, supplies their own check + their
|
|
14
|
+
own sig + their own cert ⇒ install succeeds and the malicious code runs
|
|
15
|
+
on the next scan.
|
|
16
|
+
"""
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
from wpsecscan import marketplace_v27
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_safe_aux_url_rejects_file_scheme():
|
|
23
|
+
ok, reason = marketplace_v27._safe_aux_url("file:///etc/passwd")
|
|
24
|
+
assert ok is False
|
|
25
|
+
assert "https" in reason.lower()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_safe_aux_url_rejects_foreign_host():
|
|
29
|
+
ok, reason = marketplace_v27._safe_aux_url("https://evil.example/sig.txt")
|
|
30
|
+
assert ok is False
|
|
31
|
+
assert "host" in reason.lower()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_safe_aux_url_accepts_marketplace_origin(monkeypatch):
|
|
35
|
+
monkeypatch.setattr(
|
|
36
|
+
marketplace_v27, "_INDEX_URL",
|
|
37
|
+
"https://bryanflowers.github.io/wpsecscan/marketplace.json",
|
|
38
|
+
)
|
|
39
|
+
ok, reason = marketplace_v27._safe_aux_url(
|
|
40
|
+
"https://bryanflowers.github.io/wpsecscan/sigs/foo.sig"
|
|
41
|
+
)
|
|
42
|
+
assert ok is True
|
|
43
|
+
assert reason == ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_cosign_identity_regexp_is_pinned_to_author(monkeypatch, tmp_path):
|
|
47
|
+
"""The cosign verify-blob command must derive its identity-regexp
|
|
48
|
+
from the index's author_handle, NOT use a wildcard `.+`."""
|
|
49
|
+
captured: dict[str, list[str]] = {}
|
|
50
|
+
|
|
51
|
+
class _FakeResult:
|
|
52
|
+
returncode = 0
|
|
53
|
+
stderr = ""
|
|
54
|
+
|
|
55
|
+
def _fake_run(argv, **kw):
|
|
56
|
+
captured["argv"] = argv
|
|
57
|
+
return _FakeResult()
|
|
58
|
+
|
|
59
|
+
monkeypatch.setattr("subprocess.run", _fake_run)
|
|
60
|
+
monkeypatch.setattr("shutil.which", lambda x: "/usr/bin/cosign")
|
|
61
|
+
monkeypatch.setenv("WPSECSCAN_HOME", str(tmp_path))
|
|
62
|
+
|
|
63
|
+
# Stub the cache so we don't network-fetch the index
|
|
64
|
+
fake_index = {
|
|
65
|
+
"_version": 1,
|
|
66
|
+
"checks": [{
|
|
67
|
+
"slug": "demo-check",
|
|
68
|
+
"title": "demo",
|
|
69
|
+
"description": "x",
|
|
70
|
+
"author_handle": "alice",
|
|
71
|
+
"source_url": "https://bryanflowers.github.io/wpsecscan/x.py",
|
|
72
|
+
"sigstore_sig_url": "https://bryanflowers.github.io/wpsecscan/x.sig",
|
|
73
|
+
"sigstore_pem_url": "https://bryanflowers.github.io/wpsecscan/x.pem",
|
|
74
|
+
}],
|
|
75
|
+
}
|
|
76
|
+
monkeypatch.setattr(marketplace_v27, "_fetch_index", lambda: fake_index)
|
|
77
|
+
|
|
78
|
+
# Pre-create a fake installed local check so the verify path doesn't bail
|
|
79
|
+
local = tmp_path / "marketplace" / "checks" / "demo-check.py"
|
|
80
|
+
local.parent.mkdir(parents=True, exist_ok=True)
|
|
81
|
+
local.write_bytes(b"# stub")
|
|
82
|
+
|
|
83
|
+
# Stub urlopen for sig+pem downloads
|
|
84
|
+
class _FakeResp:
|
|
85
|
+
def __init__(self, data=b""):
|
|
86
|
+
self._d = data
|
|
87
|
+
|
|
88
|
+
def read(self):
|
|
89
|
+
return self._d
|
|
90
|
+
|
|
91
|
+
def __enter__(self):
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def __exit__(self, *a):
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
monkeypatch.setattr("urllib.request.urlopen", lambda *a, **k: _FakeResp(b""))
|
|
98
|
+
|
|
99
|
+
marketplace_v27.cmd_marketplace(["verify", "demo-check"])
|
|
100
|
+
|
|
101
|
+
argv = captured["argv"]
|
|
102
|
+
assert argv[0] == "cosign"
|
|
103
|
+
# The identity regexp must include the author handle, not be `.+`
|
|
104
|
+
identity_idx = argv.index("--certificate-identity-regexp")
|
|
105
|
+
assert "alice" in argv[identity_idx + 1]
|
|
106
|
+
assert argv[identity_idx + 1] != ".+"
|
|
107
|
+
# OIDC issuer must be pinned, not a wildcard regexp
|
|
108
|
+
assert "--certificate-oidc-issuer-regexp" not in argv
|
|
109
|
+
issuer_idx = argv.index("--certificate-oidc-issuer")
|
|
110
|
+
assert argv[issuer_idx + 1] == "https://token.actions.githubusercontent.com"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_install_rejects_sig_url_from_foreign_host(monkeypatch, tmp_path, capsys):
|
|
114
|
+
"""During install, a malicious index pointing sigstore_sig_url at
|
|
115
|
+
`evil.example` must be refused before any download happens."""
|
|
116
|
+
monkeypatch.setenv("WPSECSCAN_HOME", str(tmp_path))
|
|
117
|
+
fake_index = {
|
|
118
|
+
"_version": 1,
|
|
119
|
+
"checks": [{
|
|
120
|
+
"slug": "demo-check",
|
|
121
|
+
"title": "demo",
|
|
122
|
+
"description": "x",
|
|
123
|
+
"author_handle": "alice",
|
|
124
|
+
"source_url": "https://bryanflowers.github.io/wpsecscan/x.py",
|
|
125
|
+
"sigstore_sig_url": "https://evil.example/x.sig",
|
|
126
|
+
"sigstore_pem_url": "https://bryanflowers.github.io/wpsecscan/x.pem",
|
|
127
|
+
}],
|
|
128
|
+
}
|
|
129
|
+
monkeypatch.setattr(marketplace_v27, "_fetch_index", lambda: fake_index)
|
|
130
|
+
with pytest.raises(SystemExit) as exc:
|
|
131
|
+
marketplace_v27.cmd_marketplace(["install", "demo-check"])
|
|
132
|
+
captured = capsys.readouterr()
|
|
133
|
+
assert exc.value.code != 0
|
|
134
|
+
assert "sigstore" in captured.err.lower() or "host" in captured.err.lower()
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""Regression tests for v2.7.2 Wave 2 — High-severity fixes.
|
|
2
|
+
|
|
3
|
+
Covers C3 (audit-log key file atomic 0o600 create), C4 (share-link
|
|
4
|
+
TTL), C7 (argparse off-by-one bounds in 3 subcommands), C9 (verify=
|
|
5
|
+
False removed from login-redirect check), C10 (tarfile extraction
|
|
6
|
+
filter), C11 (api_server token-echo redaction), and C5/C6 (PowerShell
|
|
7
|
+
EncodedCommand path).
|
|
8
|
+
|
|
9
|
+
C8 (companion plugin email-hash) lives in the PHP layer and is
|
|
10
|
+
smoke-tested via grep below — pytest can't exercise PHP directly.
|
|
11
|
+
"""
|
|
12
|
+
import inspect
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# C3 — audit-log HMAC key is created atomically with 0o600
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
def test_audit_log_key_created_atomically_at_0o600(tmp_path, monkeypatch):
|
|
24
|
+
"""The HMAC key file must be created via O_EXCL|0o600 so there is
|
|
25
|
+
no window between create and chmod where another local user can
|
|
26
|
+
read the freshly-written key."""
|
|
27
|
+
monkeypatch.setenv("WPSECSCAN_HOME", str(tmp_path))
|
|
28
|
+
from wpsecscan.auth import audit_log
|
|
29
|
+
_ = audit_log._hmac_key()
|
|
30
|
+
key_path = tmp_path / ".audit-hmac-key"
|
|
31
|
+
assert key_path.exists()
|
|
32
|
+
# On POSIX, st_mode & 0o777 should be 0o600 immediately.
|
|
33
|
+
if os.name == "posix":
|
|
34
|
+
mode = key_path.stat().st_mode & 0o777
|
|
35
|
+
assert mode == 0o600, f"key file mode {oct(mode)} != 0o600"
|
|
36
|
+
# Regardless of OS, the source must use the atomic pattern.
|
|
37
|
+
src = inspect.getsource(audit_log._hmac_key)
|
|
38
|
+
assert "O_EXCL" in src
|
|
39
|
+
assert "0o600" in src
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
# C4 — share-link payload carries issued_at/expires_at; verify rejects expired
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
def _build_test_payload(monkeypatch, tmp_path, ttl=3600):
|
|
47
|
+
monkeypatch.setenv("WPSECSCAN_HOME", str(tmp_path))
|
|
48
|
+
from wpsecscan.models import Finding, CheckResult, ScanReport
|
|
49
|
+
from wpsecscan.reporters import share_link
|
|
50
|
+
f = Finding(severity="high", title="x", evidence="y")
|
|
51
|
+
cr = CheckResult(check_id="demo", check_name="d", findings=[f])
|
|
52
|
+
rep = ScanReport(target="https://t", scanned_at="2026-05-28T00:00:00Z",
|
|
53
|
+
duration_ms=0, results=[cr])
|
|
54
|
+
return share_link.build_share_payload(rep, "demo", 0, ttl_seconds=ttl)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_share_link_payload_includes_ttl_fields(monkeypatch, tmp_path):
|
|
58
|
+
p = _build_test_payload(monkeypatch, tmp_path)
|
|
59
|
+
assert "issued_at" in p
|
|
60
|
+
assert "expires_at" in p
|
|
61
|
+
assert p["expires_at"] > p["issued_at"]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_share_link_verify_accepts_fresh(monkeypatch, tmp_path):
|
|
65
|
+
from wpsecscan.reporters import share_link
|
|
66
|
+
p = _build_test_payload(monkeypatch, tmp_path, ttl=3600)
|
|
67
|
+
assert share_link.verify(p) is True
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_share_link_verify_rejects_expired(monkeypatch, tmp_path):
|
|
71
|
+
"""A payload whose expires_at is in the past must verify False
|
|
72
|
+
even though the HMAC is still cryptographically valid."""
|
|
73
|
+
from wpsecscan.reporters import share_link
|
|
74
|
+
p = _build_test_payload(monkeypatch, tmp_path, ttl=3600)
|
|
75
|
+
# Re-sign with a back-dated expires_at to simulate an expired link.
|
|
76
|
+
import hmac as _hmac
|
|
77
|
+
import hashlib
|
|
78
|
+
import json as _json
|
|
79
|
+
body = {k: v for k, v in p.items() if k not in ("signature", "share_id")}
|
|
80
|
+
body["issued_at"] = body["issued_at"] - 4000
|
|
81
|
+
body["expires_at"] = body["expires_at"] - 4000 # now in the past
|
|
82
|
+
raw = _json.dumps(body, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
83
|
+
body["signature"] = _hmac.new(share_link._share_secret(), raw,
|
|
84
|
+
hashlib.sha256).hexdigest()
|
|
85
|
+
assert share_link.verify(body) is False
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_share_link_verify_rejects_pre_v272_payload(monkeypatch, tmp_path):
|
|
89
|
+
"""A pre-v2.7.2 payload (no expires_at) must NOT verify under the
|
|
90
|
+
new rules — otherwise leaked legacy links survive the fix."""
|
|
91
|
+
from wpsecscan.reporters import share_link
|
|
92
|
+
p = _build_test_payload(monkeypatch, tmp_path, ttl=3600)
|
|
93
|
+
# Strip the TTL fields and re-sign as if produced by v2.7.1.
|
|
94
|
+
import hmac as _hmac
|
|
95
|
+
import hashlib
|
|
96
|
+
import json as _json
|
|
97
|
+
body = {k: v for k, v in p.items()
|
|
98
|
+
if k not in ("signature", "share_id", "issued_at", "expires_at")}
|
|
99
|
+
raw = _json.dumps(body, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
100
|
+
body["signature"] = _hmac.new(share_link._share_secret(), raw,
|
|
101
|
+
hashlib.sha256).hexdigest()
|
|
102
|
+
assert share_link.verify(body) is False
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# C7 — argparse bounds off-by-one (3 sites in __main__.py)
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
def test_main_no_longer_has_off_by_one_bounds_guards():
|
|
110
|
+
"""The three sites flagged by C7 used `i + N < len(args) + M` forms
|
|
111
|
+
that simplify to `i + (N-M) < len(args)`, causing args[i+N] to
|
|
112
|
+
IndexError when the flag is the last token. The fix removes the
|
|
113
|
+
`+ M` and uses `i + N < len(args)`. Pin both: the bad form must
|
|
114
|
+
be gone and the new form must be present."""
|
|
115
|
+
import wpsecscan.__main__ as m
|
|
116
|
+
src = _strip_py_comments(inspect.getsource(m))
|
|
117
|
+
# All `< len(args) + N` (N >= 1) forms were the bug pattern.
|
|
118
|
+
import re as _re
|
|
119
|
+
bad_hits = _re.findall(r"<\s*len\(args\)\s*\+\s*\d", src)
|
|
120
|
+
assert bad_hits == [], (
|
|
121
|
+
f"v2.7.2 C7 — `< len(args) + N` off-by-one form must be gone, "
|
|
122
|
+
f"still found: {bad_hits!r}"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
# C9 / C10 / C11 / C5 / C6 — source-inspection regressions
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
def _strip_py_comments(src: str) -> str:
|
|
131
|
+
"""Strip `# ...` line-comments AND docstrings so source-pattern
|
|
132
|
+
asserts don't trip on the explanatory text that references the
|
|
133
|
+
old buggy pattern."""
|
|
134
|
+
import re as _re
|
|
135
|
+
out_lines = []
|
|
136
|
+
in_doc = False
|
|
137
|
+
doc_marker = None
|
|
138
|
+
for line in src.splitlines():
|
|
139
|
+
s = line.lstrip()
|
|
140
|
+
if not in_doc and (s.startswith('"""') or s.startswith("'''")):
|
|
141
|
+
doc_marker = s[:3]
|
|
142
|
+
# single-line docstring?
|
|
143
|
+
if s.count(doc_marker) >= 2 and len(s) > 3:
|
|
144
|
+
continue
|
|
145
|
+
in_doc = True
|
|
146
|
+
continue
|
|
147
|
+
if in_doc:
|
|
148
|
+
if doc_marker in line:
|
|
149
|
+
in_doc = False
|
|
150
|
+
continue
|
|
151
|
+
# strip trailing # comment
|
|
152
|
+
line = _re.sub(r"\s+#.*$", "", line)
|
|
153
|
+
# skip whole-line comments
|
|
154
|
+
if line.lstrip().startswith("#"):
|
|
155
|
+
continue
|
|
156
|
+
out_lines.append(line)
|
|
157
|
+
return "\n".join(out_lines)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_login_redirect_no_unconditional_verify_false():
|
|
161
|
+
from wpsecscan.checks import login_redirect_http_hop
|
|
162
|
+
src = _strip_py_comments(inspect.getsource(login_redirect_http_hop))
|
|
163
|
+
assert "verify=False" not in src, (
|
|
164
|
+
"login_redirect_http_hop must not pass verify=False unconditionally"
|
|
165
|
+
)
|
|
166
|
+
assert "WPSECSCAN_INSECURE_TLS" in src or "verify=not" in src
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_trust_v27_tarfile_uses_filter():
|
|
170
|
+
from wpsecscan import trust_v27
|
|
171
|
+
src = inspect.getsource(trust_v27.reproducible_build_verify)
|
|
172
|
+
assert 'filter="data"' in src or "filter='data'" in src
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_api_server_does_not_echo_token_prefix():
|
|
176
|
+
from wpsecscan import api_server
|
|
177
|
+
src = _strip_py_comments(inspect.getsource(api_server))
|
|
178
|
+
# The pre-fix `{token[:6]}***` leaked 6 chars of a token to stdout.
|
|
179
|
+
assert "token[:6]" not in src
|
|
180
|
+
assert "token[:4]" not in src
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_gui_toast_uses_encodedcommand_not_fstring():
|
|
184
|
+
"""GUI toast notifier must not pass an f-string built from finding
|
|
185
|
+
titles as a single PowerShell argv string (C5)."""
|
|
186
|
+
import wpsecscan.gui as g
|
|
187
|
+
src = inspect.getsource(g)
|
|
188
|
+
# The fix uses EncodedCommand with base64-encoded UTF-16LE.
|
|
189
|
+
assert "EncodedCommand" in src
|
|
190
|
+
# And the prior bad pattern — Popen with a single ps string — is gone.
|
|
191
|
+
assert "subprocess.Popen(ps, shell=False" not in src
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_gui_v27_extras_shortcut_uses_encodedcommand():
|
|
195
|
+
from wpsecscan import gui_v27_extras
|
|
196
|
+
src = inspect.getsource(gui_v27_extras)
|
|
197
|
+
assert "EncodedCommand" in src
|
|
198
|
+
# The prior `-Command` + raw f-string form is gone.
|
|
199
|
+
assert '"-Command", ps_cmd' not in src
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Regression tests for v2.7.2 Wave 3 — Medium-severity fixes.
|
|
2
|
+
|
|
3
|
+
C12 perf_v27.etag_set — atomic temp+replace
|
|
4
|
+
C13 continuous_monitor — atomic temp+replace state file
|
|
5
|
+
C14 hardware_keys — absolute primary.ctx path (source check)
|
|
6
|
+
C15 history — timestamped snapshot written before "latest" promotion
|
|
7
|
+
C16 integrations_v27.find_snyk_dups — handles non-dict f.extra
|
|
8
|
+
C17 creds_vault index — 0o600 atomic write
|
|
9
|
+
"""
|
|
10
|
+
import inspect
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# C12 — perf_v27.etag_set atomic write
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
def test_etag_set_writes_atomically(monkeypatch, tmp_path):
|
|
22
|
+
"""Source check: etag_set must use os.replace, not bare write_text."""
|
|
23
|
+
monkeypatch.setenv("WPSECSCAN_HOME", str(tmp_path))
|
|
24
|
+
from wpsecscan import perf_v27
|
|
25
|
+
src = inspect.getsource(perf_v27.etag_set)
|
|
26
|
+
assert "os.replace" in src
|
|
27
|
+
# Functional check: still works.
|
|
28
|
+
perf_v27.etag_set("https://example.com", "tag-1", "Wed, 1 Jan 2026 00:00 GMT")
|
|
29
|
+
assert perf_v27.etag_get("https://example.com") == ("tag-1", "Wed, 1 Jan 2026 00:00 GMT")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# C13 — continuous_monitor atomic state-file write
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
def test_continuous_monitor_state_write_is_atomic():
|
|
37
|
+
from wpsecscan import continuous_monitor
|
|
38
|
+
src = inspect.getsource(continuous_monitor)
|
|
39
|
+
# The pre-fix bare `state_file.write_text(json.dumps(new)...)` is gone.
|
|
40
|
+
assert "state_file.write_text(json.dumps(new)" not in src
|
|
41
|
+
# The new pattern is temp + os.replace.
|
|
42
|
+
assert "os.replace" in src and "_tmp" in src
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# C14 — hardware_keys uses absolute primary.ctx path
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
def test_hardware_keys_no_bare_primary_ctx_string():
|
|
50
|
+
"""The pre-fix code passed the bare literal `"primary.ctx"` AS THE
|
|
51
|
+
subprocess argv slot — relative path, resolved against caller cwd.
|
|
52
|
+
The fix routes it via an absolute Path. We confirm the bug pattern
|
|
53
|
+
(the bare literal occupying an argv slot inside subprocess.run) is
|
|
54
|
+
gone."""
|
|
55
|
+
from wpsecscan import hardware_keys
|
|
56
|
+
src = inspect.getsource(hardware_keys)
|
|
57
|
+
# The bug pattern: "-C", "primary.ctx" (comma between, exact argv).
|
|
58
|
+
assert '"-C", "primary.ctx"' not in src
|
|
59
|
+
assert '"-c", "primary.ctx"' not in src
|
|
60
|
+
# The new pattern uses str(primary_ctx) in those argv slots.
|
|
61
|
+
assert 'str(primary_ctx)' in src
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
# C15 — history.save_report writes snapshot before promoting latest
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
def test_history_save_report_atomically_promotes_latest():
|
|
69
|
+
from wpsecscan import history
|
|
70
|
+
src = inspect.getsource(history.save_report_snapshot)
|
|
71
|
+
# The new ordering: snap_path.write_text appears BEFORE the os.replace
|
|
72
|
+
# call that promotes "latest".
|
|
73
|
+
snap_pos = src.find("snap_path.write_text")
|
|
74
|
+
latest_pos = src.find("_os.replace(latest_tmp, latest)")
|
|
75
|
+
assert snap_pos >= 0, "snap_path.write_text not found"
|
|
76
|
+
assert latest_pos >= 0, "_os.replace(latest_tmp, latest) not found"
|
|
77
|
+
assert snap_pos < latest_pos, "snapshot must be written BEFORE latest is promoted"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
# C16 — find_snyk_dups handles non-dict f.extra
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
def test_import_snyk_findings_handles_non_dict_extra(monkeypatch):
|
|
85
|
+
"""A Finding whose `.extra` got overwritten with a non-dict value
|
|
86
|
+
(e.g. JSON null on deserialisation) must not crash the dedup pass
|
|
87
|
+
with TypeError when import_snyk_findings tries to write through
|
|
88
|
+
`f.extra["snyk_dup"] = True`."""
|
|
89
|
+
import httpx as _httpx
|
|
90
|
+
from wpsecscan import integrations_v27
|
|
91
|
+
from wpsecscan.models import Finding, CheckResult, ScanReport
|
|
92
|
+
|
|
93
|
+
f_bad = Finding(severity="high", title="t", evidence="e")
|
|
94
|
+
f_bad.extra = None # type: ignore[assignment]
|
|
95
|
+
|
|
96
|
+
cr = CheckResult(check_id="x", check_name="x", findings=[f_bad])
|
|
97
|
+
rep = ScanReport(target="https://t", scanned_at="now", duration_ms=0, results=[cr])
|
|
98
|
+
|
|
99
|
+
# Stub httpx.Client so the snyk fetch returns no CVE matches —
|
|
100
|
+
# the contract check (non-dict f.extra rebind) runs regardless of
|
|
101
|
+
# whether any CVE matched.
|
|
102
|
+
class _StubResp:
|
|
103
|
+
status_code = 200
|
|
104
|
+
def json(self):
|
|
105
|
+
return {"data": []}
|
|
106
|
+
|
|
107
|
+
class _StubClient:
|
|
108
|
+
def __init__(self, *a, **kw): pass
|
|
109
|
+
def __enter__(self): return self
|
|
110
|
+
def __exit__(self, *a): return False
|
|
111
|
+
def get(self, *a, **kw): return _StubResp()
|
|
112
|
+
|
|
113
|
+
monkeypatch.setattr(_httpx, "Client", _StubClient)
|
|
114
|
+
monkeypatch.setenv("SNYK_TOKEN", "fake")
|
|
115
|
+
monkeypatch.setenv("SNYK_ORG", "fake-org")
|
|
116
|
+
|
|
117
|
+
# Must not raise TypeError. The contract fix rebinds f.extra to {}
|
|
118
|
+
# at the top of the per-finding loop so any subsequent write is safe.
|
|
119
|
+
try:
|
|
120
|
+
integrations_v27.import_snyk_findings(rep)
|
|
121
|
+
except TypeError as e:
|
|
122
|
+
pytest.fail(f"import_snyk_findings raised TypeError on non-dict f.extra: {e}")
|
|
123
|
+
assert isinstance(f_bad.extra, dict)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
# C17 — creds_vault index file is 0o600 on POSIX
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
def test_creds_vault_index_is_0600_atomic(monkeypatch, tmp_path):
|
|
131
|
+
monkeypatch.setenv("WPSECSCAN_HOME", str(tmp_path))
|
|
132
|
+
from wpsecscan import creds_vault
|
|
133
|
+
src = inspect.getsource(creds_vault._save_index)
|
|
134
|
+
assert "0o600" in src
|
|
135
|
+
# Functional: index file mode is restricted on POSIX after a save.
|
|
136
|
+
creds_vault._save_index({"https://t": ["user"]})
|
|
137
|
+
p = creds_vault._index_path()
|
|
138
|
+
assert p.exists()
|
|
139
|
+
if os.name == "posix":
|
|
140
|
+
mode = p.stat().st_mode & 0o777
|
|
141
|
+
assert mode == 0o600
|
|
142
|
+
# Content roundtrip
|
|
143
|
+
assert json.loads(p.read_text(encoding="utf-8")) == {"https://t": ["user"]}
|