Skip to content

Connector Approval experiment to limit what plugins can access what connectors#467

Merged
dkotter merged 33 commits into
WordPress:developfrom
dkotter:feature/connector-approval
May 19, 2026
Merged

Connector Approval experiment to limit what plugins can access what connectors#467
dkotter merged 33 commits into
WordPress:developfrom
dkotter:feature/connector-approval

Conversation

@dkotter
Copy link
Copy Markdown
Collaborator

@dkotter dkotter commented Apr 23, 2026

What?

Closes #441, #342

Adds a new settings screen that allows administrators to determine which plugin and theme can access which AI Connectors

Why?

WordPress 7.0 is releasing a new Connectors API that allows users to install various Connector plugins and then configure those once.

There is no approval / permission system in place though, meaning once a Connector is set up, any plugin can then use it even if you don't want that. This PR adds in a new experiment to showcase one approach to adding an approval system.

Worth noting this is (on purpose) a granular approval system. An administrator can decide which plugin has access to which connector, it's not an all or nothing approach.

How?

  • Adds a new Connector Approval experiment
  • When turned on, a new settings page is added (Settings > AI Connector Approvals). This settings page will show any pending requests and will also show an approval matrix for each active plugin and theme, allowing you to toggle support for each connector
  • Auto detection when a non-approved plugin or theme tries to use an AI Connector. This will then show as a pending request on the settings page and an admin notice will also render
  • Hook into pre_http_request and determine which plugin/theme is making the request and which provider they are using. If that plugin/theme has not been approved for that provider, stop the request
  • Adds necessary tests and documentation

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code, Cursor
Model(s): Opus 4.7, GPT-5.3 Codex
Used for: Used Claude Code for initial review and planning. Tried out a number of approaches before finally landing on the current approach. Testing and refinement done by me. Used Cursor to add tests and documentation.

Testing Instructions

  1. Pull this PR down and run npm i && npm run build
  2. Ensure you have at least one AI Connector in place
  3. Turn on the Connector Approval Experiment in Settings > AI
  4. Go to the Dashboard you should see an admin notice about a plugin requesting access
  5. Go to Settings > AI Connector Approvals and you should see a pending request for the AI plugin (yes, we even block our own plugin here)
  6. Either approve that request or toggle the connector settings for the AI plugin in the Approval matrix below
  7. Ensure the admin notice goes away and any AI experiments you have turned on work
  8. Remove the approval and test those experiments again and ensure they don't work

Screenshots or screencast

Connector Approval experiment settings Admin notice showing that a plugin has been blocked from using a connector AI Connector Approvals settings page showing pending approvals AI Connector Approvals settings page showing the Approval matrix

Changelog Entry

Added - Connector Approval experiment, allowing administrators the ability to determine which plugins can access which connectors.

Open WordPress Playground Preview

dkotter added 18 commits April 21, 2026 14:34
… of pre option filters, since the AI Client grabs options prior to plugins making requests, meaning we can't accurately filter there. Make a handful of changes to support this
…er through as well. Ensure we don't require approval for a provider that doesn't exist
…ing of provider name to api key to determine which provider is being used
@dkotter dkotter added this to the Future Release milestone Apr 23, 2026
@dkotter dkotter self-assigned this Apr 23, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @addedlovely.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: addedlovely.

Co-authored-by: dkotter <dkotter@git.wordpress.org>
Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: itsgajendraSingh <gajendrasingh@git.wordpress.org>
Co-authored-by: jeffpaul <jeffpaul@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>
Co-authored-by: haktan-suren <haktansuren@git.wordpress.org>
Co-authored-by: HILAYTRIVEDI <hilayt24@git.wordpress.org>
Co-authored-by: DarshitRajyaguru <darshitrajyaguru97@git.wordpress.org>
Co-authored-by: nikolas4175-godaddy <nikskyverge@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 70.57823% with 173 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.04%. Comparing base (35a4e1c) to head (b145aa0).

