📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
---
|
||||
name: "woo-guard"
|
||||
description: "Review generated or changed WooCommerce extensions, payment and shipping integrations, checkout customizations, and order or product logic."
|
||||
risk: "critical"
|
||||
source: "community"
|
||||
source_repo: "amElnagdy/guard-skills"
|
||||
source_type: "community"
|
||||
date_added: 2026-07-13
|
||||
author: "community"
|
||||
tags: []
|
||||
tools: []
|
||||
---
|
||||
|
||||
|
||||
# Woo Guard
|
||||
|
||||
You are reviewing generated or changed WooCommerce code before it ships. Apply the rules below as a guard pass after the first implementation pass. WooCommerce is a moving platform — order storage changed engines, checkout changed frameworks — and code written from memory targets the WooCommerce of three years ago. With money on the line, "works on my demo store" is not a standard.
|
||||
|
||||
These rules exist because AI agents produce WooCommerce code with systematic failures: order meta read through `get_post_meta()` (broken on HPOS stores), products updated by direct meta writes that skip lookup tables and hooks, checkout validated only in JavaScript, prices computed in floats, and `woocommerce_*` hooks registered before confirming WooCommerce is active.
|
||||
|
||||
## When to Use
|
||||
|
||||
Use this skill when reviewing generated or changed WooCommerce code — extensions, payment and shipping integrations, checkout customizations, and order/product logic — before it ships. Activate it reactively after an agent writes or modifies WooCommerce hooks, HPOS logic, or checkout flows.
|
||||
|
||||
## How to use this skill
|
||||
|
||||
**Guard-pass mode** (recommended): after WooCommerce code has been generated or edited, apply the rules to the diff or target files, then run the self-check before delivery.
|
||||
|
||||
**Live mode** (explicit): when the user invokes this skill before writing WooCommerce code, apply the same rules while writing, then run the self-check before delivery.
|
||||
|
||||
**Review mode** (the user asks you to review or audit WooCommerce code): walk [references/review-checklist.md](references/review-checklist.md) and produce a structured findings report. Do not edit code in review mode unless asked.
|
||||
|
||||
**Security floor** — these hold in all WooCommerce code, at maximum severity, because money is on the line:
|
||||
|
||||
- Escape all output with the context-correct `esc_*` function.
|
||||
- `wp_unslash()` then sanitize all request data before it touches logic.
|
||||
- Capability check plus nonce on every state change.
|
||||
- `$wpdb->prepare()` for every query containing a variable.
|
||||
|
||||
If wp-guard is installed, run it alongside for the full WordPress layer.
|
||||
|
||||
## Adapt to the project first
|
||||
|
||||
1. Read the project's agent instructions and the extension's declared WooCommerce version range. Project conventions win on conflict.
|
||||
2. Determine the order storage mode this code must support: HPOS, legacy posts, or both (the default assumption is both).
|
||||
3. Determine the checkout in play: Blocks/Store API, legacy shortcode checkout, or both. Hooks for one do not fire in the other.
|
||||
4. Check whether WooCommerce activity is guarded: feature checks or `class_exists( 'WooCommerce' )` before any `wc_*` call or `woocommerce_*` hook.
|
||||
|
||||
## The Rules
|
||||
|
||||
### Order and product data — must fix
|
||||
|
||||
1. **Orders are not posts.** Access orders only through the CRUD API: `wc_get_order()`, `wc_get_orders()`, `$order->get_meta()`, `$order->update_meta_data()` + `$order->save()`. Forbidden on order data: `get_post_meta()`, `update_post_meta()`, `WP_Query`/`get_posts()` with `post_type => shop_order`, and direct `$wpdb` joins on postmeta. These work on legacy stores and silently break on HPOS stores. Details: [references/hpos-and-crud.md](references/hpos-and-crud.md).
|
||||
|
||||
2. **CRUD objects, getters/setters, then save.** Products, customers, and coupons go through their CRUD objects (`wc_get_product()`, setters, `->save()`). Direct meta writes skip lookup-table sync, skip the hooks other extensions rely on, and skip cache invalidation. Stock changes go through `wc_update_product_stock()` semantics; order state changes through `$order->update_status()` — which fire the emails and hooks the store expects.
|
||||
|
||||
3. **Declare feature compatibility.** Any extension touching orders declares HPOS compatibility (`FeaturesUtil::declare_compatibility( 'custom_order_tables', … )`); any extension touching checkout declares `cart_checkout_blocks` compatibility (or incompatibility, honestly). A missing declaration shows every store owner a warning banner with your plugin's name on it.
|
||||
|
||||
### Checkout and money — must fix
|
||||
|
||||
4. **Checkout validation is server-side.** Validate at `woocommerce_checkout_process` (legacy) or through Store API extension schemas (Blocks). JavaScript validation is UX, never security. Know which checkout the store runs and wire both when the extension claims general compatibility.
|
||||
|
||||
5. **Money is not a float.** Prices and totals go through `wc_format_decimal()` for storage-safe values, `wc_price()` for display, and WooCommerce's own tax/rounding settings for arithmetic. No hand-rolled currency symbols, no `number_format()` on prices, no float equality on totals.
|
||||
|
||||
### Runtime discipline — should fix
|
||||
|
||||
6. **Guard the runtime context.** `WC()->cart` and `WC()->session` are null in REST, cron, CLI, and admin contexts — check before touching them. Never assume a logged-in customer in webhook or gateway callbacks. Verify every `woocommerce_*` hook and `wc_*` function exists in the supported version range — WooCommerce renames and retires hooks across majors.
|
||||
|
||||
7. **Hooks over template overrides.** Prefer, in order: existing WooCommerce hooks/filters → the `woocommerce_locate_template` filter → a theme-level override. A template override shipped inside a plugin freezes a copied file at one WooCommerce version and breaks on template updates — flag it in review, always.
|
||||
|
||||
8. **Background work scales with order volume.** Batch jobs, syncs, and webhook fan-out go through Action Scheduler (bundled with WooCommerce), not raw WP-Cron loops. Handlers are idempotent — order events fire more than once in real stores.
|
||||
|
||||
## Self-check before delivery
|
||||
|
||||
1. Grep your diff for `get_post_meta`, `update_post_meta`, `post_type => 'shop_order'`: any of them touching orders? (Rule 1)
|
||||
2. Any product/order/customer write that bypasses a CRUD object's `save()`? (Rule 2)
|
||||
3. Does the extension declare HPOS (and checkout-blocks, if relevant) compatibility? (Rule 3)
|
||||
4. Is every checkout rule enforced server-side, for the checkout(s) the store actually runs? (Rule 4)
|
||||
5. Any float arithmetic, hardcoded currency symbol, or `number_format()` on money? (Rule 5)
|
||||
6. Any `WC()->cart`/`WC()->session` access that can run in REST/cron/CLI? Any unverified hook name? (Rule 6)
|
||||
7. Any template file shipped in the plugin? (Rule 7)
|
||||
8. Security floor: every output escaped, every request input unslashed then sanitized, every state change capability-checked and nonce-verified, every variable query prepared?
|
||||
|
||||
If any answer is wrong, fix it before showing the user.
|
||||
|
||||
## Reporting format (review mode)
|
||||
|
||||
```
|
||||
**Rule N violation** in `path/file.php:<line or function>`
|
||||
- What: <one sentence>
|
||||
- Risk: <HPOS breakage / skipped hooks / money error / checkout bypass — one phrase>
|
||||
- Fix: <one sentence>
|
||||
```
|
||||
|
||||
Group by file, lead with Rules 1–5 findings. If a file is clean, don't mention it.
|
||||
|
||||
## Severity guide
|
||||
|
||||
- **Must fix:** Rules 1–5 — broken stores, skipped business logic, wrong money
|
||||
- **Should fix:** Rules 6–8 — context crashes, update fragility, jobs that die at scale
|
||||
|
||||
## References
|
||||
|
||||
- [references/hpos-and-crud.md](references/hpos-and-crud.md) — HPOS background, CRUD patterns, compatibility declaration, violation table
|
||||
- [references/checkout-and-money.md](references/checkout-and-money.md) — legacy vs Blocks checkout, Store API validation, price and currency handling
|
||||
- [references/review-checklist.md](references/review-checklist.md) — structured walk-through for review mode
|
||||
- [references/sources.md](references/sources.md) — WooCommerce developer documentation URLs; read only when citing
|
||||
|
||||
## What this skill does not do
|
||||
|
||||
- Cover the full WordPress layer beyond the security floor — i18n and asset/query discipline are wp-guard's jurisdiction when it is installed.
|
||||
- Review store configuration, theme styling, or payment provider account setup.
|
||||
- Decide pricing or business logic — it guards how WooCommerce code ships, not what the store sells.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Woo Guard — Checkout and Money Reference
|
||||
|
||||
## Contents
|
||||
|
||||
- Two checkouts, two hook surfaces
|
||||
- Server-side validation
|
||||
- Cart and session context guards
|
||||
- Money handling
|
||||
- Gateway and webhook callbacks
|
||||
|
||||
## Two checkouts, two hook surfaces
|
||||
|
||||
Modern stores run the Blocks checkout (Store API); legacy stores run the shortcode checkout (`woocommerce_checkout_*` hooks, `wp_ajax` fragments). The hook surfaces do not overlap:
|
||||
|
||||
- Legacy-only: `woocommerce_checkout_fields`, `woocommerce_checkout_process`, `woocommerce_after_order_notes`.
|
||||
- Blocks: Store API extensions (`ExtendSchema`), additional-fields API, server-side endpoint validation.
|
||||
|
||||
An extension claiming general compatibility wires both or declares which one it supports (see compatibility declaration in [hpos-and-crud.md](hpos-and-crud.md)). AI-generated checkout code overwhelmingly targets the legacy surface only — on a Blocks store it simply never runs: a hook on the wrong surface fails silently, no error, no behavior.
|
||||
|
||||
## Server-side validation
|
||||
|
||||
```php
|
||||
/**
|
||||
* Reject checkout when the VAT number is malformed.
|
||||
*
|
||||
* JS validation on the field is UX; this hook is the actual gate.
|
||||
*/
|
||||
add_action( 'woocommerce_checkout_process', function () {
|
||||
$vat = isset( $_POST['ncs_vat'] ) ? sanitize_text_field( wp_unslash( $_POST['ncs_vat'] ) ) : '';
|
||||
|
||||
if ( '' !== $vat && ! ncs_vat_is_valid( $vat ) ) {
|
||||
wc_add_notice( __( 'Please enter a valid VAT number.', 'ncs-checkout' ), 'error' );
|
||||
}
|
||||
} );
|
||||
```
|
||||
|
||||
For Blocks, the equivalent lives in the Store API additional-fields/extension schema callbacks. Either way: the server decides, every field is unslashed then sanitized with the type-correct function, and error messages go through `wc_add_notice()`/schema errors — never `die()`.
|
||||
|
||||
## Cart and session context guards
|
||||
|
||||
`WC()->cart`, `WC()->session`, and `WC()->customer` are initialized for front-end requests — they are null in REST, cron, CLI, webhooks, and most admin requests:
|
||||
|
||||
```php
|
||||
if ( function_exists( 'WC' ) && WC()->cart instanceof WC_Cart ) {
|
||||
$count = WC()->cart->get_cart_contents_count();
|
||||
}
|
||||
```
|
||||
|
||||
Code that touches the cart inside an API callback or scheduled job is a fatal error wearing a demo-store disguise. Flag it in review even when "it worked locally."
|
||||
|
||||
## Money handling
|
||||
|
||||
- Storage and arithmetic inputs: `wc_format_decimal( $value )` — normalizes locale decimals and precision.
|
||||
- Display: `wc_price( $amount )` — currency symbol, position, separators, all from store settings. Hardcoded `'$' . $amount` fails on the other 150 currencies.
|
||||
- Totals and tax: use order/cart getters (`get_total()`, `get_subtotal()`, `WC_Tax` methods) — they apply the store's rounding mode. Re-deriving totals with raw float math produces penny drift that accountants will find.
|
||||
- Comparisons: compare formatted decimals or integer minor units; never `==` on floats.
|
||||
- Refund/discount logic: negative amounts have meaning — test the zero and partial cases explicitly (test-guard says hi).
|
||||
|
||||
## Gateway and webhook callbacks
|
||||
|
||||
- Verify webhook signatures/secrets before touching any order; fail closed with the provider's expected status code.
|
||||
- Look up orders from gateway references via `wc_get_orders( array( 'transaction_id' => … ) )` or stored CRUD meta — not custom SQL.
|
||||
- Callbacks run unauthenticated by design: capability checks don't apply, signature verification is the authentication, and every input is still unslashed and sanitized before use.
|
||||
- Idempotency: providers retry. Processing the same payment event twice must not complete an order twice.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Woo Guard — HPOS and CRUD Reference
|
||||
|
||||
## Contents
|
||||
|
||||
- Why HPOS breaks legacy code
|
||||
- Order access patterns
|
||||
- Order meta done right
|
||||
- Products, stock, and lookup tables
|
||||
- Status transitions
|
||||
- Declaring compatibility
|
||||
- Violation table for review
|
||||
|
||||
## Why HPOS breaks legacy code
|
||||
|
||||
High-Performance Order Storage moved orders out of `wp_posts`/`wp_postmeta` into dedicated tables, and it is the default on new stores. Code that treats orders as posts returns empty results or stale data on HPOS stores — silently, with no error. Code written "from memory" almost always targets the legacy storage, because that is what most training-era tutorials show.
|
||||
|
||||
## Order access patterns
|
||||
|
||||
```php
|
||||
// Wrong — assumes orders are posts. Fails on HPOS.
|
||||
$total = get_post_meta( $order_id, '_order_total', true );
|
||||
$orders = get_posts( array( 'post_type' => 'shop_order', 'numberposts' => 20 ) );
|
||||
|
||||
// Right — storage-agnostic CRUD API.
|
||||
$order = wc_get_order( $order_id );
|
||||
$total = $order ? $order->get_total() : 0;
|
||||
$orders = wc_get_orders( array( 'status' => 'wc-processing', 'limit' => 20 ) );
|
||||
```
|
||||
|
||||
`wc_get_orders()` takes its own argument schema (not `WP_Query` args): `limit`, `status`, `customer_id`, `date_created`, `meta_query` equivalents via `field_query`. Never run an unbounded order query — always set a `limit` (wp-guard covers query discipline in depth when installed).
|
||||
|
||||
## Order meta done right
|
||||
|
||||
```php
|
||||
$order = wc_get_order( $order_id );
|
||||
$order->update_meta_data( '_ncs_sync_status', 'queued' );
|
||||
$order->save(); // persists to whichever storage backend is active
|
||||
|
||||
$value = $order->get_meta( '_ncs_sync_status' ); // single value, unserialized
|
||||
```
|
||||
|
||||
`save()` is not optional — without it, meta changes exist only in memory. Batched changes: set everything, save once.
|
||||
|
||||
## Products, stock, and lookup tables
|
||||
|
||||
Product meta writes through `update_post_meta()` skip `wc_product_meta_lookup` sync, skip `woocommerce_update_product`-family hooks, and skip cache invalidation — three classes of bugs other plugins will blame on you.
|
||||
|
||||
```php
|
||||
$product = wc_get_product( $product_id );
|
||||
$product->set_stock_quantity( $new_qty );
|
||||
$product->set_regular_price( wc_format_decimal( $price ) );
|
||||
$product->save();
|
||||
```
|
||||
|
||||
Stock specifically: `wc_update_product_stock()` and the CRUD setters handle backorders, stock status flips, and concurrency better than any hand-rolled meta math. Direct increments via meta are race-prone on busy stores.
|
||||
|
||||
## Status transitions
|
||||
|
||||
```php
|
||||
$order->update_status( 'completed', 'Synced to fulfillment.' ); // fires emails + hooks
|
||||
```
|
||||
|
||||
Never set status by writing meta or post fields — `update_status()` triggers the `woocommerce_order_status_*` hooks and customer emails the rest of the store depends on. If suppressing side effects is the goal, that is a design discussion to surface, not a meta write to sneak in.
|
||||
|
||||
## Declaring compatibility
|
||||
|
||||
```php
|
||||
add_action( 'before_woocommerce_init', function () {
|
||||
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
|
||||
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
|
||||
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', __FILE__, true );
|
||||
}
|
||||
} );
|
||||
```
|
||||
|
||||
Declare only what is true — a false `custom_order_tables` declaration on postmeta-reading code converts a visible warning into an invisible data bug.
|
||||
|
||||
## Violation table for review
|
||||
|
||||
| Grep hit | Why it fails |
|
||||
|---|---|
|
||||
| `get_post_meta( $order_id` | HPOS: order meta is not postmeta |
|
||||
| `post_type => 'shop_order'` (or `shop_order_refund`) | HPOS: orders are not posts |
|
||||
| `update_post_meta( $product_id` | Skips lookup tables, hooks, caches |
|
||||
| `wp_update_post` on order/product IDs | Same class of bypass |
|
||||
| `$wpdb` joins on `postmeta` for order data | HPOS + fragile schema coupling |
|
||||
| `posts_per_page => -1` over products/orders | Unbounded query, store-sized blast radius |
|
||||
@@ -0,0 +1,44 @@
|
||||
# Woo Guard — Review Checklist
|
||||
|
||||
Structured walk for review mode. First sweep the security floor on the same files — output escaping, unslash-then-sanitize on request data, capability plus nonce on state changes, prepared queries — money code gets zero security slack (wp-guard covers the full WordPress layer when installed). Cite file:line.
|
||||
|
||||
## Contents
|
||||
|
||||
- Pass 1: HPOS and CRUD greps
|
||||
- Pass 2: Checkout and money
|
||||
- Pass 3: Runtime context
|
||||
- Pass 4: Compatibility and packaging
|
||||
- Reporting
|
||||
|
||||
## Pass 1: HPOS and CRUD greps (must fix)
|
||||
|
||||
Run the violation table in [hpos-and-crud.md](hpos-and-crud.md):
|
||||
|
||||
- `get_post_meta` / `update_post_meta` / `wp_update_post` touching order or product IDs
|
||||
- `post_type => 'shop_order'` in any query; `$wpdb` joins on postmeta for order data
|
||||
- Meta changes without a following `save()`
|
||||
- Stock or order status set by meta/post-field writes instead of `wc_update_product_stock()` / `$order->update_status()`
|
||||
- Unbounded `wc_get_orders()` / product queries (no `limit`)
|
||||
|
||||
## Pass 2: Checkout and money (must fix)
|
||||
|
||||
- Checkout rules enforced server-side (`woocommerce_checkout_process` or Store API schema)? JS-only validation is a finding.
|
||||
- Which checkout does the code target — and does that match what it claims to support?
|
||||
- Money: `wc_format_decimal()` on inputs, `wc_price()` on display, store rounding on totals; flag float arithmetic, `number_format()`, hardcoded symbols, float `==`.
|
||||
- Webhook/gateway callbacks: signature verified before order access? Idempotent on retries?
|
||||
|
||||
## Pass 3: Runtime context (should fix)
|
||||
|
||||
- `WC()->cart` / `WC()->session` / `WC()->customer` reachable from REST, cron, CLI, or webhooks without guards?
|
||||
- `woocommerce_*` hooks and `wc_*` functions verified to exist in the supported version range?
|
||||
- WooCommerce-active checks before hooking (`class_exists` / feature checks)?
|
||||
|
||||
## Pass 4: Compatibility and packaging (should fix)
|
||||
|
||||
- `FeaturesUtil` declarations present and truthful (`custom_order_tables`, `cart_checkout_blocks`)?
|
||||
- Template overrides shipped inside the plugin? (Always a finding — hooks or `woocommerce_locate_template`.)
|
||||
- Background/batch work on Action Scheduler, idempotent handlers?
|
||||
|
||||
## Reporting
|
||||
|
||||
Use the SKILL.md format (What / Risk / Fix). Lead with Pass 1–2 findings and an overall verdict (merge / fix first / do not merge). Note explicitly when security-floor findings exist on the same files so the user sees the full bill at once.
|
||||
@@ -0,0 +1,17 @@
|
||||
# Woo Guard — Sources
|
||||
|
||||
Central bibliography. Operational guidance lives in the other references; read this file only when a source URL is needed.
|
||||
|
||||
## Contents
|
||||
|
||||
- WooCommerce developer documentation
|
||||
|
||||
## WooCommerce developer documentation
|
||||
|
||||
- HPOS (High-Performance Order Storage): https://developer.woocommerce.com/docs/features/high-performance-order-storage/
|
||||
- HPOS recipe book (compatibility patterns): https://developer.woocommerce.com/docs/features/high-performance-order-storage/recipe-book/
|
||||
- CRUD objects: https://developer.woocommerce.com/docs/category/data-management/crud-objects/
|
||||
- Store API: https://developer.woocommerce.com/docs/apis/store-api/
|
||||
- Cart and Checkout Blocks — extensibility: https://developer.woocommerce.com/docs/category/cart-and-checkout-blocks/
|
||||
- Action Scheduler: https://actionscheduler.org/
|
||||
- Payment gateway API: https://developer.woocommerce.com/docs/woocommerce-payment-gateway-api/
|
||||
Reference in New Issue
Block a user