WP Manifestindependent plugin directory
manifest / events / blastqr

BlastQR WP Registry grade D+. High-severity findings · 3 findingsWP RegistryHigh-severity findings · 3 findingsOpen the reportD+

Modulo Quick Reserve Blastness

★ 0stars
833release downloads
≈10active sites
1.0.0latest · 1 year ago

Install

Download blastqr-main.zip From the 1.0.0 release on GitHub, Jul 21, 2025

Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/assistenzablastness/blastqr/releases/download/1.0.0/blastqr-main.zip
Updates itself. It ships its own updater, so new versions appear under Dashboard → Updates.

Releases

6 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
1.0.0 latest
Jul 21, 2025 1 year ago
blastqr-main.zip
805
Dec 2, 2024 1 year ago
blastqr-main.zip
11
Nov 27, 2024 1 year ago
blastqr-main.zip
6
Nov 27, 2024 1 year ago
blastqr-main.zip
5
Nov 20, 2024 1 year ago
blastqr-main.zip
4
Oct 24, 2024 1 year ago
blastqr-main.zip
2

Active-site estimate ≈10 comes from the median of recent superseded releases. Method.

D+ grade

Security

BlastQR 1.0.0 · audited by WP Registry

High-severity findings.

2 high 1 medium
Audited release
1.0.0
Findings
3
Worst severity
high
Content hash
c471e14a19e0321f69f4ed20…

Findings

  • high CSRF: BlastQR settings save has no nonce and overwrites Blastness booking-engine identifiers

    csrf

    blastqr_settings_page() is the add_menu_page callback (capability manage_options, slug blastqr-settings). On any request where isset($_POST['submit']), it writes blastqr_id_albergo, blastqr_id_stile, blastqr_dc, blastqr_action_type, blastqr_enable_qr, blastqr_fixed_discount_code, blastqr_type_qr, blastqr_custom_css, and related flags via update_option() with no wp_nonce_field in the <form method="post" action="#"> and no check_admin_referer / wp_verify_nonce / extra current_user_can in the handler. WordPress still capability-gates the page, so a Subscriber cannot POST it directly, but a logged-in administrator who submits a cross-site form to /wp-admin/admin.php?page=blastqr-settings persists the attacker's values. Observable consequence: aggiungi_form_html() (hooked to wp_footer as stampaForm, or registered as a shortcode) consumes those IDs as the public form's action and hidden id_albergo/id_stile/dc fields pointing at https://book.blastness.com/results or https://www.blastnessbooking.com/premium|reservations, so every subsequent reservation and the modify/cancel link is sent to a property the attacker controls. The same POST can set blastqr_enable_qr=1 (show the form to guests) and plant the stored-XSS payloads in the sibling finding. This is POST (SameSite=Lax may block some browsers); it is still a missing nonce on a payment-flow setting.

    includes/settings-page.php:4-12,125-143,286

    Recommendation

    Add wp_nonce_field('blastqr_save_settings') to the settings form and call check_admin_referer('blastqr_save_settings') plus current_user_can('manage_options') before any update_option. Prefer the Settings API (register_setting + settings_fields) so the options.php nonce is automatic. Whitelist action_type to premium|advanced|aibe and type_qr to full-width|box.

  • high Stored XSS: Quick Reserve form interpolates unsanitized options into HTML attributes and a JS template literal

    xss_stored

    aggiungi_form_html() builds the public booking widget from options and either returns that HTML (echoed by stampaForm on wp_footer, or via the dynamic shortcode) or injects it with anchor.innerHTML = `...`. blastqr_fixed_discount_code is concatenated into input value="..." and into the prenota href as &generic_codice=; blastqr_type_qr is concatenated into the form/container class attribute; blastqr_data_apertura is concatenated into data-mindate="...". All three are saved with sanitize_text_field(), which does not encode double quotes, single quotes, or backticks. A payload such as " autofocus onfocus="alert(document.domain)" in the discount code or box" onmouseover="alert(1) x=" in type_qr executes in every guest browser once blastqr_enable_qr is on (or in every logged-in user if only preview is on). The innerHTML path uses addslashes() inside a JavaScript template literal, so a backtick in any of those fields also breaks out of the JS string; innerHTML will run event-handler attributes. Write path is the nonce-less manage_options POST in settings-page.php, so an unauthenticated attacker who CSRFs an administrator plants site-wide frontend XSS. id_albergo/id_stile/dc on the same form are correctly passed through esc_attr().

    includes/form-output.php:107-139,168-175

    Recommendation

    Pass every option used in HTML through esc_attr() / esc_url() at the echo site (value, class, data-mindate, href query). Whitelist type_qr to {full-width, box}. Do not build JS with string concatenation or addslashes(); if the form must be assigned in JS, JSON-encode it (wp_json_encode) and assign via text/DOM APIs rather than innerHTML with template literals.

  • medium Unauthenticated [offerte] path unserializes a partner HTTP body and echoes booking links unescaped

    unsafe_unserialize

    blastqr_visualizza_offerte() is always registered as shortcode [offerte]. If get_option('offerte_scaricate_{lang}') is older than 7200 seconds, any visitor rendering that shortcode calls blastqr_scarica_offerte(). That function concatenates stored hotel IDs into a hardcoded VerticalBooking or Fox URL, file_get_contents()s it with no timeout and no status check, then unserialize()s the raw body (no allowed_classes) and update_option('offerte_{lang}', $data). Request parameters are not folded into the URL, so this is not first-party SSRF or trivially attacker-chosen bytes on a default install — exploitation of object instantiation requires the HTTPS response from xml.verticalbooking.com / api.foxtechnologies.it to contain O:/C: payloads (compromised partner, or a future change of those options via the CSRF finding). That is still unserialize-of-network-data on a public cache miss. The stored array is then rendered: nome/descrizione go through sanitize_text_field and img_file through esc_url, but $link_pren (offerta['link_prenotazione'] or a str_replace of it) is concatenated into <a href="..."> with no esc_url(), so a javascript: or attribute-breaking link from that API becomes stored XSS on the offers list.

    includes/download_offers.php:3-24

    Recommendation

    Replace file_get_contents+unserialize with wp_remote_get() and json_decode() (or unserialize(..., ['allowed_classes' => false]) only if the vendor contract is documented PHP serialization of arrays). Check the HTTP status and Content-Type before decoding; fail closed. Escape offer links with esc_url() (and skip non-http(s) schemes). Do not refresh the remote fetch on an unauthenticated front-end render; use a cron with a timeout.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.