Files with missing lines Patch % Lines
includes/Connector_Approval/Admin_Notice.php 15.25% 50 Missing ⚠️
includes/Connector_Approval/Caller_Identifier.php 68.37% 37 Missing ⚠️
...udes/Experiments/Connector_Approval/Admin_Page.php 2.94% 33 Missing ⚠️
includes/Connector_Approval/REST_Controller.php 84.37% 20 Missing ⚠️
...ncludes/Connector_Approval/Connector_Key_Index.php 79.56% 19 Missing ⚠️
includes/Connector_Approval/Approvals_Store.php 90.32% 9 Missing ⚠️
includes/Connector_Approval/Http_Guard.php 92.68% 3 Missing ⚠️
...eriments/Connector_Approval/Connector_Approval.php 90.90% 2 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #467      +/-   ##
=============================================
- Coverage      73.18%   73.04%   -0.14%     
- Complexity      1527     1729     +202     
=============================================
  Files             77       85       +8     
  Lines           6873     7461     +588     
=============================================
+ Hits            5030     5450     +420     
- Misses          1843     2011     +168     
Flag Coverage Δ
unit 73.04% <70.57%> (-0.14%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

dkotter added 3 commits April 23, 2026 14:16
…ized

  into URL query strings still attribute correctly, prefer the plugin
  basename matching the calling frame's file when a directory registers
  multiple plugin header files, and return 404 from delete_pending when the
  pending key is unknown instead of silently re-rendering state.
@justlevine
Copy link
Copy Markdown
Contributor

(I didn't have a chance to pull down and test but wanted to say that this looks super cool and exciting. Does the matrix scale for more than 3 connectors? )

@dkotter
Copy link
Copy Markdown
Collaborator Author

dkotter commented May 11, 2026

As discussed in today's AI contributor call, we'll explore adding in something like https://github.com/ericmann/displace-secrets-manager to help ensure Connector details are kept secret.

In diving into this more today, this secrets manager is interesting but I don't think fully solves the problem we were discussing. In essence, it provides a standardized way to encrypt and decrypt data in WordPress, allowing us to encrypt API keys before saving and decrypt them when we need to make a request.

I think that's sorely needed in WordPress (and there is already a Trac ticket open around this for the Connectors API) but still doesn't solve the issue of preventing someone from accessing an API key a site owner doesn't want them to have.

That said, if we were to add this alongside what's already in this PR, that does get us closer to a "secure" state. But there are still edge cases, like a plugin directly pulling the API key out of the database or just using our built-in encryption/decryption methods that we would need to add. I'm not sure there's a way for us in a plugin to 100% cover all bases.

My suggestion here would be to proceed with this PR (and any feedback given) but then open a separate PR to look at integrating encryption (either with that Secrets Manager or a custom approach). I don't think these two need to be shipped within the same PR and I'm cognizant at the large size of this PR already.

@swissspidy
Copy link
Copy Markdown
Member

Auto detection when a non-approved plugin or theme tries to use an AI Connector. This will then show as a pending request on the settings page and an admin notice will also render

How does this work? Plugins and themes by nature have complete unrestricted access to WordPress, they could just circumvent this.

@dkotter
Copy link
Copy Markdown
Collaborator Author

dkotter commented May 11, 2026

How does this work? Plugins and themes by nature have complete unrestricted access to WordPress, they could just circumvent this.

Correct, only so much we can do here from within another plugin. At a high-level, we hook into pre_http_request and if the request is for an AI Connector, we try and determine who is making the request and if they have been given permission. If not, we block that request.

But yes, nothing stopping another plugin from hooking in earlier, removing our hook or making a direct curl request and not using the WP HTTP API at all.

This isn't meant to be 100% effective (or I should say I don't think it's possible to be 100% effective), it's meant more as a PoC that can hopefully stir conversation about getting something more permanent in WordPress Core itself.

@haktan-suren
Copy link
Copy Markdown

I built a small plugin exploring plugin-level AI Client access control (probably that's why I was tagged here too), so I wanted to share a few implementation tradeoffs from that work https://wordpress.org/plugins/handl-ai-connector-access-control/#is%20attribution%20perfect%3F.

My approach used wp_ai_client_prevent_prompt, which is a cleaner interception point because it avoids scanning outbound requests for credentials. The limitation I ran into is that it does not expose enough connector/provider context for granular per-connector approval. This PR’s HTTP-layer approach solves that, but it also depends on stack traces (which is unfortunate!) plus credential/base-URL matching.

Longer term, I think the ideal API would be a higher-level AI Client/Connector hook that passes the resolved connector/provider, caller identity, and request capability before execution. That would avoid both SDK reflection and credential scanning.

A few smaller points:

  • Should unknown callers be configurable as allow/block/log? Currently unidentified callers are allowed through.
  • It may be useful to add optional audit logging for approved usage, not just pending blocked attempts.
  • Since caller identification supports themes, the approval matrix may also need active theme entries so admins can preapprove themes before they generate a pending request.

@dkotter
Copy link
Copy Markdown
Collaborator Author

dkotter commented May 12, 2026

My approach used wp_ai_client_prevent_prompt, which is a cleaner interception point because it avoids scanning outbound requests for credentials. The limitation I ran into is that it does not expose enough connector/provider context for granular per-connector approval.

Yeah, my initial exploration used wp_ai_client_prevent_prompt but that does not allow the level of granularity I wanted. It's either an all or nothing approach.

Longer term, I think the ideal API would be a higher-level AI Client/Connector hook that passes the resolved connector/provider, caller identity, and request capability before execution

Yep, I agree and added those thoughts on the original issue.

Should unknown callers be configurable as allow/block/log? Currently unidentified callers are allowed through.

Good question and this is where I'd love some more testing. I didn't run into any unidentified callers in my testing but if that's a common occurrence, likely need to decide if those should be allowed through or not. I'd lean towards allowing them in as part of this exploration work as this is meant as an experiment but open to opinions on that.

It may be useful to add optional audit logging for approved usage, not just pending blocked attempts.

We have #437 open right now that adds logging. That PR and this PR will work nicely together

Since caller identification supports themes, the approval matrix may also need active theme entries so admins can preapprove themes before they generate a pending request.

I debated whether I should look at themes but I know some themes add in quite a bit of functionality so likely there will be some that try and make AI requests. That said, I did miss having those show in the approval matrix by default so that's likely a good thing to consider adding.

@swissspidy
Copy link
Copy Markdown
Member

Yeah, my initial exploration used wp_ai_client_prevent_prompt but that does not allow the level of granularity I wanted. It's either an all or nothing approach.

We might still have time to get something into 7.0 to make the filter more useful, if anyone wants to open a ticket :)

@jeffpaul
Copy link
Copy Markdown
Member

  1. Perhaps move the new admin page to under Tools and rename to Connector Approvals on the page header and in the admin menu (this more closely matches the Experiment name).
  2. Can probably hide the alerts on the Connector Approvals page as you'll already see the list of pending requests there and can review / approve / dismiss and the admin notice is mostly duplicative there
  3. Could be nice to have a list of the dismissed requests (in case someone mistakenly dismissed, wants to review all dismissed items), but not necessary for this iteration
  4. Will be curious how the Approval matrix table scales to sites with a LOT of plugins and themes active, but maybe we leave any special handling there for the next iteration here
  5. How is this determining an "AI" connector from a "non-AI" connector (e.g. how does it know Mistral is one to show but Akismet or Jetpack are not? Could be with the changes in (1) above that making this be for any/all Connector plugins could be helpful and not just AI ones?

@dkotter
Copy link
Copy Markdown
Collaborator Author

dkotter commented May 19, 2026

  • Perhaps move the new admin page to under Tools and rename to Connector Approvals on the page header and in the admin menu (this more closely matches the Experiment name).

Done in 1d86430

Can probably hide the alerts on the Connector Approvals page as you'll already see the list of pending requests there and can review / approve / dismiss and the admin notice is mostly duplicative there

Good call, removed that from showing on that page in 26cd034

Could be nice to have a list of the dismissed requests (in case someone mistakenly dismissed, wants to review all dismissed items), but not necessary for this iteration

Yeah, not something we store right now. Dismissing just removes it from the current view but if the plugin tries making a request again, it will show up again (so dismissing doesn't remove it forever)

Will be curious how the Approval matrix table scales to sites with a LOT of plugins and themes active, but maybe we leave any special handling there for the next iteration here

Definitely open to design ideas here. I'm not as concerned with the list of plugins/themes as this table can just keep expanding down forever (though maybe there's a better approach). More worried if sites have lots of connectors, there's only so many columns we can show before you have to start scrolling (I would assume this is an edge case and most sites will only have one or two AI connectors)

How is this determining an "AI" connector from a "non-AI" connector (e.g. how does it know Mistral is one to show but Akismet or Jetpack are not? Could be with the changes in (1) above that making this be for any/all Connector plugins could be helpful and not just AI ones?

Right now we use the core wp_get_connectors function and then filter down to only showing connectors with the ai_provider type, so no other connectors will show here.

If we want this to work for any connector, that's doable though likely better as a follow up as there's probably a few other changes needed.

@dkotter dkotter merged commit ab0eb20 into WordPress:develop May 19, 2026
16 of 18 checks passed
@dkotter dkotter deleted the feature/connector-approval branch May 19, 2026 20:45
@t-hamano
Copy link
Copy Markdown
Contributor

When the "Connector Approval" experiment setting is enabled, the API key is considered invalid even when the correct key is entered. I didn't understand what was happening until I reached this PR. Is it intended behavior that the API key input itself is put on hold?

@dkotter
Copy link
Copy Markdown
Collaborator Author

dkotter commented May 20, 2026

When the "Connector Approval" experiment setting is enabled, the API key is considered invalid even when the correct key is entered. I didn't understand what was happening until I reached this PR. Is it intended behavior that the API key input itself is put on hold?

@t-hamano Do you happen to have any other experiments turned on? I could only reproduce this if I had the Request Logging experiment turned on alongside Connector Approval. Otherwise it allows the API key to be validated and saved just fine. Definitely a bug we'll want to fix but curious if that's what you're running into or if you're seeing this with only having the Connector Approval experiment on?

@t-hamano
Copy link
Copy Markdown
Contributor

I could only reproduce this if I had the Request Logging experiment turned on alongside Connector Approval.

That's correct. I was able to reproduce the issue in my environment with those settings as well.

image

@dkotter
Copy link
Copy Markdown
Collaborator Author

dkotter commented May 20, 2026

@t-hamano Thanks for verifying! I have #595 up to fix this and a few other bugs I found while testing. If you have a second to test and verify it fixes your problem, that would be great.

simison pushed a commit to simison/ai that referenced this pull request May 25, 2026
…onnectors (WordPress#467)

Added - Connector Approval experiment, allowing administrators the ability to determine which plugins can access which AI connectors.

Unlinked contributors: addedlovely.

Co-authored-by: dkotter <dkotter@git.wordpress.org>
Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: itsgajendraSingh <gajendrasingh@git.wordpress.org>
Co-authored-by: jeffpaul <jeffpaul@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>
Co-authored-by: haktan-suren <haktansuren@git.wordpress.org>
Co-authored-by: HILAYTRIVEDI <hilayt24@git.wordpress.org>
Co-authored-by: DarshitRajyaguru <darshitrajyaguru97@git.wordpress.org>
Co-authored-by: nikolas4175-godaddy <nikskyverge@git.wordpress.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

7 participants