[security][high] update-audit AI agent: WebFetch + broad token + writes to a published issue (prompt-injection exfil) #197

Closed
opened 2026-07-14 09:47:42 +00:00 by claude-bot · 1 comment
Member

Finding

.woodpecker/update-audit.yml runs the claude agent with --allowedTools including WebFetch,WebSearch AND a broad GITEA_TOKEN (the registry/release-writing gitea_token) in the same step env. Its job is to fetch untrusted third-party release pages. Its Write output is posted verbatim to a Gitea issue by the pipeline. Read is not repo-sandboxed. So injected content in a fetched page could read env (token) and exfiltrate it via the published issue body (or WebFetch).

Verified

update-audit.yml allowedTools list + GITEA_TOKEN from_secret gitea_token in the same step; report body cat into the issue POST. No settings.json sandbox exists in repo.

Fix

Use a minimal write:issue-only token (not the broad gitea_token); drop WebFetch/WebSearch or restrict to a pre-fetched allowlist; constrain Write to a single output path; add injection-defense framing.


Automated security review 2026-07-14. Path verified; live exploit not executed.

## Finding .woodpecker/update-audit.yml runs the claude agent with --allowedTools including WebFetch,WebSearch AND a broad GITEA_TOKEN (the registry/release-writing gitea_token) in the same step env. Its job is to fetch untrusted third-party release pages. Its Write output is posted verbatim to a Gitea issue by the pipeline. Read is not repo-sandboxed. So injected content in a fetched page could read env (token) and exfiltrate it via the published issue body (or WebFetch). ## Verified update-audit.yml allowedTools list + GITEA_TOKEN from_secret gitea_token in the same step; report body cat into the issue POST. No settings.json sandbox exists in repo. ## Fix Use a minimal write:issue-only token (not the broad gitea_token); drop WebFetch/WebSearch or restrict to a pre-fetched allowlist; constrain Write to a single output path; add injection-defense framing. --- _Automated security review 2026-07-14. Path verified; live exploit not executed._
Author
Member

Fixed (PR): tool-surface hardening

PR #238 (gitea_admin/homelab#238): drops WebSearch (unlike WebFetch, it can't be domain-scoped — verified live), adds ci/agents/update-audit/settings.json denying Read on /proc, /root, /home, /etc, and this repo's own .git/ (blocks the env-var-dump and credential-file read paths — verified live against the pinned Claude Code CLI, not just per docs), and adds explicit prompt-injection-defense framing to ci/agents/update-audit/prompt.md (fetched content = untrusted data, never instructions, never echoed verbatim).

Two things I deliberately did not do, both verified/reasoned in the PR body:

  • Write path-scoping — tested Write(<path>) allow/deny rules under --permission-mode acceptEdits (this pipeline's mode) and found them not enforced; shipping a control that looks restrictive but silently isn't seemed worse than documenting the gap. Low severity in practice: the agent has no Bash/Edit, and the pipeline only ever trusts /tmp/update-audit.md.
  • A WebFetch domain allowlist — the Helm-chart-version check legitimately needs to reach whichever domain each HelmRelease's own already-trusted source URL points at (~20 charts, many different domains today); a fixed allowlist would silently drop audit coverage for anything added later.

Needs operator action: token scoping

The broad gitea_token secret is still what's bound to GITEA_TOKEN in this step — I can't fix that myself. Per the code comment history, security_bot_token was tried first and rejected because its Gitea PAT lacks write:issue scope, and switching to gitea_token (also used for registry/release writes in build-ci-images.yml) was the workaround.

Proposal: mint a new Gitea PAT scoped to write:issue only (on whichever bot account you prefer — reusing security-bot if you're willing to add that one scope to it, or a fresh minimal identity), register it as a Woodpecker secret (e.g. update_audit_token), and I'll swap from_secret: gitea_token to from_secret: update_audit_token in a follow-up PR once it exists.

Question: do you want to (a) add write:issue to the existing security_bot_token PAT, or (b) create a separate minimally-scoped token just for this pipeline? Either works from my side — just let me know which and I'll wire it up once it's created.

## Fixed (PR): tool-surface hardening PR #238 (https://gitea.renner.uno/gitea_admin/homelab/pulls/238): drops `WebSearch` (unlike `WebFetch`, it can't be domain-scoped — verified live), adds `ci/agents/update-audit/settings.json` denying `Read` on `/proc`, `/root`, `/home`, `/etc`, and this repo's own `.git/` (blocks the env-var-dump and credential-file read paths — verified live against the pinned Claude Code CLI, not just per docs), and adds explicit prompt-injection-defense framing to `ci/agents/update-audit/prompt.md` (fetched content = untrusted data, never instructions, never echoed verbatim). Two things I deliberately did not do, both verified/reasoned in the PR body: - Write path-scoping — tested `Write(<path>)` allow/deny rules under `--permission-mode acceptEdits` (this pipeline's mode) and found them not enforced; shipping a control that looks restrictive but silently isn't seemed worse than documenting the gap. Low severity in practice: the agent has no Bash/Edit, and the pipeline only ever trusts `/tmp/update-audit.md`. - A WebFetch domain allowlist — the Helm-chart-version check legitimately needs to reach whichever domain each `HelmRelease`'s own already-trusted source URL points at (~20 charts, many different domains today); a fixed allowlist would silently drop audit coverage for anything added later. ## Needs operator action: token scoping The broad `gitea_token` secret is still what's bound to `GITEA_TOKEN` in this step — I can't fix that myself. Per the code comment history, `security_bot_token` was tried first and rejected because its Gitea PAT lacks `write:issue` scope, and switching to `gitea_token` (also used for registry/release writes in `build-ci-images.yml`) was the workaround. Proposal: mint a new Gitea PAT scoped to `write:issue` only (on whichever bot account you prefer — reusing `security-bot` if you're willing to add that one scope to it, or a fresh minimal identity), register it as a Woodpecker secret (e.g. `update_audit_token`), and I'll swap `from_secret: gitea_token` to `from_secret: update_audit_token` in a follow-up PR once it exists. Question: do you want to (a) add `write:issue` to the existing `security_bot_token` PAT, or (b) create a separate minimally-scoped token just for this pipeline? Either works from my side — just let me know which and I'll wire it up once it's created.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: konairius/homelab#197