# Agent exercise solution notes

Kevin O'Connor. Version 1.0, September 9, 2026. Facilitator copy.

Run the downloaded scripts from their directory. The reference run passes 14 document-policy test methods, 8 retrieval methods and 8 credential methods. These are method counts, not independent attacks or a coverage percentage. Use the original source and [reference README](https://www.tkoresearch.com/examples/agent-security-review/README.md) for the complete fixture boundary.

## Exact-edit approval

```sh
python3 document_permissions.py
python3 -m unittest -v document_permissions.PolicyTests.test_altered_body document_permissions.PolicyTests.test_reviewer_revocation document_permissions.PolicyTests.test_stale_document
```

| Method | Expected result and teaching point |
| --- | --- |
| `test_read` | Maya reads a permitted Amber document. A negative-only exercise would miss this necessary utility check. |
| `test_approved_write` | The intended write succeeds with the trusted reviewer's approval. |
| `test_cross_tenant_read_and_write` | Both access paths deny the other tenant. |
| `test_no_approval` | The write is denied and the original document body remains. |
| `test_altered_body` | An approval for one body cannot authorize a changed body; document state remains unchanged. |
| `test_unknown_fields_and_types` | Unexpected arguments and wrong types are rejected before a write. |
| `test_expired_approval`, `test_replay` | Expiry and one-use consumption prevent stale/repeated authorization. |
| `test_acl_revocation`, `test_subject_revocation`, `test_reviewer_revocation` | The current permissions still matter after an approval was issued. |
| `test_self_approval_denied` | The actor cannot supply their own reviewer approval in this fixture. |
| `test_stale_document` | A changed document version blocks the old edit. |
| `test_disable` | The stop flag prevents further access through this fixture path. |

The original negative test methods assert denial; several do not separately assert the post-call document body. Run this supplemental check from the directory holding `document_permissions.py` to check the unchanged document and unused approval explicitly:

```python
from copy import deepcopy
from document_permissions import Documents, Denied

for altered in (False, True):
    store = Documents()
    actor = {"user": "maya", "tenant": "amber"}
    reviewer = {"user": "lee", "tenant": "amber"}
    request = {"action": "write", "document_id": "DOC-001",
               "body": "Reviewed change", "expected_version": 1}
    approval = store.approve(reviewer, actor, request, 100) if altered else None
    before = deepcopy(store.docs)
    if altered:
        request["body"] = "Unreviewed change"
    try:
        store.execute(actor, request, 101, approval)
    except Denied:
        pass
    else:
        raise AssertionError("Expected denial")
    assert store.docs == before
    if approval:
        assert store.approvals[approval]["used"] is False
print("Missing and altered approval: denied without document mutation")
```

Expected permission reasoning: bind approval to the actor, document, tenant, action, body and version; check the current subject/reviewer policy before the effect. The approval digest binds bytes in this implementation. It does not authenticate the reviewer. The fixture trusts identity inputs and is single-threaded; durable atomic approval consumption, concurrent writers and downstream retries remain untested.

A useful participant acceptance criterion for a real service is: when two workers submit the same approved change concurrently, at most one intended effect occurs and both attempts receive correlated outcomes. That is a proposed test, not a result from these scripts.

## Retrieval and citations

```sh
python3 rag_boundaries.py
python3 -m unittest -v rag_boundaries.RetrievalTests.test_revoked_acl_after_search rag_boundaries.RetrievalTests.test_citation_leakage
```

Amber's literal `Calibration` search returns `A-1` and `A-2`; Birch's returns `B-1`. Searching for the other tenant's marker returns an empty candidate list. A forged Birch candidate is also removed at Amber's context boundary. The second check matters because candidates can come from somewhere other than this search function.

`test_revoked_acl_after_search` expects both context and citations to be empty after readers are cleared. `test_citation_leakage` expects only Amber citation metadata: the Birch title and URL must be absent. `test_stale_version` and `test_deleted_document` remove an otherwise previously allowed candidate from context.

`test_hostile_instruction_remains_data` confirms an `untrusted_source` label and an unavailable send action. It does not run a model or demonstrate that a model would ignore the instruction. No vector index, embeddings, reranker, answer cache or external export path is implemented. A participant who claims prompt-injection resistance from this result should revise the claim to the specific checks observed.

## Credential lifecycle and stop discussion

```sh
python3 credential_lifecycle.py
python3 -m unittest -v credential_lifecycle.CredentialTests.test_revoked_denial_before_downstream credential_lifecycle.CredentialTests.test_rotation_invalidates_old_grant
```

The valid fixture request returns status 200. After revocation it returns 401 and the downstream-call list stays at one entry. Replacement-grant acceptance is paired with continued denial of the old grant. Wrong audience, expired and unknown handles return 401; wrong tenant and missing write scope return 403. Handles are explicit non-secrets and error output omits them.

The program is an in-memory reference monitor, not OAuth or MCP. It does not verify signatures, discover an issuer, contact a provider, revoke a real token or stop an in-flight operation. For the tabletop, expect separate owners for application connector disablement, identity/provider changes, queued-job handling and confirmation of downstream behavior. A credible answer leaves a measurable revocation target to be tested against the actual provider and deployment.

## Example final decision

“Proceed to implementation testing for read/draft access on synthetic Amber documents. Keep send, delete, share and execute unavailable. The application owner must demonstrate current source ACL checks on cached answers and exports; the platform owner must demonstrate atomic approval consumption; the identity owner must measure provider revocation and in-flight behavior. Review those results before any expansion to client data.”

This is a proposed decision for the teaching scenario. It neither authorizes production access nor reports that those additional checks passed. Assess each participant's own rationale with the worksheet rubric.
