Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/skills/running-tend/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
No project-specific tend preferences yet. Add guidance here as
needed — this file is loaded by tend workflows alongside AGENTS.md.
11 changes: 11 additions & 0 deletions .config/tend.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
bot_name: dormouse-bot

secrets:
allowed:
- CHROMATIC_PROJECT_TOKEN

workflows:
ci-fix:
watched_workflows:
- CI
- Chromatic
6 changes: 6 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,17 @@ jobs:
vscode-ext/*.vsix
vscode-ext/artifact-manifest.sha256

security-audit:
name: Security audit
uses: ./.github/workflows/security-audit.yaml
secrets: inherit

publish-vscode:
name: Publish VSCode Extension
needs:
- build-standalone
- build-vscode
- security-audit
runs-on: ubuntu-latest
environment:
name: vscode-extension-publish
Expand Down
133 changes: 133 additions & 0 deletions .github/workflows/security-audit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: security-audit

# Audits this repo against SECURITY.md. Reusable: runs nightly via the
# schedule trigger, on-demand via workflow_dispatch, and is called from
# release.yml as a precondition to publishing.

on:
schedule:
- cron: "21 4 * * *"
workflow_dispatch:
workflow_call:

permissions:
contents: read
actions: read
issues: write

jobs:
audit:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22

- uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with:
version: 11.0.6

- name: Install workspace dependencies
run: pnpm install --frozen-lockfile

- name: Audit against SECURITY.md
uses: anthropics/claude-code-action@4481e6d3c7bbb88db2a928ca3444c536f589c7c1 # v1
env:
GH_TOKEN: ${{ github.token }}
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
You are auditing this repository against SECURITY.md. The
specifications are concrete `FAIL IF` lines plus the
explicit clause that the list is not exhaustive — any code
change that creates a security hole or reveals an existing
one should fail this job.

Process:
1. Read SECURITY.md.
2. For each `FAIL IF` line, identify the mechanical check
(gh api, grep, file presence, running a script) and
execute it. Record PASS or FAIL with concrete evidence
— file path and line number, API response excerpt, or
command output.
3. After the FAIL IF list is exhausted, do a qualitative
pass. Inspect `.github/workflows/`, `.config/tend.yaml`,
`.github/dependabot.yml`, `scripts/`, and any code that
references secrets, for security holes the specs don't
cover.

Produce a Markdown report with three sections:
- `## FAIL IF results` — one line per check with PASS/FAIL
and concrete evidence
- `## Qualitative findings` — free-form findings with
severity (BLOCKER / WARNING / INFO)
- `## Summary` — overall PASS or FAIL with a one-paragraph
rationale

Write the report to `audit-report.md` in the workspace.
Write `PASS` or `FAIL` (no other text, no newline required)
to `audit-status.txt`. Status is FAIL if any `FAIL IF` is
violated or any qualitative finding is BLOCKER severity.
Do not call `exit`; the next workflow step inspects the
status file and surfaces the result.

Available environment: `$GH_TOKEN` is the workflow's
GitHub token (read repo, write issues), `$GITHUB_REPOSITORY`
is `owner/name`. Use `gh api` for GitHub configuration
queries (rulesets, secrets, environments, collaborators).

- name: Surface result, file or close issue
if: always()
env:
GH_TOKEN: ${{ github.token }}
run: |
set -eo pipefail

STATUS=$(tr -d '[:space:]' < audit-status.txt 2>/dev/null || echo "FAIL")
DATE=$(date -u +%Y-%m-%dT%H:%MZ)
RUN_URL="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"

# Idempotent label creation; ignore "already exists" errors.
gh label create security-audit-failure \
--color B60205 --description "Security audit failure" 2>/dev/null || true

if [ "$STATUS" = "PASS" ]; then
# Auto-close any open audit-failure issues so the issue
# tracker reflects the live state.
for n in $(gh issue list --label security-audit-failure \
--state open --json number --jq '.[].number'); do
gh issue close "$n" --comment "Audit passed at $DATE. [Run]($RUN_URL)"
done
echo "Audit passed."
exit 0
fi

if [ ! -s audit-report.md ]; then
printf '%s\n' \
"Audit step produced no \`audit-report.md\`. See workflow run logs." \
> audit-report.md
fi

{
echo "Audit failed at $DATE. [Run]($RUN_URL)"
echo
cat audit-report.md
} > audit-comment.md

EXISTING=$(gh issue list --label security-audit-failure \
--state open --json number --jq '.[0].number' || true)
if [ -n "$EXISTING" ]; then
gh issue comment "$EXISTING" --body-file audit-comment.md
echo "Appended re-audit failure to issue #$EXISTING"
else
gh issue create \
--title "[security-audit] FAIL on $(date -u +%Y-%m-%d)" \
--label security-audit-failure \
--body-file audit-comment.md
fi
exit 1
44 changes: 44 additions & 0 deletions .github/workflows/tend-ci-fix.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by tend 0.0.25. Regenerate with: uvx tend@latest init
#
# Do not edit this file directly — it will be overwritten on regeneration.
# To customize behavior, edit the relevant skill (for example,
# `running-tend`) in this repo's .claude/skills/ directory, or open an issue at
# https://github.com/max-sixty/tend/issues for changes that need to
# happen upstream in the tend-ci-runner plugin.

name: tend-ci-fix
on:
workflow_run:
workflows: ["CI", "Chromatic"]
types: [completed]
branches: ["main"]

jobs:
fix-ci:
if: github.repository_owner == 'diffplug' && github.event.workflow_run.conclusion == 'failure'
runs-on: ubuntu-24.04
permissions:
contents: write
pull-requests: write
id-token: write
actions: read
steps:
- uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.TEND_BOT_TOKEN }}

- uses: max-sixty/tend@0.0.25
with:
github_token: ${{ secrets.TEND_BOT_TOKEN }}
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
bot_name: dormouse-bot
model: opus
prompt: |
/tend-ci-runner:ci-fix ${{ github.event.workflow_run.id }}
- Run URL: ${{ github.event.workflow_run.html_url }}
- Commit: ${{ github.event.workflow_run.head_sha }}
- Commit message: ${{ github.event.workflow_run.head_commit.message }}
71 changes: 71 additions & 0 deletions .github/workflows/tend-install-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Generated by tend 0.0.25. Regenerate with: uvx tend@latest init
#
# Do not edit this file directly — it will be overwritten on regeneration.
# To customize behavior, edit the relevant skill (for example,
# `running-tend`) in this repo's .claude/skills/ directory, or open an issue at
# https://github.com/max-sixty/tend/issues for changes that need to
# happen upstream in the tend-ci-runner plugin.

name: tend-install-test
on:
pull_request:
paths:
- .github/workflows/tend-*.yaml
- .config/tend.yaml

jobs:
install-test:
# Same-repo PRs only. Fork PRs don't carry secrets and the workflow
# is short-lived (removed on the next nightly regen), so cross-fork
# validation isn't worth special-casing.
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v6
- name: Verify required secrets are set
env:
BOT_TOKEN: ${{ secrets.TEND_BOT_TOKEN }}
CLAUDE_OAUTH: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
ANTHROPIC_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
missing=""
[ -n "$BOT_TOKEN" ] || missing="$missing TEND_BOT_TOKEN"
if [ -z "$CLAUDE_OAUTH$ANTHROPIC_KEY" ]; then
missing="$missing harness-auth(set CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY)"
fi
if [ -n "$missing" ]; then
echo "::error::Missing repo secrets:$missing"
exit 1
fi
- name: Verify generator output matches committed files
env:
GH_TOKEN: ${{ github.token }}
run: |
# actions/checkout's default shallow clone leaves
# refs/remotes/origin/<default-branch> unfetched, so
# `git remote set-head origin --auto` errors with
# "Not a valid ref: refs/remotes/origin/main" before any
# default-branch detection runs (tend issue #582). Query the
# API and set the symbolic-ref directly.
DEFAULT_BRANCH=$(gh api "repos/$GITHUB_REPOSITORY" --jq .default_branch)
git fetch origin --depth 1 "+refs/heads/$DEFAULT_BRANCH:refs/remotes/origin/$DEFAULT_BRANCH"
git symbolic-ref refs/remotes/origin/HEAD "refs/remotes/origin/$DEFAULT_BRANCH"
# Pin the regen to the version in the committed header so a tend
# release between local `init` and this CI run doesn't fail the
# drift check for an irrelevant reason.
HEADER=$(head -1 .github/workflows/tend-install-test.yaml)
HEADER="${HEADER#'# Generated by tend '}"
TEND_VERSION="${HEADER%%'. Regenerate'*}"
uvx "tend@$TEND_VERSION" init --with-install-test
# Exclude this file from the drift comparison: it's locally
# patched to work around tend issue #582, so it diverges from
# generator output by design until upstream lands a fix. The
# file is removed on the next nightly regen anyway.
if ! git diff --quiet -- ':!.github/workflows/tend-install-test.yaml' .github/workflows/; then
echo "::error::Committed workflows differ from 'uvx tend@$TEND_VERSION init --with-install-test' output. Run it locally and commit the result."
git --no-pager diff -- ':!.github/workflows/tend-install-test.yaml' .github/workflows/
exit 1
fi
Loading
Loading