You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A PHP extension powered by Rust 🦀 and ext-php-rs, delivering
essential security utilities for PHP applications. It features the following core classes:
Hardened\Hostname — secure hostname parsing, normalization, and comparison.
Hardened\Path — safe, purely-lexical filesystem path handling to prevent directory traversal.
with fine-grained tag, attribute, and URL policy controls.
Hardened\ShellCommand — secure subprocess launcher: build up a command with arguments, configure timeouts,
environment inheritance or overrides, live or captured I/O modes, and execute without shell interpolation.
Hardened\Rng — stateless random-data generator: alphanumeric, alphabetic, byte sequences, integer ranges, and
custom Unicode or ASCII sampling. Using rand crate.
Hardened\CsrfProtection — synchronized CSRF token–cookie
protection using AES-GCM, with a PHP-friendly API for
token/cookie generation, verification, and cookie management. Using csrf crate.
Hardened\ConstantTime — timing-safe comparison of secrets (tokens, HMACs, signatures), with hex and
base64 variants. Using subtle crate.
Hardened\Redirect — open-redirect validator that parses untrusted targets the way browsers do,
defeating //evil.com, backslash, https:/evil.com, userinfo, and encoded-host bypasses.
Hardened\SsrfGuard — outbound network policy / SSRF prevention: scheme, port and CIDR policy with
resolve-then-validate DNS pinning (CURLOPT_RESOLVE-ready) against DNS rebinding.
Hardened\Password — Argon2id (and bcrypt) password hashing with OWASP defaults, timing-safe
verify(), and needsRehash(). Using argon2 and
bcrypt crates.
Hardened\RateLimiter — token-bucket rate limiter with a process-local store, a stateless
mode whose opaque state string can live in APCu/Redis/anything shared, and a CL.THROTTLE
backend (DragonflyDB / redis-cell) for atomic server-side GCRA.
Hardened\JwtVerifier — hardened JWT verification: alg: none impossible, key type bound to the
algorithm family (no HS/RS confusion), mandatory exp, nbf/iat validation. Using
jsonwebtoken crate.
Hardened\Filename — safe download/upload filenames: traversal, control bytes, Unicode bidi
spoofing (RLO), reserved Windows names, double extensions; Content-Disposition builder.
Hardened\Unicode — UTS #39 homoglyph/confusable detection, mixed-script and restriction-level
checks, NFKC/NFC normalization, invisible-character handling.
As well as blazingly fast sanitizers:
Hardened\Sanitizers\HtmlSanitizer — configurable HTML sanitization
via Ammonia. There's also truncateAndClean() for safe HTML truncation.
Hardened\Sanitizers\File\ArchiveSanitizer — sanitization against ZIP/RAR bombs.
Hardened\Sanitizers\File\PngSanitizer — sanitization against PNG bombs.
Hardened\Sanitizers\File\ImageSanitizer — header-only image hardening: dimension/bomb checks,
magic-byte vs extension/MIME verification, polyglot detection, and metadata stripping — all without
invoking an image decoder.
Ergonomic builders of HTTP security headers:
Hardened\SecurityHeaders\StrictTransportPolicy — builder for HTTP Strict-Transport-Security (HSTS); configure
max-age,
includeSubDomains, and preload, then emit the header.
Hardened\SecurityHeaders\ReferrerPolicy — Referrer-Policy header builder; initialize with or set any valid policy
token, build the header value, or send it directly.
Hardened\SecurityHeaders\Whatnot — builder for miscellaneous HTTP security headers (X-Frame-Options,
X-XSS-Protection, X-Content-Type-Options, X-Permitted-Cross-Domain-Policies, Report-To, Integrity-Policy,
and Integrity-Policy-Report-Only); configure via set…() methods, build a header map with build(), or emit all
via send().
Hardened\SecurityHeaders\CrossOrigin\EmbedderPolicy — configure Cross-Origin-Embedder-Policy: choose between
unsafe-none,
require-corp, or credentialless.
Hardened\SecurityHeaders\CrossOrigin\OpenerPolicy — configure Cross-Origin-Opener-Policy: e.g. same-origin,
same-origin-allow-popups, or unsafe-none.
Hardened\SecurityHeaders\CrossOrigin\ResourcePolicy — configure Cross-Origin-Resource-Policy: choose
same-origin, same-site, or
cross-origin.
Hardened\SecurityHeaders\CrossOrigin\ReferrerPolicy — set any valid Referrer-Policy token and emit header.
Hardened\SecurityHeaders\CrossOrigin\PermissionsPolicy — configure Permissions-Policy features, allow or deny
per‐feature with allowlists (*,
self, 'src', specific origins), build header, or send it.
directives, keyword sources, hosts, and automatic nonce generation.
Installation
Supported Platforms: Linux, macOS, Windows (where ext-php-rs is available)
# Install cargo-php if you haven't already# (ensures you have the latest cargo-php installer)
cargo install cargo-php --locked
# Build and install the PHP extensioncd php-hardened-cdylib
cargo php install --release --yes
All features are enabled by default.
If you want to choose what features to include in the build, use --features.
For example, cargo php install --release --yes --features rng,
join a segment and enforce that result stays within base.
setFileName(mixed $file_name): Path
Replace the file name component.
setExtension(mixed $extension): Path
Replace the file extension (without leading dot).
validateExtension(array $allowed): bool
Check if the file extension is in a custom allowed list.
validateExtensionImage(): bool
Returns true if extension is a common image (png, jpg, jpeg, gif, webp, bmp, tiff, svg).
validateExtensionVideo(): bool
Returns true if extension is a common video (mp4, mov, avi, mkv, webm, flv).
validateExtensionAudio(): bool
Returns true if extension is a common audio (mp3, wav, ogg, flac, aac).
validateExtensionDocument(): bool
Returns true if extension is a common document (pdf, doc, docx, xls, xlsx, ppt, pptx).
Hardened\ShellCommand
Secure subprocess launcher without shell interpolation.
Build a command with explicit executable and arguments.
Configure timeouts (seconds or milliseconds) and environment inheritance/overrides.
Choose I/O modes: ignore, passthrough (print to PHP), or callback per chunk.
Entry-points:
executable() – start from a specific binary.
shell() – use your login shell ($SHELL or /bin/sh).
Example
useHardened\ShellCommand;
// 1) Basic builder:$cmd = newShellCommand('ls');
$cmd->passArg('-la');
$cmd->setTimeout(5); // seconds$cmd->inheritEnvs(['PATH', 'HOME']);
$cmd->passEnv('FOO', 'bar');
$cmd->passthroughStdout(); // print live$cmd->pipeCallbackStderr(function($chunk) { /* handle stderr chunks */ });
// 2) Run and capture both streams internally:$code = $cmd->run($stdoutVar, $stderrVar);
// $stdoutVar and $stderrVar now contain full output, $code is exit code.// 3) One-line helpers:$result = Hardened\shell_exec('echo hello', ['echo']);
// Enforces top-level command 'echo' only, returns output or exit code.$args = ['status', '--short'];
$result2 = Hardened\safe_exec('git', $args);
// Spawns `git status --short` without any shell interpretation.
API Reference
Method
Description
executable(string $exe): Self
Create a new instance targeting the given executable path (no arguments).
__construct(string $exe, array $args = []): Self
Same as executable() plus initial argument list.
shell(): Self
Shortcut to executable(env('SHELL') ?? '/bin/sh').
safeFromString(string $cmd): Self
Shell-split safely (handles quotes/escapes, disallows NUL), then configure the command.
unsafeFromString(string $cmd): Self
Like shell_exec(): runs via /bin/sh -c, but records top-level commands to detect injection.
arg(string $arg): Self
join a single argument (no shell interpretation).
passArgs(array $args): Self
join multiple positional or --key value arguments.
setTimeout(int $secs): Self
Set an execution timeout in seconds (process is killed on expiry).
setTimeoutMs(int $ms): Self
Set an execution timeout in milliseconds.
inheritAllEnvs(): Self
Inherit all of the parent process’s environment variables.
inheritEnvs(array $names): Self
Restrict inherited environment variables to this set.
passEnv(string $key, string $val): Self
Add or override a single environment variable for the child.
passEnvOnly(array $map): Self
Clear all inherited vars and set only these environment variables.
passthroughBoth(): Self
Stream both stdout and stderr live into PHP output.
passthroughStdout(): Self
Stream stdout live into PHP output.
passthroughStderr(): Self
Stream stderr live into PHP output.
ignoreBoth(): Self
Discard both stdout and stderr.
ignoreStdout(): Self
Discard stdout only.
ignoreStderr(): Self
Discard stderr only.
pipeCallbackBoth(callable $cb): Self
Invoke the PHP callable for each chunk on both stdout and stderr.
pipeCallbackStdout(callable $cb): Self
Invoke the PHP callable for each chunk on stdout.
pipeCallbackStderr(callable $cb): Self
Invoke the PHP callable for each chunk on stderr.
run(?string &$out = null, ?string &$err = null): int
Execute the command, stream according to configured modes, optionally capture stdout/stderr into the provided variables, and return exit code (-1 on timeout or signal).
topLevelCommands(): ?array
Get the list of top-level command names parsed by unsafeFromString(), or null if not in unsafe mode.
Drop-in replacement for PHP’s shell_exec(). Runs /bin/sh -c $command, records the top-level command names, and if you pass an $expectedCommands list it will throw on any deviation (to catch injection). Returns the captured stdout (or exit-code string on non-zero).
Safe alternative that never invokes a shell. Splits $commandLine into tokens, disallows NUL, joins $arguments, then spawns directly. Captures stdout into the return string (or exit-code string on non-zero).
Hardened\Sanitizers\HtmlSanitizer
Provides a powerful fine-grained HTML sanitization using Ammonia.
Configuration methods for URL policies, tags, attributes, and filters.
Attribute filter callback support.
Enum:HtmlSanitizerFlag for truncation modes (ExtendedGraphemes, Graphemes, Unicode, Ascii, PreserveWords).
A built-in truncator:cleanAndTruncate($html, $max, $flags, $etc = '…') is useful when you need to get a snippet of a dynamic HTML
content. Length of $etc is included in the limit. Supported flags (use HtmlSanitizerFlag enum):
HtmlSanitizerFlag::ExtendedGraphemes — units of $max will be Unicode extended grapheme clusters.
HtmlSanitizerFlag::Graphemes — units of $max will be Unicode grapheme clusters.
HtmlSanitizerFlag::Unicode — (default) units of $max will be Unicode code points.
HtmlSanitizerFlag::Ascii — units of $max will be bytes. Even this mode doesn't chop Unicode code points in half.
Open HTML tags will automatically close at all times, but beware that added closing tags may cause the result length
to flow over $max if you are truncating.
The current cleanAndTruncate() implementation is NOT safe to use if you allow dangerous block tags like <script>
and <style>, so an exception will be thrown.
Sets the tags whose contents will be completely removed from the output.
addCleanContentTags(array $tags): void
Add additional blacklisted clean-content tags without overwriting old ones.
rmCleanContentTags(array $tags): void
Remove already-blacklisted clean-content tags.
isValidUrl(string $url): bool
Checks whether a URL is allowed by the configured scheme whitelist or, for relative URLs, by the relative-URL policy.
Hardened\Sanitizers\File\Archive
Detects “decompression bombs” in ZIP and RAR archives.
ZIP: sums all central‑directory uncompressed sizes and compares against the first local‑header uncompressed size.
RAR: checks the first entry’s unpacked size versus total compressed size (default 1000× ratio).
On detection or any file/format error, throws an exception; otherwise returns normally.
Example
<?phpuseHardened\Sanitizers\File\Archive;
try {
// If neither a ZIP nor RAR bomb is found, this returns void
Archive::defuse('/path/to/archive.zip');
echo"Archive looks safe\n";
} catch (Exception$e) {
// On bomb detection or parse errorecho"Bomb detected or error: ", $e->getMessage(), "\n";
}
try {
// You can equally defuse a RAR file
Archive::defuse('/path/to/archive.rar');
echo"RAR safe\n";
} catch (Exception$e) {
echo"RAR bomb or error: ", $e->getMessage(), "\n";
}
API Reference
Method
Description
defuse(string $path): void
Inspect the given file at $path as ZIP or RAR. Throws if a “bomb” is detected or on any I/O/parse error.
Hardened\Sanitizers\File\PngSanitizer
Detects “PNG bombs”—images whose IHDR dimensions are unreasonably large (>10000 px).
Reads only the PNG signature and IHDR chunk; no full decode.
On detection or any I/O/format error, throws an exception; otherwise returns normally.
Example
<?phpuseHardened\Sanitizers\File\PngSanitizer;
try {
// Throws if width or height > 10000 or IHDR missing/invalid
PngSanitizer::defuse('/tmp/huge.png');
echo"PNG is safe\n";
} catch (Exception$e) {
echo"PNG bomb or error: ", $e->getMessage(), "\n";
}
API Reference
Method
Description
defuse(string $path): void
Inspect the file at $path. Throws if it’s a valid PNG with width>10000 or height>10000, or if the IHDR chunk is malformed.
Hardened\Sanitizers\File\ImageSanitizer
Header-only image hardening: inspect untrusted uploads without ever invoking an image decoder
(imagecreatefromstring() runs the full C codec on attacker bytes with no sandbox).
Magic-byte format detection for JPEG/PNG/GIF/WebP/BMP/TIFF/ICO/AVIF/HEIF/JXL and more — never
trusts the extension or the client-declared MIME type.
Decompression-bomb guards from header-declared dimensions: per-side limits and a pixel budget
for extreme aspect ratios.
Polyglot detection: a valid image that is also HTML/SVG/PHP (content-sniffing XSS, upload RCE).
Metadata stripping without decoding pixels: JPEG APP1–APP15 + COM (EXIF incl. GPS, XMP, IPTC;
keeps JFIF/Adobe/ICC), PNG eXIf/tEXt/zTXt/iTXt, WebP EXIF/XMP (with RIFF size and
VP8X flag fix-up).
API Highlights:
new ImageSanitizer(string $data) / ImageSanitizer::fromBytes(string $data) / ImageSanitizer::fromFile(string $path).
$img->stripMetadata(): string — sanitized copy of the image bytes.
Example
useHardened\Sanitizers\File\ImageSanitizer;
$img = ImageSanitizer::fromFile($_FILES['avatar']['tmp_name']);
// Reject anything that isn't what it claims to be — before any decoder runsif ($img->format() === null
|| !$img->matchesExtension($_FILES['avatar']['name'])
|| !$img->matchesMime($_FILES['avatar']['type'])) {
http_response_code(415);
exit;
}
$img->assertDimensionsWithin(10000, 10000); // throws on decompression bombs$img->assertPixelsWithin(50_000_000);
$img->assertNotPolyglot(); // throws on embedded <?php / <script / <svg// Store a copy with EXIF/GPS/XMP removedfile_put_contents($dest, $img->stripMetadata());
useHardened\CsrfProtection;
//// 1) Initialization//$key = '7sVldqnZoPUIY7wWp1We-mbaZ5SAoe04QXUFiNnwJFE='; // must decode to 32 bytes$ttl = 3600; // token lifetime in seconds// If you have a previous token (for rotation), pass it as third argument:// $previous = $_COOKIE['csrf'] ?? null;// $csrf = new CsrfProtection($key, $ttl, $previous);$csrf = newCsrfProtection($key, $ttl);
//// 2) Send the cookie to the client//$csrf->sendCookie(
expires: time() + $ttl,
path: '/',
domain: '', // default: current host
secure: true, // only over HTTPS
httponly: true// inaccessible to JavaScript
);
//// 3) Embed the CSRF token in your form or AJAX request//$token = $csrf->token(); // Base64URL-encoded token string?>
<!doctype html>
<html>
<body>
<form method="POST" action="submit.php">
<input type="hidden" name="csrf_token" value="<?=htmlspecialchars($token, \ENT_QUOTES) ?>">
<!-- other form fields… -->
<button type="submit">Submit Securely</button>
</form>
</body>
</html>
<?phpreturn;
//// 4) On form submission (submit.php)://try {
// Reconstruct with same key/ttl and pass previous cookie if rotating:$csrf = newCsrfProtection($key, $ttl, $_COOKIE['csrf'] ?? null);
// Verify the token against the cookie$csrf->verifyToken(
/* token value from form: */$_POST['csrf_token'] ?? '',
/* cookie value: */$_COOKIE['csrf'] ?? null
);
// If we get here, CSRF check passedecho"CSRF validated — proceed with action.";
} catch (\Exception$e) {
// Invalid or expired tokenhttp_response_code(403);
echo"CSRF validation failed: " . htmlspecialchars($e->getMessage());
}
API Reference
Method
Description
__construct(string $key, int $ttl, ?string $previousTokenValue = null): void
Outbound network policy for URLs built from untrusted input (SSRF prevention).
Resolve-then-validate: the hostname is resolved once, every resolved address is policy-checked,
and the validated addresses are returned so the connection can be pinned (DNS-rebinding safe).
Secure defaults: http/https only, ports 80/443, and loopback, RFC 1918 private, link-local
(incl. the 169.254.169.254 cloud metadata endpoint), CGNAT, unique-local (incl. fd00:ec2::254),
multicast, broadcast and other reserved ranges denied — for both address families, including
IPv4-mapped IPv6 and decimal/octal/hex IPv4 notations.
useHardened\SsrfGuard;
$guard = newSsrfGuard();
var_dump($guard->isIpAllowed("169.254.169.254"));
// bool(false) — cloud metadata endpoint// DNS-rebinding-safe fetch: resolve once, validate, pin the connection.$url = $_POST['webhook_url'];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RESOLVE, [$guard->curlResolve($url)]); // throws if forbiddencurl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); // validate each redirect hop insteadcurl_exec($ch);
API Reference
Method
Description
__construct()
Guard with secure defaults (http/https, ports 80/443, reserved ranges denied).
setAllowedSchemes(array $schemes): void
Replace the allowed scheme set.
setAllowedPorts(array $ports): void
Replace the allowed port set (empty = any).
allowCidr(string $cidr): void
Allow a CIDR/IP, overriding built-in reserved ranges.
denyCidr(string $cidr): void
Deny a CIDR/IP; deny entries always win.
setBlockReservedRanges(bool $block): void
Toggle the built-in reserved-range denylist.
isIpAllowed(string $ip): bool
Check a single address against the policy.
validateUrl(string $url): array
Validate scheme/port/userinfo + every resolved address; returns IPs.
curlResolve(string $url): string
Validate and return a CURLOPT_RESOLVE entry (host:port:ip,…).
Hardened\Text
Control-character and protocol-injection sanitizers; all methods are binary-safe.
Covers log forging (CR/LF), HTTP/SMTP header injection (response splitting), null-byte
truncation, and control bytes used as separators in delimited backend protocols.
Strips C0 controls (0x00–0x1f), DEL (0x7f), and UTF-8 encoded C1 controls (U+0080–U+009F).
useHardened\Text;
error_log("login failed for " . Text::sanitizeLogLine($username));
// "user\r\n[CRITICAL] fake" logs as "user[CRITICAL] fake" — no forged entriesheader("Content-Disposition: " . Text::sanitizeHeaderValue($disposition));
// throws on CR/LF/NUL instead of splitting the response
Text::assertNoNullBytes($filename);
// throws on "file.php\0.jpg"
Token-bucket limiter: bursts up to capacity, sustained refillTokens per refillIntervalMs.
Process-local keyed store with zero setup (per-worker under php-fpm), plus a stateless mode
for shared backends: the limiter hands you an opaque state string to keep in APCu/Redis/a
session and pass back on the next attempt.
CL.THROTTLE backend — the strongest option: atomic server-side GCRA in
DragonflyDB (built in)
or Redis with redis-cell. One round-trip, shared across
all workers and hosts, no read-modify-write race. The limiter maps its configuration onto the
command exactly and works with any client (phpredis, predis, Relay).
Tamper-resistant state: claimed tokens are clamped to capacity, future timestamps to now.
API Highlights:
new RateLimiter(int $capacity, int $refillTokens, int $refillIntervalMs).
alg: none is always rejected — there is no way to allow it.
The key type is bound to an algorithm family at construction (forHmac() accepts only HS*,
forRsa() only RS*/PS*, forEcdsa() only ES*, forEd25519() EdDSA), so HS/RS algorithm
confusion is unrepresentable.
The token's alg header must be in your explicit allowlist.
exp is mandatory and validated; nbf is validated when present; a future iat is
rejected; optional max-age makes iat mandatory.
Reject tokens older than this; makes iat mandatory.
setLeeway(int $seconds): void
Clock-skew tolerance for exp/nbf/iat (default 60).
verify(string $token): array
Verify and return claims, or throw.
Hardened\Filename
Safe filenames for downloads and uploads: keeps only the last path component, removes control
bytes and invisible/bidi-override characters (U+202E RLO makes …cod.exe display as
…exe.doc), replaces Windows-forbidden punctuation, strips leading/trailing dots and spaces,
neutralizes reserved device names (CON, NUL, COM1…), caps length at 255 bytes.
Flags dangerous (invoice.pdf.php) and double (invoice.pdf.exe) extensions.
Builds safe Content-Disposition header values with an RFC 5987 filename* form for
non-ASCII names.
useHardened\SecretRedactor;
$redactor = newSecretRedactor();
$redactor->addPattern('\binternal-[a-z0-9]+\b');
error_log($redactor->redact($exception->getMessage()));
// "Authorization: Bearer [REDACTED]" instead of the live token
Hardened\RequestGuard
Request-level CSRF guard from browser metadata, with the holes of hand-rolled checks closed:
exact origin matching (scheme + host + port — no prefix/subdomain surprises), the opaque
null origin never passes, header-less state-changing requests are rejected by default.
Check order: safe methods pass → Sec-Fetch-Site honored when present (same-origin/none
pass; same-site only if opted in) → Origin against the allowlist → Referer as last
resort.
Defense in depth: combine with Hardened\CsrfProtection tokens.
API Highlights:
new RequestGuard(array $allowedOrigins) — e.g. ["https://app.example"].
UTS #39 homoglyph hardening for identifiers people read: usernames, display names, email
local-parts.
Confusable skeletons (pаypal with Cyrillic а ⇒ skeleton paypal — enforce uniqueness on
skeletons, not raw strings), mixed-script detection, restriction levels, the General Security
Profile, NFKC/NFC normalization, and invisible/bidi character handling.
useHardened\Unicode;
$username = Unicode::nfkc($_POST['username']);
if (Unicode::hasInvisibleCharacters($username) || !Unicode::isSingleScript($username)) {
thrownewInvalidArgumentException("suspicious username");
}
// Enforce uniqueness on the skeleton so "pаypal" can't sit next to "paypal"$skeleton = Unicode::skeleton(mb_strtolower($username));
Hardened\SecurityHeaders\ContentSecurityPolicy
Builder for HTTP Content-Security-Policy headers.
Configure directives (default-src, script-src, etc.) with CspRule enum and keyword tokens via CspKeyword enum.
Automatically generates nonces for 'nonce-…' directives.
Produces a valid header string with build(), and convenience method send() to emit it.
Retrieve the last-generated nonce with getNonce().
Set or replace a CSP directive with the given keywords and host sources.
build(): string
Build the Content-Security-Policy header value from the configured directives.
send(): void
Send the constructed CSP header to the client (via PHP SAPI).
getNonce(): ?string
Return the most recently generated nonce (without the 'nonce-' prefix), or null if none has been generated.
resetNonce(): void
Clears the generated nonce. The next call of build() or send() will generate a new one.
Hardened\SecurityHeaders\StrictTransportSecurity
HTTP Strict Transport Security (HSTS) header builder.
Configure max-age, includeSubDomains, and preload flags for best‐practice transport security.
Build the header string with build(), or emit it directly with send() (uses PHP header()).
Example
useHardened\SecurityHeaders\StrictTransportSecurity;
// Create and configure HSTS$hsts = newStrictTransportSecurity();
$hsts->maxAge(31536000); // one year$hsts->includeSubDomains(true); // apply to all subdomains$hsts->preload(true); // request inclusion in browser preload lists// Get header value$value = $hsts->build();
// e.g. "max-age=31536000; includeSubDomains; preload"// Send header to clientheader('Strict-Transport-Security: ' . $value);
// Or simply:$hsts->send();
API Reference
Method
Description
__construct()
Initialize with max-age=0, no subdomains, no preload.
maxAge(int $maxAge): void
Set the max-age directive (in seconds).
includeSubDomains(bool $enable): void
Enable or disable the includeSubDomains flag.
preload(bool $enable): void
Enable or disable the preload flag.
build(): string
Return the Strict-Transport-Security header value, e.g. "max-age=31536000; includeSubDomains; preload".
Allow a feature for the given list of origins. Valid entries: '*', 'self', 'src', or specific origins.
deny(PermissionsPolicyFeature $feature): void
Deny a feature entirely (empty allowlist).
build(): string
Render the header value, e.g. geolocation=(self "https://maps.example.com"), fullscreen=(*).
send(): void
Emit Permissions-Policy: <value> via PHP header() calls.
Performance
Only Hardened\Sanitizers\HtmlSanitizer is covered with benchmarks as of this moment.
HtmlSanitizer::cleanAndTruncate() may call clean() an extra time to deal with unenclosed tags.
Rust benchmark suite
Command:
cargo bench --features test
M1 Max results:
html_sanitizer_10kb time: [188.64 µs 189.33 µs 190.09 µs]
Found 6 outliers among 100 measurements (6.00%)
4 (4.00%) high mild
2 (2.00%) high severe
html_sanitizer_truncate_10k_to_5kb_in_ascii_mode
time: [294.66 µs 298.40 µs 303.62 µs]
Found 8 outliers among 100 measurements (8.00%)
3 (3.00%) high mild
5 (5.00%) high severe
PHP benchmarks
Run:
cd benches
curl -s https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer | php -- --quiet
./composer.phar require phpbench/phpbench ezyang/htmlpurifier --dev
./vendor/bin/phpbench run benchmark.php
As you can see, Hardened\Sanitizers\HtmlSanitizer (effectively Ammonia)
runs 13.7 times faster than the widely used htmlpurifier written in PHP and
2.8 times faster than tidy (a library written in C).
Running Tests
cargo test
PHP examples in examples directory are getting smoke tested automatically with cargo test (provided that you have
PHP installed).