Summary
CodeKanban automatically prefers config.yaml from its process current working directory and uses terminal.shell.linux as the command for newly created terminal sessions. A repository contributor can commit this file with a command and arguments that CodeKanban will execute when a user starts CodeKanban from that checkout and creates a terminal. The command runs with the CodeKanban process user's local privileges and no workspace-trust, first-use command confirmation, configuration fingerprint, or execution-time revalidation is applied.
This is deterministic configuration execution and does not depend on prompt injection, an LLM request, or the user typing a command inside the terminal. The minimum trigger is more specific than opening a directory: the victim must launch CodeKanban with the checkout as CWD and then create or restore a terminal for the project.
Affected Revision
Observed on v0.44.1, commit 7d4900acee04c6755982fb0251950f5ace62a182 (master as of 2026-08-10). The same source path was present in the previously reviewed v0.41.0 commit 7c63e5d.
Impact and Preconditions
Impact: an attacker-controlled existing executable and argument vector can be started as a PTY child with the user's CodeKanban privileges and with the selected worktree as its working directory. A shell or interpreter can also be selected if available on the host, so the practical effect is arbitrary local command execution subject to the host's existing binaries and the victim's permissions.
Preconditions: the victim must run CodeKanban from the malicious checkout, such as npx codekanban while the checkout is the current directory, and must create a terminal or restore one for that project. A project merely added through the UI while CodeKanban was started from another directory does not load that project's config.yaml through this specific path. A login may be required in deployments with authentication enabled; login does not approve the resolved shell command.
Reproduction
The following PoC uses only the host touch executable and creates a marker in the test workspace. It does not read credentials, make an external network request, or modify shell profiles.
- Build the affected revision and create an isolated checkout workspace:
git clone --branch v0.44.1 https://github.com/fy0/CodeKanban.git CodeKanban-poc
cd CodeKanban-poc
go build -o CodeKanban .
mkdir -p poc-workspace
cd poc-workspace
cat > config.yaml <<'YAML'
disableAutoOpenBrowser: true
terminal:
shell:
linux: "touch .codekanban-poc-marker"
YAML
export HOME="$PWD/.poc-home"
mkdir -p "$HOME"
../CodeKanban -H -b 127.0.0.1 -p 39071
- In another terminal, from the
poc-workspace directory, create a project pointing at the current workspace and obtain its worktree ID:
WORKSPACE="$PWD"
PROJECT_JSON=$(curl -sS -X POST http://127.0.0.1:39071/api/v1/projects/create \
-H 'Content-Type: application/json' \
--data "{\"name\":\"CodeKanban config PoC\",\"path\":\"$WORKSPACE\",\"description\":\"isolated test\"}")
PROJECT_ID=$(printf '%s' "$PROJECT_JSON" | jq -r '.item.id')
WORKTREE_ID=$(curl -sS "http://127.0.0.1:39071/api/v1/projects/$PROJECT_ID/worktrees" | jq -r '.items[0].id')
- Create a terminal, which is the same backend action used by the terminal-create UI:
curl -i -X POST "http://127.0.0.1:39071/api/v1/projects/$PROJECT_ID/worktrees/$WORKTREE_ID/terminals" \
-H 'Content-Type: application/json' \
--data '{"workingDir":"","title":"config PoC","rows":24,"cols":80}'
test -f .codekanban-poc-marker
Expected result: the terminal-create request returns HTTP 201 and .codekanban-poc-marker exists. The terminal may already report closed because touch exits immediately. No confirmation dialog is shown before the configured command runs.
- Control test: repeat the same steps from a fresh workspace that has no
config.yaml and a fresh HOME. The generated data-directory configuration selects the default /bin/bash; creating a terminal must not create .codekanban-poc-marker in that workspace.
Source-to-Sink Evidence
utils/app_config.go:263-274 selects config.yaml from the process CWD whenever it exists, before the data-directory configuration.
main.go:91-92 loads that configuration during normal startup.
api/terminal_routes.go:70-86 registers terminal creation, and api/terminal_routes.go:446-500 calls terminal.Manager.CreateSession after only project/worktree and working-directory validation.
service/terminal/manager.go:94-112 resolves the configured shell; utils/shell.go:54-115 tokenizes it and checks executable availability, but has no trust or approval check.
service/terminal/session.go:362-373 calls exec.CommandContext(sessionCtx, s.command[0], s.command[1:]...), sets the worktree working directory, and starts the PTY.
The repository .gitignore ignores config.yaml, but this does not prevent an attacker-controlled repository from tracking the file or removing that ignore entry.
Suggested Fix
Do not automatically load command-bearing terminal settings from the process CWD. If project-level terminal settings are required, keep them disabled by default or separate them from safe project configuration and require explicit approval that displays the canonical workspace, exact executable, and arguments. Bind approval to the user, workspace/revision or configuration digest, launcher surface, and the resolved executable or referenced script. Recompute and revalidate the effective command immediately before the PTY spawn and repeat approval after configuration or referenced-content changes.
Summary
CodeKanban automatically prefers
config.yamlfrom its process current working directory and usesterminal.shell.linuxas the command for newly created terminal sessions. A repository contributor can commit this file with a command and arguments that CodeKanban will execute when a user starts CodeKanban from that checkout and creates a terminal. The command runs with the CodeKanban process user's local privileges and no workspace-trust, first-use command confirmation, configuration fingerprint, or execution-time revalidation is applied.This is deterministic configuration execution and does not depend on prompt injection, an LLM request, or the user typing a command inside the terminal. The minimum trigger is more specific than opening a directory: the victim must launch CodeKanban with the checkout as CWD and then create or restore a terminal for the project.
Affected Revision
Observed on
v0.44.1, commit7d4900acee04c6755982fb0251950f5ace62a182(masteras of 2026-08-10). The same source path was present in the previously reviewedv0.41.0commit7c63e5d.Impact and Preconditions
Impact: an attacker-controlled existing executable and argument vector can be started as a PTY child with the user's CodeKanban privileges and with the selected worktree as its working directory. A shell or interpreter can also be selected if available on the host, so the practical effect is arbitrary local command execution subject to the host's existing binaries and the victim's permissions.
Preconditions: the victim must run CodeKanban from the malicious checkout, such as
npx codekanbanwhile the checkout is the current directory, and must create a terminal or restore one for that project. A project merely added through the UI while CodeKanban was started from another directory does not load that project'sconfig.yamlthrough this specific path. A login may be required in deployments with authentication enabled; login does not approve the resolved shell command.Reproduction
The following PoC uses only the host
touchexecutable and creates a marker in the test workspace. It does not read credentials, make an external network request, or modify shell profiles.poc-workspacedirectory, create a project pointing at the current workspace and obtain its worktree ID:Expected result: the terminal-create request returns HTTP 201 and
.codekanban-poc-markerexists. The terminal may already reportclosedbecausetouchexits immediately. No confirmation dialog is shown before the configured command runs.config.yamland a freshHOME. The generated data-directory configuration selects the default/bin/bash; creating a terminal must not create.codekanban-poc-markerin that workspace.Source-to-Sink Evidence
utils/app_config.go:263-274selectsconfig.yamlfrom the process CWD whenever it exists, before the data-directory configuration.main.go:91-92loads that configuration during normal startup.api/terminal_routes.go:70-86registers terminal creation, andapi/terminal_routes.go:446-500callsterminal.Manager.CreateSessionafter only project/worktree and working-directory validation.service/terminal/manager.go:94-112resolves the configured shell;utils/shell.go:54-115tokenizes it and checks executable availability, but has no trust or approval check.service/terminal/session.go:362-373callsexec.CommandContext(sessionCtx, s.command[0], s.command[1:]...), sets the worktree working directory, and starts the PTY.The repository
.gitignoreignoresconfig.yaml, but this does not prevent an attacker-controlled repository from tracking the file or removing that ignore entry.Suggested Fix
Do not automatically load command-bearing terminal settings from the process CWD. If project-level terminal settings are required, keep them disabled by default or separate them from safe project configuration and require explicit approval that displays the canonical workspace, exact executable, and arguments. Bind approval to the user, workspace/revision or configuration digest, launcher surface, and the resolved executable or referenced script. Recompute and revalidate the effective command immediately before the PTY spawn and repeat approval after configuration or referenced-content changes.