From a445cd193d962c4a7e55dcc3f938c8368a211cc9 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:49:54 +0100 Subject: [PATCH 1/9] build: add git and cargo permissions to Claude Code workflow Allow Claude to run git fetch/merge/checkout/rebase/push and cargo build/test/clippy/fmt commands. Switch model to opus. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/claude.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 9d5239dd1..e20730012 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -34,4 +34,6 @@ jobs: id: claude uses: anthropics/claude-code-action@v1 with: + model: opus claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN_LKLIMEK }} + allowed_tools: "Bash(git fetch:*),Bash(git merge:*),Bash(git checkout:*),Bash(git rebase:*),Bash(git push:*),Bash(cargo build:*),Bash(cargo test:*),Bash(cargo clippy:*),Bash(cargo fmt:*)" From 1f1fb32244a7f6dbcf06dba82690691f3ad905f0 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:00:24 +0100 Subject: [PATCH 2/9] fix: use claude_args for model and allowed tools The `model` and `allowed_tools` inputs are not declared in claude-code-action@v1 and are silently ignored. Move them to `claude_args` with --model and --allowedTools flags. Also fix deprecated colon syntax (`:*`) to space syntax (` *`). Co-Authored-By: Claude Opus 4.6 --- .github/workflows/claude.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index e20730012..b1bf4a1d4 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -34,6 +34,7 @@ jobs: id: claude uses: anthropics/claude-code-action@v1 with: - model: opus claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN_LKLIMEK }} - allowed_tools: "Bash(git fetch:*),Bash(git merge:*),Bash(git checkout:*),Bash(git rebase:*),Bash(git push:*),Bash(cargo build:*),Bash(cargo test:*),Bash(cargo clippy:*),Bash(cargo fmt:*)" + claude_args: | + --model opus + --allowedTools "Bash(git fetch *),Bash(git merge *),Bash(git checkout *),Bash(git rebase *),Bash(git push *),Bash(cargo build *),Bash(cargo test *),Bash(cargo clippy *),Bash(cargo fmt *)" From c620496c7d84fcf318982eb14628c076666779cc Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:01:32 +0100 Subject: [PATCH 3/9] fix: use single quotes to prevent glob expansion in allowed tools Co-Authored-By: Claude Opus 4.6 --- .github/workflows/claude.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index b1bf4a1d4..837f0783e 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -37,4 +37,4 @@ jobs: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN_LKLIMEK }} claude_args: | --model opus - --allowedTools "Bash(git fetch *),Bash(git merge *),Bash(git checkout *),Bash(git rebase *),Bash(git push *),Bash(cargo build *),Bash(cargo test *),Bash(cargo clippy *),Bash(cargo fmt *)" + --allowedTools 'Bash(git fetch *),Bash(git merge *),Bash(git checkout *),Bash(git rebase *),Bash(git push *),Bash(cargo build *),Bash(cargo test *),Bash(cargo clippy *),Bash(cargo fmt *)' From c0a32c8e733c23d46427d7bdf2a9a6412220655e Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:06:50 +0100 Subject: [PATCH 4/9] build: sandbox cargo commands and add git permissions for Claude - Add safe-cargo.sh wrapper that strips CI secrets before running cargo - Use --allowedTools for git and safe-cargo, --disallowedTools for raw cargo - Document safe-cargo usage in CLAUDE.md Co-Authored-By: Claude Opus 4.6 --- .github/scripts/safe-cargo.sh | 15 +++++++++++++++ .github/workflows/claude.yml | 3 ++- CLAUDE.md | 11 +++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100755 .github/scripts/safe-cargo.sh diff --git a/.github/scripts/safe-cargo.sh b/.github/scripts/safe-cargo.sh new file mode 100755 index 000000000..0670cfb87 --- /dev/null +++ b/.github/scripts/safe-cargo.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Runs cargo in a sanitized environment without CI secrets. +# This prevents build.rs scripts and proc macros from accessing +# credentials via environment variables. +exec env \ + -u GITHUB_TOKEN \ + -u CLAUDE_CODE_OAUTH_TOKEN \ + -u CLAUDE_CODE_OAUTH_TOKEN_LKLIMEK \ + -u INPUT_CLAUDE_CODE_OAUTH_TOKEN \ + -u INPUT_ANTHROPIC_API_KEY \ + -u ANTHROPIC_API_KEY \ + -u ACTIONS_ID_TOKEN_REQUEST_TOKEN \ + -u ACTIONS_ID_TOKEN_REQUEST_URL \ + -u ACTIONS_RUNTIME_TOKEN \ + cargo "$@" diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 837f0783e..d306ff24a 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -37,4 +37,5 @@ jobs: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN_LKLIMEK }} claude_args: | --model opus - --allowedTools 'Bash(git fetch *),Bash(git merge *),Bash(git checkout *),Bash(git rebase *),Bash(git push *),Bash(cargo build *),Bash(cargo test *),Bash(cargo clippy *),Bash(cargo fmt *)' + --allowedTools 'Bash(git fetch *),Bash(git merge *),Bash(git checkout *),Bash(git rebase *),Bash(git push *),Bash(.github/scripts/safe-cargo.sh build *),Bash(.github/scripts/safe-cargo.sh test *),Bash(.github/scripts/safe-cargo.sh clippy *),Bash(.github/scripts/safe-cargo.sh fmt *)' + --disallowedTools 'Bash(cargo *)' diff --git a/CLAUDE.md b/CLAUDE.md index a1552d670..a061a4e18 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,6 +35,17 @@ Test locations: Always run `cargo clippy` and `cargo +nightly fmt` when finalizing your work. +## CI: Safe Cargo Wrapper + +In GitHub Actions (Claude Code workflow), use `.github/scripts/safe-cargo.sh` instead of `cargo` directly. This wrapper strips CI secrets from the environment before running cargo, preventing build scripts from accessing credentials. + +```bash +.github/scripts/safe-cargo.sh build --all-features +.github/scripts/safe-cargo.sh test --all-features --workspace +.github/scripts/safe-cargo.sh clippy --all-features --all-targets -- -D warnings +.github/scripts/safe-cargo.sh fmt --all +``` + ## Architecture Overview **Dash Evo Tool** is a cross-platform GUI application (Rust + egui) for interacting with Dash Evolution. It enables DPNS username registration, contest voting, state transition viewing, wallet management, and identity operations across Mainnet/Testnet/Devnet. From bebcdf1bf35c2e5f97f615bfbd5dc443275f5bfb Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:18:19 +0100 Subject: [PATCH 5/9] fix: switch safe-cargo.sh from denylist to allowlist approach Use `env -i` (start with empty environment, explicitly pass only what cargo needs) instead of `env -u` (strip known secrets). This is more robust against future secrets being added to the workflow. Co-Authored-By: Claude Opus 4.6 --- .github/scripts/safe-cargo.sh | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/.github/scripts/safe-cargo.sh b/.github/scripts/safe-cargo.sh index 0670cfb87..d3aabbb7d 100755 --- a/.github/scripts/safe-cargo.sh +++ b/.github/scripts/safe-cargo.sh @@ -1,15 +1,20 @@ #!/bin/bash +set -euo pipefail # Runs cargo in a sanitized environment without CI secrets. -# This prevents build.rs scripts and proc macros from accessing -# credentials via environment variables. -exec env \ - -u GITHUB_TOKEN \ - -u CLAUDE_CODE_OAUTH_TOKEN \ - -u CLAUDE_CODE_OAUTH_TOKEN_LKLIMEK \ - -u INPUT_CLAUDE_CODE_OAUTH_TOKEN \ - -u INPUT_ANTHROPIC_API_KEY \ - -u ANTHROPIC_API_KEY \ - -u ACTIONS_ID_TOKEN_REQUEST_TOKEN \ - -u ACTIONS_ID_TOKEN_REQUEST_URL \ - -u ACTIONS_RUNTIME_TOKEN \ +# Uses env -i (allowlist) instead of env -u (denylist) so that +# any new secrets added in the future are stripped automatically. +exec env -i \ + HOME="$HOME" \ + PATH="$PATH" \ + USER="${USER:-}" \ + SHELL="${SHELL:-/bin/bash}" \ + TMPDIR="${TMPDIR:-/tmp}" \ + LANG="${LANG:-C.UTF-8}" \ + TERM="${TERM:-dumb}" \ + CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}" \ + RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" \ + PROTOC="${PROTOC:-}" \ + CC="${CC:-}" \ + CXX="${CXX:-}" \ + PKG_CONFIG_PATH="${PKG_CONFIG_PATH:-}" \ cargo "$@" From 9deac6b12816f89a1d7696d776c166f04e200cda Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:22:00 +0100 Subject: [PATCH 6/9] build: deny Claude from editing CI scripts and workflows Prevent Claude from modifying .github/scripts/ and .github/workflows/ to ensure the safe-cargo wrapper cannot be tampered with. Co-Authored-By: Claude Opus 4.6 --- .claude/settings.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.claude/settings.json b/.claude/settings.json index b8d0bd970..9cb8d0bea 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,4 +1,12 @@ { + "permissions": { + "deny": [ + "Edit(.github/scripts/**)", + "Write(.github/scripts/**)", + "Edit(.github/workflows/**)", + "Write(.github/workflows/**)" + ] + }, "hooks": { "SessionStart": [ { From 9f17205d3b208f6e7176652b05be47570b9c9337 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:28:07 +0100 Subject: [PATCH 7/9] fix: conditionally pass optional env vars in safe-cargo.sh Empty PROTOC="" caused prost build scripts to fail with "protoc not found". Now optional vars (PROTOC, CC, CXX, etc.) are only passed when set and non-empty. Tested: build, test, fmt all pass through the wrapper. Co-Authored-By: Claude Opus 4.6 --- .github/scripts/safe-cargo.sh | 36 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/.github/scripts/safe-cargo.sh b/.github/scripts/safe-cargo.sh index d3aabbb7d..86c9c4faa 100755 --- a/.github/scripts/safe-cargo.sh +++ b/.github/scripts/safe-cargo.sh @@ -3,18 +3,24 @@ set -euo pipefail # Runs cargo in a sanitized environment without CI secrets. # Uses env -i (allowlist) instead of env -u (denylist) so that # any new secrets added in the future are stripped automatically. -exec env -i \ - HOME="$HOME" \ - PATH="$PATH" \ - USER="${USER:-}" \ - SHELL="${SHELL:-/bin/bash}" \ - TMPDIR="${TMPDIR:-/tmp}" \ - LANG="${LANG:-C.UTF-8}" \ - TERM="${TERM:-dumb}" \ - CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}" \ - RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" \ - PROTOC="${PROTOC:-}" \ - CC="${CC:-}" \ - CXX="${CXX:-}" \ - PKG_CONFIG_PATH="${PKG_CONFIG_PATH:-}" \ - cargo "$@" + +# Build the environment allowlist. Only pass variables that are set +# to avoid empty values confusing tools (e.g. PROTOC="" breaks prost). +ENV_ARGS=( + HOME="$HOME" + PATH="$PATH" + CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}" + RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" + TMPDIR="${TMPDIR:-/tmp}" + LANG="${LANG:-C.UTF-8}" + TERM="${TERM:-dumb}" +) + +# Conditionally pass optional variables only if they are set and non-empty. +for var in PROTOC CC CXX PKG_CONFIG_PATH USER SHELL; do + if [ -n "${!var:-}" ]; then + ENV_ARGS+=("$var=${!var}") + fi +done + +exec env -i "${ENV_ARGS[@]}" cargo "$@" From d8c4ceb72f15c10e2512c54e4729d6fbc77f24c2 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:40:15 +0100 Subject: [PATCH 8/9] rabbit feedback --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index a061a4e18..46675755a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -43,7 +43,7 @@ In GitHub Actions (Claude Code workflow), use `.github/scripts/safe-cargo.sh` in .github/scripts/safe-cargo.sh build --all-features .github/scripts/safe-cargo.sh test --all-features --workspace .github/scripts/safe-cargo.sh clippy --all-features --all-targets -- -D warnings -.github/scripts/safe-cargo.sh fmt --all +.github/scripts/safe-cargo.sh +nightly fmt --all ``` ## Architecture Overview From cbd59c2768421eeeeba5c2c43fc567a77eddca68 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:57:11 +0100 Subject: [PATCH 9/9] build: move safe-cargo.sh to scripts/ and allow +nightly fmt Move safe-cargo.sh from .github/scripts/ to top-level scripts/ for better discoverability. Add detailed comment explaining why the wrapper exists (prevent CI secret exfiltration via build scripts). Update all references in claude.yml, CLAUDE.md, and permission settings. Add `+nightly fmt` to allowedTools so Claude can follow CLAUDE.md formatting instructions in CI. Co-Authored-By: Claude Opus 4.6 --- .claude/settings.json | 4 ++-- .github/scripts/safe-cargo.sh | 26 --------------------- .github/workflows/claude.yml | 2 +- CLAUDE.md | 10 ++++---- scripts/safe-cargo.sh | 44 +++++++++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 34 deletions(-) delete mode 100755 .github/scripts/safe-cargo.sh create mode 100755 scripts/safe-cargo.sh diff --git a/.claude/settings.json b/.claude/settings.json index 9cb8d0bea..524891982 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,8 +1,8 @@ { "permissions": { "deny": [ - "Edit(.github/scripts/**)", - "Write(.github/scripts/**)", + "Edit(scripts/**)", + "Write(scripts/**)", "Edit(.github/workflows/**)", "Write(.github/workflows/**)" ] diff --git a/.github/scripts/safe-cargo.sh b/.github/scripts/safe-cargo.sh deleted file mode 100755 index 86c9c4faa..000000000 --- a/.github/scripts/safe-cargo.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -set -euo pipefail -# Runs cargo in a sanitized environment without CI secrets. -# Uses env -i (allowlist) instead of env -u (denylist) so that -# any new secrets added in the future are stripped automatically. - -# Build the environment allowlist. Only pass variables that are set -# to avoid empty values confusing tools (e.g. PROTOC="" breaks prost). -ENV_ARGS=( - HOME="$HOME" - PATH="$PATH" - CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}" - RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" - TMPDIR="${TMPDIR:-/tmp}" - LANG="${LANG:-C.UTF-8}" - TERM="${TERM:-dumb}" -) - -# Conditionally pass optional variables only if they are set and non-empty. -for var in PROTOC CC CXX PKG_CONFIG_PATH USER SHELL; do - if [ -n "${!var:-}" ]; then - ENV_ARGS+=("$var=${!var}") - fi -done - -exec env -i "${ENV_ARGS[@]}" cargo "$@" diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index d306ff24a..b0607826b 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -37,5 +37,5 @@ jobs: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN_LKLIMEK }} claude_args: | --model opus - --allowedTools 'Bash(git fetch *),Bash(git merge *),Bash(git checkout *),Bash(git rebase *),Bash(git push *),Bash(.github/scripts/safe-cargo.sh build *),Bash(.github/scripts/safe-cargo.sh test *),Bash(.github/scripts/safe-cargo.sh clippy *),Bash(.github/scripts/safe-cargo.sh fmt *)' + --allowedTools 'Bash(git fetch *),Bash(git merge *),Bash(git checkout *),Bash(git rebase *),Bash(git push *),Bash(scripts/safe-cargo.sh build *),Bash(scripts/safe-cargo.sh test *),Bash(scripts/safe-cargo.sh clippy *),Bash(scripts/safe-cargo.sh +nightly fmt *),Bash(scripts/safe-cargo.sh fmt *)' --disallowedTools 'Bash(cargo *)' diff --git a/CLAUDE.md b/CLAUDE.md index 46675755a..4d92446c2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,13 +37,13 @@ Always run `cargo clippy` and `cargo +nightly fmt` when finalizing your work. ## CI: Safe Cargo Wrapper -In GitHub Actions (Claude Code workflow), use `.github/scripts/safe-cargo.sh` instead of `cargo` directly. This wrapper strips CI secrets from the environment before running cargo, preventing build scripts from accessing credentials. +In GitHub Actions (Claude Code workflow), use `scripts/safe-cargo.sh` instead of `cargo` directly. This wrapper strips CI secrets from the environment before running cargo, preventing build scripts from accessing credentials. ```bash -.github/scripts/safe-cargo.sh build --all-features -.github/scripts/safe-cargo.sh test --all-features --workspace -.github/scripts/safe-cargo.sh clippy --all-features --all-targets -- -D warnings -.github/scripts/safe-cargo.sh +nightly fmt --all +scripts/safe-cargo.sh build --all-features +scripts/safe-cargo.sh test --all-features --workspace +scripts/safe-cargo.sh clippy --all-features --all-targets -- -D warnings +scripts/safe-cargo.sh +nightly fmt --all ``` ## Architecture Overview diff --git a/scripts/safe-cargo.sh b/scripts/safe-cargo.sh new file mode 100755 index 000000000..b299eaa3f --- /dev/null +++ b/scripts/safe-cargo.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -euo pipefail +# +# safe-cargo.sh — Run cargo without CI secrets leaking to build scripts. +# +# WHY THIS FILE EXISTS +# -------------------- +# Cargo build scripts (build.rs / proc-macros) execute arbitrary code during +# compilation. In CI the runner environment contains secrets such as +# CLAUDE_CODE_OAUTH_TOKEN and GITHUB_TOKEN. A compromised or malicious +# dependency could read those variables and exfiltrate them. +# +# This wrapper uses `env -i` (an allowlist approach) so that cargo and every +# child process it spawns start with only the variables listed below. +# Any new secret added to CI in the future is automatically excluded without +# having to update a denylist. +# +# USAGE (GitHub Actions) +# scripts/safe-cargo.sh build --all-features +# scripts/safe-cargo.sh test --all-features --workspace +# scripts/safe-cargo.sh clippy --all-features --all-targets -- -D warnings +# scripts/safe-cargo.sh +nightly fmt --all +# + +# Build the environment allowlist. Only pass variables that are set +# to avoid empty values confusing tools (e.g. PROTOC="" breaks prost). +ENV_ARGS=( + HOME="$HOME" + PATH="$PATH" + CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}" + RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" + TMPDIR="${TMPDIR:-/tmp}" + LANG="${LANG:-C.UTF-8}" + TERM="${TERM:-dumb}" +) + +# Conditionally pass optional variables only if they are set and non-empty. +for var in PROTOC CC CXX PKG_CONFIG_PATH USER SHELL; do + if [ -n "${!var:-}" ]; then + ENV_ARGS+=("$var=${!var}") + fi +done + +exec env -i "${ENV_ARGS[@]}" cargo "$@"