# Comprehensive Security Architecture for international-press-zone

## 1. Introduction

The security perimeter of a WordPress installation is defined by the quality of its weakest plugin. This document defines the security architecture and scanning logic for the `international-press-zone` plugin, covering vulnerabilities from SQL Injection and XSS to PHP-specific issues like deserialization and type juggling.

## 2. Scanner Architecture

### Sources and Sinks

| Category | Description | Examples |
|----------|-------------|---------|
| Sources | Untrusted input entry points | `$_GET`, `$_POST`, `$_REQUEST`, `$_COOKIE`, `$_SERVER`, `file_get_contents('php://input')` |
| Sinks (Execution) | Code/command execution | `eval()`, `system()`, `passthru()`, `shell_exec()`, `call_user_func()` |
| Sinks (SQL) | Database interaction | `$wpdb->query()`, `$wpdb->get_results()`, `$wpdb->insert()`, `$wpdb->update()` |
| Sinks (Output) | Browser output | `echo`, `print`, `printf`, `die()`, `exit()` |
| Sinks (File) | Filesystem writes | `file_put_contents()`, `fwrite()`, `move_uploaded_file()`, `copy()`, `unlink()` |

## 3. Authentication, Authorization, and Access Control

### 3.1 The is_admin() Fallacy

`is_admin()` checks the page location, NOT the user role. Since AJAX requests go through `/wp-admin/admin-ajax.php`, `is_admin()` returns `true` for ALL AJAX requests regardless of privilege level.

**Rule:** Never use `is_admin()` as a security gate. Always use `current_user_can()`.

### 3.2 Privilege Escalation Prevention

- `wp_ajax_{action}` hooks fire for ALL authenticated users (including Subscribers)
- `wp_ajax_nopriv_{action}` hooks fire for unauthenticated users
- Always check specific capabilities: `current_user_can('manage_options')` for admin actions

### 3.3 Nonce System (CSRF Protection)

- **Creation:** `wp_create_nonce()` or `wp_nonce_field()` in forms
- **Verification:** `wp_verify_nonce()`, `check_admin_referer()`, or `check_ajax_referer()` in handlers
- Use unique action strings (e.g., `presszone_international_delete_translation_123`)

### 3.4 REST API Authorization

All `register_rest_route` calls MUST have a `permission_callback` using `current_user_can()`. Setting `permission_callback => '__return_true'` on write endpoints is a Critical vulnerability.

### 3.5 IDOR Prevention

When processing user-supplied IDs (`$post_id`, `$user_id`), always verify ownership or capability before performing actions.

## 4. Input Lifecycle: Validate, Sanitize, Escape

### 4.1 Validation
- `is_email()` for email fields
- `is_numeric()` / `absint()` for IDs
- `validate_file()` for file paths (returns `0` if valid)

### 4.2 Sanitization (Before Storage)
- Text: `sanitize_text_field()`
- Rich HTML: `wp_kses_post()` or `wp_kses()`
- Keys/Slugs: `sanitize_key()`
- Titles: `sanitize_title()`
- SQL ORDER BY: `sanitize_sql_orderby()`
- Filenames: `sanitize_file_name()`

### 4.3 Escaping (At Output)
- HTML content: `esc_html()`
- HTML attributes: `esc_attr()`
- URLs: `esc_url()`
- JavaScript: `esc_js()`
- SQL: `$wpdb->prepare()`

## 5. SQL Injection Prevention

- NEVER concatenate variables into SQL strings
- ALWAYS use `$wpdb->prepare()` with format specifiers (`%d`, `%s`)
- Use strict whitelists for ORDER BY columns
- All `ipz_*` table queries must use prepared statements

## 6. XSS Prevention

- Escape ALL output at the point of rendering
- Use `textContent` instead of `innerHTML` for user data in JavaScript
- Validate and sanitize all data before storage

## 7. Plugin-Specific Conventions

| Convention | Value |
|-----------|-------|
| Namespace | `InternationalPressZone` |
| Text domain | `international-press-zone` |
| Function prefix | `presszone_international_*` |
| CSS prefix | `presszone-international-*` |
| DB table prefix | `ipz_*` |
| Constants prefix | `IPZ_*` |
| Option prefix | `presszone_international_*` |
