Skip to content

Commit 30d91a1

Browse files
committed
plan: add t143 wp.org plugin review compliance fixes
1 parent e882db4 commit 30d91a1

2 files changed

Lines changed: 184 additions & 0 deletions

File tree

TODO.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@
211211
- Benchmark page (React SPA with REST API) shipped with no E2E coverage
212212
- Create tests/e2e/benchmark-page.spec.js with ≥4 test cases: page render, form inputs, run list
213213

214+
- [ ] t143 Fix wp.org plugin review blockers: SSL, uninstall, i18n, permissions #devops #auto-dispatch ~4h ref:GH#724 logged:2026-04-03
215+
- Remove sslverify=>false (8 locations), create uninstall.php, add load_plugin_textdomain + wp_set_script_translations
216+
- Move auth into permission_callback for webhook/resale endpoints, harden CLI exec, wrap error_log in WP_DEBUG
217+
- Fix blueprint slugs, add MODELS.md to .distignore, update PHPCS min WP version to 6.9
218+
214219
## Backlog
215220

216221
### Onboarding & First-Run Experience (P0)

todo/tasks/t143-brief.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# t143: Fix wp.org plugin review blockers: SSL, uninstall, i18n, permissions
2+
3+
## Origin
4+
5+
- **Created:** 2026-04-03
6+
- **Session:** opencode:unknown-2026-04-03
7+
- **Created by:** ai-interactive
8+
- **Parent task:** t124 (WordPress.org submission prep)
9+
- **Conversation context:** Comprehensive wp.org plugin check audit identified 9 critical/high issues that will cause rejection during WordPress.org manual review. This task addresses all blockers.
10+
11+
## What
12+
13+
Fix all issues that would cause the WordPress.org Plugin Review Team to reject the plugin submission. The plugin must pass manual review without requiring back-and-forth with reviewers.
14+
15+
Deliverables:
16+
1. Remove all `sslverify => false` from HTTP calls (8 locations)
17+
2. Create `uninstall.php` that cleans up all plugin data
18+
3. Add `load_plugin_textdomain()` and `wp_set_script_translations()` for all entry points
19+
4. Move authentication logic into `permission_callback` for webhook/resale endpoints
20+
5. Harden CLI tool execution or add `escapeshellarg()` on user-controlled command parts
21+
6. Wrap `error_log()` calls in WP_DEBUG check
22+
7. Add `MODELS.md` to `.distignore`
23+
8. Fix blueprint slugs to use current `gratis-ai-agent` naming
24+
9. Update PHPCS `minimum_supported_wp_version` to 6.9
25+
26+
## Why
27+
28+
WordPress.org plugin review is a manual process with 1-4 week turnaround. Each rejection-and-resubmit cycle costs weeks. Fixing all known blockers before submission avoids multiple review rounds and gets the plugin listed faster.
29+
30+
The SSL verification issue alone is an automatic rejection — reviewers have tooling that flags `sslverify => false`. Missing `uninstall.php` is explicitly called out in the Plugin Review Handbook as a requirement for plugins that create database tables.
31+
32+
## How (Approach)
33+
34+
### SSL Verification (Critical)
35+
Remove `'sslverify' => false` from all `wp_remote_post()`/`wp_remote_request()` calls. Remove `'verify_peer' => false` and `'verify_peer_name' => false` from the `stream_context_create()` call. WordPress's HTTP API handles SSL correctly — disabling it is unnecessary and insecure.
36+
37+
Files:
38+
- `includes/Core/AgentLoop.php:1146,1205-1206` — streaming and non-streaming calls
39+
- `includes/Core/OpenAIProxy.php:117`
40+
- `includes/REST/RestController.php:2241,2431,6390`
41+
- `includes/REST/WebhookController.php:691`
42+
43+
### Uninstall (Critical)
44+
Create `uninstall.php` at plugin root. Must:
45+
- Check `defined('WP_UNINSTALL_PLUGIN')` guard
46+
- Drop all 10 `{$wpdb->prefix}gratis_ai_agent_*` tables
47+
- Delete all `gratis_ai_agent_*` options from `wp_options`
48+
- Delete user meta with `gratis_ai_agent_*` prefix
49+
- Reference `includes/Core/Database.php` for table names
50+
51+
### i18n (Critical)
52+
- Add `load_plugin_textdomain('gratis-ai-agent', false, dirname(plugin_basename(__FILE__)) . '/languages')` on `init` hook in `gratis-ai-agent.php`
53+
- Add `wp_set_script_translations()` calls for all 8 JS entry points (admin-page, floating-widget, screen-meta, settings-page, changes-page, abilities-explorer, benchmark-page, unified-admin)
54+
55+
### Permission Callbacks (High)
56+
Move API key validation from `handle_proxy()` into the `permission_callback` for `/resale/proxy` in `ResaleApiController.php:58`.
57+
Move webhook secret validation from `handle_trigger()` into the `permission_callback` for `/webhook/trigger` in `WebhookController.php:68`.
58+
59+
### CLI Hardening (High)
60+
In `CustomToolExecutor.php:265-276`, the `$command` variable after placeholder replacement should have each argument passed through `escapeshellarg()`. The current `preg_replace('/[;&|`$]/', '', $command)` denylist is insufficient.
61+
62+
### error_log (High)
63+
Wrap `error_log()` calls in `NotificationDispatcher.php:337,356` with `if (defined('WP_DEBUG') && WP_DEBUG)`.
64+
65+
### Minor Fixes (Medium)
66+
- Add `MODELS.md` to `.distignore`
67+
- Update `.wordpress-org/blueprints/blueprint.json` line 9: `page=ai-agent``page=gratis-ai-agent`, line 46: `ai_agent_settings``gratis_ai_agent_settings`
68+
- Update `phpcs.xml` line 24: `minimum_supported_wp_version` from `6.7` to `6.9`
69+
70+
## Acceptance Criteria
71+
72+
- [ ] Zero instances of `sslverify => false` or `verify_peer => false` in plugin PHP files (excluding vendor/)
73+
```yaml
74+
verify:
75+
method: bash
76+
run: "! rg -q 'sslverify.*false|verify_peer.*false' -g '*.php' -g '!vendor/*' -g '!tests/*'"
77+
```
78+
- [ ] `uninstall.php` exists at plugin root with `WP_UNINSTALL_PLUGIN` guard
79+
```yaml
80+
verify:
81+
method: bash
82+
run: "test -f uninstall.php && rg -q 'WP_UNINSTALL_PLUGIN' uninstall.php"
83+
```
84+
- [ ] `load_plugin_textdomain()` called on `init` hook
85+
```yaml
86+
verify:
87+
method: codebase
88+
pattern: "load_plugin_textdomain.*gratis-ai-agent"
89+
path: "gratis-ai-agent.php"
90+
```
91+
- [ ] `wp_set_script_translations()` called for all 8 JS entry points
92+
```yaml
93+
verify:
94+
method: bash
95+
run: "count=$(rg -c 'wp_set_script_translations' -g '*.php' -g '!vendor/*' -g '!tests/*' | awk -F: '{s+=$2}END{print s}'); [ \"$count\" -ge 8 ]"
96+
```
97+
- [ ] No `__return_true` in permission_callback for REST routes
98+
```yaml
99+
verify:
100+
method: bash
101+
run: "! rg -q \"permission_callback.*__return_true\" -g '*.php' -g '!vendor/*' -g '!tests/*'"
102+
```
103+
- [ ] `error_log()` calls wrapped in WP_DEBUG check
104+
```yaml
105+
verify:
106+
method: bash
107+
run: "! rg -q '^[^/]*error_log\\(' includes/Automations/NotificationDispatcher.php"
108+
```
109+
- [ ] `MODELS.md` listed in `.distignore`
110+
```yaml
111+
verify:
112+
method: codebase
113+
pattern: "MODELS\\.md"
114+
path: ".distignore"
115+
```
116+
- [ ] Blueprint uses `gratis-ai-agent` slug
117+
```yaml
118+
verify:
119+
method: bash
120+
run: "! rg -q 'page=ai-agent[^-]|ai_agent_settings' .wordpress-org/blueprints/blueprint.json"
121+
```
122+
- [ ] PHPCS passes clean
123+
```yaml
124+
verify:
125+
method: bash
126+
run: "composer phpcs 2>&1 | tail -1 | grep -q 'Time:'"
127+
```
128+
- [ ] PHPStan passes clean
129+
```yaml
130+
verify:
131+
method: bash
132+
run: "composer phpstan 2>&1 | grep -q 'No errors'"
133+
```
134+
- [ ] Tests pass
135+
- [ ] Lint clean
136+
137+
## Context & Decisions
138+
139+
- The audit was performed manually because `wp plugin check` could not run (another plugin on the site has a fatal error).
140+
- `sslverify => false` was likely added during development with self-signed certs. It must be removed for production/wp.org.
141+
- The `fopen()` streaming approach in AgentLoop is acceptable with documentation — WordPress HTTP API doesn't support SSE streaming natively. The SSL verification on that stream context must still be enabled.
142+
- `__return_true` on webhook/resale endpoints is functionally fine (they do their own auth), but wp.org reviewers flag it automatically. Moving auth into the callback is a cosmetic fix that satisfies the review.
143+
- The `exec()` in CustomToolExecutor is an admin-only feature (CLI tools). Full hardening is important but the denylist approach is the minimum viable fix. Long-term, `WP_CLI::runcommand()` would be better.
144+
145+
## Relevant Files
146+
147+
- `gratis-ai-agent.php` — main plugin file, add i18n loading
148+
- `includes/Core/AgentLoop.php:1146,1205` — SSL verification
149+
- `includes/Core/OpenAIProxy.php:117` — SSL verification
150+
- `includes/REST/RestController.php:2241,2431,6390` — SSL verification
151+
- `includes/REST/WebhookController.php:68,691` — permission callback + SSL
152+
- `includes/REST/ResaleApiController.php:58` — permission callback
153+
- `includes/Tools/CustomToolExecutor.php:265-276` — CLI command hardening
154+
- `includes/Automations/NotificationDispatcher.php:337,356` — error_log
155+
- `includes/Admin/*.php` — wp_set_script_translations additions
156+
- `includes/Core/Database.php` — table names reference for uninstall.php
157+
- `.distignore` — add MODELS.md
158+
- `.wordpress-org/blueprints/blueprint.json` — fix slugs
159+
- `phpcs.xml:24` — min WP version
160+
161+
## Dependencies
162+
163+
- **Blocked by:** none
164+
- **Blocks:** WordPress.org submission (can't submit until these are fixed)
165+
- **External:** none
166+
167+
## Estimate Breakdown
168+
169+
| Phase | Time | Notes |
170+
|-------|------|-------|
171+
| SSL removal | 30m | 8 locations, straightforward removal |
172+
| uninstall.php | 45m | Create file, enumerate tables/options |
173+
| i18n loading | 30m | Add textdomain + script translations |
174+
| Permission callbacks | 30m | Move auth logic into callbacks |
175+
| CLI hardening | 30m | escapeshellarg on command parts |
176+
| error_log wrapping | 10m | 2 locations |
177+
| Minor fixes | 15m | distignore, blueprint, phpcs |
178+
| Testing/verification | 30m | Run all linters, verify |
179+
| **Total** | **~4h** | |

0 commit comments

Comments
 (0)