~bigbes/agents-dev · parade

main · last commit 13 days ago · 7g0stsfu

← Back to the parade

ah-tls [SECURITY] the agent pane inherits the daemon's full environment — every token is readable with a bare `env` Lined Up

status: open P1 bug milestone:v0.2.0
bd update ah-tls --claim bd close ah-tls
Created byEugene Blikh
Ownerbigbes@gmail.com
Created2026-08-05T00:03:32Z
Updated2026-08-05T00:03:32Z
Description
Found while refreshing SPEC §15 (ah-e5l). §15 previously CLAIMED 'the pi process inherits only the env it needs (PATH, HOME, ...)'. That claim is false, and the spec now records the reality instead.

Nothing anywhere sets cmd.Env:
- internal/runner/runner.go:984 runCmd uses exec.CommandContext with a nil Env, so the zellij SERVER created by zellijEnsureSession inherits agenthubd's full environment;
- every pane spawned into that session inherits the server's env;
- cmd/ahub-run/main.go:281 syscall.Execs the keep-alive shell with os.Environ().

So every ${VAR} the daemon expands at config load — the Vikunja API token, the notifier token (Telegram bot token or ntfy token), the Yonote BOT token, the mem0 API key — is readable from inside the agent's own pane with a bare 'env'. The agent is an LLM running attacker-influenced content (card descriptions, Yonote doc text, comments from anyone with board access), and it has a shell.

Blast radius is the whole external surface: board write access, notification spoofing, publishing/editing Yonote docs as the bot, and reading/writing the memory store. This is strictly worse than the per-task bearer token the same system carefully scopes to three calls on one task — that token is 256-bit, attempt-bound, in-memory and invalidated at finalize, while the credentials that actually matter sit in plain view next to it.

Filed P1 as the highest-severity finding of the 2026-08-05 sweep. Note this is a homelab single-user deployment, so it is not an active incident — but it is the one defect that makes every other credential control decorative.
Design
Set an explicit allowlist Env on the processes the runner spawns rather than trying to scrub. Three places, and the ZELLIJ SERVER is the one that actually matters — a pane inherits the server's env, not the run-client's, so passing a clean Env only to the pane command is NOT enough (this is the same server-env-inheritance property that F12 already forced absolute binary paths for; see the LookPath comments in runner.Start).

Sketch: build the child env from an explicit list — PATH, HOME, USER, SHELL, TERM, LANG/LC_*, TMPDIR, the proxy vars the notifier lane needs, plus whatever pi itself requires for its provider config — and nothing else. Then decide deliberately what pi needs to reach LiteLLM: if its key comes from the environment, that one credential has to be present, and the honest statement becomes 'the agent can read the LLM key and nothing else', which is a defensible position worth writing into §15.

Watch out for: (a) an already-running zellij server will keep its old env, so this needs a server restart to take effect and the deploy lane should be told; (b) ahub-run's keep-pane syscall.Exec passes os.Environ() — by then it is already inside the pane, so it inherits whatever the pane got, but it should not re-add anything; (c) the e2e harness stubs may rely on inherited env.
Acceptance criteria
A pane spawned for a task shows none of vikunja.token, the notifier token, yonote.token or mem0.api_key in 'env'. SPEC §15 states the resulting inheritance rule and names any credential deliberately left reachable. Covered by a runner test asserting the constructed Env is the allowlist and not os.Environ().

Depends on

No outgoing dependencies.

Depended on by

Nothing depends on this issue.

No comments.

  • Eugene Blikh created the issue · 2026-08-05T03:03:31Z
  • Eugene Blikh added label milestone:v0.2.0 · 2026-08-05T03:10:42Z
