wpsecscan 2.4.0__tar.gz → 2.6.0__tar.gz

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