From f136451e222a03c8ee70baa28e44fb10f2a9bb7c Mon Sep 17 00:00:00 2001 From: cynarAI <155274210+cynarAI@users.noreply.github.com> Date: Sun, 18 Oct 2026 01:13:37 +0200 Subject: [PATCH] Stop agents from using the broker run token as a GitHub password (#212) Agents kept configuring git with RAKAZO_GITHUB_BROKER_RUN_TOKEN as a password because the system prompt listed it among "managed credentials" to use in shell commands. GitHub rejects it, and the agent then asks the user for a personal access token. - List only the user's own agent secrets in that instruction; the broker variables stay in the environment for the git shim. - Make /usr/bin/git the shim as well, so git called by absolute path still goes through the broker; the real binary moves out of PATH. - Prompt no longer names the broker variable. Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ Co-authored-by: Claude --- infra/sandboxes/computer/Dockerfile | 6 +++++- infra/sandboxes/computer/bin/rakazo-git | 2 +- infra/sandboxes/computer/computer-image-tools.test.sh | 2 ++ packages/adapters/src/executor-prompt.test.ts | 2 +- packages/adapters/src/executor-prompt.ts | 2 +- packages/adapters/src/executor.ts | 6 ++++-- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/infra/sandboxes/computer/Dockerfile b/infra/sandboxes/computer/Dockerfile index 71f2ed5..3e2b974 100644 --- a/infra/sandboxes/computer/Dockerfile +++ b/infra/sandboxes/computer/Dockerfile @@ -52,7 +52,11 @@ WORKDIR /home/rakazo # --chmod normalizes modes: a restrictive host umask (e.g. 077 on hardened hosts) would # otherwise leave these files unreadable to USER 1000 at runtime. COPY --chmod=755 bin/rakazo-git bin/rakazo-gh bin/rakazo-broker /usr/local/lib/rakazo/bin/ -RUN ln -sf /usr/local/lib/rakazo/bin/rakazo-git /usr/local/lib/rakazo/bin/git \ +# /usr/bin/git is the shim too, so calling git by absolute path still goes through the broker. +RUN mkdir -p /usr/local/libexec/rakazo \ + && mv /usr/bin/git /usr/local/libexec/rakazo/git \ + && ln -sf /usr/local/lib/rakazo/bin/rakazo-git /usr/bin/git \ + && ln -sf /usr/local/lib/rakazo/bin/rakazo-git /usr/local/lib/rakazo/bin/git \ && ln -sf /usr/local/lib/rakazo/bin/rakazo-gh /usr/local/lib/rakazo/bin/gh COPY --chmod=755 start.sh /usr/local/bin/rakazo-computer COPY --chmod=755 control.py /usr/local/bin/rakazo-computer-control diff --git a/infra/sandboxes/computer/bin/rakazo-git b/infra/sandboxes/computer/bin/rakazo-git index c97fc4a..fbc2b28 100755 --- a/infra/sandboxes/computer/bin/rakazo-git +++ b/infra/sandboxes/computer/bin/rakazo-git @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -euo pipefail -REAL_GIT=/usr/bin/git +REAL_GIT=/usr/local/libexec/rakazo/git if [[ "${RAKAZO_GITHUB_BROKER_URL:-}" == "" || "${RAKAZO_GITHUB_BROKER_RUN_TOKEN:-}" == "" ]]; then exec "$REAL_GIT" "$@" fi diff --git a/infra/sandboxes/computer/computer-image-tools.test.sh b/infra/sandboxes/computer/computer-image-tools.test.sh index 6143e7c..f5710b2 100755 --- a/infra/sandboxes/computer/computer-image-tools.test.sh +++ b/infra/sandboxes/computer/computer-image-tools.test.sh @@ -10,3 +10,5 @@ done docker run --rm "$IMAGE" bash -c 'git --version | grep -q "^git version "' docker run --rm "$IMAGE" bash -c 'gh --version | grep -q "^gh version "' docker run --rm "$IMAGE" bash -c 'test "$(command -v git)" = "/usr/local/lib/rakazo/bin/git"' +docker run --rm "$IMAGE" bash -c 'test "$(readlink /usr/bin/git)" = "/usr/local/lib/rakazo/bin/rakazo-git"' +docker run --rm "$IMAGE" bash -c '/usr/bin/git --version | grep -q "^git version "' diff --git a/packages/adapters/src/executor-prompt.test.ts b/packages/adapters/src/executor-prompt.test.ts index 397ec8b..c586982 100644 --- a/packages/adapters/src/executor-prompt.test.ts +++ b/packages/adapters/src/executor-prompt.test.ts @@ -217,7 +217,7 @@ describe("formatConnectionsInstruction", () => { const line = formatGithubGitInstruction(["GitHub"]); expect(line).toMatch(/Clone private repositories with git/); expect(line).toMatch(/Never ask the user for a personal access token/); - expect(line).toMatch(/RAKAZO_GITHUB_BROKER_RUN_TOKEN is not a GitHub token/); + expect(line).toMatch(/never put a token into URLs, credential helpers, or git config/); }); it("says plainly when nothing is connected", () => { diff --git a/packages/adapters/src/executor-prompt.ts b/packages/adapters/src/executor-prompt.ts index 7d7d090..79b2fc1 100644 --- a/packages/adapters/src/executor-prompt.ts +++ b/packages/adapters/src/executor-prompt.ts @@ -79,7 +79,7 @@ export const ROUTINE_RUN_INSTRUCTION = export function formatGithubGitInstruction(connectedApps: readonly string[]): string | undefined { const hasGithub = connectedApps.some((app) => app.trim().toLowerCase() === "github"); if (!hasGithub) return undefined; - return "GitHub is connected. Clone private repositories with git on your computer (authentication is brokered) or use the GitHub integration tools for issues, pull requests, and repository APIs. Plain `git clone https://github.com/owner/repo` works; RAKAZO_GITHUB_BROKER_RUN_TOKEN is not a GitHub token, so never put it or any token into URLs, credential helpers, or git config. Never ask the user for a personal access token, password, or SSH key for GitHub."; + return "GitHub is connected. Clone private repositories with git on your computer (authentication is brokered) or use the GitHub integration tools for issues, pull requests, and repository APIs. Plain `git clone https://github.com/owner/repo` works as is; never put a token into URLs, credential helpers, or git config. Never ask the user for a personal access token, password, or SSH key for GitHub."; } export function formatConnectionsInstruction(input: { diff --git a/packages/adapters/src/executor.ts b/packages/adapters/src/executor.ts index 2174f2f..c8c8dea 100644 --- a/packages/adapters/src/executor.ts +++ b/packages/adapters/src/executor.ts @@ -1221,15 +1221,17 @@ export function createRunExecutor(deps: ExecutorDeps) { containerRoot: projectWorkspacePath ? "/home/rakazo/project" : "/home/rakazo", }).catch(() => undefined); } + const agentSecrets = decryptAgentEnvironment(agentSecretRows, deps.secretStore); const agentEnvironment = { - ...decryptAgentEnvironment(agentSecretRows, deps.secretStore), + ...agentSecrets, ...githubIntegrationEnvironmentForRun({ runId, githubToken: githubIntegrationToken, }), }; runSecrets.push(...Object.values(agentEnvironment)); - const agentEnvironmentInstruction = formatAgentEnvironmentInstruction(agentEnvironment); + // The broker variables are plumbing for the git shim; naming them invites the model to use them as a GitHub token. + const agentEnvironmentInstruction = formatAgentEnvironmentInstruction(agentSecrets); const hasModelOverride = Boolean(bot.modelProvider && bot.modelId); const overrideCredential = hasModelOverride && bot.modelProvider