v2.1.6
This commit is contained in:
parent
6269b77f13
commit
c2ead8e434
13 changed files with 800 additions and 251 deletions
151
docs/audit-pre-deploy-2.1.6.md
Normal file
151
docs/audit-pre-deploy-2.1.6.md
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
# Pre-Deploy AI Audit — v2.1.6
|
||||
|
||||
**Plugin slug:** robotstxt-smtp-amazonses
|
||||
**Version:** 2.1.6
|
||||
**Minimum PHP:** 8.2
|
||||
**Minimum WP:** 5.7
|
||||
**Architecture:** Single-class OOP, namespace `Robotstxt_SMTP_AmazonSES`
|
||||
**Exposes:** AJAX-style admin-post handlers, Settings API filter, WP-Cron hook
|
||||
**Target:** Private / commercial
|
||||
|
||||
**Audit scope:** Full diff from 5ee811d (initial tooling commit) to HEAD — covers all changes introduced in this session.
|
||||
|
||||
---
|
||||
|
||||
## 1. SECURITY
|
||||
|
||||
### 1.1 handle_clear_credential() — new in 2.1.6
|
||||
|
||||
- **File:** `includes/class-plugin.php:1793`
|
||||
- **Nonce:** `wp_verify_nonce()` with GET input via `wp_unslash()`. ✓
|
||||
- **Capability:** `current_user_can('manage_options')` / `manage_network_options` based on scope. ✓
|
||||
- **Scope validation:** `sanitize_key(wp_unslash($scope_raw))` before comparison. ✓
|
||||
- **Redirect:** `wp_safe_redirect(add_query_arg([...], admin_url('admin.php')))` — both `$page_slug` and `$cleared_key` are hardcoded literals, not from user input. ✓
|
||||
- **Option update:** uses `update_option()` / `update_site_option()` (WordPress API). ✓
|
||||
- **ABSPATH guard:** present in main file. ✓
|
||||
|
||||
### 1.2 display_clear_key_notice() — new in 2.1.6
|
||||
|
||||
- **File:** `includes/class-plugin.php:1861`
|
||||
- `FILTER_UNSAFE_RAW` used to read `robotstxt_smtp_amazonses_cleared`. Value is checked with strict equality (`'access_key'` / `'secret_key'`) before any output. Output escaped with `esc_html()`. No XSS path. ✓
|
||||
- Notice only rendered when `get_current_screen()->id` contains `'robotstxt-smtp'`. ✓
|
||||
|
||||
### 1.3 Credential validation change (2.1.5)
|
||||
|
||||
- **File:** `includes/class-plugin.php:1473–1537`
|
||||
- Blocking only on `InvalidClientTokenId`, `SignatureDoesNotMatch`, `InvalidAccessKeyId` — correct. AWS authentication errors that definitively prove wrong credentials.
|
||||
- All other `AwsException` errors (wrong region, missing IAM permission) and `Throwable` errors (network) now allow saving. `unset($exception)` used to satisfy PHPCS empty-catch requirement. ✓
|
||||
- No sensitive data exposed in error messages — access key masked in debug context. ✓
|
||||
|
||||
### 1.4 Reply-to clearing (2.1.5)
|
||||
|
||||
- **File:** `includes/class-plugin.php:1269`
|
||||
- `sanitize_email(trim($options['reply_to_email']))` before setting to empty. ✓
|
||||
- Only acts when key `reply_to_email` exists in submitted `$options`. ✓
|
||||
|
||||
### 1.5 Dependency check / missing parent notice (2.1.5)
|
||||
|
||||
- **File:** `robotstxt-smtp-amazonses.php:82`
|
||||
- Pure output using `esc_html__()`. No user input read. ✓
|
||||
- Registered on `admin_notices` and `network_admin_notices` only when parent class is absent. ✓
|
||||
|
||||
### 1.6 Previously existing code — no regressions found
|
||||
|
||||
- All form field renders use `esc_attr()` / `esc_html_e()`. ✓
|
||||
- `wp_nonce_field()` / `check_admin_referer()` not needed here (Settings API handles nonces for save; admin-post handlers use `wp_verify_nonce()`). ✓
|
||||
- No `eval()`, `base64_decode()`, `system()`, `exec()`, `unserialize()`, `extract()`. ✓
|
||||
- Direct file access prevention on all PHP files. ✓
|
||||
|
||||
---
|
||||
|
||||
## 2. ROBUSTNESS & FATAL ERRORS
|
||||
|
||||
- `handle_clear_credential()` sets `$this->clearing_field` before `add_filter()` and unconditionally clears it after `update_option()`. If `update_option()` fails (returns false, does not throw), the priority-11 filter is still removed. No filter leak risk. ✓
|
||||
- `force_clear_credential()` only acts when `$this->clearing_field !== null`. Safe to call even if left registered by accident. ✓
|
||||
- All new code requires PHP 8.2 (`?string` property type). Declared minimum matches. ✓
|
||||
- `plugin_loaded` → dependency check → class file included → `run()`: no class can be called before it's required. ✓
|
||||
- `load_plugin_textdomain()` registered on `init` from main file (not from class), so it fires even when parent plugin is absent and the notice needs translating. ✓
|
||||
|
||||
---
|
||||
|
||||
## 3. COMPATIBILITY & CONFLICTS
|
||||
|
||||
- Multisite: both `get_site_option()` / `update_site_option()` and `get_option()` / `update_option()` paths implemented. `is_network_admin()` used for scope detection in field render, mirroring the parent plugin's password-clear pattern. ✓
|
||||
- No assumptions about sidebars, widgets, or block themes. ✓
|
||||
- `Requires Plugins: robotstxt-smtp` header enforced by WP 6.5+; PHP-level fallback added for WP 5.7–6.4. ✓
|
||||
|
||||
---
|
||||
|
||||
## 4. WORDPRESS CODING STANDARDS
|
||||
|
||||
- All new functions prefixed with `robotstxt_smtp_amazonses_`. ✓
|
||||
- All new methods documented with `@since 2.1.6`. ✓
|
||||
- No logic in constructor. ✓
|
||||
- PHPCS passes with zero errors. ✓
|
||||
|
||||
---
|
||||
|
||||
## 5. INTERNATIONALIZATION
|
||||
|
||||
- `load_plugin_textdomain()` on `init`. ✓
|
||||
- `.pot` regenerated with `wp i18n make-pot` (WP-CLI 2.12.0). ✓
|
||||
- `es_ES` and `ca` translations complete: all new strings translated, fuzzy entries resolved. ✓
|
||||
- Textdomain literal string `'robotstxt-smtp-amazonses'` — no variables. ✓
|
||||
|
||||
---
|
||||
|
||||
## 6. PERFORMANCE
|
||||
|
||||
- No N+1 queries introduced. ✓
|
||||
- `delete_site_transient(get_quota_cache_key())` called after credential clear — avoids stale quota display. ✓
|
||||
- `$client_cache` keyed by `md5(access_key|secret_key|region)` — SES clients re-used within a request. ✓
|
||||
- All new admin-post handlers exit early on scope/nonce/capability failure before touching the DB. ✓
|
||||
|
||||
---
|
||||
|
||||
## 7. DATABASE & MIGRATIONS
|
||||
|
||||
- No custom tables. All data in parent plugin's option. ✓
|
||||
- `uninstall.php` delegates to parent plugin. ✓
|
||||
|
||||
---
|
||||
|
||||
## 8. ASSETS & FRONTEND
|
||||
|
||||
- No JS or CSS added. ✓
|
||||
- Clear buttons render as `<a class="button button-secondary">` — standard WP admin styling, no inline script. ✓
|
||||
- `esc_url(wp_nonce_url(...))` used for button href. ✓
|
||||
|
||||
---
|
||||
|
||||
## 9. DEVOPS / RELEASE ARTIFACTS
|
||||
|
||||
- Version consistent across plugin header, `ROBOTSTXT_SMTP_AMAZONSES_VERSION`, `readme.txt`, `update.json`. ✓
|
||||
- `Stable tag` matches `Version`. ✓
|
||||
- `changelog.txt` and `readme.txt` follow template (date, categories, Compatibility, Tests). ✓
|
||||
- `composer audit` — no CVEs. ✓
|
||||
- `bin/deploy.sh` excludes dev files, phpstan config, composer.json/lock, `.claude/`, `.git`. ✓
|
||||
|
||||
---
|
||||
|
||||
## 10. PUBLIC API & BACKWARD COMPATIBILITY
|
||||
|
||||
- New public methods: `handle_clear_access_key()`, `handle_clear_secret_key()`, `force_clear_credential()`, `display_clear_key_notice()`, `get_cached_ses_quota()`. All additive, no removals. ✓
|
||||
- `get_quota_cache_key()` remains public static — parent plugin accesses it. ✓
|
||||
- No changes to filter/action signatures. ✓
|
||||
- Removed: `apply_ses_quota_to_rate_limits()` — was unused private method. ✓
|
||||
|
||||
---
|
||||
|
||||
## WARNINGS
|
||||
|
||||
- **[WARNING] RESOLVED** `includes/class-plugin.php:1873` — `FILTER_UNSAFE_RAW` changed to `FILTER_SANITIZE_SPECIAL_CHARS` + `sanitize_key()` for the `robotstxt_smtp_amazonses_cleared` query parameter.
|
||||
|
||||
---
|
||||
|
||||
## FINAL EXECUTIVE SUMMARY
|
||||
|
||||
1. **Overall status:** ✅ PASS
|
||||
2. **Top mandatory fixes before tagging:** None. All [CRITICAL] issues: 0.
|
||||
3. **Estimated security risk:** Low.
|
||||
4. **Version recommendation:** Safe to mark as stable.
|
||||
108
docs/deploy-checklist-2.1.6.md
Normal file
108
docs/deploy-checklist-2.1.6.md
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# PRE-DEPLOY CHECKLIST — Version 2.1.6
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
PRE-DEPLOY CHECKLIST — Version 2.1.6
|
||||
═══════════════════════════════════════════════════════════════
|
||||
|
||||
INPUT VALIDATION
|
||||
[x] Empty field validation performed
|
||||
[x] Length limits validated server-side
|
||||
[x] Format patterns validated (regex, ctype_alnum, preg_match)
|
||||
[x] Safelist validation used for finite option sets — scope param uses sanitize_key() + strict equality
|
||||
[x] Strict type checking used (===, in_array with true parameter)
|
||||
[x] Validation performed BEFORE any action or processing
|
||||
[x] Helper functions used: is_email(), sanitize_key()
|
||||
|
||||
INPUT SANITIZATION
|
||||
[x] sanitize_text_field() for single-line text — credentials
|
||||
[x] sanitize_textarea_field() for multi-line text — n/a
|
||||
[x] sanitize_email() for email addresses — reply_to_email
|
||||
[x] sanitize_key() for keys/identifiers — scope, cleared_key params
|
||||
[x] sanitize_url() for URLs — n/a (no URL inputs)
|
||||
[x] sanitize_file_name() for filenames — n/a
|
||||
[x] wp_kses() / wp_kses_post() for HTML content — n/a
|
||||
|
||||
OUTPUT ESCAPING (Escape Late)
|
||||
[x] esc_html() for HTML element content
|
||||
[x] esc_attr() for HTML attributes — field names, placeholder text
|
||||
[x] esc_url() for all URLs (src, href) — wp_nonce_url() output + esc_url()
|
||||
[x] esc_js() for inline JavaScript — n/a
|
||||
[x] Combined i18n+escape functions (esc_html__(), esc_attr_e())
|
||||
[x] Escaping performed at output time, not before storage
|
||||
|
||||
CSRF PROTECTION (Nonces)
|
||||
[x] wp_nonce_url() used for clear credential action links
|
||||
[x] wp_verify_nonce() verifies the clear credential handlers (GET via admin-post.php)
|
||||
[x] Nonce action strings are specific ('robotstxt_smtp_amazonses_clear_access_key', 'robotstxt_smtp_amazonses_clear_secret_key')
|
||||
[x] Nonces NOT relied upon for authentication/authorization — capability checks are separate
|
||||
|
||||
DATABASE SECURITY
|
||||
[x] WordPress API functions used — update_option(), update_site_option(), get_option(), get_site_option()
|
||||
[x] No custom SQL queries
|
||||
[x] No direct $_POST/$_GET interpolation in DB operations
|
||||
|
||||
CAPABILITY CHECKS
|
||||
[x] current_user_can() on every admin action — handle_clear_credential() checks manage_options / manage_network_options
|
||||
[x] current_user_can() on every AJAX/admin-post handler
|
||||
[x] Capability checks in execution logic (reject if no permission — wp_die())
|
||||
[x] Appropriate capability selected
|
||||
|
||||
FILE OPERATIONS
|
||||
[x] Direct file access prevention on all PHP files (ABSPATH guard)
|
||||
|
||||
DANGEROUS FUNCTIONS
|
||||
[x] No eval() usage
|
||||
[x] No suspicious base64_decode()
|
||||
[x] No system(), exec(), shell_exec(), passthru()
|
||||
[x] No create_function() (use anonymous functions)
|
||||
[x] No extract() on untrusted data
|
||||
[x] No unserialize() on untrusted data
|
||||
|
||||
CODE QUALITY & STANDARDS
|
||||
[x] PHPCS passes with zero errors (WordPress-Core, WordPress-Docs, WordPress-Extra)
|
||||
[x] PHPStan level 9 passes with zero errors on all modified files
|
||||
[x] PHPCompatibility scan run — 8.2-8.5 PASSED
|
||||
[x] Minimum PHP version in headers reflects real lowest compatible version (8.2 — AWS SDK)
|
||||
[x] All user-facing strings use translation functions (textdomain = 'robotstxt-smtp-amazonses')
|
||||
[x] phpDoc added for all new public methods/hooks with @since 2.1.6
|
||||
|
||||
DEBUG & TESTING
|
||||
[x] Browser console — no new JS introduced
|
||||
[x] composer audit — no CVEs found
|
||||
[x] No PHP notices, warnings, or deprecated messages expected
|
||||
|
||||
VERSIONING & DOCUMENTATION
|
||||
[x] Plugin version bumped in main plugin file header — 2.1.6
|
||||
[x] CHANGELOG.md / changelog.txt updated
|
||||
[x] readme.txt updated — last 3 versions shown (2.1.4, 2.1.5, 2.1.6)
|
||||
[x] Stable tag in readme.txt matches Version in plugin header — both 2.1.6
|
||||
[x] Required headers present, forbidden headers absent
|
||||
[x] update.json version and download_url updated
|
||||
|
||||
DATABASE & UNINSTALL
|
||||
[x] No DB schema changes in this version
|
||||
[x] uninstall.php delegates to parent plugin
|
||||
|
||||
AI AUDIT
|
||||
[x] Pre-deploy AI audit executed — docs/audit-pre-deploy-2.1.6.md
|
||||
[x] All [CRITICAL] findings resolved — none found
|
||||
[x] [WARNING] finding documented and resolved (FILTER_UNSAFE_RAW → FILTER_SANITIZE_SPECIAL_CHARS + sanitize_key)
|
||||
[x] Executive summary: PASS
|
||||
[x] Security risk assessed as: Low
|
||||
|
||||
BUILD & ARTIFACT
|
||||
[x] deploy.sh executed manually
|
||||
[x] ZIP generated in parent directory (wp-content/plugins/)
|
||||
[x] ZIP excludes: vendor/ dev deps, tests, CI config, .git, phpstan.neon, composer.json, CLAUDE.md, AGENTS.md
|
||||
[x] Production dependencies bundled (AWS SDK + runtime deps only)
|
||||
[x] License compatibility verified — GPLv3, AWS SDK Apache-2.0 (GPL-compatible)
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
DEPLOY AUTHORIZATION
|
||||
═══════════════════════════════════════════════════════════════
|
||||
All [CRITICAL] items resolved: YES
|
||||
Executive summary status: PASS
|
||||
Security risk level: Low
|
||||
|
||||
Manual approval confirmed: [x] YES
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Loading…
Reference in a new issue