Created: 2026-01-22 Project Phase: Active Development Current Completion: 65% Target Completion: 95% (Production Ready)
php-aegis is a PHP 8.1+ security and hardening toolkit providing input validation, sanitization, and security utilities. This plan outlines the path from 65% to 95%+ completion with production-ready status.
Working Features (65% complete):
- ✅ Validator class (17 methods): email, URL, IP, UUID, slug, JSON, filename safety, semver, ISO 8601, hex colors
- ✅ Sanitizer class (10 methods): HTML, JS, CSS, URL, JSON, stripTags, filename, removeNullBytes
- ✅ TurtleEscaper class (UNIQUE VALUE): W3C-compliant RDF Turtle escaping - no other PHP library does this
- ✅ Headers class: CSP, HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy
- ✅ Crypto utilities: Cryptographic functions and secure random generation
- ✅ Comprehensive test suite: 4 test files (67KB), PHPUnit configured
- ✅ Static methods throughout (no instance state needed)
- ✅ SPDX license headers on all files
- ✅ Composer package (MIT license, PHP 8.1+ requirement)
Code Metrics:
- Total lines: 3,173
- Source files: 5 (Validator.php, Sanitizer.php, Headers.php, TurtleEscaper.php, Crypto.php)
- Test files: 4 (ValidatorTest.php, SanitizerTest.php, HeadersTest.php, TurtleEscaperTest.php)
- Dependencies: zero runtime dependencies
Critical Blockers (Medium Priority):
- WordPress integration (0%) - Adapter functions, mu-plugin pattern
- sanctify-php coordination (0%) - Safe sink recognition
- Packagist publication (0%) - Not published yet
- Real-world validation (0%) - No test integrations
Feature Gaps:
- IndieWeb security (0%) - Micropub, IndieAuth, Webmention validators
- Rate limiting (0%) - Token bucket implementation
- Framework adapters (0%) - Laravel service provider, Symfony bundle
Documentation Gaps (30% complete):
- API reference incomplete
- WordPress integration guide missing
- IndieWeb examples missing
- Deployment guide incomplete
Goal: Enable seamless WordPress integration with wrapper functions and best practices guide.
Create src/WordPress/Adapter.php:
<?php
// SPDX-License-Identifier: CC-BY-SA-4.0
declare(strict_types=1);
namespace PhpAegis\WordPress;
use PhpAegis\Validator;
use PhpAegis\Sanitizer;
use PhpAegis\TurtleEscaper;
/**
* WordPress adapter functions following WordPress naming conventions.
*
* These functions provide WordPress-style wrappers around php-aegis methods
* for developers familiar with esc_html(), esc_attr(), etc.
*/
if (!function_exists('aegis_html')) {
function aegis_html(string $input): string {
return Sanitizer::html($input);
}
}
if (!function_exists('aegis_attr')) {
function aegis_attr(string $input): string {
return Sanitizer::attr($input);
}
}
if (!function_exists('aegis_js')) {
function aegis_js(string $input): string {
return Sanitizer::js($input);
}
}
if (!function_exists('aegis_url')) {
function aegis_url(string $input): string {
return Sanitizer::url($input);
}
}
if (!function_exists('aegis_turtle_literal')) {
function aegis_turtle_literal(
string $value,
?string $language = null,
?string $datatype = null
): string {
return TurtleEscaper::literal($value, $language, $datatype);
}
}
if (!function_exists('aegis_validate_email')) {
function aegis_validate_email(string $email): bool {
return Validator::email($email);
}
}
if (!function_exists('aegis_validate_url')) {
function aegis_validate_url(string $url, bool $httpsOnly = false): bool {
return $httpsOnly ? Validator::httpsUrl($url) : Validator::url($url);
}
}
if (!function_exists('aegis_send_security_headers')) {
function aegis_send_security_headers(): void {
\PhpAegis\Headers::secure();
}
}Create docs/wordpress/aegis-mu-plugin.php:
<?php
/**
* Plugin Name: php-aegis Security Enhancements
* Description: Adds php-aegis security functions to WordPress
* Version: 0.2.0
* SPDX-License-Identifier: CC-BY-SA-4.0
*/
declare(strict_types=1);
// Load Composer autoloader (adjust path if needed)
if (file_exists(WPMU_PLUGIN_DIR . '/vendor/autoload.php')) {
require_once WPMU_PLUGIN_DIR . '/vendor/autoload.php';
}
// Load WordPress adapter functions
if (class_exists('PhpAegis\\WordPress\\Adapter')) {
require_once dirname(__FILE__) . '/aegis-functions.php';
}
// Apply security headers on init
add_action('send_headers', function () {
if (!headers_sent()) {
\PhpAegis\Headers::secure();
}
});Create tests/WordPress/AdapterTest.php:
<?php
// SPDX-License-Identifier: CC-BY-SA-4.0
declare(strict_types=1);
namespace PhpAegis\Tests\WordPress;
use PHPUnit\Framework\TestCase;
class AdapterTest extends TestCase
{
public function testAegisHtmlFunction(): void
{
require_once __DIR__ . '/../../src/WordPress/Adapter.php';
$input = '<script>alert("xss")</script>';
$output = aegis_html($input);
$this->assertStringNotContainsString('<script>', $output);
$this->assertStringContainsString('<script>', $output);
}
public function testAegisTurtleLiteralFunction(): void
{
$input = 'Hello "World"';
$output = aegis_turtle_literal($input, 'en');
$this->assertStringContainsString('"Hello \\"World\\""@en', $output);
}
}Create docs/wordpress/WORDPRESS_INTEGRATION.md:
# WordPress Integration Guide
## Installation
### Method 1: Composer (Recommended)
```bash
cd wp-content/mu-plugins
composer require hyperpolymath/php-aegisDownload and place in wp-content/mu-plugins/php-aegis/
<?php
// In template files
<h1><?= aegis_html($post_title) ?></h1>
<input value="<?= aegis_attr($user_input) ?>"><?php
// For semantic themes outputting Turtle
header('Content-Type: text/turtle; charset=utf-8');
$subject = get_permalink();
$label = get_the_title();
echo aegis_turtle_literal($subject, 'en');<?php
// In functions.php
add_action('send_headers', function () {
aegis_send_security_headers();
});| Scenario | Use WordPress | Use php-aegis |
|---|---|---|
| HTML output | esc_html() |
aegis_html() |
| Attribute output | esc_attr() |
aegis_attr() |
| RDF/Turtle output | ❌ Not available | ✅ aegis_turtle_literal() |
| Security headers | ❌ Manual | ✅ aegis_send_security_headers() |
| UUID validation | ❌ Not built-in | ✅ aegis_validate_uuid() |
| Semantic versioning | ❌ Not built-in | ✅ Validator::semver() |
<?php
/**
* Template: Single post with RDF metadata
*/
// Set up Turtle output
add_action('wp_head', function () {
if (is_single()) {
echo '<link rel="alternate" type="text/turtle" href="' .
get_permalink() . '/turtle" />';
}
});
// Turtle endpoint
add_action('template_redirect', function () {
if (get_query_var('format') === 'turtle') {
header('Content-Type: text/turtle; charset=utf-8');
$subject = '<' . get_permalink() . '>';
$title = aegis_turtle_literal(get_the_title(), 'en');
$content = aegis_turtle_literal(get_the_content(), 'en');
echo "$subject <https://schema.org/name> $title .\n";
echo "$subject <https://schema.org/articleBody> $content .\n";
exit;
}
});
**Completion Criteria**:
- [ ] WordPress adapter functions implemented
- [ ] MU-plugin template created
- [ ] WordPress integration tests passing
- [ ] WordPress documentation complete
- [ ] Tested with at least 2 WordPress themes/plugins
**Time Estimate**: Remove per project policy
---
### Phase 2: IndieWeb Security Helpers (72% → 78%)
**Goal**: Provide security utilities for IndieWeb protocols (Micropub, IndieAuth, Webmention).
#### 2.1 Micropub Content Validator
Create `src/IndieWeb/Micropub.php`:
```php
<?php
// SPDX-License-Identifier: CC-BY-SA-4.0
declare(strict_types=1);
namespace PhpAegis\IndieWeb;
use PhpAegis\Validator;
use PhpAegis\Sanitizer;
final class Micropub
{
/**
* Validate Micropub entry (microformats2 format).
*
* @param array<string, mixed> $entry
* @return array{valid: bool, errors: string[]}
*/
public static function validateEntry(array $entry): array
{
$errors = [];
// Check required fields
if (!isset($entry['type'])) {
$errors[] = 'Missing required field: type';
}
if (!isset($entry['properties'])) {
$errors[] = 'Missing required field: properties';
}
// Validate content if present
if (isset($entry['properties']['content'])) {
$content = $entry['properties']['content'][0] ?? '';
if (is_array($content)) {
$html = $content['html'] ?? '';
$text = $content['value'] ?? '';
// Validate HTML doesn't contain dangerous scripts
if (str_contains($html, '<script>')) {
$errors[] = 'Content contains dangerous script tags';
}
}
}
// Validate URLs if present
if (isset($entry['properties']['url'])) {
foreach ($entry['properties']['url'] as $url) {
if (!Validator::httpsUrl($url)) {
$errors[] = "Invalid or non-HTTPS URL: $url";
}
}
}
return [
'valid' => empty($errors),
'errors' => $errors,
];
}
/**
* Sanitize Micropub content for storage.
*
* @param array<string, mixed> $entry
* @return array<string, mixed>
*/
public static function sanitizeEntry(array $entry): array
{
$sanitized = $entry;
if (isset($sanitized['properties']['name'])) {
$sanitized['properties']['name'] = array_map(
fn($name) => Sanitizer::html($name),
$sanitized['properties']['name']
);
}
if (isset($sanitized['properties']['content'])) {
foreach ($sanitized['properties']['content'] as $key => $content) {
if (is_array($content)) {
if (isset($content['html'])) {
$sanitized['properties']['content'][$key]['html'] =
Sanitizer::html($content['html']);
}
if (isset($content['value'])) {
$sanitized['properties']['content'][$key]['value'] =
Sanitizer::html($content['value']);
}
} else {
$sanitized['properties']['content'][$key] =
Sanitizer::html($content);
}
}
}
return $sanitized;
}
}
Create src/IndieWeb/IndieAuth.php:
<?php
// SPDX-License-Identifier: CC-BY-SA-4.0
declare(strict_types=1);
namespace PhpAegis\IndieWeb;
use PhpAegis\Validator;
final class IndieAuth
{
/**
* Validate "me" URL (IndieAuth profile URL).
*/
public static function validateMe(string $url): bool
{
if (!Validator::httpsUrl($url)) {
return false;
}
// Must be a valid domain, not an IP
$host = parse_url($url, PHP_URL_HOST);
if ($host === null || $host === false) {
return false;
}
return Validator::domain($host);
}
/**
* Validate redirect URI matches client ID origin.
*/
public static function validateRedirectUri(
string $redirectUri,
string $clientId
): bool {
if (!Validator::httpsUrl($redirectUri) || !Validator::httpsUrl($clientId)) {
return false;
}
$redirectHost = parse_url($redirectUri, PHP_URL_HOST);
$clientHost = parse_url($clientId, PHP_URL_HOST);
return $redirectHost === $clientHost;
}
}Create src/IndieWeb/Webmention.php:
<?php
// SPDX-License-Identifier: CC-BY-SA-4.0
declare(strict_types=1);
namespace PhpAegis\IndieWeb;
use PhpAegis\Validator;
final class Webmention
{
/**
* Check if IP address is internal/private (SSRF prevention).
*/
public static function isInternalIp(string $ip): bool
{
if (!Validator::ip($ip)) {
return false;
}
// Check if IP is in private ranges
return !filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
);
}
/**
* Validate Webmention source URL (prevent internal requests).
*/
public static function validateSource(string $url): bool
{
if (!Validator::httpsUrl($url)) {
return false;
}
$host = parse_url($url, PHP_URL_HOST);
if ($host === null || $host === false) {
return false;
}
// Check if host resolves to internal IP
$ip = gethostbyname($host);
if ($ip === $host) {
// Could not resolve
return false;
}
return !self::isInternalIp($ip);
}
/**
* Validate Webmention target matches your domain.
*/
public static function validateTarget(string $url, string $yourDomain): bool
{
if (!Validator::httpsUrl($url)) {
return false;
}
$host = parse_url($url, PHP_URL_HOST);
return $host === $yourDomain;
}
}Create tests/IndieWeb/MicropubTest.php, IndieAuthTest.php, WebmentionTest.php.
Create docs/indieweb/INDIEWEB_SECURITY.md with comprehensive examples.
Completion Criteria:
- Micropub validator implemented
- IndieAuth validator implemented
- Webmention SSRF prevention implemented
- IndieWeb tests passing (100% coverage)
- IndieWeb documentation complete
Goal: Provide rate limiting without external dependencies.
Create src/RateLimit/RateLimitStore.php:
<?php
// SPDX-License-Identifier: CC-BY-SA-4.0
declare(strict_types=1);
namespace PhpAegis\RateLimit;
interface RateLimitStore
{
/**
* Get token bucket state for a key.
*/
public function get(string $key): ?TokenBucket;
/**
* Store token bucket state for a key.
*/
public function set(string $key, TokenBucket $bucket, int $ttl): void;
}Create src/RateLimit/TokenBucket.php, MemoryStore.php, FileStore.php.
Create src/RateLimit/RateLimiter.php with attempt(), remaining(), resetAt() methods.
Completion Criteria:
- Token bucket implementation
- Memory store (for development)
- File store (for production without Redis)
- Rate limiter with clear API
- Comprehensive tests (edge cases, concurrency)
- Documentation with examples
Goal: Comprehensive documentation for all audiences.
Create docs/api/API_REFERENCE.md:
- Complete reference for all classes and methods
- Parameter descriptions with types
- Return value documentation
- Usage examples for each method
- Security considerations
Create docs/user/USER_GUIDE.md:
- Installation methods (Composer, manual)
- Quick start examples
- Common use cases
- Framework integration (WordPress, Laravel, Symfony)
- Troubleshooting
Enhance SECURE_DEFAULTS.md:
- OWASP Top 10 mapping (expand existing)
- Real-world vulnerability examples
- Secure coding patterns
- CI/CD integration examples
- WordPress: Comprehensive guide (expand Phase 1 docs)
- Laravel: Service provider usage
- Symfony: Bundle configuration
- IndieWeb: Protocol implementations
Completion Criteria:
- API reference complete (100 pages)
- User guide complete (50 pages)
- Security guide enhanced (40 pages)
- Integration guides for 3 frameworks
- Code examples tested and verified
Goal: Test php-aegis against real WordPress plugins/themes to validate effectiveness.
Create test integrations:
- Semantic blog theme (using TurtleEscaper)
- E-commerce theme (using security headers)
- Membership site theme (using validation)
Create test integrations:
- Contact form plugin (validation + sanitization)
- IndieWeb plugin (Micropub + Webmention)
- API service plugin (rate limiting + headers)
Document:
- Real vulnerabilities found and fixed
- Performance metrics
- False positive/negative analysis
- Integration pain points
- Recommendations for improvement
Completion Criteria:
- Tested with 3 WordPress themes
- Tested with 3 WordPress plugins
- Created comprehensive test report
- Documented all findings
- Updated code based on feedback
Goal: Make php-aegis easily accessible to all PHP developers.
- Register on packagist.org
- Configure GitHub webhook for auto-updates
- Add Packagist badge to README
Create Dockerfile and publish to GHCR:
FROM php:8.3-cli
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN composer global require hyperpolymath/php-aegisCreate hyperpolymath/php-aegis-action for CI integration:
- uses: hyperpolymath/php-aegis-action@v1
with:
path: ./src
report: aegis-report.txtNot applicable for PHP libraries (interpreted language).
Completion Criteria:
- Published to Packagist
- Docker image published to GHCR
- GitHub Action created and tested
- Installation docs updated
- Version 1.0.0 released
- 100% test coverage for core functionality
- PHPStan level 9 (strict)
- PHP-CS-Fixer (PSR-12 compliant)
- Zero critical security issues (Psalm)
- 200+ pages of documentation
- 50+ code examples
- 3+ framework integration guides
- 10+ real-world use cases
- 100+ Packagist installs/month
- 50+ GitHub stars
- 10+ contributors
- 5+ integration examples
- Zero reported vulnerabilities in library itself
- 10+ vulnerabilities prevented in user code
- OWASP Top 10 coverage documented
Per project guidelines, no time estimates are provided. Work proceeds based on:
- User demand (GitHub issues)
- Security criticality
- Contributor availability
Phases can be reordered based on community feedback.
What makes php-aegis unique:
- TurtleEscaper - Only PHP library with W3C-compliant RDF Turtle escaping
- Zero Dependencies - Works everywhere PHP 8.1+ runs
- Static Methods - No instance state, easy drop-in usage
- IndieWeb Focus - First-class support for Micropub, IndieAuth, Webmention
- Framework Gaps - Provides what WordPress/Laravel/Symfony don't
php-aegis (runtime) and sanctify-php (static analysis) are complementary:
| Tool | Role | When Used |
|---|---|---|
| php-aegis | Runtime protection | During request handling |
| sanctify-php | Static analysis | During development/CI |
Synergy: sanctify-php should recognize php-aegis methods as "safe sinks" in taint analysis:
-- sanctify-php configuration
safeSinks = [
"PhpAegis\\Sanitizer::html",
"PhpAegis\\Sanitizer::attr",
"PhpAegis\\Sanitizer::js",
"PhpAegis\\TurtleEscaper::literal",
"PhpAegis\\TurtleEscaper::iri"
]- sanctify-php - Static analysis for PHP security (finds the bugs)
- indieweb2-bastion - Infrastructure-layer security (network/DNS/audit)
- wp-audit-toolkit - WordPress security auditing
Development plan created 2026-01-22. Target: Production-ready php-aegis at 95%+ completion.