Skip to content

Commit 9ad527d

Browse files
release: fixes
- Enhanced security
2 parents 7e8cdfc + 7414479 commit 9ad527d

37 files changed

+28574
-21207
lines changed

.claude/commands/security-audit.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Security Audit
2+
3+
You are about to perform a fully automated WordPress security audit on this repository. Follow every step below precisely and in order. Do not skip steps. Do not ask for confirmation between steps — execute autonomously from start to finish.
4+
5+
## Scope
6+
7+
**Always audit the entire repository.** Do not limit the audit to recent commits, the current branch diff, open PRs, or changed files. Every PHP and JS file in the codebase must be analyzed regardless of when it was last modified.
8+
9+
## Instructions
10+
11+
1. **Read `CLAUDE.md` in full** before doing anything else. This is your knowledge base for the entire audit.
12+
13+
2. **Detect product type** — determine if this repo is a plugin or a theme by checking the main PHP file header for `Plugin Name:` or `style.css` for `Theme Name:`.
14+
15+
3. **Read environment requirements** — check `readme.txt`, `README.md`, `composer.json`, and `package.json` for WordPress version, PHP version, and any other requirements.
16+
17+
4. **Generate `.wp-env.json`** — following the instructions in CLAUDE.md Step 1. If one already exists, validate and update it.
18+
19+
5. **Run Semgrep** — execute `bash security-audit.sh semgrep` and wait for it to complete. Read `semgrep-results.json` in full.
20+
21+
6. **Triage findings** — following CLAUDE.md Step 3, go through every Semgrep finding. Confirm or dismiss each one. Then perform the deep code analysis described in CLAUDE.md Step 4 to find issues Semgrep may have missed.
22+
23+
7. **Generate PoCs** — for every confirmed vulnerability, create a PoC file in `security-pocs/` following the templates in CLAUDE.md Step 5.
24+
25+
8. **Run PoCs** — execute `bash security-audit.sh run-pocs` and wait for it to complete. Read `security-poc-results.json` to confirm which vulnerabilities are real and exploitable.
26+
27+
9. **Write the report** — write `SECURITY_REPORT.md` following the structure in CLAUDE.md Step 7. Only include confirmed, exploitable vulnerabilities.
28+
29+
10. **Cleanup** — run `bash security-audit.sh cleanup`.
30+
31+
11. **Summarize** — once complete, give a brief summary in the terminal of how many confirmed vulnerabilities were found and their severity breakdown.

