# Occasio — SOC 2 Control Mapping (DRAFT) **Status.** Draft. Conservative scope. The mappings below are limited to stanzas where the link between the policy and the SOC 2 control is **direct and provable from the audit log** — i.e. every claimed control evidences itself as an actual row, not as a vendor assertion. Mappings that would require interpretive bridging are intentionally absent. Before relying on this document for an audit, have it reviewed by a compliance practitioner familiar with your environment; it is published as a starting point, not a substitute for that review. **Scope.** - Framework: SOC 2 Trust Services Criteria, 2017, Common Criteria series only. - Template: `policy-templates/finance.yml`. - Evidence: rows in `~/.occasio/pipeline-events.jsonl`, verifiable by `occasio audit verify` and the independent walker at [`audit_walker.py`](audit_walker.py). What this document deliberately does not do: - Map ISO 27001, HIPAA, PCI-DSS, FedRAMP, or NIST 800-53. (Single-framework discipline; per-framework mapping is a separate effort.) - Claim coverage of any availability, processing integrity, confidentiality, or privacy criteria beyond what `finance.yml` directly produces evidence for. - Imply that Occasio alone is sufficient for SOC 2 attestation — it is one signal in a control set that includes IAM, endpoint controls, network controls, and HR processes. --- ## CC6.1 — Logical and Physical Access Controls (Restrict) > *"The entity implements logical access security software, infrastructure, and architectures over protected information assets to protect them from security events to meet the entity's objectives."* **Mapped stanza.** `deny_paths` in `finance.yml`. ```yaml deny_paths: - ~/.ssh - ~/.aws - ~/.config/gcloud - ~/.gnupg ``` **Why this maps.** A `deny_paths` entry blocks any read of a path under the listed prefix by an AI agent's tool call, regardless of which agent is calling and regardless of the routing that would otherwise apply. The control point is enforced at the Occasio boundary; the agent receives a `(blocked by policy)` synthetic refusal and the underlying file is never opened. **Evidence in the audit log.** Every blocked attempt produces a row of this exact shape: ```json { "kind": "tool_call", "tool_name": "read_file", "tool_inputs": { "path": "" }, "action": "BLOCK", "reason": "path-denied", "result_kind": "block", "prev_hash": "...", "hash": "..." } ``` A reviewer asks: *"show me every time the agent attempted to read protected credentials in the period."* The answer is `occasio report --days N`'s `blocked_accesses[]` array filtered by `reason: "path-denied"`, with row-level evidence verifiable via the hash chain. **Limitations.** - Coverage is bounded by what is in the `deny_paths` list. A path not listed is not blocked. - A developer with write access to `~/.occasio/policy.yml` can edit the list; that edit produces a `policy_loaded` row in the audit log (under v0.6.6+) carrying the SHA-256 of the new file, so the change is detectable, but it is not prevented. - Concurrent multi-process audit writes are an unmitigated risk in v0.6.5 and v0.6.6 — if two processes are appending to the same `pipeline-events.jsonl`, an interleaved write on Windows can corrupt the chain. Document the single-writer discipline alongside this control. --- ## CC7.2 — System Monitoring (Detection of Security Events) > *"The entity monitors system components and the operation of those components for anomalies that are indicative of malicious acts, natural disasters, and errors affecting the entity's ability to meet its objectives; anomalies are analyzed to determine whether they represent security events."* **Mapped stanza.** The audit log itself, as produced by the Occasio proxy and MCP server. **Why this maps.** Every governed tool call produces an immutable audit row. The hash chain detects post-hoc edits, and the `policy_loaded` synthetic event (v0.6.6+) binds tool-call rows to the specific policy file under which they were decided. A `BLOCK` row with `reason: "secret in tool result: