The toolkit bridges repository/forge content, OCR and its LLM/MCP providers, CI/operator configuration and secrets, context-adapter services, and provider APIs. No input becomes safe merely because a trusted pipeline acquired it. This document distinguishes current controls, qualification limits, and residual risks explicitly.
Current assets include immutable base, source, reviewed-head, protected-target, and release identities; review integrity and coverage; private evidence/context stores, OCR results and transient session material; configuration and receipts; CI, forge, LLM, registry, MCP, and adapter credentials; external-object confidentiality/integrity; provider-declared account classes; immutable context/DLP policy; opaque run-bound handles; projection/completeness state; published packages and provider state.
A merge-request contributor can choose repository paths, blobs, title, description, labels, source branch, discussion bodies, crafted references, and text resembling reviewer/tool instructions. Repository content, externally controlled issue/page text, OCR/LLM/MCP output, adapter/provider responses, inherited process state, and persisted artifacts are untrusted even when the toolkit produced an earlier copy. The model assumes mutable discussions, edits/reordering, automation or service-account impersonation, cross-tenant identifiers, replay, and timing races.
An operator controls CI configuration, direct MCP servers, adapter commands/endpoints, setup, credentials, and the protected-target policy after ordinary protected-branch review. An adapter service controls its authorization implementation and response truth; a direct MCP server additionally controls model-facing tool descriptions, schemas, and responses. A compromised runner or same-owner process is stronger than an ordinary contributor and remains a host-level residual risk.
- Repository paths and immutable Git blobs enter bounded collection and parsing.
- Provider MR identity always enters closed validation. Optional bounded metadata enters normalization in
metadataorenriched; target-derived rules, guidance, decisions, and enriched-context policy come only from the captured protected-target SHA. - In
enriched, the GitLab owner acquires a repeated bounded discussion snapshot. Deterministic recognizers emit syntax-only candidates; the broker combines protected policy with the operator adapter allowlist, and the adapter authorizes one tenant/object/field request before admission. - Normalized retrieval/model/publication/retention projections cross independent DLP decisions and an owner-only atomic context store. A handle is minted only for a committed record and binds run, policy, adapter, tenant, object digest, version, projection, and expiry.
- Typed repository evidence crosses into the compact bootstrap and mandatory read-only evidence MCP. During enriched OCR, only the same built-in process's fixed
context_list/context_getcan read committed local handles; adapter/provider network paths are absent from the model loop. - Persisted evidence, context, results, OCR configuration, private pre-execution status, and receipts re-enter hostile parsing. OCR runs in a fresh isolated home; session, configuration, adapter, and context data crosses deterministic cleanup before a result becomes publishable.
- In the direct-MCP GitLab profile, operator-configured external MCP is remote HTTPS only; developer-local execution may pass explicit stdio command/setup configuration. Server-authored descriptions and schemas cross into plan and main model context; model-generated arguments cross to allowed tools; textual responses cross back to the model and OCR session. This path is separate from M5 adapters.
- The complete successful OCR result crosses independent publication DLP, deterministic GitLab publication, suppression, and receipt-v5 approval policy. On non-zero OCR exit with a valid bounded
ocr.llm-retry-report/v1, only a closed provider-neutral reason may cross into a toolkit-authored failure note; one separate local line may contain closed protocol detail, a shared HTTP status, and bounded aggregate counts from the same strict parser. Raw result fields and stderr remain private, normal findings are ignored, and approval is unreachable. Missing or invalid retry diagnostics use the pre-existing generic failure boundary. - A release candidate crosses protected-base authorization, publication, provenance, and live readback.
- Repository and provider-controlled content remains data. It cannot promote itself into policy, tools, permissions, suppression, posting, approval, or release authority.
- Every security-relevant claim remains bound to immutable identity, provenance, trust class, scope, and semantic applicability.
- Bounds apply while data is acquired and emitted; malformed or exhausted domains degrade visibly without suppressing unrelated evidence.
- Persisted state is accepted only through closed schemas whose related snapshots, indexes, deltas, and receipts agree atomically.
- Model-selected tool arguments receive deterministic authorization and validation in application or server code; names, prompts, and schemas are not resource authorization.
- Direct MCP credentials, tools, and content remain least-privilege and acceptable for both model egress and OCR-session retention.
- Adapter credentials and services enforce tenant/object/operation/field authorization independently; reference syntax and authentication alone never authorize a resource.
- Context policy cannot come from the source branch, context budgets cannot evict repository evidence, and model-facing context cannot add a network, arbitrary ID/URL, search, traversal, or write path.
- Publication and retention are independent from retrieval/model egress. Cleanup uncertainty blocks publication. DLP selects exact posting sinks separately from private OCR metadata. Unsafe sinks produce an explicit safe partial result; unsafe private-only keys/values are removed or replaced before retention without discarding a still-valid manifest or safe findings. Both paths atomically destroy the rejected value/location and expose only closed counts. Publication filtering preserves prior review state, matches repeated fingerprints one-for-one, and blocks approval; private-only sanitization may continue through all existing approval gates only after exact canonical equivalence.
- Classified provider failure reporting consumes only validated retry class, phase, terminal outcome, HTTP status, and schema counters. Provider/model identity, URL, response body, error code or message, request ID, path, warning text, token, and stderr cannot enter the static note or closed numeric line or become a receipt, DLP, telemetry, severity, finding, or approval signal. A status-derived label describes protocol state only; it cannot assert the provider's billing or admission cause.
- OCR semantic-group labels, path-derived group keys, file membership, and round diagnostics are untrusted private data. They may be DLP-sanitized in the owner-only result but cannot enter findings, GitLab text, receipt v5, fingerprints, severity, lifecycle commands, toolkit telemetry, or approval. Receipt extensions fail closed.
- Provider mutations bind reviewed identity where supported; ambiguous inline creates use one author-bound readback without retry, and unresolved ambiguity preserves prior state.
- Secrets remain outside repository-controlled context, public notes, fixtures, and release artifacts.
BL-011 used the production ocr-ci review path, official checksum-verified OCR 1.9.5, a disposable synthetic Git repository, owner-only isolated homes, a local OpenAI-compatible peer, and a real stdio MCP protocol peer. The qualification observed, rather than inferred from mocks:
- toolkit configuration allowed only
tenant_readandtenant_fail; the peer also advertisedhidden_writeand reservedfile_read, but neither appeared as an external model tool, while toolkit reserved names and cross-server collisions failed before OCR; - a synthetic cross-tenant identifier generated by the model was forwarded unchanged to the allowed tool: the allowlist authorized only the tool name, not its arguments or object;
- hostile server-authored description and schema markers entered the plan prompt and main tool definitions;
- the same external tools appeared in plan and main phases; OCR has no toolkit-controlled phase-specific external policy;
- a bounded high-volume text result was returned completely to the next model request, with no toolkit-configurable response byte/character boundary;
- OCR's mode-0600 session JSONL retained prompts, responses, external arguments, tool results, and both
plan_taskandmain_taskrecords; - an MCP
isErrorresult remained model-visible and review continued; an unavailable optional server emitted a warning, review continued with mandatory evidence, and the receipt omitted the unavailable server; _ocr_toolkit.mcp_usagecounted one mandatory evidence call and two known external calls, but contained no arguments, results, completeness, content-safety, or model-correctness proof.
These observations establish the current safe-use envelope, not the safety of arbitrary direct providers. Generic external MCP is privileged operator-configured composition, not author-triggered reference resolution. Each direct server must independently authorize tenant, canonical object, fields, and operation for every request. Use only reviewed narrow read-only tools, dedicated least-privilege credentials, bounded responses, and content permissible to send to the LLM and retain in OCR sessions. Do not expose generic search, arbitrary URL/ID fetch, traversal, writes, or broad service credentials to merge-request-selected references. Every direct tool must be safe in both OCR phases.
Server command, endpoint, setup, schemas, descriptions, arguments, and responses cross separate executable or untrusted boundaries. GitLab-MR external entries are remote HTTPS only and reject stdio/setup fields; local-profile setup remains operator-owned shell configuration executed in the analyzed repository. Existing OCR MCP configuration is hostile persisted input and is revalidated against the active profile. Raw endpoint, setup, and credential values are not safe diagnostic material. Toolkit diagnostics avoid them, but OCR may emit operator-owned transport details; keep those values non-sensitive and retain OCR stderr privately. Managed OAuth authenticates a client but does not authorize an object and remains conditional.
Receipt v5 records the bounded configured capability inventory and positive calls attributed from known tool names. For the fixed evidence MCP only, an owner-only atomic count receipt can further attribute completed summary, list, and get actions after exact reconciliation with OCR's tool total; absence, malformed content, races, or mismatch remains unavailable and does not weaken the mandatory-summary gate or add an approval blocker. The receipt contains no arguments, IDs, paths, results, or provider content. Its publication state is exactly passed, private-sanitized, or publication-filtered; only a byte-equivalent canonical publication/approval projection permits private sanitization to continue through the existing approval gates. Every direct external MCP makes the review comment-only, independent of use; server-authored tool annotations are not used to upgrade that policy. The receipt is operational attribution, not security evidence for resource authorization, completeness, response safety, actual semantic use, or model output. Security severity still depends on demonstrated reachability and impact: prompt-like or Markdown text is not code execution without a privileged action path, and same-owner artifact tampering is not an ordinary-contributor escalation without a lower-privilege writer.
- Repository reads are bounded, rooted, symlink-aware, immutable-object reads that exclude common dependency/build trees and never execute repository content.
- Review context uses a closed
off|metadata|enrichedselector.offretains only validated source/protected-target/author identities;metadataadmits bounded MR fields;enrichedrequires the immutable protected policy and admits only stable bounded discussion/adapter projections. Source policy, unknown fields/classes, raw display identities, arbitrary URLs/IDs, tokens, and ambient environment values cannot expand it. - Generated Markdown neutralizes controls and GitLab quick actions. Actionable suggestions require exact reviewed-head proof; unverifiable replacements retain prose only.
- Result and provider reads have byte limits; notes enforce character and UTF-8 byte limits. Position-bearing inline creates reserve independent unguessable markers, classify closed outcomes, and perform at most one complete author-bound reconciliation read with no retry.
- Publication DLP admits ordinary horizontal tab only in
existing_codeandsuggestion_code. The unchanged code value is retained only after it passes the existing size, secret, PII, forbidden-value, and laundering checks; tabs elsewhere and vertical tab, form feed, NUL, bidi/format controls, and other unsupported controls remain blocking. - LLM provider URLs are normalized by one forge-neutral owner shared by configuration and preflight; only credential-free absolute HTTPS roots or protocol-compatible terminal endpoints are accepted. Explicit protocol remains authoritative, and ambiguous auxiliary URL derivation fails closed.
- Automatic approval binds the exact synchronized reviewed head and MR author from receipt v5, skips self-approval, and never removes an existing approval. Partial, warning, non-v5, publication-filtered, omitted, degraded metadata, DLP-rejected selected-source, required context degradation, admitted remediation-context, or direct external-MCP runs are ineligible.
- Human replies are ownership boundaries. Merge-request source SHA, protected-target policy SHA, and merge-result SHA remain distinct.
- The evidence engine stores recursively redacted typed facts/deltas in owner-only files and serves a closed bounded network-independent MCP. Absence supports a negative claim only for applicable complete scope.
- OCR rules, decisions, and guidance come only from captured protected-target blobs; source changes cannot create policy. Full guidance remains untrusted context, not authority.
- External finding images are disabled by default and, when enabled, use closed normalized display metadata only.
- Finding fingerprints, independent write markers, complete pre-run snapshots, and explicit immutable current-run identities constrain repeated runs. Rollback deletes only recorded IDs absent from the baseline; secrets and credential-shaped values are redacted before operational output.
The evidence engine reads exact base/head Git objects without checkout, refuses symlinks and submodules, and separates collection, normalization, owner-only atomic persistence, hostile readback, and bounded MCP projection. Snapshot indexes must agree with admitted records; deltas are recursively re-redacted and re-bounded before IDs or responses are derived; colliding facts retain source paths; normalized mapping-key collisions fail instead of dropping a value.
Repository-owned OCR rules, accepted decisions, and root or nested AGENTS.md/CLAUDE.md guidance come only from immutable target blobs at the captured protected-target SHA while code deltas retain the forge diff base. Guidance changed on either side is excluded; source/head content never becomes policy evidence; applicable guidance is selected before blob reads and domain-isolated admission. Structured policy values are bounded before storage, after redaction, and on hostile load. Schema-v4 binds policy provenance to the policy snapshot and applicability to atomic base/head changed paths; older schemas retain explicit historical semantics. The bootstrap contains only bounded refs, counts, coverage, summaries, paths, diagnostics, and MCP instructions; full text remains untrusted evidence in the private store.
Ansible Galaxy requirements use the same immutable-object boundary. Relative includes resolve only to YAML blobs inside the authenticated tree; absolute, home-relative, escaping, symlink, and submodule targets fail. Independent depth, file, edge, item, and diagnostic limits keep degradation visible and bounded.
The v0.7.0 implementation extended the v0.6.3 selector/approval foundation with protected policy, stable GitLab discussions, deterministic recognizers, provider-neutral adapters, a separate context store, opaque handles, fixed context tools, publication DLP, isolated OCR sessions, and receipt v4. Receipt v5 now supersedes that result/posting contract without changing the established M5 acquisition boundary. The public bounded review-context contract, engineering M5 checkpoint, and test-evidence matrix distinguish deterministic production-owner evidence from the final real-OCR and stable-release gates.
- A forge adapter acquires bounded discussion snapshots and reference-bearing text before OCR.
- An immutable policy read only from the captured protected-target SHA decides admitted fields, provider-declared author classes, origins, tenant/resource classes, projections, budgets, and retention. An explicitly selected
enrichedrun fails preflight when that policy is missing or invalid;offandmetadatado not read it. - A deterministic recognizer emits a candidate; reference presence is never authorization.
- An adapter performs object-level authorization and bounded version-aware retrieval.
- Allowed projections are normalized, DLP-filtered, and atomically stored in a run-local context store.
- Only successful storage mints an opaque unguessable run-bound handle binding adapter, tenant, canonical object, projection, version/ETag or digest, policy version, expiry, and run without exposing the upstream identifier.
- During OCR, the model may list/read only minted handles through fixed toolkit-authored closed-schema
context_listandcontext_gettools in the existing toolkit MCP process. No upstream search, arbitrary ID/URL, external schema, redirect, traversal, write, or external network access exists in the model loop. - After OCR, deterministic publication validation/DLP and retention are decided independently.
Policy v2 adds optional toolkit-owned remediation threads without changing these transitions. GitLab title, description, generic discussion bodies, remediation roots and replies, and adapter/reference text all cross normalization, closed bounds, and DLP before private-store or bootstrap admission. One remediation thread is admitted only as a verified root-to-replies relation from the same stable provider snapshot; it is not reconstructed from matching prose, paths, timestamps, or provider identifiers.
- Forge adapters own discussion snapshots and provider-declared account classes.
- The protected-policy loader owns admission and independent retrieval, model-egress, publication, and retention decisions.
- Recognizers own syntax only; adapters own resource authorization and bounded acquisition; the context store owns atomic records and handle binding.
- The existing toolkit MCP owns fixed context projections; OCR owns the model loop and session persistence; toolkit review/post owners enforce cleanup, approval ineligibility, and publication validation.
- BOLA/confused deputy and credential mismatch: authorize exact tenant, object, operation, and fields in adapter/application code; use dedicated AI-readable corpora and least-privilege credentials. Host allowlisting and successful authentication are insufficient.
- Prompt and indirect injection/data poisoning: keep external text non-authoritative; use fixed toolkit schemas; prevent content from changing tools, policy, permissions, lifecycle commands, suppression, posting, or approval.
- SSRF/traversal/broad agency: recognizers cannot fetch; the broker accepts only minted handles; generic search, URL fetch, redirects, recursion, and writes are absent.
- Oversize/denial of wallet/selective omission: enforce acquisition-time character, UTF-8 byte, record, thread, age, count, time, and aggregate budgets independent of repository evidence; expose partial, mutated, unavailable, and omitted states.
- Identity spoofing and privacy: represent authors by provider-declared class plus run-local pseudonym; omit name, email, avatar, and profile URL. Unknown classification fails closed.
- TOCTOU/replay/cache poisoning: bind snapshots and handles to version/ETag or digest, policy version, run, and expiry; use atomic store commits and uniform unavailable outcomes.
- PII/secret bypass and output laundering: make retrieval, model egress, publication sinks, and private retention separate decisions. Minimize before model egress; publication DLP cannot undo prior disclosure. Scan decoded source and rendered forms so HTML comments/entities/tags, Markdown inline/reference/autolink destinations, controls, and Unicode formatting cannot hide exact protected values. Sink uncertainty yields a safe partial result; private-only uncertainty yields an atomically sanitized valid result or falls back to that partial form. Expose only low-cardinality receipt/summary/log signals in ordinary runs. The explicit local-only diagnostic switch retains owner-only raw private state, emits no receipt, and is rejected by the validated GitLab MR profile.
- Upstream session and telemetry privacy: OCR 1.11.0 can retain readable reasoning, signed/encrypted provider-native replay payloads, tool arguments/results, sorted changed paths, model-produced group labels, and round counters in its owner-only session. Telemetry may also carry repository-derived high-cardinality grouping values. OCR telemetry is disabled by default, and the toolkit removes the isolated session home before publication. Enabling an exporter authorizes it to receive the fields OCR emits; content-logging disablement is not a promise that every identifier disappears. The toolkit neither ingests nor duplicates these values.
- OCR advisory spoofing: raw OCR output cannot supply
_ocr_toolkit_advisory. The toolkit adds only the exact numericocr.toolkit-advisory/v1shape after publication DLP; posting requires a valid receipt v5 and rejects duplicate, extended, unknown, or malformed forms. The advisory cannot change warnings, coverage, DLP, telemetry, findings, or approval. - Local executable and subprocess confused deputy: accept the OCR executable only through absolute search-path entries and reject resolution inside the reviewed repository. Stdio adapter deadlines cover request delivery as well as response/exit, use a clean environment and isolated directory, and terminate the process group on uncertainty.
- Marker and retry spoofing: recognize ownership, run-summary, and setup identities only in the exact toolkit preamble. Repository/provider text containing marker lookalikes has no authority. Duplicate findings consume visible prior occurrences one-for-one; successful setup retries replace only previous toolkit setup-pending notes.
- Approval/suppression manipulation: every admitted remediation thread blocks automatic approval; partial, required-degraded, or DLP-rejected context cannot prove absence or restore eligibility. Safe MR metadata, generic discussions, and adapter records do not themselves block an otherwise eligible receipt. Remediation text cannot change severity, prove a fix, suppress or resolve a finding, issue a lifecycle command, or authorize approval. Existing suppression, exact slash/mention commands, fingerprints, and discussion ownership remain separate deterministic consumers.
- Persistent-session leakage: run OCR under an isolated owner-only home and clean session artifacts deterministically. Containment/cleanup failure blocks publication. The explicit local diagnostic exception preserves owner-only paths and produces no posting-eligible receipt; the authoritative GitLab MR profile rejects it before OCR execution.
- Second-review-engine drift: OCR remains the sole review engine. If contextual adjudication needs a separate model phase, depend on a native structured OCR API rather than merge two toolkit-driven reviews.
Operators must maintain a dedicated AI-readable corpus and credentials whose service identity cannot read broader material than policy permits. Provider account classifications may be incomplete; unknowns remain unavailable. Model inference after egress cannot be reversed. OCR version/capability dependencies must be exact and fail closed. Even with bounds and DLP, admitted untrusted text can influence model judgment; deterministic publication checks contain authority but do not make model reasoning deterministic. A host-level compromise can read same-owner artifacts. These residuals remain visible in deployment guidance and qualification claims.
Use a dedicated bot and least-privilege forge token. Protect/mask credentials; do not expose secrets to untrusted forks. Begin with manual execution for trusted contributors and enable posting/approval only after accepting this threat model. OCR_AUTO_APPROVE=false keeps the bot comment-only; forge approval rules and protected branches remain authoritative.
Pin the exact recommended Open Code Review release from the compatibility manifest and verify its checksum. Pin Python and Actions dependencies. Follow the M3 direct-MCP safe-use envelope above and the exact public configuration and bounded-context contracts.
Optional remote finding images add a third-party rendering boundary. External finding images are disabled by default. Finding badges are presentation-only; keep text mode when a third-party image request is unacceptable. Enabling Shields.io does not send finding prose, repository paths, project identifiers, or arbitrary OCR metadata in the image URL, but ordinary viewer, proxy, and network metadata can reach that service. Toolkit-owned Git ignores process/global/system/repository/object-store overrides and replacement refs. Existing OCR configuration is hostile persisted input and is descriptor-read, regular-file and single-link checked, byte-bounded, JSON-object validated, and atomically replaced with owner-only permissions before/after update.
The OCR compatibility policy requires double-source asset digest verification, bounded downloads, executed contract probes, and protected PR/release gates; automation never promotes ambiguous releases or writes directly to main. Remote MCP is HTTPS-only, forbids userinfo/fragments, and keeps secret headers environment-backed.
Release authorization executes from protected-base code predating the candidate. GitHub reads use a closed endpoint allowlist, HTTPS-only redirect policy, redirect-safe authentication, bounded transfer, atomic replacement, and exact receipt schemas. Bandit scans runtime source at medium-or-higher severity/confidence; narrow # nosec B108 temporary-path suppressions require adjacent containment rationale.
The M3 direct boundary and M5 broker controls align with MCP authorization and MCP security best practices; OAuth resource indicators and best current practice in RFC 8707, RFC 9700, and RFC 9728; OWASP GenAI LLM risks and Agentic risks; OWASP API BOLA; and OWASP SSRF guidance. These references inform controls but do not themselves prove the implementation.
Protected main requires pull requests, signed commits, a current branch, resolved review threads, and required CI/security/build checks. One maintainer cannot provide independent human approval for maintainer-authored changes; automated review does not replace a second human. OpenSSF findings are posture signals, not vulnerabilities by themselves. The reporting process is in SECURITY.md.