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.
Files changed (546) hide show
  1. {wpsecscan-2.7.1/wpsecscan.egg-info → wpsecscan-2.7.3}/PKG-INFO +19 -19
  2. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/pyproject.toml +25 -17
  3. wpsecscan-2.7.3/tests/test_v272_audit_log_constant_time.py +49 -0
  4. wpsecscan-2.7.3/tests/test_v272_marketplace_sigstore_identity.py +134 -0
  5. wpsecscan-2.7.3/tests/test_v272_wave2_regressions.py +199 -0
  6. wpsecscan-2.7.3/tests/test_v272_wave3_regressions.py +143 -0
  7. wpsecscan-2.7.3/tests/test_v272_wave4_regressions.py +85 -0
  8. wpsecscan-2.7.3/tests/test_v273_critical_regressions.py +199 -0
  9. wpsecscan-2.7.3/tests/test_v273_wave2_regressions.py +296 -0
  10. wpsecscan-2.7.3/tests/test_v273_wave3_companion.py +79 -0
  11. wpsecscan-2.7.3/tests/test_v273_wave5_audit_log_wired.py +102 -0
  12. wpsecscan-2.7.3/wpsecscan/__init__.py +1 -0
  13. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/__main__.py +8 -3
  14. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_assist.py +37 -13
  15. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_safety.py +47 -0
  16. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_triage.py +63 -5
  17. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/api_server.py +3 -1
  18. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/audit_log.py +59 -5
  19. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/authenticated.py +7 -1
  20. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login_redirect_http_hop.py +8 -1
  21. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/continuous_monitor.py +7 -1
  22. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/creds_vault.py +32 -4
  23. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gui.py +108 -25
  24. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gui_v27_extras.py +12 -4
  25. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/hardware_keys.py +9 -2
  26. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/history.py +63 -8
  27. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations_v27.py +19 -3
  28. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/interactsh.py +16 -6
  29. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/marketplace_v27.py +101 -3
  30. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/observability.py +14 -2
  31. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf/_legacy.py +8 -3
  32. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf_v27.py +5 -1
  33. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/share_link.py +49 -6
  34. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/trust_v27.py +23 -3
  35. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ua_rotation.py +6 -2
  36. {wpsecscan-2.7.1 → wpsecscan-2.7.3/wpsecscan.egg-info}/PKG-INFO +19 -19
  37. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan.egg-info/SOURCES.txt +9 -0
  38. wpsecscan-2.7.3/wpsecscan.egg-info/requires.txt +39 -0
  39. wpsecscan-2.7.1/wpsecscan/__init__.py +0 -1
  40. wpsecscan-2.7.1/wpsecscan.egg-info/requires.txt +0 -39
  41. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/LICENSE +0 -0
  42. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/NOTICE +0 -0
  43. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/README.md +0 -0
  44. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/setup.cfg +0 -0
  45. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_audit_fixes.py +0 -0
  46. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_audit_round_r.py +0 -0
  47. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_board_one_pager.py +0 -0
  48. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_burp_zap_import.py +0 -0
  49. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_cache.py +0 -0
  50. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_checks.py +0 -0
  51. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_confidence_eta_tags.py +0 -0
  52. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_creds_vault_cli.py +0 -0
  53. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_default_creds.py +0 -0
  54. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_diff_agency.py +0 -0
  55. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_gh_check_run.py +0 -0
  56. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_global_sigs_regression.py +0 -0
  57. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_integrations_graphql_escape.py +0 -0
  58. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_login_throttle.py +0 -0
  59. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_login_throttle_deep.py +0 -0
  60. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_marketplace_install_url_guard.py +0 -0
  61. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_mobile_api_traversal.py +0 -0
  62. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_new_check_inventory.py +0 -0
  63. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_new_checks.py +0 -0
  64. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_new_checks_aggressive.py +0 -0
  65. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_new_checks_quality.py +0 -0
  66. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_notify.py +0 -0
  67. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_password_audit.py +0 -0
  68. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_payloads.py +0 -0
  69. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_phase5.py +0 -0
  70. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_playbook.py +0 -0
  71. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_prove.py +0 -0
  72. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_reference_diff.py +0 -0
  73. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_reference_diff_traversal.py +0 -0
  74. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_risk_score.py +0 -0
  75. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_54.py +0 -0
  76. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_55.py +0 -0
  77. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_56_activity.py +0 -0
  78. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_57.py +0 -0
  79. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_58.py +0 -0
  80. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_59.py +0 -0
  81. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_60.py +0 -0
  82. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_61.py +0 -0
  83. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_62.py +0 -0
  84. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_63.py +0 -0
  85. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_64.py +0 -0
  86. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_65.py +0 -0
  87. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_round_q.py +0 -0
  88. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_scan_zip.py +0 -0
  89. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_scheduler.py +0 -0
  90. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_scheduler_cron_dow.py +0 -0
  91. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_siem.py +0 -0
  92. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_sla.py +0 -0
  93. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_slack_app.py +0 -0
  94. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_ssh_audit.py +0 -0
  95. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/tests/test_user_template.py +0 -0
  96. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/_util.py +0 -0
  97. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/activity.py +0 -0
  98. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_fp_predictor.py +0 -0
  99. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ai_triage_ui.py +0 -0
  100. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/analytics.py +0 -0
  101. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/attack_checkpoint.py +0 -0
  102. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/attack_scripts.py +0 -0
  103. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/__init__.py +0 -0
  104. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/approval_workflow.py +0 -0
  105. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/rbac.py +0 -0
  106. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/sso_oidc.py +0 -0
  107. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auth/sso_saml.py +0 -0
  108. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auto_pr.py +0 -0
  109. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/auto_update.py +0 -0
  110. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/baseline.py +0 -0
  111. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/branding.py +0 -0
  112. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/bug_report.py +0 -0
  113. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/burp_import.py +0 -0
  114. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/cache.py +0 -0
  115. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/check_health.py +0 -0
  116. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/__init__.py +0 -0
  117. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/_template.py +0 -0
  118. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/a11y_deep.py +0 -0
  119. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/a11y_lite.py +0 -0
  120. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/a11y_wcag_aaa.py +0 -0
  121. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/abuseipdb_lookup.py +0 -0
  122. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/admin_ajax_brute_surface.py +0 -0
  123. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/admin_invite_link_scan.py +0 -0
  124. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ai_agent_webhook_leak.py +0 -0
  125. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ai_chatbot_endpoint_leak.py +0 -0
  126. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ai_plugin_prompt_storage.py +0 -0
  127. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ai_prompt_injection_passive.py +0 -0
  128. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ajax_surface.py +0 -0
  129. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/algolia_elastic_frontend_keys.py +0 -0
  130. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/amp_transitional_redirect.py +0 -0
  131. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/app_passwords.py +0 -0
  132. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/app_passwords_stale_audit.py +0 -0
  133. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/auth_modernisation.py +0 -0
  134. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/backup_exposure.py +0 -0
  135. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/backup_file_fuzz.py +0 -0
  136. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/block_bindings_exposure.py +0 -0
  137. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/block_style_variations_url.py +0 -0
  138. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/brand_monitor.py +0 -0
  139. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/bucket_shadow_takeover.py +0 -0
  140. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cache_headers.py +0 -0
  141. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cache_poisoning.py +0 -0
  142. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cache_poisoning_v2.py +0 -0
  143. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cdn_edge_audit.py +0 -0
  144. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cloud_metadata_ssrf.py +0 -0
  145. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cloudflare_origin_leak.py +0 -0
  146. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/companion_advanced.py +0 -0
  147. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/companion_v13.py +0 -0
  148. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/companion_v14.py +0 -0
  149. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/compliance_frameworks.py +0 -0
  150. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/composer_lock_audit.py +0 -0
  151. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/composer_npm_typosquat.py +0 -0
  152. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cookie_consent.py +0 -0
  153. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cookie_consent_desync.py +0 -0
  154. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cookies.py +0 -0
  155. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/core_checksums.py +0 -0
  156. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/core_cves.py +0 -0
  157. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/core_tampering.py +0 -0
  158. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/core_version.py +0 -0
  159. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cors.py +0 -0
  160. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/crlf_location_injection.py +0 -0
  161. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/crypto_agility.py +0 -0
  162. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/crypto_payment_callback_audit.py +0 -0
  163. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/cryptominer_js_injection.py +0 -0
  164. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csp.py +0 -0
  165. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csp_report_endpoint.py +0 -0
  166. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csrf_entropy.py +0 -0
  167. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csrf_nonce.py +0 -0
  168. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/csv_export_csp.py +0 -0
  169. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ct_log_recent_certs.py +0 -0
  170. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ct_log_shadow_cert.py +0 -0
  171. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/db_admin_login_probe.py +0 -0
  172. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/db_trigger_audit.py +0 -0
  173. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/debug_leaks.py +0 -0
  174. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/debug_log_pii_sniff.py +0 -0
  175. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/default_creds.py +0 -0
  176. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dev_params.py +0 -0
  177. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/directory_listing.py +0 -0
  178. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dns_deep.py +0 -0
  179. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dns_rebinding.py +0 -0
  180. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dns_security.py +0 -0
  181. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dns_templates.py +0 -0
  182. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/dom_xss_headless.py +0 -0
  183. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/email_obfuscation_audit.py +0 -0
  184. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/email_security_deep.py +0 -0
  185. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/env_file_enum.py +0 -0
  186. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/error_pages.py +0 -0
  187. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/exposed_files.py +0 -0
  188. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/favicon_fingerprint.py +0 -0
  189. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/file_upload.py +0 -0
  190. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/font_library_api_ssrf.py +0 -0
  191. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/forced_browse.py +0 -0
  192. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/form_builder_upload_bypass.py +0 -0
  193. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/gdpr_dsr.py +0 -0
  194. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/gdpr_dsr_endpoint_enum.py +0 -0
  195. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/git_dir_deep_scan.py +0 -0
  196. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/github_actions_workflow_leak.py +0 -0
  197. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/github_leak_search.py +0 -0
  198. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/graphql_dos.py +0 -0
  199. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/graphql_field_authz_deep.py +0 -0
  200. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/gtm_inventory.py +0 -0
  201. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/gutenberg_blocks.py +0 -0
  202. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/header_smuggling_case.py +0 -0
  203. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/headless_templates.py +0 -0
  204. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/headless_vercel_netlify_detect.py +0 -0
  205. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/headless_wp_audit.py +0 -0
  206. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/heartbeat_abuse.py +0 -0
  207. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/heartbeat_frontend.py +0 -0
  208. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/helm_compose_leak.py +0 -0
  209. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hibp.py +0 -0
  210. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/honeypot_admin.py +0 -0
  211. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/host_header_validation.py +0 -0
  212. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/host_platform_detect.py +0 -0
  213. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/host_recon.py +0 -0
  214. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hosting_platform_audit.py +0 -0
  215. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hostname_collision.py +0 -0
  216. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hpp.py +0 -0
  217. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hsts_preload_eligibility.py +0 -0
  218. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/hsts_preload_mismatch.py +0 -0
  219. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/html_api_csp_nonce.py +0 -0
  220. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/http2_settings.py +0 -0
  221. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/http2_smuggling.py +0 -0
  222. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/http3_fingerprint.py +0 -0
  223. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/http_methods.py +0 -0
  224. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/interactivity_api_state_leak.py +0 -0
  225. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/js_framework_deep.py +0 -0
  226. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/js_libraries.py +0 -0
  227. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/js_supply_chain.py +0 -0
  228. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/jwt_audit.py +0 -0
  229. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/jwt_auth_plugin_audit.py +0 -0
  230. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/lead_gen_list_id_enum.py +0 -0
  231. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login.py +0 -0
  232. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login_throttle.py +0 -0
  233. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login_throttle_deep.py +0 -0
  234. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/login_timing.py +0 -0
  235. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/magecart_skimmer_patterns.py +0 -0
  236. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/mcp_endpoint_exposure.py +0 -0
  237. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/mfa_priv_account_audit.py +0 -0
  238. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/misc_injection_audit.py +0 -0
  239. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/mixed_content.py +0 -0
  240. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/mobile_app_endpoints.py +0 -0
  241. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/multisite.py +0 -0
  242. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/multisite_sso_key_reuse.py +0 -0
  243. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/nft_mint_pubapi.py +0 -0
  244. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/nonce_freshness.py +0 -0
  245. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/nosql_injection.py +0 -0
  246. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/oauth_oidc.py +0 -0
  247. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/oauth_redirect.py +0 -0
  248. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/oauth_redirect_misconfig.py +0 -0
  249. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/object_cache_dropin.py +0 -0
  250. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/open_redirect.py +0 -0
  251. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/open_registration.py +0 -0
  252. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/openapi_scanner.py +0 -0
  253. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/origin_ip_discovery.py +0 -0
  254. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/osint_enrich.py +0 -0
  255. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/package_lock_audit.py +0 -0
  256. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/page_builder_cve.py +0 -0
  257. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/path_bypass.py +0 -0
  258. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/path_traversal.py +0 -0
  259. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/payment_commerce_deep.py +0 -0
  260. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/payment_gateway_test_keys.py +0 -0
  261. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/perf_budget.py +0 -0
  262. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/perf_of_target.py +0 -0
  263. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/permissions_policy.py +0 -0
  264. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/php_eol.py +0 -0
  265. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/phpinfo_dangerous_directives.py +0 -0
  266. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_archive_fuzz.py +0 -0
  267. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_cemetery.py +0 -0
  268. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_cves.py +0 -0
  269. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_hash_fingerprint.py +0 -0
  270. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_install_rest_race.py +0 -0
  271. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_route_fuzz.py +0 -0
  272. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_specific_audit.py +0 -0
  273. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugin_typosquat_detection.py +0 -0
  274. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/plugins.py +0 -0
  275. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/postmeta_stored_xss_scan.py +0 -0
  276. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/premium_license_leak.py +0 -0
  277. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/privacy_inventory.py +0 -0
  278. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/prototype_pollution.py +0 -0
  279. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/pwa_service_worker_cache.py +0 -0
  280. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/race_condition.py +0 -0
  281. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/redirect_chain.py +0 -0
  282. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/referenced_buckets.py +0 -0
  283. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_api.py +0 -0
  284. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_app_passwords_enum.py +0 -0
  285. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_fields_dos.py +0 -0
  286. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_link_header.py +0 -0
  287. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_namespace_leak.py +0 -0
  288. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_permission_audit.py +0 -0
  289. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rest_schema_field_leak.py +0 -0
  290. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/robots_sitemap.py +0 -0
  291. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/rum_beacons.py +0 -0
  292. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/s3_bucket_discovery.py +0 -0
  293. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/saml_xsw.py +0 -0
  294. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/search_highlight_xss.py +0 -0
  295. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/secret_leak.py +0 -0
  296. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/security_txt.py +0 -0
  297. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sendmail_injection.py +0 -0
  298. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/server_stack_reveal.py +0 -0
  299. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/server_timing.py +0 -0
  300. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/service_exposure.py +0 -0
  301. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/service_worker_scope_hijack.py +0 -0
  302. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/session_fixation.py +0 -0
  303. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sitemap_cve_probe.py +0 -0
  304. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/smuggling_probe.py +0 -0
  305. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/solidity_abi_leak.py +0 -0
  306. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/source_maps.py +0 -0
  307. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/speculation_rules_audit.py +0 -0
  308. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/spider_crawl.py +0 -0
  309. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sqli.py +0 -0
  310. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sri_audit.py +0 -0
  311. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/sri_pwa_misc.py +0 -0
  312. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ssrf.py +0 -0
  313. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/ssti.py +0 -0
  314. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/stripe_webhook_audit.py +0 -0
  315. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/subdomains.py +0 -0
  316. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tailwind_css_comment_leak.py +0 -0
  317. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/theme_cves.py +0 -0
  318. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/theme_json_font_ssrf.py +0 -0
  319. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/themes.py +0 -0
  320. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/timthumb.py +0 -0
  321. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tls_deep.py +0 -0
  322. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tls_headers.py +0 -0
  323. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tls_modern.py +0 -0
  324. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/tls_reneg_dos.py +0 -0
  325. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/translation_plugin_key_leak.py +0 -0
  326. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/trellis_yaml_audit.py +0 -0
  327. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/turnstile_sitekey_reuse.py +0 -0
  328. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/upload_bypass_deep.py +0 -0
  329. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/upload_path_predictable.py +0 -0
  330. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/uploads_year_listing.py +0 -0
  331. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/users.py +0 -0
  332. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/users_deep.py +0 -0
  333. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/users_me_capability_leak.py +0 -0
  334. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/vendor_backdoor_patterns.py +0 -0
  335. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/vercel_preview_url_leak.py +0 -0
  336. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf.py +0 -0
  337. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf_brand_deep.py +0 -0
  338. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf_bypass_probe.py +0 -0
  339. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf_lockout_guard.py +0 -0
  340. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/waf_ruleset.py +0 -0
  341. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wallet_seed_phrase_leak.py +0 -0
  342. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wc_api_key_escalation.py +0 -0
  343. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/web3_wallet_connector_audit.py +0 -0
  344. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/webdav.py +0 -0
  345. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/webhook_signing_secrets.py +0 -0
  346. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/webhook_url_fingerprint.py +0 -0
  347. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/webhooks.py +0 -0
  348. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/websocket_audit.py +0 -0
  349. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/websocket_fuzz.py +0 -0
  350. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/well_known.py +0 -0
  351. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woo_blocks_checkout_drift.py +0 -0
  352. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woo_subscriptions_renewal_race.py +0 -0
  353. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woocommerce_audit.py +0 -0
  354. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woocommerce_deep.py +0 -0
  355. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woocommerce_order_idor.py +0 -0
  356. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/woocommerce_storefront.py +0 -0
  357. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_builder_audit.py +0 -0
  358. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cli_http_exposure.py +0 -0
  359. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cli_inject.py +0 -0
  360. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_commerce_alt_audit.py +0 -0
  361. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cron_cpu.py +0 -0
  362. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cron_disabled.py +0 -0
  363. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_cron_dos.py +0 -0
  364. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_debug_display_via_rest.py +0 -0
  365. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_engine_misconfig.py +0 -0
  366. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_fork_detection.py +0 -0
  367. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_form_audit.py +0 -0
  368. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_mail_smtp_site_health_leak.py +0 -0
  369. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_membership_lms_audit.py +0 -0
  370. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_multisite_deep.py +0 -0
  371. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_playground_sqlite.py +0 -0
  372. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_plugin_ecosystem_audit.py +0 -0
  373. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_query_sqli.py +0 -0
  374. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_rest_methods.py +0 -0
  375. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wp_salts_age.py +0 -0
  376. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wpconfig_hardening_audit.py +0 -0
  377. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wpcron_suspicious_jobs.py +0 -0
  378. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/wpgraphql.py +0 -0
  379. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xmlrpc_amplification.py +0 -0
  380. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xmlrpc_deep.py +0 -0
  381. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xmlrpc_method_brute.py +0 -0
  382. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xss_dom_sinks.py +0 -0
  383. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xss_reflected.py +0 -0
  384. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/xxe_upload.py +0 -0
  385. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/yaml_templates.py +0 -0
  386. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/yaml_workflows.py +0 -0
  387. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/checks/yarn_pnpm_lock_audit.py +0 -0
  388. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/cli_extras.py +0 -0
  389. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/completion.py +0 -0
  390. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/confidence.py +0 -0
  391. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/config.py +0 -0
  392. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/console_live.py +0 -0
  393. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/crash_submit.py +0 -0
  394. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/daemon/__init__.py +0 -0
  395. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/daemon/_legacy.py +0 -0
  396. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/daemon/webhook_v2.py +0 -0
  397. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/check_tags.json +0 -0
  398. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/common_paths.txt +0 -0
  399. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/compliance_extra.json +0 -0
  400. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/compliance_map.json +0 -0
  401. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/compliance_v2.json +0 -0
  402. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/dashboard.html.j2 +0 -0
  403. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/datadog-dashboard.json +0 -0
  404. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/exploit_playbook.json +0 -0
  405. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/exploit_signatures.json +0 -0
  406. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/known_paths.txt +0 -0
  407. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/marketplace.json +0 -0
  408. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/newrelic-dashboard.json +0 -0
  409. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/openapi-scan-report.json +0 -0
  410. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/payloads.json +0 -0
  411. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/plugin_cves.json +0 -0
  412. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/plugin_file_hashes.json +0 -0
  413. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/quick_fixes.json +0 -0
  414. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/references.json +0 -0
  415. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/remediation_videos.json +0 -0
  416. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/report.html.j2 +0 -0
  417. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/report.schema.json +0 -0
  418. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/data/security_tutorial.json +0 -0
  419. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/db.py +0 -0
  420. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/demo.py +0 -0
  421. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/diff.py +0 -0
  422. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/edu_v27.py +0 -0
  423. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/education.py +0 -0
  424. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/enterprise/__init__.py +0 -0
  425. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/enterprise/billing_stub.py +0 -0
  426. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/enterprise/multi_tenant.py +0 -0
  427. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/enterprise/quota.py +0 -0
  428. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/eta.py +0 -0
  429. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/fun/__init__.py +0 -0
  430. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/fun/bingo_card.py +0 -0
  431. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gh_check_run.py +0 -0
  432. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gui_payloads.py +0 -0
  433. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/gui_windows.py +0 -0
  434. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/har_replay.py +0 -0
  435. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/heatmap.py +0 -0
  436. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/http.py +0 -0
  437. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/i18n.py +0 -0
  438. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/importers/__init__.py +0 -0
  439. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/importers/burp_zap.py +0 -0
  440. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/incremental/__init__.py +0 -0
  441. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/incremental/_legacy.py +0 -0
  442. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/incremental/diff_scan.py +0 -0
  443. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/incremental/smart_skip.py +0 -0
  444. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/__init__.py +0 -0
  445. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/cisa_kev.py +0 -0
  446. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/epss.py +0 -0
  447. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/github_issues.py +0 -0
  448. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/osint.py +0 -0
  449. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/sucuri_sitecheck.py +0 -0
  450. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/threat_intel.py +0 -0
  451. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/ticketing.py +0 -0
  452. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/tor_proxy.py +0 -0
  453. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/virustotal.py +0 -0
  454. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/integrations/webhooks_chat.py +0 -0
  455. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/issue_push.py +0 -0
  456. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/js_plugin.py +0 -0
  457. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/kev.py +0 -0
  458. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/licensing.py +0 -0
  459. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/log.py +0 -0
  460. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/marketplace.py +0 -0
  461. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/mobile_api.py +0 -0
  462. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/mobile_app_discovery.py +0 -0
  463. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/mobile_v27.py +0 -0
  464. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/models.py +0 -0
  465. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/monitors.py +0 -0
  466. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/notify.py +0 -0
  467. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/password_audit.py +0 -0
  468. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/payloads.py +0 -0
  469. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf/__init__.py +0 -0
  470. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf/connection_pool.py +0 -0
  471. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/perf/parallel_sites.py +0 -0
  472. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/playbook.py +0 -0
  473. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/policy.py +0 -0
  474. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/pr_inspector.py +0 -0
  475. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/prove.py +0 -0
  476. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/py.typed +0 -0
  477. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/recommend.py +0 -0
  478. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reference_diff.py +0 -0
  479. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/region_egress.py +0 -0
  480. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/remediation_videos.py +0 -0
  481. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/report_query.py +0 -0
  482. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/__init__.py +0 -0
  483. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/attestation.py +0 -0
  484. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/auditor_pdf.py +0 -0
  485. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/badge_svg.py +0 -0
  486. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/board_one_pager.py +0 -0
  487. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/bounty_format.py +0 -0
  488. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/burp_export.py +0 -0
  489. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/comparison_two_sites.py +0 -0
  490. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/compliance_attestation.py +0 -0
  491. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/console.py +0 -0
  492. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/csv_out.py +0 -0
  493. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/d3fend_mapping.py +0 -0
  494. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/dashboard.py +0 -0
  495. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/diff_agency.py +0 -0
  496. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/diff_viewer.py +0 -0
  497. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/docx_report.py +0 -0
  498. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/eli5_toggle.py +0 -0
  499. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/exec_pdf.py +0 -0
  500. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/executive_pack.py +0 -0
  501. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/executive_tldr.py +0 -0
  502. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/finding_heatmap.py +0 -0
  503. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/gdpr_dsr_report.py +0 -0
  504. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/html.py +0 -0
  505. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/issue_export.py +0 -0
  506. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/json_out.py +0 -0
  507. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/live_sync.py +0 -0
  508. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/markdown.py +0 -0
  509. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/org_dashboard.py +0 -0
  510. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/pdf_custom_branding.py +0 -0
  511. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/public_page.py +0 -0
  512. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/risk_forecast.py +0 -0
  513. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/sarif.py +0 -0
  514. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/snapshot_compare.py +0 -0
  515. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/translated_summary.py +0 -0
  516. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/trend_over_time.py +0 -0
  517. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/user_template.py +0 -0
  518. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/vex_export.py +0 -0
  519. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/xlsx_out.py +0 -0
  520. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/reporters/xlsx_pivot.py +0 -0
  521. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/risk.py +0 -0
  522. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/risk_weights.py +0 -0
  523. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/sbom.py +0 -0
  524. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/scan_zip.py +0 -0
  525. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/scanner.py +0 -0
  526. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/scheduler.py +0 -0
  527. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/siem.py +0 -0
  528. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/sites.py +0 -0
  529. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/sla.py +0 -0
  530. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/slack_app.py +0 -0
  531. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/spider.py +0 -0
  532. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ssh_audit.py +0 -0
  533. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/tags.py +0 -0
  534. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/template_engine.py +0 -0
  535. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/template_signature.py +0 -0
  536. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/threat_intel_v2.py +0 -0
  537. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/tray.py +0 -0
  538. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/turbo_engine.py +0 -0
  539. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/ux_extras.py +0 -0
  540. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/waf_rules.py +0 -0
  541. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/watchers.py +0 -0
  542. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/workflow.py +0 -0
  543. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan/workflow_cmds.py +0 -0
  544. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan.egg-info/dependency_links.txt +0 -0
  545. {wpsecscan-2.7.1 → wpsecscan-2.7.3}/wpsecscan.egg-info/entry_points.txt +0 -0
  546. {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.1
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>=24
696
+ Requires-Dist: keyring<26,>=24
697
697
  Provides-Extra: pdf
698
- Requires-Dist: reportlab>=4.0; extra == "pdf"
698
+ Requires-Dist: reportlab<5,>=4.0; extra == "pdf"
699
699
  Provides-Extra: browser
700
- Requires-Dist: playwright>=1.40; extra == "browser"
700
+ Requires-Dist: playwright<2,>=1.40; extra == "browser"
701
701
  Provides-Extra: yaml
702
- Requires-Dist: pyyaml>=6.0; extra == "yaml"
702
+ Requires-Dist: pyyaml<7,>=6.0; extra == "yaml"
703
703
  Provides-Extra: ops
704
- Requires-Dist: redis>=5.0; extra == "ops"
705
- Requires-Dist: bcrypt>=4.0; extra == "ops"
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>=10; extra == "ui"
708
- Requires-Dist: pystray>=0.19; extra == "ui"
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>=0.7; extra == "security"
710
+ Requires-Dist: defusedxml<0.8,>=0.7; extra == "security"
711
711
  Provides-Extra: all
712
- Requires-Dist: reportlab>=4.0; extra == "all"
713
- Requires-Dist: playwright>=1.40; extra == "all"
714
- Requires-Dist: pyyaml>=6.0; extra == "all"
715
- Requires-Dist: redis>=5.0; extra == "all"
716
- Requires-Dist: bcrypt>=4.0; extra == "all"
717
- Requires-Dist: Pillow>=10; extra == "all"
718
- Requires-Dist: pystray>=0.19; extra == "all"
719
- Requires-Dist: keyring>=24; extra == "all"
720
- Requires-Dist: defusedxml>=0.7; extra == "all"
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.1"
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
- "keyring>=24",
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"]}