Read-only agent providers¶
The experimental provider layer connects the scanner to installed Codex and
Claude Code CLIs without giving either provider authority over scanner truth.
The local CLI binary and remote provider are still trusted process and data
processing dependencies. The current mode is deliberately suggest: an agent receives only content-addressed
copies of declared relevant text files and returns a schema-constrained patch
proposal. It cannot inspect the source workspace, edit files, run shell commands,
use web or MCP tools, change capabilities, or mark an assertion as passed.
The implementation follows the current official documentation for Codex non-interactive mode, Codex sandboxing, Claude Code headless mode, and Claude Code permissions. Provider flags are also covered by golden launch-plan tests so an accidental dangerous flag fails review.
Capability boundary¶
Both launch plans require:
- a content-addressed
prc.agent-task/v0.2task bound to one canonical finding ID and stable fingerprint; - explicit acknowledgement that relevant source may be processed by a remote provider;
- an exact workspace inventory plus bounded, content-addressed task inputs;
- a fail-closed preflight that prevents obvious private keys, distinctive provider-token shapes, and credential-bearing URLs from entering a remote task without logging the matched value;
- a private execution/output directory disjoint from the source workspace;
- denied agent-tool network access and no task secrets;
- schema-constrained, non-interactive output;
- scanner-enforced time and output limits;
- executable and output-schema digests rechecked immediately before execution; and
- filtered process environments that exclude unrelated credentials such as cloud, repository, and deployment tokens.
The scanner can reuse only a login created by prc login codex or prc login claude.
Those commands call the provider's official authentication flow with a private
scanner credential directory. They do not reuse the provider's normal user
configuration, sessions, instructions, plugins, hooks, or MCP servers. Supported
API-key environment variables remain an alternative. Each scan still gets a
new private home; only the selected credential, basic runtime variables, and
scanner-owned overrides reach the process.
Codex runs from the private output directory with ignored user configuration,
strict configuration, ephemeral sessions, the read-only sandbox, approval policy
never, no inherited shell environment, and the default shell tool disabled.
Hosted web search, MCP, multi-agent, goal, remote-plugin, app, browser,
computer-use, and image-generation features are also disabled. Claude Code runs
from the same isolated directory with an empty tool allowlist; Bash, reading,
editing, writing, notebook editing, web tools, slash commands, ambient MCP
servers, project setting sources, and session persistence are disabled. The
source text needed for either provider is inside the sealed task prompt. Claude's
optional provider-side cost limit is passed through; the current Codex CLI
adapter rejects a nonzero cost limit because it cannot enforce one.
These command flags cannot contain a malicious local executable. A replaced or
compromised codex or claude program runs as the current operating-system
user and could ignore every argument before the scanner detects the changed
digest. Install the CLI from its official source, keep it updated, inspect the
resolved path and digest with prc doctor, and use a separate OS account or
strong external sandbox when the host contains secrets the CLI must never see.
The scanner also stops when it can see local Claude managed settings that may
force hooks, plugins, or MCP configuration, but it cannot inspect every policy
delivered by a provider server.
Repository text is always untrusted data, including comments that resemble instructions or the scanner's task delimiter. The scanner JSON-encodes the entire content-addressed task, so delimiter characters inside a source file are escaped and cannot terminate the authoritative envelope. Checked-in adversarial tests reconstruct that envelope and require the hostile fixture to round-trip only as an input value. This is defense in depth: output schema validation, capability denial, patch-path validation, and independent candidate checks remain mandatory even when the prompt boundary is intact. These controls implement the OWASP guidance to identify external content, constrain privileges, validate outputs, and test adversarially.
These controls are not an R2 write sandbox. Neither provider may mutate a
candidate workspace. An agent proposal remains untrusted data. The
scanner-owned remediate-proposal
path can explicitly parse one validated proposal into a fresh isolated
candidate and run deterministic acceptance checks; the provider does not apply
or approve that result. The bounded prc fix --provider path composes those
same two stages only for a scanner-planned missing-test task.
Create and seal a task¶
Start from the checked-in
fixtures/providers/suggest-task.json
example.
For a draft, leave task_id empty, keep arrays sorted, tailor the goal and path
allowlists, and make the remote-processing decision explicit. Seal it without
modifying the draft:
./prc provider seal-task \
--file /path/to/draft-task.json \
--workspace /path/to/project \
--config /path/to/project/production-readiness.yaml \
> /safe/path/task.json
Sealing reads only the sorted relevant_paths, rejects non-regular, binary, or
larger-than-256-KiB inputs, and embeds their text and SHA-256 digests. Total input
text is limited to 768 KiB. Before remote processing, sealing also rejects
high-confidence secret-like input without including the matched material in the
error. This conservative guard is not a comprehensive repository secret scan;
it covers selected high-precision generic and provider credential families
recognized by GitHub's supported secret-scanning pattern
catalog,
and projects should still run a dedicated secret scanner. Sealing also binds the
current workspace inventory digest into the task. The task's finding_id names the exact scan finding that caused
the task to be created; both it and finding_fingerprint are revalidated
against a fresh baseline before a proposal can be applied. For a manual draft,
copy both values from the same canonical finding in a current JSON scan; the
bounded loop does this automatically. When configured, that inventory includes
the declared-scope digest, and the scanner merges its default guards, configured
protected paths, and the in-target configuration path into the sealed task.
Changing a task field, finding binding, configuration declaration, or any
inventoried workspace file invalidates the execution plan.
Inspect a launch plan¶
Create a new output directory outside the workspace, then inspect the exact executable identity, arguments, environment names, schema identity, and capabilities before any provider call:
install -d -m 700 /safe/path/provider-output
export OPENAI_API_KEY='your-provider-key'
./prc provider plan \
--provider codex \
--task /safe/path/task.json \
--workspace /path/to/project \
--output-dir /safe/path/provider-output
Use --provider claude for Claude Code. provider capabilities reports the
static envelope for either adapter.
Run explicitly¶
provider run invokes the installed provider and can consume provider quota or
incur provider charges. It should be called only after the operator reviews the
task's remote-source acknowledgement and launch plan.
./prc provider run \
--provider codex \
--task /safe/path/task.json \
--workspace /path/to/project \
--output-dir /safe/path/provider-output
The scanner writes raw standard output and diagnostics as mode-0600 transcript
files in the output directory, records their byte counts and SHA-256 digests,
and emits a prc.agent-execution/v0.1 record after valid output. A failed
invocation instead returns a content-addressed prc.agent-failure/v0.1 record
with scanner-authored stage and reason codes plus complete or partial transcript
metadata. Treat transcripts as sensitive source-derived evidence.
Validate without running¶
Golden and adversarial provider outputs can be checked independently:
./prc provider validate-output \
--provider codex \
--task fixtures/providers/suggest-task.json \
--file fixtures/providers/valid-output.json
Validation rejects prose-only responses, duplicate JSON keys, task mismatches,
unsorted or duplicate files, changes outside the allowlist, protected paths,
reported command execution, requested capability expansion, trailing JSON, and
oversized output. Claude's outer JSON envelope must contain a non-error
structured_output value that passes the same validation.
After validation, follow the isolated R2 proposal
workflow when the task's assertion is R2. provider run itself never applies a
proposal. The optional bounded R2 loop
constructs and seals its own narrow task, invokes the same read-only provider
protocol, and hands valid output to the isolated scanner-owned application
path. A proposal is never applied to the source workspace.