mirror of
https://github.com/forkless/Piperless.git
synced 2026-08-16 00:46:38 +02:00
80 lines
5.3 KiB
Markdown
80 lines
5.3 KiB
Markdown
# Security Audit — Piperless v1.1.3
|
||
|
||
**Audit date:** 2026-05-10
|
||
**Scope:** All 8 PHP classes (`includes/*.php`), plus `uninstall.php`
|
||
**Findings:** 1 (fixed). Open: 0
|
||
**Auditor:** DeepSeek V4 Pro (systematic automated review)
|
||
|
||
|
||
## Summary
|
||
|
||
29 categories reviewed. One logic-level bug found and fixed mid-session (settings data loss on partial form save). Zero outstanding security vulnerabilities — critical, high, medium, or low.
|
||
|
||
|
||
## Results
|
||
|
||
| # | Category | Finding | Notes |
|
||
|---|----------|---------|-------|
|
||
| 1 | Command injection | ✅ Pass | 30+ `escapeshellarg()` calls. Zero `escapeshellcmd()`. |
|
||
| 2 | SQL injection | ✅ Pass | Single `$wpdb->get_results()` uses `$wpdb->prepare()` with `%s`. |
|
||
| 3 | XSS (frontend player) | ✅ Pass | `esc_attr()`, `esc_html()`, `esc_url()` on all outputs. |
|
||
| 4 | XSS (admin panel) | ✅ Pass | Voice dropdown pre-rendered with `esc_attr`/`esc_html`. Field values escaped. |
|
||
| 5 | XSS (Gutenberg sidebar) | ✅ Pass | REST responses sanitized via `sanitize_text_field()`. |
|
||
| 6 | AJAX authorization | ✅ Pass | All 10 handlers check `manage_options` capability. |
|
||
| 7 | REST authorization | ✅ Pass | `current_user_can('edit_post', $post_id)` on generate/status/remove. |
|
||
| 8 | Post meta auth | ✅ Pass | 11 `register_post_meta()` calls have `current_user_can` auth callback. |
|
||
| 9 | Nonce verification | ✅ Pass | All 11 AJAX endpoints call `check_ajax_referer()`. |
|
||
| 10 | Rate limiting | ✅ Pass | Transient-based per-IP on audio proxy. Configurable 1–600 req/min. |
|
||
| 11 | Cache key validation | ✅ Pass | Regex `^[a-zA-Z0-9_-]+$` on proxy endpoint + AJAX delete handler. |
|
||
| 12 | Path traversal | ✅ Pass | Cache keys regex-validated. Model paths stripped to basename in REST. |
|
||
| 13 | Concurrency mutex | ✅ Pass | Transient-based, 5-min TTL, released on success and failure paths. |
|
||
| 14 | Process timeout | ✅ Pass | `set_time_limit()` guard on Piper calls; restored in `finally` block. |
|
||
| 15 | Log file security | ✅ Pass | `chmod 0600` after every write. |
|
||
| 16 | Settings sanitization (partial saves) | 🔧 Fixed | Logs tab save wiped all settings. Fixed with `array_merge($existing, $input)`. |
|
||
| 17 | CSS sanitization | ✅ Pass | `sanitize_css()` strips `url()`, `expression()`, `@import`, `behavior:`, `-moz-binding`. |
|
||
| 18 | Input validation (Piper) | ✅ Pass | Model paths checked with `file_exists()`. Quality parameter enum-validated. |
|
||
| 19 | Input validation (voice aliases) | ✅ Pass | `sanitize_text_field()` on both keys and values. |
|
||
| 20 | Directory creation | ✅ Pass | `wp_mkdir_p()` used; validates under `WP_CONTENT_DIR`. |
|
||
| 21 | File write safety | ✅ Pass | `error_clear_last()` before every `@file_put_contents()`. |
|
||
| 22 | open_basedir awareness | ✅ Pass | String-prefix matching before any `is_dir()`/`file_exists()` call. |
|
||
| 23 | Cron job safety | ✅ Pass | `wp_schedule_single_event()` with deduplication check. |
|
||
| 24 | Uninstall cleanliness | ✅ Pass | Options and post meta cleaned; cron hook deregistered. |
|
||
| 25 | Error handling | ✅ Pass | All `exec()` calls check return codes. Temp files cleaned with `@unlink`. |
|
||
| 26 | Activation safety | ✅ Pass | PHP/WordPress version check only. No destructive operations. |
|
||
| 27 | Sensitive data exposure | ✅ Pass | No API keys, passwords, or credentials stored or logged. |
|
||
| 28 | File permissions | ✅ Pass | Cache directory created with proper permissions. Log `chmod 0600`. |
|
||
| 29 | HTTP security headers | ✅ Pass | `Content-Range`, `Accept-Ranges: bytes`, `Cache-Control: public, max-age=86400`. |
|
||
|
||
|
||
## Legend
|
||
|
||
| Symbol | Meaning |
|
||
|--------|---------|
|
||
| ✅ Pass | No vulnerability found |
|
||
| 🔧 Fixed | Bug found and resolved |
|
||
|
||
|
||
## Defense-in-Depth Layers
|
||
|
||
Piperless shells out to the Piper TTS engine and ffmpeg — both system binaries. The security model is built around controlling this boundary:
|
||
|
||
1. **Input layer:** All user input — POST params, REST params, cache keys, model paths — is validated before touching the filesystem or shell.
|
||
2. **Authorization layer:** Every admin action checks capabilities. Every post-specific action checks ownership. Public endpoints (audio proxy) use rate limiting instead.
|
||
3. **Shell boundary:** `escapeshellarg()` wraps every argument passed to `exec()`, `proc_open()`, and `proc_close()`. No string concatenation into shell commands anywhere.
|
||
4. **Output layer:** All HTML output goes through WordPress escaping functions. REST responses use `sanitize_text_field()`. Player attributes use `esc_attr()` and `esc_url()`.
|
||
5. **Failure safety:** Every execution path has a fallback. Failed MP3 or Opus conversion stores WAV. Failed ffprobe reads the WAV header. Failed temp writes clean up. Mutexes release in all branches.
|
||
|
||
|
||
## Audit Methodology
|
||
|
||
Each PHP file was reviewed against all 29 categories. The review examined:
|
||
|
||
- Every `exec()`, `proc_open()`, `shell_exec()` call path
|
||
- Every `echo`, `printf`, direct HTML output
|
||
- Every `$_POST`, `$_GET`, `$_SERVER`, `get_param()` access point
|
||
- Every `file_exists()`, `is_dir()`, `fopen()`, `file_put_contents()` call
|
||
- Every `wpdb` query and `maybe_unserialize()` usage
|
||
- Every `add_action`, `register_post_meta`, `register_rest_route` registration
|
||
|
||
The audit was performed programmatically by scanning source files for known vulnerability patterns, then reviewed manually for false positives and context.
|