mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 18:39:33 +01:00
fix: say what Allow network access really opens (release review)
The setting told users the sandbox and approvals stay on. With it on, a command in Codex's sandbox can call Codeman's API, which can start a session outside the sandbox with no approval prompt. Measured on codex 0.162.0 with `codex sandbox`: a touch the sandbox refused landed through POST /api/sessions, and from inside a Codeman pane it landed with a password set too, because a pane inherits CODEMAN_PASSWORD. Without that password the same call gets 401. The settings row, the wiki, the skill and the changeset now say so, the wiki Security page and security-architecture name it, and the docs scope the claims to what the code does: app-wide, local launches only, not cron jobs, and --no-daemon is a local launch's flag. Agent-CLIs gains the --no-daemon trade-off. A test pins that no surface sells the sandbox as still in charge. Text only: the switch, the clamp and the launch are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
131a12d195
commit
353fe7ebcf
11 files changed
+90
-35
No files matched your search
@@ -2,4 +2,4 @@
|
||||
"aicodeman": minor
|
||||
---
|
||||
|
||||
**Codex sessions can drive other sessions through the Codeman skill.** A new opt-in Codex setting, **Allow network access** (App Settings, Codex, off by default), starts Codex with `sandbox_workspace_write.network_access=true`, so the skill's calls to this server are no longer blocked by Codex's sandbox; the sandbox and approval prompts stay on, and it is forced off for users without the privileged-commands grant in multi-user mode. It applies where Codex runs the folder workspace-write and does not change Codex's sandbox mode: a folder Codex runs read-only keeps the network closed and asks per command. Codex 0.162 and later now also starts with `--no-daemon`, so each session runs its own Codex app server: a shared background server (another version's included) no longer stops a launch on an "incompatible feature settings" dialog, and commands inside Codex see their own session's `CODEMAN_*` variables. Version gates in the CLI registry are now measured against that CLI's own version instead of Claude's. The skill falls back to its `curl` path when `codeman` is not on PATH, and asks Codex to re-run a blocked call with approval instead of giving up.
|
||||
**Codex sessions can drive other sessions through the Codeman skill.** A new opt-in Codex setting, **Allow network access** (App Settings, Codex, off by default), starts every new local Codex session with `sandbox_workspace_write.network_access=true`, so the skill's calls to this server are no longer blocked by Codex's sandbox. It has a cost: commands in Codex's sandbox can then call Codeman's API, which can start a session outside the sandbox with no approval prompt, even when a password is set, so the sandbox no longer contains a prompt-injected command. It is forced off for users without the privileged-commands grant in multi-user mode, and scheduled jobs do not use it. It applies where Codex runs the folder workspace-write and does not change Codex's sandbox mode: a folder Codex runs read-only keeps the network closed and asks per command. A local Codex 0.162 or later now also starts with `--no-daemon`, so each session runs its own Codex app server: a shared background server (another version's included) no longer stops a launch on an "incompatible feature settings" dialog, and commands inside Codex see their own session's `CODEMAN_*` variables. Remote and Docker launches do not get the flag. Version gates in the CLI registry are now measured against that CLI's own version instead of Claude's. The skill falls back to its `curl` path when `codeman` is not on PATH, and asks Codex to re-run a blocked call with approval instead of giving up.
|
||||
@@ -558,7 +558,7 @@ Full feature guide: [`docker-cases.md`](docker-cases.md).
|
||||
- **It strictly improves network posture.** It removes the single shared `CODEMAN_PASSWORD` and gives each person a revocable credential; a non‑loopback bind and the tunnel‑enable guard are satisfied by "multi‑user with ≥1 enabled user" without a shared password.
|
||||
- **Auth is a parallel branch** (`middleware/auth.ts`) that leaves the single‑user path untouched: per‑user scrypt verify (`timingSafeEqual`, timing‑equalized against user enumeration), identity‑carrying cookies, a per‑username failure bucket (a botnet can't brute one account across IPs; one NATed user can't lock out the rest), and a `mustChangePassword` lockbox. The hook‑secret loopback bypass, host guard, and Origin/CSRF guard are unchanged (hooks authenticate the INSTANCE, not a user).
|
||||
- **Ownership is enforced server‑side only** and fails closed: `req.authUser` (a synthetic admin in single‑user), `findSessionOrFail` returns NOT_FOUND (never 403) for a foreign session, list/SSE/WS/file‑preview/search all filter by `session.owner`, and SSE routing defaults session‑scoped events to their owner (unresolved owner → withheld). The load‑bearing rule is **non‑admin `workingDir` confinement**: a non‑admin's session/one‑shot working dir must realpath‑resolve inside `~/codeman-users/<name>/cases`, checked BEFORE any disk write.
|
||||
- **Privileged actions are a one‑bit grant** (`canBypassPermissions`, default off): only granted users (and admins) get `--dangerously-skip-permissions` (others are silently downgraded to `--permission-mode auto`), shell‑mode sessions, cron `launchCommand`, and other CLIs' bypass flags. Machine‑level resources (remote/Docker host definitions, tunnel, self‑update, settings writes) are admin‑only.
|
||||
- **Privileged actions are a one‑bit grant** (`canBypassPermissions`, default off): only granted users (and admins) get `--dangerously-skip-permissions` (others are silently downgraded to `--permission-mode auto`), shell‑mode sessions, cron `launchCommand`, other CLIs' bypass flags, and Codex's **Allow network access** (`networkAccess`, forced off for a user without the grant, since it lets a command in Codex's sandbox call Codeman's API and start a session outside that sandbox). Machine‑level resources (remote/Docker host definitions, tunnel, self‑update, settings writes) are admin‑only.
|
||||
- **Clone Repo does not lend the server's git sign-in to non-admins.** A clone writes only inside the caller's own case space, so it is not admin-gated, but the server account's git credential helpers (the Docker image's opt-in `gh`/`az` helpers, or any `gh auth setup-git`) are shared by every user. A non-admin's clone and preflight therefore run with `git -c credential.helper=`, which empties the helper list including the URL-scoped entries (`cloneWithoutCredentialHelpers` in `case-routes.ts`, argv pinned in `test/git-clone.test.ts`). This closes the Clone Repo path only: the account's SSH keys still apply to an `ssh://` URL, and a non-admin's agent sessions run as the same account, consistent with the first bullet above. Docker cases are a second route to the same sign-in: with `CODEMAN_AGENT_IMAGE_INSTALL_GH`/`_AZ` on, a non-admin's Docker case with credential seeding on (the default) receives a copy of the server account's `gh`/`az` sign-in, exactly as it receives the Claude and Codex credentials.
|
||||
- **Admin actions are audited** append‑only to `~/.codeman/admin-audit.jsonl` (acting admin, action, target, IP). Passwords set by an admin create/reset are one‑time (returned once, force change). Under Basic auth, `logout` only truly ends QR‑issued sessions — to lock someone out, disable the account or reset the password (a proper login form is a deferred Phase 6).
|
||||
|
||||
|
||||
+13
-2
@@ -143,10 +143,21 @@ Changing a default affects new sessions and does not edit Codex configuration fi
|
||||
**Allow network access** reaches the same local launches. It opens the network only where
|
||||
Codex runs the folder in its workspace-write sandbox and does not change Codex's sandbox
|
||||
mode: a folder Codex runs read-only keeps the network closed, and Codex asks per command.
|
||||
See [The Codeman Skill](Codeman-Skill#when-the-agent-is-codex).
|
||||
With it on, commands in the sandbox can call Codeman's API, which can start a session
|
||||
outside the sandbox with no approval prompt, even when a password is set. See
|
||||
[The Codeman Skill](Codeman-Skill#when-the-agent-is-codex) and
|
||||
[Security](Security#what-codeman-does-not-protect-against).
|
||||
|
||||
Two behaviours that are deliberate and worth knowing:
|
||||
Behaviours that are deliberate and worth knowing:
|
||||
|
||||
- **Each local session runs its own app server.** Codex 0.162 and later otherwise hands
|
||||
every client's commands to one shared background server, which runs them with the
|
||||
environment of whichever session started it, and a server left by another Codex version
|
||||
stops every launch on a dialog. So Codeman starts a local Codex 0.162 or later with
|
||||
`--no-daemon`. The trade-off, going by Codex's own help text: `codex agents` browses the
|
||||
sessions on that shared server and `codex remote-control` manages it, so do not expect
|
||||
either to see a session started this way. Remote and Docker launches do not get the
|
||||
flag, and neither does a local launch whose Codex version Codeman could not read.
|
||||
- **Predictive echo instead of buffered echo.** Codex's composer reacts to every keystroke,
|
||||
a `/` opens a live-filtering picker, arrows edit server-side state. Buffering keystrokes
|
||||
until Enter starved it, so Codex paints each keystroke at the predicted cell while the
|
||||
|
||||
+16
-11
@@ -140,20 +140,25 @@ message a worker directly instead of typing into its terminal.
|
||||
|
||||
Two things to know:
|
||||
|
||||
- Codex's default sandbox blocks network access, even to this machine, so turn on
|
||||
**Allow network access** (App Settings, Codex) for a Codex session that should start and
|
||||
drive others. It applies to Codex sessions started after you switch it on, and it keeps
|
||||
Codex's sandbox and approval prompts. It opens the network only where Codex runs the
|
||||
folder in its workspace-write sandbox, which is what Codex does for a project you
|
||||
trusted. It does not change Codex's sandbox mode: a folder Codex runs read-only (a new
|
||||
case that is not a git repository, for one) keeps the network closed, and Codex asks you
|
||||
to approve each call to Codeman. Trust the project in Codex, or use `/permissions` in
|
||||
- Codex's default sandbox blocks network access, even to this machine, so a Codex session
|
||||
needs **Allow network access** (App Settings, Codex) before it can start and drive
|
||||
others. The setting is app-wide: it applies to every local Codex session started after
|
||||
you switch it on, not only the ones you mean to orchestrate. It also has a cost: commands
|
||||
in Codex's sandbox can then call Codeman's API, which can start a session outside the
|
||||
sandbox with no approval prompt, even when a password is set, so the sandbox no longer
|
||||
contains a prompt-injected command. See
|
||||
[Security](Security#what-codeman-does-not-protect-against). It opens the network only
|
||||
where Codex runs the folder in its workspace-write sandbox, which is what Codex does for
|
||||
a project you trusted. It does not change Codex's sandbox mode: a folder Codex runs
|
||||
read-only (a new case that is not a git repository, for one) keeps the network closed,
|
||||
and Codex asks you to approve each call to Codeman. Trust the project in Codex, or use `/permissions` in
|
||||
Codex for that session, to get workspace-write there. **Bypass approvals and sandbox**
|
||||
works everywhere, but removes the sandbox and the approval prompts.
|
||||
- Codex 0.162 runs its commands in a shared background process, whose Codeman variables can
|
||||
belong to the session that started it. Codeman starts Codex 0.162 and later with
|
||||
`--no-daemon`, so each session runs its own; with an older Codex the skill notices and
|
||||
says so instead of acting.
|
||||
belong to the session that started it. Codeman starts a local Codex 0.162 or later with
|
||||
`--no-daemon`, so each session runs its own. A remote or Docker session does not get the
|
||||
flag, and neither does a local one whose Codex version Codeman could not read; there,
|
||||
and with an older Codex, the skill notices and says so instead of acting.
|
||||
|
||||
OpenCode and DeepSeek Harness need nothing extra: in their default setup neither keeps its
|
||||
shell commands off the network, so the skill works there as soon as it is injected.
|
||||
|
||||
@@ -99,6 +99,12 @@ Stated plainly, because a security page that only lists strengths is not useful:
|
||||
bypass requires a secret unconditionally when auth is on.
|
||||
- **The agent CLIs have their own trust models.** Pi's project trust executes repo-local
|
||||
TypeScript, for instance. See [Agent CLIs](Agent-CLIs).
|
||||
- **Allow network access takes Codex's sandbox out of the picture.** The setting (App
|
||||
Settings, Codex, off by default) lets commands in Codex's sandbox call Codeman's API,
|
||||
which can start a session outside the sandbox with no approval prompt. A password does
|
||||
not stop this, because a session inherits the server's password. It applies to every new
|
||||
local Codex session, so a prompt-injected command in any of them can run anything you
|
||||
can. Leave it off unless you trust what those sessions run.
|
||||
|
||||
## Privacy
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ instead of its native cloud backend. See [Custom Model Endpoints](Custom-Model-E
|
||||
| Default Codex model | Model for new local Codex sessions; empty uses Codex's own config. Letters, digits, `.` `_` `-` `/` only. |
|
||||
| Default Codex reasoning effort | Reasoning level for new local Codex sessions; empty uses Codex's own config. |
|
||||
| Bypass approvals and sandbox | Starts new Codex sessions with `--dangerously-bypass-approvals-and-sandbox`. Read [Agent CLIs](Agent-CLIs) before enabling. |
|
||||
| Allow network access | Starts new local Codex sessions with `sandbox_workspace_write.network_access=true`, so commands in Codex's workspace-write sandbox can use the network. The [Codeman Skill](Codeman-Skill) needs it to reach this server from a Codex session. It applies where Codex runs the folder in workspace-write (a project you trusted in Codex) and does not change Codex's sandbox mode: a folder Codex runs read-only keeps the network closed, and Codex asks for approval per command. The sandbox and approval prompts stay on. Synced, off by default; forced off for users without the privileged-commands grant in multi-user mode. |
|
||||
| Allow network access | Starts new local Codex sessions (Run menu, Resume and the API; not scheduled jobs) with `sandbox_workspace_write.network_access=true`, so commands in Codex's workspace-write sandbox can use the network. The [Codeman Skill](Codeman-Skill) needs it to reach this server from a Codex session. It applies to every such session, and those commands can then call Codeman's API, which can start a session outside the sandbox with no approval prompt, even when a password is set: see [Security](Security#what-codeman-does-not-protect-against). It applies where Codex runs the folder in workspace-write (a project you trusted in Codex) and does not change Codex's sandbox mode: a folder Codex runs read-only keeps the network closed, and Codex asks for approval per command. Synced, off by default; forced off for users without the privileged-commands grant in multi-user mode. |
|
||||
| Animated status effects | Cosmetic. |
|
||||
| MCP server sync | Copies the MCP servers each installed, enabled CLI (Claude, Codex, Gemini, OpenCode, Antigravity) and GitHub Copilot CLI has into the others' own config files. Synced, off by default, admin only in multi-user mode. Turn it on and **Apply** or **Save** (Apply keeps Settings open), then **Preview** shows what would change and **Sync now** applies it. It only adds missing servers, keeps the previous file as `.codeman-bak`, and leaves a file that receives env values or headers readable by you only. A config dir moved by `CODEX_HOME`, `CLAUDE_CONFIG_DIR`, `XDG_CONFIG_HOME`, `GEMINI_CLI_HOME` or `COPILOT_HOME` in Codeman's own environment is followed. |
|
||||
|
||||
|
||||
@@ -77,17 +77,20 @@ retrying.
|
||||
can ask the user to run a command outside its sandbox (Codex can), re-run **the same
|
||||
command** once that way, never a different one; if the user declines, stop. For lasting
|
||||
access in Codex, the user turns on **Allow network access** (Codeman App Settings,
|
||||
Codex) and starts a new Codex session: it opens the network wherever Codex runs the
|
||||
folder workspace-write, and keeps the sandbox and approvals. It does not change the
|
||||
sandbox mode: a folder Codex runs read-only keeps the network closed, so each call
|
||||
still needs the per-command approval (or `/permissions`). **Bypass approvals and
|
||||
sandbox** works everywhere but drops the sandbox and approvals.
|
||||
Codex) and starts a new Codex session. Tell them what it costs first: it applies to
|
||||
every new local Codex session, and commands in Codex's sandbox can then call Codeman's
|
||||
API, which can start a session outside the sandbox with no approval prompt, even when
|
||||
a password is set. It opens the network wherever Codex runs the folder workspace-write
|
||||
and does not change the sandbox mode: a folder Codex runs read-only keeps the network
|
||||
closed, so each call still needs the per-command approval (or `/permissions`).
|
||||
**Bypass approvals and sandbox** works everywhere but drops the sandbox and approvals.
|
||||
- **`"${CURL[@]}" -o /dev/null -w '%{http_code}' "$API/api/v1/sessions/$SELF"` prints
|
||||
`404`.** The commands you run do not carry this session's environment: Codex can run them in a shared background
|
||||
process another session started, so `$CODEMAN_SESSION_ID` (and with it `$SELF` and
|
||||
the delete guard) is that session's. Delete nothing; tell the user. Codeman starts
|
||||
Codex 0.162 and later with `--no-daemon` to prevent this, so seeing it means an older
|
||||
Codex or a server that predates that.
|
||||
the delete guard) is that session's. Delete nothing; tell the user. Codeman starts a
|
||||
local Codex 0.162 or later with `--no-daemon` to prevent this, so seeing it means a
|
||||
Codex started without that flag: an older Codex, a local session whose version Codeman
|
||||
could not read, a remote or Docker session, or a server that predates it.
|
||||
|
||||
⚠️ **Your shell state does not survive between tool calls.** Each Bash call starts a
|
||||
fresh shell, so `$API`, `$SELF`, the `CURL` array and `delete_session` are all gone by
|
||||
|
||||
+11
-8
@@ -77,17 +77,20 @@ retrying.
|
||||
can ask the user to run a command outside its sandbox (Codex can), re-run **the same
|
||||
command** once that way, never a different one; if the user declines, stop. For lasting
|
||||
access in Codex, the user turns on **Allow network access** (Codeman App Settings,
|
||||
Codex) and starts a new Codex session: it opens the network wherever Codex runs the
|
||||
folder workspace-write, and keeps the sandbox and approvals. It does not change the
|
||||
sandbox mode: a folder Codex runs read-only keeps the network closed, so each call
|
||||
still needs the per-command approval (or `/permissions`). **Bypass approvals and
|
||||
sandbox** works everywhere but drops the sandbox and approvals.
|
||||
Codex) and starts a new Codex session. Tell them what it costs first: it applies to
|
||||
every new local Codex session, and commands in Codex's sandbox can then call Codeman's
|
||||
API, which can start a session outside the sandbox with no approval prompt, even when
|
||||
a password is set. It opens the network wherever Codex runs the folder workspace-write
|
||||
and does not change the sandbox mode: a folder Codex runs read-only keeps the network
|
||||
closed, so each call still needs the per-command approval (or `/permissions`).
|
||||
**Bypass approvals and sandbox** works everywhere but drops the sandbox and approvals.
|
||||
- **`"${CURL[@]}" -o /dev/null -w '%{http_code}' "$API/api/v1/sessions/$SELF"` prints
|
||||
`404`.** The commands you run do not carry this session's environment: Codex can run them in a shared background
|
||||
process another session started, so `$CODEMAN_SESSION_ID` (and with it `$SELF` and
|
||||
the delete guard) is that session's. Delete nothing; tell the user. Codeman starts
|
||||
Codex 0.162 and later with `--no-daemon` to prevent this, so seeing it means an older
|
||||
Codex or a server that predates that.
|
||||
the delete guard) is that session's. Delete nothing; tell the user. Codeman starts a
|
||||
local Codex 0.162 or later with `--no-daemon` to prevent this, so seeing it means a
|
||||
Codex started without that flag: an older Codex, a local session whose version Codeman
|
||||
could not read, a remote or Docker session, or a server that predates it.
|
||||
|
||||
⚠️ **Your shell state does not survive between tool calls.** Each Bash call starts a
|
||||
fresh shell, so `$API`, `$SELF`, the `CURL` array and `delete_session` are all gone by
|
||||
|
||||
@@ -669,9 +669,13 @@ const CODEX: CliEntry = {
|
||||
{ flag: '--dangerously-bypass-approvals-and-sandbox', when: { param: 'bypassApprovals', is: true } },
|
||||
// Lets commands in codex's workspace-write sandbox open network connections,
|
||||
// Codeman's own API on loopback included (the codeman skill's curl calls fail
|
||||
// with "couldn't connect" without it). Narrower than the bypass above: the
|
||||
// sandbox and the approval prompts stay. Measured on 0.162.0 (`codex sandbox` and
|
||||
// with "couldn't connect" without it). Measured on 0.162.0 (`codex sandbox` and
|
||||
// a live pane): loopback stays blocked under workspace-write until this is set.
|
||||
// ⚠️ Not a safe middle ground below the bypass above: that API can start a session
|
||||
// outside the sandbox with no approval prompt, so codex's sandbox no longer contains
|
||||
// a command once this is on. Measured 2026-11-06: a `touch` the sandbox refused
|
||||
// landed through POST /api/sessions, and with a password set too from inside a
|
||||
// Codeman pane, which inherits CODEMAN_PASSWORD by design.
|
||||
// ⚠️ Workspace-write ONLY: a folder codex runs read-only (one it does not trust,
|
||||
// such as a fresh non-git case) keeps the network closed and asks per command.
|
||||
// Deliberately not paired with `--sandbox workspace-write` (owner decision,
|
||||
|
||||
@@ -3048,7 +3048,7 @@
|
||||
<div class="set-row" data-search="codex network access sandbox loopback codeman skill orchestrate spawn">
|
||||
<div class="set-row-text">
|
||||
<span class="set-row-label">Allow network access</span>
|
||||
<span class="set-row-desc">Lets commands in Codex's workspace-write sandbox use the network, which the Codeman skill needs to reach this server. It does not change Codex's sandbox mode: a folder Codex runs read-only (one it does not trust) keeps the network closed and asks per command. The sandbox and approvals stay on. Applies to new local Codex sessions.</span>
|
||||
<span class="set-row-desc">Lets commands in Codex's workspace-write sandbox use the network, which the Codeman skill needs to reach this server. Those commands can then call Codeman's API, which can start a session outside the sandbox with no approval prompt, even when a password is set. So with this on, Codex's sandbox does not contain a prompt-injected command: treat it as close to Bypass. Applies to every new local Codex session, not scheduled jobs. It does not change Codex's sandbox mode: a folder Codex runs read-only (one it does not trust) keeps the network closed and asks per command.</span>
|
||||
</div>
|
||||
<label class="switch switch-sm"><input type="checkbox" id="appSettingsCodexNetworkAccess"><span class="slider"></span></label>
|
||||
</div>
|
||||
|
||||
@@ -237,6 +237,29 @@ describe('App Settings modal structure', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('never sells Allow network access as leaving the sandbox in charge', () => {
|
||||
// Measured 2026-11-06 on codex 0.162.0: with the network open, a command the sandbox
|
||||
// refused (`touch` outside the workspace) landed by asking Codeman's API for a shell
|
||||
// session, and from inside a Codeman pane it did so with a password set, because a pane
|
||||
// inherits CODEMAN_PASSWORD. So "the sandbox and approvals stay on" is false comfort.
|
||||
const modal = settingsModal();
|
||||
const start = modal.indexOf('data-search="codex network access');
|
||||
expect(start).toBeGreaterThan(-1);
|
||||
const row = modal.slice(start, modal.indexOf('id="appSettingsCodexNetworkAccess"', start));
|
||||
const read = (path: string) => readFileSync(resolve(import.meta.dirname, '..', path), 'utf8');
|
||||
const referenceRow = read('docs/wiki/Settings-Reference.md')
|
||||
.split('\n')
|
||||
.find((line) => line.startsWith('| Allow network access'));
|
||||
expect(referenceRow).toBeDefined();
|
||||
const safetyClaim = /sandbox and approvals? (prompts )?stay on|keeps? (Codex's |the )?sandbox and approval/i;
|
||||
for (const text of [row, referenceRow!, read('docs/wiki/Codeman-Skill.md'), read('skills/codeman/SKILL.md')]) {
|
||||
expect(text).not.toMatch(safetyClaim);
|
||||
}
|
||||
for (const text of [row, referenceRow!, read('docs/wiki/Security.md')]) {
|
||||
expect(text).toContain("Codeman's API");
|
||||
}
|
||||
});
|
||||
|
||||
it('has retired the modal-tab chrome everywhere, not just here', () => {
|
||||
// Session Options and Add Case moved onto this same `set-*` surface, so the
|
||||
// old tab classes have no users left. A reappearance means a modal drifted
|
||||
|
||||
Reference in new issue
Block a user