From f07fd36d74b6e595a32465f7c99693fb4a8d0990 Mon Sep 17 00:00:00 2001 From: dormouse-bot <287024035+dormouse-bot@users.noreply.github.com> Date: Tue, 22 Sep 2026 11:46:20 +0000 Subject: [PATCH 1/2] =?UTF-8?q?chore:=20update=20tend=20workflows=20(0.2.1?= =?UTF-8?q?3=20=E2=86=92=200.2.15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/tend-ci-fix.yaml | 4 ++-- .github/workflows/tend-mention-relay.yaml | 2 +- .github/workflows/tend-mention.yaml | 20 +++++++++++--------- .github/workflows/tend-nightly.yaml | 4 ++-- .github/workflows/tend-notifications.yaml | 6 +++--- .github/workflows/tend-review-runs.yaml | 4 ++-- .github/workflows/tend-review.yaml | 10 +++++----- .github/workflows/tend-triage.yaml | 4 ++-- .github/workflows/tend-weekly.yaml | 4 ++-- 9 files changed, 30 insertions(+), 28 deletions(-) diff --git a/.github/workflows/tend-ci-fix.yaml b/.github/workflows/tend-ci-fix.yaml index 1203d7e05..a9613d5f5 100644 --- a/.github/workflows/tend-ci-fix.yaml +++ b/.github/workflows/tend-ci-fix.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.13. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.15. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To change this workflow, edit .config/tend.yaml. To change what the agent @@ -42,7 +42,7 @@ jobs: fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.13 + - uses: max-sixty/tend/claude@0.2.15 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-mention-relay.yaml b/.github/workflows/tend-mention-relay.yaml index c88dc2813..5120fbffa 100644 --- a/.github/workflows/tend-mention-relay.yaml +++ b/.github/workflows/tend-mention-relay.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.13. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.15. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To change this workflow, edit .config/tend.yaml. To change what the agent diff --git a/.github/workflows/tend-mention.yaml b/.github/workflows/tend-mention.yaml index 68d4885fb..818dde318 100644 --- a/.github/workflows/tend-mention.yaml +++ b/.github/workflows/tend-mention.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.13. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.15. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To change this workflow, edit .config/tend.yaml. To change what the agent @@ -50,7 +50,7 @@ jobs: url: ${{ steps.check.outputs.url }} ts: ${{ steps.check.outputs.ts }} steps: - - uses: astral-sh/setup-uv@v10.1.0 + - uses: astral-sh/setup-uv@v10.2.0 id: tend_uv env: UV_NO_MODIFY_PATH: "1" @@ -326,12 +326,8 @@ jobs: || format('pulls/comments/{0}', github.event.client_payload.id) }} GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} - - uses: actions/checkout@v7 - with: - fetch-depth: 0 - fetch-tags: true - token: ${{ secrets.TEND_BOT_TOKEN }} - + # Before checkout and `setup:`, so the delay measures the wait for this + # job to start rather than the time its setup takes. - name: Compute queue delay id: delay run: | @@ -346,7 +342,13 @@ jobs: # the API record — the dispatch payload never carries one to spoof. EVENT_TS: ${{ github.event.comment.updated_at || needs.verify.outputs.ts || github.event.issue.updated_at }} - - uses: max-sixty/tend/claude@0.2.13 + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + fetch-tags: true + token: ${{ secrets.TEND_BOT_TOKEN }} + + - uses: max-sixty/tend/claude@0.2.15 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-nightly.yaml b/.github/workflows/tend-nightly.yaml index ac97b3e51..a1c1a67f7 100644 --- a/.github/workflows/tend-nightly.yaml +++ b/.github/workflows/tend-nightly.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.13. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.15. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To change this workflow, edit .config/tend.yaml. To change what the agent @@ -33,7 +33,7 @@ jobs: fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.13 + - uses: max-sixty/tend/claude@0.2.15 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-notifications.yaml b/.github/workflows/tend-notifications.yaml index 18452deaf..e51c30ac8 100644 --- a/.github/workflows/tend-notifications.yaml +++ b/.github/workflows/tend-notifications.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.13. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.15. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To change this workflow, edit .config/tend.yaml. To change what the agent @@ -29,7 +29,7 @@ jobs: actions: read issues: write steps: - - uses: astral-sh/setup-uv@v10.1.0 + - uses: astral-sh/setup-uv@v10.2.0 id: tend_uv env: UV_NO_MODIFY_PATH: "1" @@ -244,7 +244,7 @@ jobs: fetch-depth: 0 fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.13 + - uses: max-sixty/tend/claude@0.2.15 if: steps.check.outputs.count != '0' || steps.check.outputs.conflict_count != '0' || github.event_name == 'workflow_dispatch' with: github_token: ${{ secrets.TEND_BOT_TOKEN }} diff --git a/.github/workflows/tend-review-runs.yaml b/.github/workflows/tend-review-runs.yaml index 25c3098c1..a594e5e34 100644 --- a/.github/workflows/tend-review-runs.yaml +++ b/.github/workflows/tend-review-runs.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.13. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.15. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To change this workflow, edit .config/tend.yaml. To change what the agent @@ -33,7 +33,7 @@ jobs: fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.13 + - uses: max-sixty/tend/claude@0.2.15 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-review.yaml b/.github/workflows/tend-review.yaml index 1598d00f5..649972f76 100644 --- a/.github/workflows/tend-review.yaml +++ b/.github/workflows/tend-review.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.13. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.15. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To change this workflow, edit .config/tend.yaml. To change what the agent @@ -42,16 +42,16 @@ jobs: TARGET: issues/${{ github.event.pull_request.number }} GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} - # The runner checkout remains on the reviewed base tree for local setup - # actions and their POST chains. The harness creates and selects the PR - # topology in an independent disposable clone. + # The runner checkout stays on the reviewed base tree for `setup:` and + # local actions' POST chains; the harness moves the agent's view of it to + # the PR's topology. - uses: actions/checkout@v7 with: fetch-depth: 0 fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.13 + - uses: max-sixty/tend/claude@0.2.15 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-triage.yaml b/.github/workflows/tend-triage.yaml index ef0a28848..9f56e0c81 100644 --- a/.github/workflows/tend-triage.yaml +++ b/.github/workflows/tend-triage.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.13. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.15. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To change this workflow, edit .config/tend.yaml. To change what the agent @@ -45,7 +45,7 @@ jobs: fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.13 + - uses: max-sixty/tend/claude@0.2.15 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-weekly.yaml b/.github/workflows/tend-weekly.yaml index 9f5db9596..e57513f68 100644 --- a/.github/workflows/tend-weekly.yaml +++ b/.github/workflows/tend-weekly.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.13. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.15. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To change this workflow, edit .config/tend.yaml. To change what the agent @@ -33,7 +33,7 @@ jobs: fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.13 + - uses: max-sixty/tend/claude@0.2.15 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} From 39c8d6d4a907119bca12e84d3789137d69391c2d Mon Sep 17 00:00:00 2001 From: dormouse-bot <287024035+dormouse-bot@users.noreply.github.com> Date: Tue, 22 Sep 2026 11:55:20 +0000 Subject: [PATCH 2/2] docs: security-ci describes the agent's copy-on-write view, not a clone tend 0.2.14 replaced the harness's separate clone with a copy-on-write view of the job's own checkout (max-sixty/tend#1346), which is what the reworded tend-review.yaml comment in this PR reflects. The spec's mechanism sentence still named the clone; the revert rule and the 0.1.19 floor it backs are unchanged. Also refreshes the rationale's note of the version the workflows currently run. --- docs/specs/security-ci.md | 2 +- docs/specs/security-ci.rationale.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/specs/security-ci.md b/docs/specs/security-ci.md index b159b712a..6744139c9 100644 --- a/docs/specs/security-ci.md +++ b/docs/specs/security-ci.md @@ -26,7 +26,7 @@ This repository runs the [tend](https://github.com/max-sixty/tend) agent harness **Prompt-injection through user-supplied content.** The harness reads PR descriptions, code diffs, issue text, comments, and CI logs — all attacker-influenceable. **Assume a malicious prompt can push a workflow that sends a repo-level secret to an external URL**: admin-gated release paths stay sealed, but a workflow on a bot-pushed feature branch still executes with repo-level secrets in scope. -**Instruction files are part of that surface.** On a fork PR the privileged `pull_request_target` runner workspace holds the base tree; the attacker-controlled PR tree exists only in the harness's own disposable clone, and that is where the *project instructions* Claude Code loads (`CLAUDE.md`, `AGENTS.md`, `.claude/`, `.mcp.json`) come from. **Must revert those paths from the reviewed base branch before the agent starts**, so instructions come from code a maintainer merged — tend's `shared/steps/restore-sensitive-config.sh` does it. **That control's completeness is a property of the pinned upstream version, not of anything in this repo** — hence the `0.1.19` floor below (rationale). +**Instruction files are part of that surface.** On a fork PR the privileged `pull_request_target` runner workspace holds the base tree; the attacker-controlled PR tree exists only in the agent's copy-on-write view, and that is where the *project instructions* Claude Code loads (`CLAUDE.md`, `AGENTS.md`, `.claude/`, `.mcp.json`) come from. **Must revert those paths from the reviewed base branch before the agent starts**, so instructions come from code a maintainer merged — tend's `shared/steps/restore-sensitive-config.sh` does it. **That control's completeness is a property of the pinned upstream version, not of anything in this repo** — hence the `0.1.19` floor below (rationale). **Credential isolation bounds an injection.** The agent runs as a separate, non-sudo sandbox user behind a local credential-injecting proxy. **`TEND_BOT_TOKEN` and the Anthropic credential must live only in that proxy — never in the agent's environment, its disk, or `.git/config`** — setup strips the credential `actions/checkout` persists there, so an injection can make the bot *act* within its permissions, never read a token value out (rationale). diff --git a/docs/specs/security-ci.rationale.md b/docs/specs/security-ci.rationale.md index ab9fc3873..e014bd84b 100644 --- a/docs/specs/security-ci.rationale.md +++ b/docs/specs/security-ci.rationale.md @@ -10,7 +10,7 @@ **Why instruction files are a class of their own.** They are not read as data the way a diff is; Claude Code loads them as authoritative guidance, which is what makes a fork PR's copy of them a different class of input from the fork's code. -**The `0.1.18` gap, reported from this audit and now fixed.** At the previously pinned `0.1.18` the revert list was a flat, root-relative `SENSITIVE` array naming `CLAUDE.md` but no `AGENTS.md` at all — and this repo keeps its instructions in `AGENTS.md` with `CLAUDE.md` as a one-line `@AGENTS.md` pointer, so the control reverted a pointer and left the content it pointed at attacker-controlled. The fix ([max-sixty/tend#1005](https://github.com/max-sixty/tend/pull/1005), merged 2026-08-22, released in `0.1.19` on 2026-08-26) replaces that list with pathspec globs — `':(glob)**/AGENTS.md'`, `':(glob)**/CLAUDE.md'`, `':(glob)**/.claude/**'` — which `restore-sensitive-config.sh` passes to `pin_to_base`, covering every depth rather than a hand-enumerated set of root paths. `0.1.19` remains the minimum security floor; the version each workflow actually runs is the one in its own generated header, `0.2.13` at the September 2026 inspection. +**The `0.1.18` gap, reported from this audit and now fixed.** At the previously pinned `0.1.18` the revert list was a flat, root-relative `SENSITIVE` array naming `CLAUDE.md` but no `AGENTS.md` at all — and this repo keeps its instructions in `AGENTS.md` with `CLAUDE.md` as a one-line `@AGENTS.md` pointer, so the control reverted a pointer and left the content it pointed at attacker-controlled. The fix ([max-sixty/tend#1005](https://github.com/max-sixty/tend/pull/1005), merged 2026-08-22, released in `0.1.19` on 2026-08-26) replaces that list with pathspec globs — `':(glob)**/AGENTS.md'`, `':(glob)**/CLAUDE.md'`, `':(glob)**/.claude/**'` — which `restore-sensitive-config.sh` passes to `pin_to_base`, covering every depth rather than a hand-enumerated set of root paths. `0.1.19` remains the minimum security floor; the version each workflow actually runs is the one in its own generated header, `0.2.15` at the September 2026 inspection. **The local remedy if it ever regresses.** The nightly regen overwrites the *workflow*, not this repository's instruction files, so moving the instruction body into `CLAUDE.md` and dropping the pointer would close it with no upstream dependency, at the cost of the filename convention other agent harnesses read.