laravelsecurityaudit/laravel-ai-egress-guard
Composer 安装命令:
composer require laravelsecurityaudit/laravel-ai-egress-guard
包简介
Scan, redact, and optionally block outbound requests to AI providers (OpenAI, Anthropic, Gemini, and more) for leaked secrets and PII. Review in an inbox and fail CI before a prompt leaks. SDK-agnostic, at the HTTP layer.
README 文档
README
Scan every outbound request your Laravel app makes to an AI provider (OpenAI, Anthropic, Gemini, and more) for leaked secrets and PII. Review findings in a built-in inbox, fail your test suite when a prompt would leak, and optionally block an unsafe request before it leaves the app.
Mail Guard answers "is this email safe to send". Egress Guard answers "is this prompt safe to send to a third-party model". It works at the HTTP-client layer, so it is independent of which AI SDK you use.
This is an independent open-source package. It is not affiliated with, endorsed by, or sponsored by Laravel, Laravel LLC, or any AI provider.
Why this exists
A prompt is built from runtime data: a user record, a support ticket, a row from your database. That data can carry an API key, a card number, an email address, a private key, straight to a third party you do not control. The leak is in the data interpolated at call time, not in the code, so static review cannot see it. Egress Guard runs on the actual outbound request body, so it catches what review cannot.
Requirements
- PHP 8.2+
- Laravel 12 or 13
Installation
composer require laravelsecurityaudit/laravel-ai-egress-guard php artisan migrate
The service provider is auto-discovered. Capture and the inbox are enabled outside production by default. It depends on laravelsecurityaudit/laravel-secret-scanner for the detection engine.
How it works
Egress Guard registers a global HTTP-client middleware. For every outbound request to a host in egress-guard.providers, it builds a scan context from the request body, runs the rule set, stores the request and its findings (with high-confidence secrets redacted and the provider key stripped from the stored headers), and, when guard mode is on, can stop the request.
Requests to any other host are ignored on a fast path.
Coverage: Prism, the Http facade, and other SDKs
The global hook covers every request made through Laravel's Http client. Prism is built on Laravel's HTTP client, so Prism calls are covered automatically, as are the official laravel/ai SDK and anything using the Http facade.
A client that ships its own Guzzle stack (for example openai-php) is not seen by the global hook. Attach the guard to those clients directly:
use LaravelSecurityAudit\EgressGuard\Support\EgressGuard; $client = new \GuzzleHttp\Client(['handler' => EgressGuard::stack()]); // or push EgressGuard::handler() onto an existing handler stack.
If you have disabled the global hook and want to guard a single Prism call, pass the guard through Prism's withClientOptions():
use LaravelSecurityAudit\EgressGuard\Support\EgressGuard; // ...->withClientOptions(EgressGuard::clientOptions())
Provider-aware prompt parsing
For OpenAI, Anthropic, and Gemini request shapes, Egress Guard extracts the model and a readable prompt (messages[].content, Anthropic system, Gemini contents[].parts[].text) and stores it alongside the raw body for the inbox. By default the full raw body is scanned (most thorough). Set scan.target to prompt (or EGRESS_GUARD_SCAN_TARGET=prompt) to scan only the extracted message text when the raw JSON is too noisy.
The inbox
In an unguarded environment, open /egress-guard. Each captured request shows its provider, model, a risk badge, and the security findings, with the body redacted. The inbox can contain sensitive prompt data, so it is closed by default unless explicitly opened:
- If
egress-guard.gateis set, that gate must pass for every request. - If no gate is set, the inbox is served only in
egress-guard.unguarded_environments(default['local']). Anywhere else it returns a 403.
// config/egress-guard.php 'middleware' => ['web', 'auth'], 'gate' => 'viewEgressGuard',
Failing tests when a prompt leaks
use LaravelSecurityAudit\EgressGuard\Support\EgressGuard; public function test_the_summary_prompt_leaks_nothing(): void { $this->generateSummaryFor($user); // makes the AI call EgressGuard::assertNoCriticalFindings(); }
Also available: assertNoLeaks(), assertFlagged($ruleId), and assertNotFlagged($ruleId).
Failing CI with SARIF
php artisan test
php artisan egress-guard:scan --min-severity=critical --format=sarif --output=egress-guard.sarif
egress-guard:scan exits non-zero when any finding meets the threshold. Formats are table, json, and sarif. SARIF results point at the calling route action when known, otherwise at a synthetic egress-guard://request/{id} location.
Guard mode
Guard mode blocks a request when a finding meets the configured threshold. It is off by default and is the part you opt into, often in production.
EGRESS_GUARD_BLOCK=true
Defaults are conservative: it blocks only critical findings at high confidence, and fail_open is true so a scanner error never silently breaks a real AI call. A blocked request throws EgressGuardBlocked. Bypass a known-safe call with the X-Egress-Guard-Bypass header or the guard.allow_source allowlist of route actions. Every block fires an EgressBlocked event and a log warning with the rule ids, never the secret.
Residency
Egress Guard can enforce a data-residency policy. Declare the regions you allow and the region each provider actually processes in, then turn enforcement on:
// config/egress-guard.php 'residency' => [ 'enabled' => true, 'allowed_regions' => ['EU'], 'regions' => ['openai' => 'EU', 'anthropic' => 'US'], ],
A request to a provider whose region is not allowed (here Anthropic, US) is blocked with EgressGuardBlocked, exactly like a secret leak, and fires a ResidencyViolation event. With enabled off, the violation still fires the event (and laravel-ai-ledger records it), but the request is not blocked. Set block_unknown to also block a provider whose region you have not declared.
Rules
| Rule id | Severity | Confidence | Source |
|---|---|---|---|
secrets.private_key |
critical | high | secret-scanner |
secrets.stripe_key |
critical | high | secret-scanner |
secrets.api_key |
critical | high | egress-guard |
secrets.aws_access_key |
critical | high | egress-guard |
secrets.bearer_token |
warning | medium | egress-guard |
pii.credit_card |
critical | high | secret-scanner |
pii.email |
warning | medium | egress-guard |
pii.phone |
warning | low | egress-guard |
Toggle rules, override severities, and suppress findings in config/egress-guard.php. Add your own by implementing LaravelSecurityAudit\SecretScanner\Scanning\Contracts\Rule.
Configuration
php artisan vendor:publish --tag=egress-guard-config
EGRESS_GUARD_ENABLED=true EGRESS_GUARD_PATH=egress-guard EGRESS_GUARD_GATE=viewEgressGuard EGRESS_GUARD_RETENTION_DAYS=7 EGRESS_GUARD_REDACT=true EGRESS_GUARD_BLOCK=false
Captured requests older than EGRESS_GUARD_RETENTION_DAYS are removed by the scheduled model:prune command. Set it to 0 to disable.
Testing
composer test
composer analyse
License
The MIT License (MIT). See LICENSE.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 3
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-28