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 <noreply@anthropic.com>
This commit is contained in:
parent
d962e5e7f4
commit
f136451e22
@ -52,7 +52,11 @@ WORKDIR /home/rakazo
|
|||||||
# --chmod normalizes modes: a restrictive host umask (e.g. 077 on hardened hosts) would
|
# --chmod normalizes modes: a restrictive host umask (e.g. 077 on hardened hosts) would
|
||||||
# otherwise leave these files unreadable to USER 1000 at runtime.
|
# 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/
|
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
|
&& 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 start.sh /usr/local/bin/rakazo-computer
|
||||||
COPY --chmod=755 control.py /usr/local/bin/rakazo-computer-control
|
COPY --chmod=755 control.py /usr/local/bin/rakazo-computer-control
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
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
|
if [[ "${RAKAZO_GITHUB_BROKER_URL:-}" == "" || "${RAKAZO_GITHUB_BROKER_RUN_TOKEN:-}" == "" ]]; then
|
||||||
exec "$REAL_GIT" "$@"
|
exec "$REAL_GIT" "$@"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -10,3 +10,5 @@ done
|
|||||||
docker run --rm "$IMAGE" bash -c 'git --version | grep -q "^git version "'
|
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 '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 "$(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 "'
|
||||||
|
|||||||
@ -217,7 +217,7 @@ describe("formatConnectionsInstruction", () => {
|
|||||||
const line = formatGithubGitInstruction(["GitHub"]);
|
const line = formatGithubGitInstruction(["GitHub"]);
|
||||||
expect(line).toMatch(/Clone private repositories with git/);
|
expect(line).toMatch(/Clone private repositories with git/);
|
||||||
expect(line).toMatch(/Never ask the user for a personal access token/);
|
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", () => {
|
it("says plainly when nothing is connected", () => {
|
||||||
|
|||||||
@ -79,7 +79,7 @@ export const ROUTINE_RUN_INSTRUCTION =
|
|||||||
export function formatGithubGitInstruction(connectedApps: readonly string[]): string | undefined {
|
export function formatGithubGitInstruction(connectedApps: readonly string[]): string | undefined {
|
||||||
const hasGithub = connectedApps.some((app) => app.trim().toLowerCase() === "github");
|
const hasGithub = connectedApps.some((app) => app.trim().toLowerCase() === "github");
|
||||||
if (!hasGithub) return undefined;
|
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: {
|
export function formatConnectionsInstruction(input: {
|
||||||
|
|||||||
@ -1221,15 +1221,17 @@ export function createRunExecutor(deps: ExecutorDeps) {
|
|||||||
containerRoot: projectWorkspacePath ? "/home/rakazo/project" : "/home/rakazo",
|
containerRoot: projectWorkspacePath ? "/home/rakazo/project" : "/home/rakazo",
|
||||||
}).catch(() => undefined);
|
}).catch(() => undefined);
|
||||||
}
|
}
|
||||||
|
const agentSecrets = decryptAgentEnvironment(agentSecretRows, deps.secretStore);
|
||||||
const agentEnvironment = {
|
const agentEnvironment = {
|
||||||
...decryptAgentEnvironment(agentSecretRows, deps.secretStore),
|
...agentSecrets,
|
||||||
...githubIntegrationEnvironmentForRun({
|
...githubIntegrationEnvironmentForRun({
|
||||||
runId,
|
runId,
|
||||||
githubToken: githubIntegrationToken,
|
githubToken: githubIntegrationToken,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
runSecrets.push(...Object.values(agentEnvironment));
|
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 hasModelOverride = Boolean(bot.modelProvider && bot.modelId);
|
||||||
const overrideCredential =
|
const overrideCredential =
|
||||||
hasModelOverride && bot.modelProvider
|
hasModelOverride && bot.modelProvider
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user