.claude/security-audit.md

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
# WordPress Security Audit Guide
2+
3+
This file defines how Claude Code should conduct automated security audits on this WordPress plugin/theme. It is used in conjunction with the `/security-audit` slash command.
4+
5+
---
6+
7+
## Product Environment
8+
9+
Before running the audit, read the following files if they exist to understand environment requirements:
10+
- `readme.txt` or `README.md` — for WordPress and PHP version requirements
11+
- `composer.json` — for PHP version constraints
12+
- `package.json` — for Node/build tooling
13+
- Any existing `.wp-env.json`
14+
15+
Use this information to generate or update `.wp-env.json` accordingly.
16+
17+
---
18+
19+
## Step 1 — Generate `.wp-env.json`
20+
21+
If `.wp-env.json` does not exist, create it. If it does exist, validate it has the correct structure.
22+
23+
The file must:
24+
- Mount the current directory as a plugin or theme (detect by checking for `*plugin*` in the main PHP file header or a `style.css` with `Theme Name:`)
25+
- Set the correct WordPress version (from readme.txt or default to `latest`)
26+
- Set the correct PHP version (from composer.json or default to `8.1`)
27+
- Include a test admin user
28+
29+
Example structure for a plugin:
30+
```json
31+
{
32+
"core": "WordPress/WordPress#6.5.0",
33+
"phpVersion": "8.1",
34+
"plugins": ["."],
35+
"themes": [],
36+
"config": {
37+
"WP_DEBUG": true,
38+
"WP_DEBUG_LOG": true,
39+
"SCRIPT_DEBUG": true
40+
}
41+
}
42+
```
43+
44+
Example structure for a theme:
45+
```json
46+
{
47+
"core": "WordPress/WordPress#6.5.0",
48+
"phpVersion": "8.1",
49+
"plugins": [],
50+
"themes": ["."],
51+
"config": {
52+
"WP_DEBUG": true,
53+
"WP_DEBUG_LOG": true,
54+
"SCRIPT_DEBUG": true
55+
}
56+
}
57+
```
58+
59+
---
60+
61+
## Step 2 — Run Semgrep
62+
63+
Run the helper script to execute Semgrep:
64+
65+
```bash
66+
bash bin/security-audit.sh semgrep
67+
```
68+
69+
This will output `semgrep-results.json`. Read this file fully before proceeding.
70+
71+
---
72+
73+
## Step 3 — Triage Semgrep Findings
74+
75+
For each finding in `semgrep-results.json`:
76+
77+
### Confirm or dismiss:
78+
- Trace the vulnerable variable back to its source. Is it user-controlled input (`$_GET`, `$_POST`, `$_REQUEST`, `$_COOKIE`, REST API params, `get_option()` if user-controlled)?
79+
- Check if proper sanitization/escaping is applied before the sink
80+
- Check if nonce verification exists where needed
81+
- Check if capability checks exist where needed
82+
83+
### Assign severity:
84+
- **Critical** — Unauthenticated exploit, direct data exposure or RCE possible
85+
- **High** — Authenticated exploit with low privilege (subscriber), significant impact
86+
- **Medium** — Authenticated exploit requiring higher privilege (editor+), moderate impact
87+
- **Low** — Requires admin privilege or has limited impact
88+
89+
### Dismiss if:
90+
- The variable is sanitized/escaped correctly before use
91+
- The function is only accessible to admins and the risk is negligible
92+
- It is a false positive due to Semgrep pattern limitations — document why
93+
94+
---
95+
96+
## Step 4 — Deep Code Analysis
97+
98+
Beyond Semgrep findings, manually analyze the following high-risk areas:
99+
100+
### REST API Endpoints
101+
- Find all `register_rest_route()` calls
102+
- Check `permission_callback` — is it `__return_true` or missing?
103+
- Check if parameters are sanitized with `sanitize_*` functions
104+
- **For endpoints that accept settings objects or arrays** (e.g. `visualizer-settings`, `meta`, `config`): trace each individual field through to where it is stored (post meta, options) and where it is later output (admin pages, frontend). Verify that each field is either sanitized on save (`sanitize_text_field()`, `wp_kses()`, `absint()`, etc.) or escaped on every output (`esc_attr()`, `esc_html()`, `wp_kses_post()`). A valid `permission_callback` does not make the stored data safe — a contributor-level user can still inject a stored XSS payload that executes when an admin views the data.
105+
106+
### AJAX Handlers
107+
- Find all `wp_ajax_` and `wp_ajax_nopriv_` hooks
108+
- Check nonce verification with `check_ajax_referer()` or `wp_verify_nonce()`
109+
- Check capability checks with `current_user_can()`
110+
111+
### Database Queries
112+
- Find all `$wpdb->query()`, `$wpdb->get_results()`, `$wpdb->get_var()`, `$wpdb->get_row()`
113+
- Confirm all use `$wpdb->prepare()` when user input is involved
114+
115+
### File Operations
116+
- Find `file_get_contents()`, `file_put_contents()`, `include()`, `require()`, `include_once()`, `require_once()`
117+
- Check if paths are user-controlled
118+
119+
### Output
120+
- Find `echo`, `print`, `_e()`, `esc_*` usage
121+
- Check unescaped output of user-controlled data
122+
123+
### Options & User Meta
124+
- Find `get_option()`, `get_user_meta()`, `update_option()`, `update_user_meta()`
125+
- Check if values stored or retrieved are sanitized
126+
127+
### Shortcodes
128+
- Find `add_shortcode()` — are attributes sanitized before output?
129+
130+
---
131+
132+
## Step 5 — Generate PoCs
133+
134+
For each confirmed real vulnerability, generate a Proof of Concept.
135+
136+
Store all PoCs in `security-pocs/` directory. Create one file per vulnerability named `poc-{severity}-{short-name}.sh` or `.php` as appropriate.
137+
138+
### PoC requirements:
139+
- Must be self-contained and runnable
140+
- Must include comments explaining what it does and what to expect
141+
- Must specify the required user role (unauthenticated / subscriber / editor / admin)
142+
- Use `curl` for HTTP-based exploits
143+
- Use WP-CLI for database/option-based exploits
144+
- Use PHP scripts for complex payloads
145+
146+
### PoC templates by vulnerability type:
147+
148+
**SQL Injection (curl):**
149+
```bash
150+
#!/bin/bash
151+
# Vulnerability: SQL Injection in [function name] at [file:line]
152+
# Severity: [severity]
153+
# Required role: Unauthenticated
154+
# Expected result: Database error or data leakage in response
155+
156+
TARGET="http://localhost:8888"
157+
PAYLOAD="1 UNION SELECT 1,user_login,user_pass,4,5,6,7,8,9,10 FROM wp_users--"
158+
159+
curl -s -G "$TARGET/wp-admin/admin-ajax.php" \
160+
--data-urlencode "action=your_action" \
161+
--data-urlencode "id=$PAYLOAD"
162+
```
163+
164+
**XSS (curl):**
165+
```bash
166+
#!/bin/bash
167+
# Vulnerability: Reflected XSS in [function name] at [file:line]
168+
# Severity: [severity]
169+
# Required role: Unauthenticated
170+
# Expected result: <script>alert(1)</script> appears unescaped in response
171+
172+
TARGET="http://localhost:8888"
173+
PAYLOAD='<script>alert(1)</script>'
174+
175+
curl -s -G "$TARGET/?your_param=$PAYLOAD" | grep -o '<script>alert(1)</script>'
176+
```
177+
178+
**CSRF (HTML form):**
179+
```html
180+
<!--
181+
Vulnerability: CSRF in [action] at [file:line]
182+
Severity: [severity]
183+
Required role: Victim must be logged in as [role]
184+
Expected result: Action executes without user consent
185+
Instructions: Host this file and trick a logged-in user to open it
186+
-->
187+
<form method="POST" action="http://localhost:8888/wp-admin/admin-ajax.php">
188+
<input type="hidden" name="action" value="your_action">
189+
<input type="hidden" name="data" value="malicious_value">
190+
<input type="submit" value="Click me">
191+
</form>
192+
<script>document.forms[0].submit();</script>
193+
```
194+
195+
**Privilege Escalation (curl with auth):**
196+
```bash
197+
#!/bin/bash
198+
# Vulnerability: Privilege escalation in [endpoint] at [file:line]
199+
# Severity: [severity]
200+
# Required role: Subscriber
201+
# Expected result: Subscriber can perform admin-only action
202+
203+
TARGET="http://localhost:8888"
204+
205+
# Get auth cookie as subscriber
206+
COOKIE=$(curl -s -c - -X POST "$TARGET/wp-login.php" \
207+
-d "log=subscriber&pwd=password&wp-submit=Log+In&redirect_to=%2F&testcookie=1" \
208+
-b "wordpress_test_cookie=WP+Cookie+check" | grep wordpress_logged_in | awk '{print $7"="$8}')
209+
210+
# Fire privileged action as subscriber
211+
curl -s -X POST "$TARGET/wp-admin/admin-ajax.php" \
212+
-b "$COOKIE" \
213+
-d "action=privileged_action&data=malicious"
214+
```
215+
216+
---
217+
218+
## Step 6 — Run PoCs Against wp-env
219+
220+
Run the helper script to start wp-env and execute all PoCs:
221+
222+
```bash
223+
bash security-audit.sh run-pocs
224+
```
225+
226+
The script will:
227+
1. Start wp-env
228+
2. Create test users (admin, editor, subscriber) with known passwords
229+
3. Execute each PoC in `security-pocs/`
230+
4. Log results to `security-poc-results.json`
231+
5. Stop wp-env
232+
233+
Read `security-poc-results.json` and determine which vulnerabilities are confirmed exploitable.
234+
235+
---
236+
237+
## Step 7 — Write SECURITY_REPORT.md
238+
239+
Write a comprehensive security report. Only include **confirmed, exploitable** vulnerabilities.
240+
241+
### Report structure:
242+
243+
```markdown
244+
# Security Audit Report — [Plugin/Theme Name]
245+
**Date:** [date]
246+
**Audited by:** Claude Code Automated Security Pipeline
247+
**Environment:** WordPress [version], PHP [version]
248+
249+
## Summary
250+
- Total confirmed vulnerabilities: X
251+
- Critical: X | High: X | Medium: X | Low: X
252+
253+
## Findings
254+
255+
### [SEVERITY][Vulnerability Type] in [File]
256+
257+
**Location:** `path/to/file.php` line X
258+
**Severity:** Critical / High / Medium / Low
259+
**Required role:** Unauthenticated / Subscriber / Editor / Admin
260+
261+
**Description:**
262+
[Clear explanation of the vulnerability and why it is dangerous]
263+
264+
**Reproduction:**
265+
[Step by step instructions]
266+
267+
**Payload / PoC:**
268+
\`\`\`bash
269+
[PoC command or script]
270+
\`\`\`
271+
272+
**Expected Result:**
273+
[What happens when exploited]
274+
275+
**Recommended Fix:**
276+
[Specific code-level fix with example]
277+
278+
---
279+
```
280+
281+
---
282+
283+
## Cleanup
284+
285+
After the report is written, run:
286+
287+
```bash
288+
bash security-audit.sh cleanup
289+
```
290+
291+
This removes temporary files but preserves `SECURITY_REPORT.md` and `security-pocs/`.

