dos
wp_ajax_nopriv_vortex_create_cart_link shares vortex_ajax_create(). The only gate is wp_verify_nonce on action vortex-create; there is no current_user_can, IP/session rate limit, or cap on how many links exist. vortex_cart_button() prints that nonce as data-nonce on the public cart page for every visitor, including uid 0. WordPress guest nonces hash uid 0 plus an empty session token, so one harvested nonce is valid for every logged-out caller until it ticks. After confirming WC()->cart->get_cart() is non-empty, the handler wp_generate_password(8, false) and update_option('vortex_'.$token, $export, false) with no uniqueness check. The inline comment says links are stored permanently so they do not expire; vortex_load_token still falls back to a transient only for old rows. There is no garbage collection, no uninstall/deactivation cleanup of vortex_* options, and autoload=false only avoids front-end autoload cost — it does not bound table growth. Attack: guest add-to-cart of any purchasable product, GET /cart/ for the nonce, then loop POST admin-ajax.php; each success inserts another wp_options row whose value is the full cart snapshot plus leftover cart_item_data (objects stripped only at the top level). Observable consequence is unbounded option_name LIKE 'vortex_%' growth and backup/DB size, not XSS or payment-script injection (the option is consumed by add_to_cart, not echoed).
vortex.php:112-144
RecommendationDo not use wp_options as an unbounded store for visitor-created content. Persist cart snapshots in a custom table or CPT with a TTL (or restore transients), enforce a per-session/IP rate limit, generate a longer token (32+ chars) with a uniqueness check, and delete leftover vortex_* rows on uninstall. Optionally require a logged-in customer to create links.