| 开发者 | agenticdaisy |
|---|---|
| 更新时间 | 2026年9月11日 13:31 |
| PHP版本: | 8.1 及以上 |
| WordPress版本: | 7.1 |
| 版权: | GPLv2 or later |
| 版权网址: | 版权信息 |
ag_live_...), stored as a SHA-256 hash and displayed a single time at issuance. For clients that can only send a username and password - most MCP clients - you can instead bind one of that user's application passwords to the agent, which identifies it just as the token does. An agent can never exceed the capabilities of its linked user, and scopes can pin it down further, to specific action categories or ability patterns.
Write actions are intercepted at both doors. Ability execution callbacks are wrapped at registration, and direct REST writes are caught at rest_pre_dispatch, so switching transports doesn't dodge the rules. Policies match on ability name, action category, entity type or id, source, and whether the ability declares its own action destructive, in priority order. Each rule decides: allow, deny, log only, or require human approval. If nothing matches, destructive actions require approval and reads pass. A sliding-window rate limiter shuts down runaway loops, and the admin gets an email when it trips - or a message in Slack, Google Chat or Discord, if you give it a webhook address.
Held actions land in an approval queue along with the target's modification timestamp. If a person edits that content before you approve, the stored action aborts with a conflict rather than overwriting the newer work. If you have set a webhook address, each held action announces itself there with a link straight to the queue, so nothing waits on someone happening to open the dashboard.
Every decision is written to an append-only audit ledger. Each entry carries a SHA-256 hash chained to the previous one, so any after-the-fact tampering breaks verification. Secrets in action payloads (passwords, API keys, tokens) are redacted before they reach the ledger.
Executed actions can be undone from the dashboard. Post edits restore through the normal revision history, deletions come back from trash, and settings changes restore from stored snapshots. Where a clean undo isn't possible (say, user creation), the ledger says so instead of pretending.
The plugin also watches for two quieter risks: it fingerprints every registered ability and alerts you when one appears or changes definition (a known tool-poisoning pattern), and it records REST writes made with an application password that no agent has claimed - automation carrying no identity for a policy to apply to - in the audit ledger as "untracked". A setting on the Agents screen refuses those outright instead.
All decisions are made by plain PHP on your server. The plugin collects no telemetry and calls no third-party service; the one outbound request it can make is to the alert webhook you configure, if you configure one. The admin dashboard (Agent Firewall menu) runs on permission-checked REST endpoints. The Abilities API interception itself is what sets the WordPress 6.9 floor.
What this does and does not claim
Every agent write that reaches WordPress is evaluated, and every one that succeeds is recorded. That is the promise, and it is deliberately narrower than "blocks all AI attacks".
What it cannot see, stated plainly so you can judge the fit:
wp-content/plugins/.Authorization: Bearer header.Not unless you ask it to. All policy decisions run as plain PHP on your server, and by default nothing leaves your site. The one exception is the optional webhook: if you save an endpoint under "Webhook URL" on the Activity Ledger screen, four events are posted there as JSON - an action held for your approval, an agent tripping its rate limit, an agent denied for reaching outside its scopes or while the kill switch is engaged, and an AI tool changing definition. Each delivery carries what the decision was about and nothing more: the agent's name and identifier, the action category, the kind of entity and its id, the ability names involved, your site's address, the time, the plugin version, and a link back to your own dashboard. The content of a post, an option or an agent's payload is never included, and neither is any credential. You choose that address, clearing the field stops it, and there is no phone-home, no analytics and no third-party service behind any of it.
Nothing, by default. The audit ledger is evidence, so it stays. If you want uninstall to remove all Agent Firewall tables and options, opt in first: wp option update adaf_delete_data_on_uninstall 1.
Yes. Each agent is linked to a WordPress user and can never exceed that user's capabilities; policies and scopes only restrict further. Give it a user with just the capabilities it needs. Administrators are refused outright: an agent inherits its user's capabilities, and an administrator-linked agent would hold the capability that governs the firewall itself. For the same reason no agent token can reach the firewall's own screens or REST routes, whatever its user may do - the only exception is polling the status of its own held action.
The Abilities API, the structured way AI agents act on WordPress, shipped in 6.9. Intercepting it is a core feature of this firewall.
Use the kill switch: the "Emergency stop: pause all agents" button at the top of every Agent Firewall screen. While engaged, every agent action - reads included - is denied with a clear machine-readable reason, and every denial still lands in the audit ledger. Held approvals stay put; nothing is lost. Release it from the same place when the incident is over. It is a hard deny rather than hold-for-approval so a runaway agent cannot flood your approval queue while you investigate.
No. Approval endpoints require the manage_options capability. The firewall's own admin routes are exempt from agent gating precisely so a human can still step in when an agent is misbehaving.
A structured error, not a bare refusal. Denials carry a reason (policy, out_of_scope, rate_limited, untracked_automation, lockdown), a retryable flag, and a plain-language explanation of what would change the answer. Rate limits say that waiting clears them; scope and policy denials say that retrying will not help and a site administrator has to act. Held actions report that they have not executed and should not be resubmitted; over the Abilities API that arrives as the error code adaf_pending_approval, carrying the id to poll, because WordPress checks an ability's return against the output the ability says it produces and "held for review" matches no ability's declared output. Rate-limit denials also carry a retry_after value and a standard Retry-After header saying when a slot frees, and held actions can be polled at /pending/{id}/status with the agent's own token. Well-behaved agents act on this instead of hammering the endpoint.
The policy list starts empty, but the site is not unprotected. When no rule matches, the firewall falls back to built-in defaults: anything the ability itself declares destructive is held for approval; reads are allowed; creating or updating posts, pages and media is allowed; every deletion is held for approval; and any write to settings, user accounts, roles, plugins, themes, templates, global styles or widgets is held for approval even when it is a create or an update. Anything the firewall cannot categorize is held rather than allowed. The Policies screen lists these defaults so an empty policy list is not mistaken for no protection.
Use the rule builder on the Policies screen, which offers only values that can actually occur, or switch it to JSON if you prefer. A rule matches on any combination of action category (create, update, delete, read), entity type, entity id, ability name, source, and whether the ability declares itself destructive, and carries a decision: ALLOW, DENY, REQUIRE_APPROVAL, or LOG_ONLY. Rules are tried in order and the first match wins, so a rule with no match conditions must be last. A rule may also carry a sliding-window write limit instead of a decision.
Each policy is either global or scoped to a single agent, chosen with the "Applies to" control and changeable later. At equal priority an agent's own policy is consulted before a global one.
The full grammar is available over the API at /wp-json/agenticdaisy-firewall/v1/policies/schema, which returns the JSON Schema, the accepted values for each dimension, and the defaults.
Yes. The Policies screen has "Export policies (JSON)" and "Import policies" buttons. Importing only ever adds: existing policies are never edited or deleted, and an entry identical to one you already have is skipped. Policies scoped to a single agent attach to an agent with the same name on the destination site; if none exists, the entry is skipped and the import report says so, because silently applying an agent-specific rule to every agent would change what it protects.
Use "Test an action" on the Policies screen. Pick an action, an entity type and optionally an agent, and it reports what would happen and which rule decided it - or that no rule matched and a built-in default applied. It is a dry-run: nothing is written, no rate limit is consumed, and no agent is affected. This matters because rules are tried in order and the first match wins, so reading a list of policies does not always tell you which rule governs a given action. After the fact, the Activity Ledger's "Decided by" column reports the same thing for every action the firewall has already seen, so you can trace any verdict back to the rule that produced it. You can also filter the ledger by deciding policy, to see what one rule has actually been doing, or by "Built-in defaults" to see the actions no rule covered yet. The Policies screen lists any rule that has decided nothing at all, which catches a rule quietly shadowed by an earlier one - though a rule can also be unfired simply because nothing has attempted what it guards against, so it reports the fact rather than telling you to remove anything. If you have set a retention window, that list says so and names how far back the ledger still reaches, since a rule whose matches were pruned looks the same as one that never matched. That provenance is covered by the ledger's hash chain, so it cannot be rewritten without detection.
It is rejected when you save it and nothing is stored. Structural problems, such as a decision that is not one of the recognized values, name the exact position of the error. Beyond that, the plugin rejects rules that would silently do nothing: a rule carrying neither a decision nor a limit, and any rule made unreachable by an earlier rule that already matches everything. If a stored rule is somehow unrecognizable at evaluation time, the action is held for approval; the engine never fails open.
Yes, but it is off by default because deleting audit records is not something a plugin should do to you unasked. Set a retention window under "Ledger retention" on the Activity Ledger screen and a daily job trims entries older than that. Because the ledger is hash-chained, pruning re-anchors the chain rather than breaking it, and every prune writes its own entry recording how many entries were removed and when - retention that left no trace of itself would be a way to erase an audit trail. Actions still waiting for approval always keep their records. Snapshots are removed with their entries, so actions older than the window can no longer be undone.
Both jobs are also available to WP-CLI, along with a summary: wp agent-firewall status reports the ledger size, its oldest entry, whether the chain verifies and whether history has been trimmed; wp agent-firewall verify checks the chain and exits non-zero if it has been tampered with, so you can run it from cron and be told rather than having to look; wp agent-firewall prune applies the window, with --dry-run to see what would go first.
Yes. The Activity Ledger screen has a checkbox: "Email me when an agent trips a rate limit or an ability changes definition". It is on by default, sends only to the site administrator, and is throttled so a runaway loop produces one message rather than hundreds. Turning it off stops the mail; the anomalies are still recorded in the ledger either way. These are event alerts about your own site - the plugin sends no newsletters, digests or marketing of any kind.
Yes, with a second checkbox on the same screen: "Email me when an action is held for my approval". It is off by default, and stays off when you update - the plugin will not start emailing you because of an upgrade, only because you ticked the box. Switched on, you get one message per held action, addressed to the site administrator, with a link that opens the approval queue directly. It is a separate switch from the alert one above, so turning either off leaves the other alone. Held actions are listed on the Pending Approvals screen and counted in the admin menu whether or not you enable the mail. Denials are not emailed at all. An agent refused for reaching outside its scopes, or while the kill switch is engaged, is reported to your webhook if you have one - that is information you may want to watch, rather than something waiting on you, and a denied agent in a loop would fill an inbox you cannot mute.
Yes. Save your incoming webhook URL under "Webhook URL" on the Activity Ledger screen, then press "Send a test message" to confirm it arrives rather than finding out during an incident. Four things are delivered: an action held for your approval, an agent tripping its rate limit, an agent denied for reaching outside its scopes or while the kill switch is engaged, and an AI tool changing definition. Denials by policies you wrote are deliberately not sent - those are the firewall doing its job, and an agent stuck in a loop would bury everything else. The message line is sent as both "text" and "content", which is what Slack, Google Chat and Discord incoming webhooks read, so those work with nothing else configured; n8n, a log collector or an endpoint of your own get the structured fields - event, site, timestamp and the details - in the same JSON. Microsoft Teams is the exception: the connector webhooks that accepted a plain message were switched off in May 2026, and the Workflows endpoints that replaced them want a card-shaped body, so a Teams channel needs something in between for now. Every delivery is signed as an X-ADAF-Signature header holding a sha256 HMAC of the body, using the secret shown once you save an endpoint, so the receiving end can tell a real delivery from anyone who learned the URL. Email and webhook are independent: turning one off leaves the other alone, and both are throttled the same way, so a runaway loop is one message rather than hundreds.
Yes. Bind one of the linked user's application passwords to the agent, on the Agents screen. Many MCP clients authenticate with an application password over Basic auth and cannot send a custom Authorization: Bearer header; without a binding they arrive with no agent identity, so no policy can apply to them. A bound password identifies the agent exactly as its token would, and the same policies, scopes, rate limits and ledger entries apply.
Create the password on that user's own profile in WordPress, then choose it from the picker. One password identifies one agent: a password another agent already holds is offered but not selectable. Deleting the password on the profile screen ends the binding, and the Agents screen says so.
A write authenticated with an application password that no agent has claimed carries no identity, so there is nothing for a policy to apply to. By default it proceeds and is recorded in the Activity Ledger with the "untracked" status - it names the route, the action, and the acting WordPress user, and it joins the tamper-evident hash chain like every other entry. The Agents screen has a checkbox, "Refuse writes from application passwords no agent claims", which rejects them instead. Turning it on will also stop any existing integration that uses one, so bind the passwords you want to keep working first.
Yes, and there is nothing WooCommerce-specific to configure. WooCommerce registers seven abilities in core - product and order queries, product create, update and delete, order status changes and order notes - and declares for each whether it is read-only and whether it is destructive. The firewall reads those declarations, so store lookups are allowed while changing a price, changing an order's status and deleting a product are held for a person. One rule covers all of it: {"match": {"destructive": true}, "decision": "REQUIRE_APPROVAL"}. Note that WooCommerce does not expose refunds as an ability at all, so there is nothing there for this or any other plugin to govern.
No, and deliberately. (The plugin's own wp agent-firewall commands, above, are a separate thing - those are for you, not for agents.) A wp command runs with no HTTP request and no credential to identify, so no agent identity exists for it and no policy can attach. It is also not a boundary worth defending: anyone who can run wp on the server can equally run wp plugin deactivate agentic-daisy-ai-agent-firewall. The firewall governs what arrives over HTTP - agent tokens, bound application passwords, the REST API, the Abilities API and MCP endpoints.
product-delete rather than delete-post - which the firewall could make nothing of. A store lookup sat in the approval queue waiting for a human, rules written for products or for deletions never fired, and the audit ledger recorded every store action as "unknown". Classification now reads what an ability declares about itself through the Abilities API - whether it is read-only, and whether it is destructive - and reads the name only when an ability declares nothing. Queries are allowed; price changes and deletions are held. None of this is WooCommerce-specific: any plugin that describes its abilities properly is now governed properly.data as an object, as every real event already did. An empty PHP array encodes as [], so a test delivery carried "data": [] where a rate-limit or ability-drift alert carried "data": {...} - two types for one field, on the one delivery a person is most likely to point a new parser at.adaf_pending_approval, carrying the id to poll and a 202 status. WordPress validates what an ability returns against the output that ability says it produces, and "held for review" matches no ability's declared output - so on WooCommerce, and on anything else declaring one, the agent was told its tool was broken rather than that a person had to decide, and never received the id it needs to check back. The action itself was always held correctly; only the answer was wrong. If you have written an agent client that reads the held-action payload from the Abilities API, branch on the error code instead. Denials already worked this way, and the REST API is unchanged.adaf_action_pending and adaf_action_denied now fire from the interception pipeline, after the audit ledger row exists, carrying the agent, the action and - for denials - the reason. The plugin previously exposed almost no extension surface at the point where decisions are made.Authorization: Bearer header, so until now they arrived with no identity at all - every policy, scope and rate limit was skipped, and their writes were only recorded as "untracked". A bound password identifies the agent exactly as its token does, and the whole pipeline applies: per-call classification of MCP requests, scopes, policy, rate limiting, the audit ledger and undo. Bind one on the Agents screen, choosing from the linked user's own passwords by name. One password identifies one agent, so a password another agent already holds is shown but not selectable, and deleting it on the user's profile ends the binding, which the Agents screen reports rather than hiding.wp command carries no request and no credential to identify, and it is not a boundary worth defending in any case: anyone who can run wp on the server can equally deactivate this plugin. This was previously an open question rather than a stated answer.agent_slug now and still accepts agent_key, returning both, so anything already scripted against it keeps working.tools/list and a tools/call that deletes an event are no longer indistinguishable. Protocol handshakes are treated as reads, tool names map to a policy action and entity, batches are judged call by call, and anything unparseable is held for approval rather than passing through. Blocked calls return a JSON-RPC error the agent can act on. Policy rules can target this door with source: mcp.source: unclassified.lockdown reason.