Phase 18: Hardening & Final Build
Security review pass, rate limiting on public endpoints, and final verification that the docs site builds cleanly with all content pages.
Security Hardening
Section titled “Security Hardening”Input validation
Section titled “Input validation”All REST API inputs go through WP_REST_Request::sanitize_params() with explicit sanitize_callback per parameter. Example from ValidateController:
'license_token' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => fn($v) => is_string($v) && strlen($v) > 0,],SQL injection prevention
Section titled “SQL injection prevention”Every repository query uses $wpdb->prepare(). The only exceptions are column name and ORDER BY direction strings, which are validated against an explicit allowlist before interpolation:
$allowed_cols = [ 'created_at', 'status', 'expires_at' ];if ( ! in_array( $order_by, $allowed_cols, true ) ) { $order_by = 'created_at';}Output escaping
Section titled “Output escaping”All template output uses esc_html(), esc_attr(), esc_url(), or wp_kses_post() depending on context. No raw variable echoes anywhere in templates.
Nonce verification
Section titled “Nonce verification”- Admin form submissions:
check_admin_referer()before processing$_POST. - My Account POST actions:
wp_verify_nonce( $_POST['_wpnonce'], 'wplm_sub_action_' . $sub_id ). - AJAX endpoints:
check_ajax_referer().
Capability checks
Section titled “Capability checks”All admin action handlers check current_user_can('manage_options') (or a custom wplm_manage_licenses capability for non-admin staff roles).
Rate Limiting
Section titled “Rate Limiting”Public endpoints (validate, activate, heartbeat) are rate-limited via transient counters keyed on wplm_rl_{ip}_{endpoint}:
$key = 'wplm_rl_' . md5( $ip ) . '_validate';$count = (int) get_transient( $key );if ( $count >= apply_filters( 'wplm_rate_limit_validate', 60 ) ) { return new WP_REST_Response( [ 'code' => 'rate_limited' ], 429 );}set_transient( $key, $count + 1, 60 ); // 60-second windowDefault limits (all filterable):
| Endpoint | Limit | Window |
|---|---|---|
/validate | 60 req | 60 s |
/activate | 10 req | 60 s |
/heartbeat | 120 req | 60 s |
Docs-Site Build
Section titled “Docs-Site Build”The Astro Starlight documentation site is located in docs-site/.
Known issue: Windows path with spaces
Section titled “Known issue: Windows path with spaces”docsLoader() from @astrojs/starlight/loaders fails on Windows paths containing spaces. Resolved by replacing it with Astro’s built-in glob() loader in docs-site/src/content.config.ts:
import { glob } from 'astro/loaders';loader: glob({ base: './src/content/docs', pattern: '**/*.{md,mdx}' })Build command
Section titled “Build command”cd docs-sitenpm installnpm run buildExpected output: 20+ pages built to docs-site/dist/.
Pages built
Section titled “Pages built”/— Introduction/build-log/phase-01through/build-log/phase-18/guides/*— User guides/rest-api/*— API reference/php-api/*— PHP function reference
Uninstall
Section titled “Uninstall”uninstall.php is guarded by WP_UNINSTALL_PLUGIN constant and:
- Checks
wplm_keep_data_on_uninstalloption — if truthy, exits without deleting anything. - Drops all 15
wp_wplm_*tables via$wpdb->query("DROP TABLE IF EXISTS ..."). - Deletes all options with
delete_option()for each registered option name. - Clears any scheduled WP-Cron events:
wp_clear_scheduled_hook('wplm_process_renewals'),wplm_cull_zombies.
Final State
Section titled “Final State”At the end of Phase 18, the plugin is production-ready with:
- ✓ 15-table schema, installed idempotently via dbDelta
- ✓ Ed25519 signing + AES-256-GCM encryption
- ✓ 7-step license validation with offline CRL support
- ✓ Seat accounting with floating leases and zombie cull
- ✓ Native subscription engine with dunning
- ✓ 40+ REST endpoints with scoped API keys
- ✓ Full WooCommerce integration (product panel, checkout delivery, My Account)
- ✓ Admin UI (dashboard, list table, settings)
- ✓ Signed webhooks with per-endpoint secrets
- ✓ Anomaly detection (impossible travel, churn, spikes)
- ✓ Documentation site (Astro Starlight)
- ✓ Rate limiting on public endpoints
- ✓ WordPress Coding Standards compliant