"Look around, but don't change anything" is a useful instruction. It is not a reason to give an assistant a credential that can change everything.
I want the instruction and the permission to agree. The assistant should understand the boundary, and the service should enforce it if the assistant gets it wrong. That includes an ordinary mistake, a badly chosen tool call or an instruction hidden in a document it reads.
You can test the second part without asking a model to behave badly. Send a forbidden operation directly to the same service boundary and check what happens. Use a disposable environment you control, not a production record you hope will remain untouched.
Separate the caller from the requested target
An integration request has at least two relevant identities: who is making it and which resource it wants to access. A workspace name in the prompt establishes neither.
The service needs to authenticate the connection, derive its permissions from trusted state, then check the requested operation and target. If the caller supplies its own workspace membership or write permission as ordinary tool arguments, the service must not simply believe it.
The small companion makes that distinction visible without implementing an authentication system. It has three synthetic connections: a reader and writer for workspace alpha, and a reader for workspace beta. Their permissions live in a registry owned by the example service.
Download the companion folder and open a terminal there. With Python 3 installed, run:
python3 -B demo.py permissions
python3 -B -m unittest -v test_permissions
No credentials, network or installed packages are needed. The connection names are fixtures. They are deliberately guessable, which makes them unsuitable as credentials in a real service. Anyone running this code controls its process and can select the writer fixture or alter the registry. The example teaches authorization decisions after an assumed identity; it provides no security boundary against its operator.
Check what the service refuses
The reader for alpha can retrieve its note. A write through that reader fails. The writer for alpha can change that same note, but cannot write to beta. An unknown connection also fails.
After each refused write, the tests inspect the notes to confirm that they did not change. An error response by itself isn't enough if a handler performs the mutation before checking permission.
The important part of the example is the order inside WorkspaceService.call: resolve the connection, check the workspace, check the operation, validate the write data, then change the note. The returned identity is a description of the grant, not an editable grant. Adding write to the returned list doesn't alter the registry, and a test checks that too.
Another test stores a note containing an instruction to write to a different workspace. Reading the note returns that text. It doesn't grant permission to follow it. No model reads or acts on the text in this test, so this is not evidence that prompt injection has been solved. It checks only that document contents aren't used to assign authority in this small service.
Apply the check to a real integration
Start by identifying the credential or approved connection the client actually uses. Inspect its granted workspace and operations through the provider's supported interface. A label such as "research assistant" or a hidden write tool in one client is not proof of a read-only grant.
For a disposable test workspace you administer, establish a baseline record and check these cases:
- An allowed read succeeds and returns the expected record.
- A forbidden write fails, and an independent read confirms the record is unchanged.
- Access to a second test workspace fails, even if the caller knows its identifier.
- A deliberately authorized write through a separate writer succeeds, so the negative result wasn't merely a broken endpoint.
Keep the two connections separate. Don't paste tokens into prompts or logs. Record the operation, target, observed response and readback, not the credential value. If testing requires privileges or environments you don't control, stop at reviewing the documented permissions and ask the owner to perform the boundary test.
In a production service, also test expired and revoked credentials through the supported authentication path. Our fixture registry doesn't implement either. Nor does it cover every alternate endpoint that might mutate the same data; each real write path needs the same authority rules.
Read access still deserves restraint
Read-only access can expose sensitive data. It does not mean public, harmless or safe to copy into another system. Choose the smallest useful resource scope, and consider where the client sends what it reads.
Permission also differs from approval. A writer connection may permit an operation technically while the task still requires a person to approve it. Enforced scope limits what the service will accept. Instructions and review decide which permitted actions the assistant should attempt.
Both matter. But when the task only needs reading, I would rather remove write authority than depend on another paragraph asking the assistant not to use it.