WordPress extension code runs inside a shared request lifecycle with mutable globals, a hook graph assembled by many packages, long-lived public data, and administrators who can update core or neighboring plugins independently. This pattern constrains authorization and output boundaries, records hook and data ownership conventions, separates plugin behavior from theme presentation, and provides repeatable compatibility and release audits. It is not a generic PHP or Laravel pattern: it focuses on WordPress actions, filters, capabilities, nonces, enqueue APIs, database prefixes, activation, deactivation, uninstall, and backward-compatible extension contracts.
Suggested path map
Pathrule places each piece on the matching path, so your assistant only sees it where it belongs. This is the scoping you get on import; you can adjust it in your workspace.
/ workspace root
audit-wordpress-extension-security
release-wordpress-extension
wp-content/
plugins/
Check capability and intent before every privileged change
Escape at the final rendering context
Run migrations and remote effects outside the hot hook path
Hooks are a public ordering contract
themes/
Themes present content while plugins own portable behavior
Rules
3
Check capability and intent before every privileged change/wp-content/pluginshighstrictAuthorize the current user, verify request intent, validate input, and fail before mutating WordPress or external state.
1
A WordPress nonce demonstrates request intent and origin freshness, not permission. Privileged actions need both a capability decision and nonce verification before any update, upload, option write, or remote effect.
2
3
- Use a capability that describes the resource and action, then call the matching authorization check before loading mutable state or invoking a service.
4
- Verify the nonce for state-changing browser requests and reject missing or invalid values; do not treat successful nonce verification as proof of role or ownership.
5
- Validate identifiers, enums, and structured input before use, then pass parameters through the appropriate WordPress APIs or prepared database statements.
6
- Return a generic public error while recording enough structured context for an administrator to identify the extension, action, and rejected actor.
7
8
See /wp-content/plugins for the adjacent decision or procedure that completes this constraint.
Escape at the final rendering context/wp-content/pluginshighstrictSanitize stored input by domain, then escape dynamic output for HTML, attributes, URLs, JavaScript, or structured markup at the sink.
1
Sanitization and escaping solve different problems. Values can be safe for storage but unsafe in a particular output context, while escaping before transformation can be undone by later concatenation.
2
3
- Validate and sanitize incoming values according to their domain before storage; do not erase meaningful content with a generic sanitizer chosen only for convenience.
4
- Escape plain text at HTML output, attribute values at attribute output, and URLs at URL output. Choose the function for the sink, not the source variable name.
5
- Avoid inline script assembly with dynamic strings. Pass structured data through the supported script-data APIs and let JSON encoding preserve boundaries.
6
- When intentionally allowing rich HTML, use an explicit allowlist and test disallowed elements and attributes rather than trusting editor permissions alone.
7
8
See /wp-content/themes for the adjacent decision or procedure that completes this constraint.
Run migrations and remote effects outside the hot hook path/wp-content/pluginshighstrictVersion data migrations, schedule bounded work, and make activation fast and retryable instead of blocking every request.
1
Plugin hooks can run on front-end, admin, REST, cron, and command-line requests. An expensive migration or remote call registered broadly can slow the entire site or leave it half-upgraded after a timeout.
2
3
- Store a schema or data version and run idempotent, bounded migration steps only when the installed version lags behind the code.
4
- Keep activation limited to capability setup, schedules, and small deterministic initialization; enqueue long work for a background or command-line path.
5
- Register hooks conditionally and as late as necessary so admin-only or endpoint-only behavior does not execute on unrelated front-end requests.
6
- Make remote calls timeout-bounded and failure-aware. Cache stable results, and never make page rendering depend on an unbounded third-party response.
7
8
See /tests for the adjacent decision or procedure that completes this constraint.
Memories
2
Hooks are a public ordering contract/wp-content/pluginsName callbacks, choose priorities deliberately, and keep filter functions pure enough for other extensions to compose around them.
1
Actions and filters form a shared graph across core, the active theme, and every plugin. A callback that relies on accidental registration order can break when another extension adds work at the same hook.
2
3
- Register named callbacks from one bootstrap surface so hook, priority, accepted arguments, and owner are reviewable together.
4
- Use priority only to express a real ordering dependency and document what must run before or after; avoid extreme priorities used merely to win a race.
5
- A filter returns the transformed value on every path and avoids unrelated side effects. An action performs effects but should remain idempotent when hooks can fire more than once.
6
- Remove or replace callbacks using the same callable identity and priority that registered them; anonymous callbacks cannot be reliably detached.
7
8
See /wp-content/themes for the rule or workflow that puts this decision into practice.
Themes present content while plugins own portable behavior/wp-content/themesKeep content types, data, integrations, and business behavior in plugins so changing presentation does not remove site capability.
1
A theme can be replaced from the administration interface, so behavior embedded only in the theme disappears with a visual redesign. Portable site capability belongs in a plugin even when the first user interface is theme-specific.
2
3
- Register custom content types, taxonomies, scheduled work, external integrations, and persistent domain data from a plugin.
4
- Keep templates, theme supports, presentation assets, and visual block styles in the theme, consuming stable plugin APIs where behavior is needed.
5
- Enqueue scripts and styles through WordPress dependency APIs with accurate dependencies and versions instead of printing tags directly into templates.
6
- Use child-theme or block-theme extension points for site presentation changes rather than editing a vendor theme that will be overwritten by updates.
7
8
See /wp-content/plugins for the rule or workflow that puts this decision into practice.
Skills
2
audit-wordpress-extension-security/rootReview a WordPress extension for capability, nonce, input, output, query, upload, and remote-request boundaries.
1
---
2
name: audit-wordpress-extension-security
3
description: Audit a WordPress plugin or theme before a public release or security-sensitive change.
4
---
5
6
# Audit Wordpress Extension Security
7
8
Run this procedure when the affected surface changes, before the result is promoted to production. Record evidence for every step instead of accepting a plausible-looking result.
9
10
- [ ] Enumerate every state-changing admin, REST, AJAX, and form entry point; verify capability, ownership, intent, and validation before mutation.
11
- [ ] Trace dynamic values to their HTML, attribute, URL, script, query, file, and redirect sinks; apply the context-specific boundary at the final use.
12
- [ ] Inspect direct database access for table prefixes, prepared parameters, bounded result sets, and a reason the official data API cannot satisfy the operation.
13
- [ ] Exercise upload and remote-request paths with unexpected MIME types, large bodies, redirects, private destinations, timeouts, and failed cleanup.
14
- [ ] Run the extension with debugging enabled and a minimal neighboring-plugin set, then inspect logs for notices that signal hook timing or compatibility errors.
15
16
## Exit criteria
17
18
The change is complete only when the expected behavior, failure behavior, and rollback path have all been exercised with representative data. Preserve the evidence with the change so the next operator can repeat the same checks.
release-wordpress-extension/rootPackage and test activation, upgrade, rollback, deactivation, and uninstall without losing user-controlled data.
1
---
2
name: release-wordpress-extension
3
description: Release a WordPress plugin or theme update across supported core and PHP environments.
4
---
5
6
# Release Wordpress Extension
7
8
Run this procedure when the affected surface changes, before the result is promoted to production. Record evidence for every step instead of accepting a plausible-looking result.
9
10
1. Build the distributable from a clean checkout and confirm development files, secrets, tests, and local dependencies are excluded while runtime assets are present.
11
2. Install fresh and activate with realistic permissions; then upgrade from the oldest supported data version and verify each migration is idempotent when interrupted and rerun.
12
3. Deactivate and reactivate without deleting user data or duplicating schedules, roles, options, tables, or generated content.
13
4. Exercise uninstall separately and require an explicit product decision before removing user-created or business-critical data.
14
5. Test rollback to the prior package against migrated data, record incompatibilities, and publish the required backup or forward-fix instructions before release.
15
16
## Exit criteria
17
18
The change is complete only when the expected behavior, failure behavior, and rollback path have all been exercised with representative data. Preserve the evidence with the change so the next operator can repeat the same checks.
Why this pattern
AI agents often treat a nonce as authorization, escape too early or too late, run expensive work on every hook, write directly to core tables, or break sites during plugin activation and upgrade.
Built for Teams maintaining WordPress plugins, themes, blocks, and site-specific integrations.
Keeps your assistant from:
Using nonce verification as a substitute for a capability check
Rendering unescaped dynamic values into HTML attributes or URLs
Executing migrations or remote calls on every page request
Removing user data during ordinary plugin deactivation