Releases: padosoft/laravel-ai-guardrails
v1.0.0 — Every documented limitation, closed
The 1.0 milestone closes every entry in the README "Known limitations" list plus the deferred MCP surface. Each is a default-safe, both-states-tested config toggle; the deterministic security guarantees of v0.3.0 are unchanged. 464 tests / 1182 assertions green on PHP 8.3 / 8.4 / 8.5 × Laravel 13; PHPStan level 8; Pint clean; Infection ≥ 80 % MSI in CI.
What's closed
- L1 — Cross-script homoglyph defense (Control B). A curated
ConfusablesFoldermaps Cyrillic/Greek Latin look-alikes to an ASCII skeleton before matching, soignоre(Cyrillicо) no longer evades the screening regexes. Togglenormalization.fold_confusables(default on). - L2 — HTMLPurifier-grade allowlist (Control C).
html_mode=allowlistnow uses HTMLPurifier whenezyang/htmlpurifieris installed (robust against malformed / entity-encoded / mutation-XSS markup, all attributes stripped), with graceful fallback to the built-in allowlist when absent. - L3 — Opt-in tool-call sanitization (Control C). New
output_handler.sanitize_tool_calls(default off — tool args are executed and governed by Controls A/D) adds a defense-in-depth pass over tool-call argument string leaves when you render/log them. - L4 — Turnkey HITL (Control D).
ai-guardrails:hitl-installruns laravel-flow's migrations scoped from vendor;ai-guardrails:hitl-statusdiagnoses the whole setup and exits non-zero with targeted guidance until HITL can actually gate a call. - L5 — MCP surface (4th surface). Expose the guardrails to AI clients via
laravel/mcp: toolsscreen_prompt,sanitize_output,recent_injection_audit. Default-OFF behindmcp.enabled; registered as a local server under the handleai-guardrails.
Hardening notes
- MCP tools are treated as an external surface: input length capped, the active ruleset version is withheld on benign screens (anti-fingerprinting), and principal ids are omitted from the audit tool by default (opt-in).
- Compose-not-couple holds:
laravel-flowonly insrc/Hitl,pii-redactor+HTMLPurifieronly insrc/Output,laravel/mcponly insrc/Mcp— all enforced by the architecture test. Every new dependency issuggest(optional) with graceful degradation.
Full changelog: v0.3.0...v1.0.0
v0.3.0 — Enterprise hardening
Enterprise-hardening line on top of the v0.2.0 admin HTTP API. Additive and backward-compatible — the four controls, the facade, the Artisan surface, and the …api.v1 envelope are unchanged; every new behaviour is a default-safe config toggle.
Highlights
- E3 — Enforce / monitor / off modes (shadow rollout). Every control gains a
modes.<control>dial.monitordetects + audits + emits but does not block — deploy guardrails in observation mode before flipping toenforce. Defaultenforce(no behaviour change). - E4 — Domain events. Every decision dispatches a domain event from the same path that writes the audit/stat record (
InjectionBlocked,InjectionObserved,ToolArgumentRejected,DestructiveToolRouted,OutputSanitized,SettingsChanged) so you can wire SIEM / Slack / PagerDuty. Gated byevents.enabled. - E5 — Audit data hygiene + GDPR retention.
audit_hygiene.prompt_storage=redact(default, composeslaravel-pii-redactor) |hash|truncate|raw. The sanctioned, actor-auditedai-guardrails:purgecommand (keep|anonymize|purge) is the only path rows leave the append-only audit. - E6 — Settings-change audit.
PUT /settingsrecords WHO changed WHAT to the immutable settings-change log (server-derived actor) + dispatchesSettingsChanged; newGET /settings/changes. - E7 — Tool authorization gate + recursive scoping. Gate tool use behind a Laravel
Gateability (fail-closed) above owner-key re-scoping;owner_key_depth=recursive(default) closes nested IDOR holes. - E9 — Mutation testing gate. CI enforces Infection ≥ 80 % MSI over the deterministic security algorithms (via the standalone PHAR + pcov).
- E9-API — Overview deltas.
GET /overviewnow surfaces each control's effectivemodeand the activeruleset_version.
Quality
- 423 tests / 1087 assertions green on PHP 8.3 / 8.4 / 8.5 × Laravel 13; PHPStan level 8; Pint clean; Infection ≥ 80 % MSI.
- Every toggle tested in both states (three for enforce/monitor/off); audit stores append-only; compose-not-couple boundary enforced by the architecture test.
Full changelog: v0.2.0...v0.3.0
v0.2.0 — Admin HTTP API surface
v0.2.0 — Admin HTTP API surface
Adds a complete, default-OFF read/config HTTP API for an admin panel, on top of the deterministic four-control core. Every endpoint is enveloped {schema_version, schema, data} and gated behind api.enabled + a required api.middleware stack (boot-throws if enabled without middleware).
Endpoints
GET /overview— control health + 24h injection countsGET /audit,/audit/{id},/audit/trend— append-only injection audit: keyset-paginated list, detail (+ matched span), dialect-safe per-day SQL trendGET /firewall— Control A tool-argument rejections (append-only)GET /output/stats— Control C per-kind neutralisation counts (html_stripped / markdown_sanitized / structured_validation_failure / pii_redaction)GET /approvals,POST /approvals/{token}/approve|reject— Control D HITL queue vialaravel-flow(actor principal derived server-side)GET /settings,PUT /settings— runtime-overridable settings (config | database), allow-listed + type-validated; DB overrides overlaid onto live config at boot (fail-safe)POST /try/screen,/try/sanitize— no-persistence sandbox
Stores
New append-only stores (Null/Array/Database) for the audit, firewall, and output-stat logs (the model + builder throw on update/delete), plus a mutable settings store — each behind its own config toggle, default-safe.
Hardening
Untrusted-input posture throughout: string-or-null query params, positive-int keyset cursors, strict ISO-8601 date parsing, LIKE-metacharacter escaping, UTF-8 scrubbing of stored text, fail-safe degradation when stores/tables are unavailable, and a compose-not-couple boundary enforced into the HTTP layer.
317 tests, PHPUnit 12 / PHP 8.3–8.5 × Laravel 13, Pint + PHPStan level 8 clean.