TL;DR
1. Nonces verified before any other request data is read
Every handler that changes state verifies a WordPress nonce as the very first statement in the function body. No $_GET, $_POST, or $_REQUEST data is read before nonce verification — including the comment ID. Static analysers (WordPress.org Plugin Check) confirm this.
| Handler | Verification helper | Nonce action |
|---|---|---|
handle_generate_reply |
check_admin_referer |
replymind_generate_reply |
handle_publish_reply |
check_admin_referer |
replymind_publish_reply |
handle_unflag_comment |
check_admin_referer |
replymind_unflag_comment_{$comment_id} |
handle_clear_logs |
check_admin_referer |
replymind_clear_logs |
handle_dismiss_onboarding |
check_admin_referer |
replymind_dismiss_onboarding |
handle_remove_api_key (legacy) |
check_admin_referer |
replymind_remove_api_key |
handle_submit_reply (AJAX) |
check_ajax_referer |
replymind_submit_reply |
handle_batch_count (AJAX) |
check_ajax_referer |
replymind_batch_nonce |
handle_batch_process (AJAX) |
check_ajax_referer |
replymind_batch_nonce |
handle_batch_publish (AJAX) |
check_ajax_referer |
replymind_batch_nonce |
| Reply Rules AJAX (save / delete / toggle / reorder) | check_ajax_referer |
replymind_pro_rules_nonce |
| Per-post meta box save | wp_verify_nonce |
replymind_pro_post_overrides_{$post_id} |
| Multisite push settings | POST nonce | admin_post_replymind_pro_push_settings |
| Email approval Approve / Reject | Single-use transient token, 48 h | — |
If a nonce check fails, WordPress halts the request immediately with a 403 — no error message that could leak information, no partial side effect.
2. Capability gates
Every action requires the user to have the right WordPress capability:
| Operation | Capability | Default roles that have it |
|---|---|---|
| Generate / publish / submit a reply on a comment | edit_comment (per-comment) |
Administrator, Editor |
| Save settings, rules and mappings, batch operations, clear logs, analytics | manage_options |
Administrator only |
Capability is checked after the nonce — so a stolen nonce by itself can't trigger anything; a stolen nonce + a privileged user is the only path. WordPress enforces both.
3. No notice state in URL query args
Many WordPress plugins pass admin-notice text through URL parameters after a redirect. That's a common XSS vector if not perfectly sanitized. ReplyMind doesn't do this.
After every state-changing action, the plugin stores a notice in a per-user transient:
replymind_notice_{user_id} → { type, message } (60-second TTL)
admin_notices() reads the transient (which is internal data, not user input), renders the message escaped, and deletes the transient. No URL query arg is read for notice content. Nothing the renderer touches is unverified user input.
4. No API keys in the plugin
Since 1.1.0 ReplyMind stores no provider credentials whatsoever. All AI requests go through the AI Client built into WordPress 7.0 (wp_ai_client_prompt()):
- Keys live in WordPress core, under Settings → Connectors, protected by core's own storage and capability rules. ReplyMind never reads, renders, or transmits them.
- The plugin makes no outbound HTTP calls of its own. It hands a prompt to the AI Client and consumes the returned text — nothing more.
- Nothing is ever sent to approidtech.com. There is no licensing server, no telemetry, no update channel outside WordPress.org.
- A legacy
replymind_api_keyoption and its sanitizer are retained only for backward compatibility; the field is hidden whenever the AI Client is active, and the option is deleted on uninstall.
5. Prompt-injection guard
A common attack against AI-driven plugins is for visitors to embed instructions in their comment, like:
ReplyMind's system prompt explicitly instructs the model:
This significantly reduces successful prompt-injection rates. The guard is unconditional — it's applied to every single generation, regardless of mode or model.
6. Comment HTML stripping
Before the comment text is sent to the AI, it's run through:
wp_strip_all_tags()— removes all HTML and PHP tags, including their content.html_entity_decode()— converts&,<,>, etc. back to plain characters so the model sees readable text without escaped sequences.
Embedded <script> tags, malformed HTML, or attempts to inject Markdown that looks like instructions ("# IMPORTANT INSTRUCTION:") are all neutralised before the model sees them.
7. API error body truncation
When the AI provider returns a non-200 response, the body could in principle contain anything — including echoed-back parts of the request. ReplyMind:
- Tries to parse the body as JSON and extract
error.message(the canonical field). - Falls back to the raw body run through
wp_strip_all_tags. - Caps the resulting string at 300 characters before logging or displaying.
This prevents pathological provider responses from inflating log entries or surfacing more detail than necessary in admin notices.
8. Daily rate limit
A misconfigured auto-mode site, a runaway batch run, or a malicious approval flow could in theory burn through a lot of API credits. ReplyMind caps it:
- Default cap: 100 successful API calls per day, per site.
- Counter: date-keyed transient (
replymind_daily_YYYYMMDD); resets daily at server-local midnight. - Counts only successful calls — failed attempts don't consume quota.
- Filterable:
add_filter( 'replymind_daily_limit', fn() => 500 ). Set to0to disable entirely. - Batch operations respect the cap. When the cap is hit mid-batch, the loop stops cleanly and the remaining comments stay unmarked for tomorrow's run.
9. Asset enqueuing — no inline scripts or styles
Every CSS and JS file is enqueued via wp_enqueue_style / wp_enqueue_script on the admin_enqueue_scripts hook. There are zero inline <script> or <style> blocks anywhere in the plugin's PHP. This means:
- Your Content Security Policy (CSP) doesn't need to allow inline scripts for ReplyMind.
- Caching plugins can correctly version and cache the assets.
- Translation tooling (
wp_localize_script) is used for dynamic data.
10. WordPress.org review process
The plugin is hosted on the WordPress.org plugin directory. Every release:
- Is statically analysed by Plugin Check, the official linting tool.
- Follows the Plugin Directory Guidelines.
- Is human-reviewed on initial submission.
The plugin's full source is openly auditable under the GPL v2 (or later) licence.
11. What about my data?
| Data | Where it goes |
|---|---|
| Comment text | The AI provider you configure (OpenAI, Anthropic Claude, or Google Gemini), via the WordPress AI Client. Sentiment scoring happens locally first. |
| Post title + 40-word excerpt + author bio | The same AI provider |
| Provider credentials | Stored by WordPress core (Settings → Connectors), never touched by ReplyMind |
| Visitor IP, email, name | Not sent to the AI — the plugin uses only the comment text and post context |
| Anything else | Doesn't leave your server |
Nothing is sent to ReplyMind's servers. We have no servers in the loop. The plugin talks only to the WordPress AI Client, which talks to the provider you chose.
12. Reporting a security issue
If you've found a vulnerability, don't post it publicly. Email security@approidtech.com with details. We'll respond within 1 business day, fix the issue, and credit you in the changelog if you'd like.
Read next
- How it works — what the plugin actually does
- GDPR compliance — privacy posture in detail
- Developer reference — filters and hooks for advanced use