.distignore

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ package-lock.json
2020
vendor/openspout/openspout/LICENSE-for-*
2121
vendor/openspout/openspout/UPGRADE-3.0.md
2222
vendor/openspout/openspout/README.md
23-
cypress
24-
cypress.json
2523
.github
2624
.idea
2725
.wordpress-org
@@ -30,4 +28,8 @@ docker-compose.ci.yml
3028
CONTRIBUTING.md
3129
artifacts
3230
phpstan.neon
33-
phpstan-baseline.neon
31+
phpstan-baseline.neon
32+
AGENTS.md
33+
.wp-env.json
34+
.claude
35+

.gitignore

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@ logs
44
dist
55
artifact
66
vendor
7-
cypress/integration/examples
8-
cypress/integration/localhost*
9-
cypress/plugins
10-
cypress.env.json
117
.DS_Store
12-
/cypress/videos/
13-
/cypress/screenshots/
148
artifacts
159
.phpunit.result.cache

.wp-env.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"core": "WordPress/WordPress#6.5.0",
3+
"phpVersion": "7.4",
4+
"plugins": ["."],
5+
"themes": [],
6+
"config": {
7+
"WP_DEBUG": true,
8+
"WP_DEBUG_LOG": true,
9+
"SCRIPT_DEBUG": true
10+
}
11+
}

0 commit comments

Comments
 (0)