Stored rows — what this pane was built from, as read
issues 1 row
id ah-tls
content_hash 4f7e99393b69a2de3f7a923407d49b7d6f2a27a93f722df4ac25187f40adfd53
title [SECURITY] the agent pane inherits the daemon's full environment — every token is readable with a bare `env`
description Found while refreshing SPEC §15 (ah-e5l). §15 previously CLAIMED 'the pi process inherits only the env it needs (PATH, HOME, ...)'. That claim is false, and the spec now records the reality instead. Nothing anywhere sets cmd.Env: - internal/runner/runner.go:984 runCmd uses exec.CommandContext with a nil Env, so the zellij SERVER created by zellijEnsureSession inherits agenthubd's full environment; - every pane spawned into that session inherits the server's env; - cmd/ahub-run/main.go:281 syscall.Execs the keep-alive shell with os.Environ(). So every ${VAR} the daemon expands at config load — the Vikunja API token, the notifier token (Telegram bot token or ntfy token), the Yonote BOT token, the mem0 API key — is readable from inside the agent's own pane with a bare 'env'. The agent is an LLM running attacker-influenced content (card descriptions, Yonote doc text, comments from anyone with board access), and it has a shell. Blast radius is the whole external surface: board write access, notification spoofing, publishing/editing Yonote docs as the bot, and reading/writing the memory store. This is strictly worse than the per-task bearer token the same system carefully scopes to three calls on one task — that token is 256-bit, attempt-bound, in-memory and invalidated at finalize, while the credentials that actually matter sit in plain view next to it. Filed P1 as the highest-severity finding of the 2026-08-05 sweep. Note this is a homelab single-user deployment, so it is not an active incident — but it is the one defect that makes every other credential control decorative.
design Set an explicit allowlist Env on the processes the runner spawns rather than trying to scrub. Three places, and the ZELLIJ SERVER is the one that actually matters — a pane inherits the server's env, not the run-client's, so passing a clean Env only to the pane command is NOT enough (this is the same server-env-inheritance property that F12 already forced absolute binary paths for; see the LookPath comments in runner.Start). Sketch: build the child env from an explicit list — PATH, HOME, USER, SHELL, TERM, LANG/LC_*, TMPDIR, the proxy vars the notifier lane needs, plus whatever pi itself requires for its provider config — and nothing else. Then decide deliberately what pi needs to reach LiteLLM: if its key comes from the environment, that one credential has to be present, and the honest statement becomes 'the agent can read the LLM key and nothing else', which is a defensible position worth writing into §15. Watch out for: (a) an already-running zellij server will keep its old env, so this needs a server restart to take effect and the deploy lane should be told; (b) ahub-run's keep-pane syscall.Exec passes os.Environ() — by then it is already inside the pane, so it inherits whatever the pane got, but it should not re-add anything; (c) the e2e harness stubs may rely on inherited env.
acceptance_criteria A pane spawned for a task shows none of vikunja.token, the notifier token, yonote.token or mem0.api_key in 'env'. SPEC §15 states the resulting inheritance rule and names any credential deliberately left reachable. Covered by a runner test asserting the constructed Env is the allowlist and not os.Environ().
notes
status open
priority 1
issue_type bug
assignee NULL
estimated_minutes NULL
created_at 2026-08-05T00:03:32Z
created_by Eugene Blikh
owner bigbes@gmail.com
updated_at 2026-08-05T00:03:32Z
closed_at NULL
closed_by_session
external_ref NULL
spec_id
compaction_level 0
compacted_at NULL
compacted_at_commit NULL
original_size NULL
sender
ephemeral 0
wisp_type
pinned 0
is_template 0
mol_type
work_type
source_system
metadata �{}
source_repo
close_reason
event_kind
actor
target
payload
await_type
await_id
timeout_ns 0
waiters
hook_bead
role_bead
agent_state
last_activity NULL
role_type
rig
due_at NULL
defer_until NULL
no_history 0
started_at NULL
is_blocked 0
labels 1 row
issue_id ah-tls
label milestone:v0.2.0
events 2 rows
id 019fcf3b-833a-7af0-b8b1-028b145b20b3
issue_id ah-tls
event_type created
actor Eugene Blikh
old_value
new_value
comment NULL
created_at 2026-08-05T03:03:31Z
id 019fcf42-14f6-71c9-9bc1-8b3d929b1b95
issue_id ah-tls
event_type label_added
actor Eugene Blikh
old_value NULL
new_value NULL
comment Added label: milestone:v0.2.0
created_at 2026-08-05T03:10:42Z