diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index c9ea38685d..3688f8f2b9 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -125,6 +125,11 @@ "version": "v2.0.3", "sha": "e95548e56dfa95d4e1a28d6f422fafe75c4c26fb" }, + "docker/build-push-action@v6": { + "repo": "docker/build-push-action", + "version": "v6", + "sha": "ee4ca427a2f43b6a16632044ca514c076267da23" + }, "docker/build-push-action@v6.18.0": { "repo": "docker/build-push-action", "version": "v6.18.0", diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml index 23014bec0d..23b191bde7 100644 --- a/.github/workflows/agent-persona-explorer.lock.yml +++ b/.github/workflows/agent-persona-explorer.lock.yml @@ -158,7 +158,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index b55ccb5d57..344c82e10f 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -182,8 +182,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | trending-data-${{ github.workflow }}- - trending-data- - trending- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/chroma-issue-indexer.lock.yml b/.github/workflows/chroma-issue-indexer.lock.yml index 7a15b473e9..b6978bd20e 100644 --- a/.github/workflows/chroma-issue-indexer.lock.yml +++ b/.github/workflows/chroma-issue-indexer.lock.yml @@ -110,8 +110,6 @@ jobs: path: /tmp/gh-aw/cache-memory-chroma restore-keys: | memory-chroma-${{ github.workflow }}- - memory-chroma- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/ci-coach.lock.yml b/.github/workflows/ci-coach.lock.yml index de9a4fc5e3..2c760f694b 100644 --- a/.github/workflows/ci-coach.lock.yml +++ b/.github/workflows/ci-coach.lock.yml @@ -166,7 +166,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 4d40e4c84b..6fd46c0b3f 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -138,7 +138,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/claude-code-user-docs-review.lock.yml b/.github/workflows/claude-code-user-docs-review.lock.yml index 18f213892a..6ec04ccda7 100644 --- a/.github/workflows/claude-code-user-docs-review.lock.yml +++ b/.github/workflows/claude-code-user-docs-review.lock.yml @@ -124,7 +124,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 69aea5f398..e4ba45d06c 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -128,7 +128,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index 0ee5b525c8..5ad96a4096 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -225,8 +225,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | cloclo-memory-${{ github.workflow }}- - cloclo-memory- - cloclo- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml index 9a6c3226db..d624672c03 100644 --- a/.github/workflows/code-scanning-fixer.lock.yml +++ b/.github/workflows/code-scanning-fixer.lock.yml @@ -123,7 +123,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (campaigns) env: diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 3fb3208558..3ff73f3980 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -135,8 +135,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | copilot-pr-data- - copilot-pr- - copilot- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index d9f87c205f..064dc5349d 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -164,8 +164,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | copilot-pr-data- - copilot-pr- - copilot- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index ff8ea40e9b..7155ebf2da 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -135,8 +135,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | copilot-pr-data- - copilot-pr- - copilot- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index 2c55a77365..71eef863b0 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -160,7 +160,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index 2556b2b0f4..6786213f50 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -149,7 +149,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/daily-compiler-quality.lock.yml b/.github/workflows/daily-compiler-quality.lock.yml index f6f9282806..2fe3edbc54 100644 --- a/.github/workflows/daily-compiler-quality.lock.yml +++ b/.github/workflows/daily-compiler-quality.lock.yml @@ -124,7 +124,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml index e173925332..f2f4fe8849 100644 --- a/.github/workflows/daily-copilot-token-report.lock.yml +++ b/.github/workflows/daily-copilot-token-report.lock.yml @@ -182,7 +182,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index d943e004cc..3f0024ddba 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -123,7 +123,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index 4d3faa42fe..e989e678c9 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -181,8 +181,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | trending-data-${{ github.workflow }}- - trending-data- - trending- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index e59240668a..c9ca1704ed 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -162,7 +162,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml index 450ddd4ae9..2672ec232c 100644 --- a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml +++ b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml @@ -124,7 +124,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 3beb4543e8..8242681028 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -220,7 +220,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index ec50bb4162..6ef1790afc 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -151,8 +151,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | trending-data-${{ github.workflow }}- - trending-data- - trending- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index ac9c91043b..bb5f8780e2 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -149,7 +149,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml index a1c928655c..019dcd3ed2 100644 --- a/.github/workflows/daily-safe-output-optimizer.lock.yml +++ b/.github/workflows/daily-safe-output-optimizer.lock.yml @@ -166,7 +166,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index 6162d00a3e..948a2d37ea 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -166,8 +166,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | weekly-issues-data- - weekly-issues- - weekly- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index fb40b035e8..4ef124a90b 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -125,8 +125,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | developer-docs-cache- - developer-docs- - developer- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/firewall-escape.lock.yml b/.github/workflows/firewall-escape.lock.yml index 9b4ad6f484..b70c472411 100644 --- a/.github/workflows/firewall-escape.lock.yml +++ b/.github/workflows/firewall-escape.lock.yml @@ -133,7 +133,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- # Repo memory git-based storage configuration from frontmatter processed below - name: Clone repo-memory branch (default) env: diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml index 54659e16c9..6bd01e446c 100644 --- a/.github/workflows/github-mcp-structural-analysis.lock.yml +++ b/.github/workflows/github-mcp-structural-analysis.lock.yml @@ -150,7 +150,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index 9f0cd10933..2f77526e63 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -127,7 +127,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index 38f918cdf7..7c16c323a4 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -136,7 +136,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index 8db9dc7bd5..cfc8aa058e 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -124,7 +124,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index 93599ebcac..aa5169e966 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -140,7 +140,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index 1bef6e4b19..5ee401251f 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -157,7 +157,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index ba08075adb..581d28ca7a 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -123,7 +123,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index 61a5228b37..8e7e28efb5 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -133,7 +133,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index e742a799c5..52d56eb6d5 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -124,7 +124,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 2c59a3356a..6bbdfc1c1d 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -180,7 +180,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml index 4fb1e786a0..bff71e2d70 100644 --- a/.github/workflows/org-health-report.lock.yml +++ b/.github/workflows/org-health-report.lock.yml @@ -153,7 +153,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index a5e8f73264..7e4cb19e0b 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -174,7 +174,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 5ed797eada..99b4122472 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -162,8 +162,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | poem-memory-${{ github.workflow }}- - poem-memory- - poem- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml index 784b95114e..2fb995c54c 100644 --- a/.github/workflows/portfolio-analyst.lock.yml +++ b/.github/workflows/portfolio-analyst.lock.yml @@ -188,8 +188,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | trending-data-${{ github.workflow }}- - trending-data- - trending- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index 0d0168b887..77c2e5ae99 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -173,7 +173,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index 079a87c579..6cb9bd5554 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -210,8 +210,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | trending-data-${{ github.workflow }}- - trending-data- - trending- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index 0a72f0e98c..370f4e00d2 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -177,7 +177,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index c89a186c74..798def2525 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -214,7 +214,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 3a4828e86d..626adc4d78 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -1196,7 +1196,7 @@ jobs: - name: Setup Docker Buildx (pre-validation) uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - name: Build Docker image (validation only) - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6 + uses: docker/build-push-action@ee4ca427a2f43b6a16632044ca514c076267da23 # v6 with: build-args: | BINARY=dist/linux-amd64 @@ -1285,7 +1285,7 @@ jobs: type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Docker image (amd64) id: build - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6 + uses: docker/build-push-action@ee4ca427a2f43b6a16632044ca514c076267da23 # v6 with: build-args: | BINARY=dist/linux-amd64 diff --git a/.github/workflows/repo-audit-analyzer.lock.yml b/.github/workflows/repo-audit-analyzer.lock.yml index 0b34d86f59..ba7c22c831 100644 --- a/.github/workflows/repo-audit-analyzer.lock.yml +++ b/.github/workflows/repo-audit-analyzer.lock.yml @@ -129,8 +129,6 @@ jobs: path: /tmp/gh-aw/cache-memory-repo-audits restore-keys: | repo-audits-${{ github.workflow }}- - repo-audits- - repo- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index cbf7faaf10..f4539e0ca0 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -125,8 +125,6 @@ jobs: path: /tmp/gh-aw/cache-memory-focus-areas restore-keys: | quality-focus-${{ github.workflow }}- - quality-focus- - quality- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index 6cc552640f..bafcba83ac 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -163,7 +163,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index f2817e76fe..26f6c7b59d 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -125,9 +125,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | schema-consistency-cache-${{ github.workflow }}- - schema-consistency-cache- - schema-consistency- - schema- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 4301e4c7f6..95f35f96cb 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -201,7 +201,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/security-review.lock.yml b/.github/workflows/security-review.lock.yml index 6f9a91112e..084f56935b 100644 --- a/.github/workflows/security-review.lock.yml +++ b/.github/workflows/security-review.lock.yml @@ -191,7 +191,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml index a526f3669f..39c7008dbe 100644 --- a/.github/workflows/sergo.lock.yml +++ b/.github/workflows/sergo.lock.yml @@ -125,7 +125,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/slide-deck-maintainer.lock.yml b/.github/workflows/slide-deck-maintainer.lock.yml index 147b67194d..526d85d1a0 100644 --- a/.github/workflows/slide-deck-maintainer.lock.yml +++ b/.github/workflows/slide-deck-maintainer.lock.yml @@ -141,7 +141,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 88355f0bab..e23819db12 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -191,7 +191,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index c2acf79b15..fbf56b19ca 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -154,7 +154,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index db104ad85c..26050248ea 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -187,7 +187,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index a22723d433..90b687f58c 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -194,8 +194,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | trending-data-${{ github.workflow }}- - trending-data- - trending- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index 27772003d1..365f2f9960 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -162,7 +162,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/step-name-alignment.lock.yml b/.github/workflows/step-name-alignment.lock.yml index 4bebd973f0..43adcc8420 100644 --- a/.github/workflows/step-name-alignment.lock.yml +++ b/.github/workflows/step-name-alignment.lock.yml @@ -123,7 +123,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index 4200193e45..81634036fd 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -132,7 +132,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 437a5499c2..e3773783ef 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -155,7 +155,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/test-create-pr-error-handling.lock.yml b/.github/workflows/test-create-pr-error-handling.lock.yml index ec94d449aa..8c403f4fe7 100644 --- a/.github/workflows/test-create-pr-error-handling.lock.yml +++ b/.github/workflows/test-create-pr-error-handling.lock.yml @@ -120,7 +120,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 74474550b7..535f983bc9 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -165,7 +165,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 89ea30704a..e9aab2df81 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -146,7 +146,6 @@ jobs: path: /tmp/gh-aw/cache-memory restore-keys: | memory-${{ github.workflow }}- - memory- - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 77da432af3..ad53b9bace 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -3121,6 +3121,12 @@ "restore-only": { "type": "boolean", "description": "If true, only restore the cache without saving it back. Uses actions/cache/restore instead of actions/cache. No artifact upload step will be generated." + }, + "scope": { + "type": "string", + "enum": ["workflow", "repo"], + "default": "workflow", + "description": "Cache restore key scope: 'workflow' (default, only restores from same workflow) or 'repo' (restores from any workflow in the repository). Use 'repo' with caution as it allows cross-workflow cache sharing." } }, "additionalProperties": false, @@ -3161,6 +3167,12 @@ "restore-only": { "type": "boolean", "description": "If true, only restore the cache without saving it back. Uses actions/cache/restore instead of actions/cache. No artifact upload step will be generated." + }, + "scope": { + "type": "string", + "enum": ["workflow", "repo"], + "default": "workflow", + "description": "Cache restore key scope: 'workflow' (default, only restores from same workflow) or 'repo' (restores from any workflow in the repository). Use 'repo' with caution as it allows cross-workflow cache sharing." } }, "required": ["id", "key"], diff --git a/pkg/workflow/action_pins_test.go b/pkg/workflow/action_pins_test.go index 9cbe8948a9..b7bfe74af2 100644 --- a/pkg/workflow/action_pins_test.go +++ b/pkg/workflow/action_pins_test.go @@ -297,9 +297,9 @@ func TestApplyActionPinToStep(t *testing.T) { func TestGetActionPinsSorting(t *testing.T) { pins := getActionPins() - // Verify we got all the pins (38 as of February 2026) - if len(pins) != 38 { - t.Errorf("getActionPins() returned %d pins, expected 38", len(pins)) + // Verify we got all the pins (39 as of February 2026) + if len(pins) != 39 { + t.Errorf("getActionPins() returned %d pins, expected 39", len(pins)) } // Verify they are sorted by version (descending) then by repository name (ascending) diff --git a/pkg/workflow/cache.go b/pkg/workflow/cache.go index 188833f7ac..d70803547b 100644 --- a/pkg/workflow/cache.go +++ b/pkg/workflow/cache.go @@ -23,6 +23,7 @@ type CacheMemoryEntry struct { Description string `yaml:"description,omitempty"` // optional description for this cache RetentionDays *int `yaml:"retention-days,omitempty"` // retention days for upload-artifact action RestoreOnly bool `yaml:"restore-only,omitempty"` // if true, only restore cache without saving + Scope string `yaml:"scope,omitempty"` // scope for restore keys: "workflow" (default) or "repo" } // generateDefaultCacheKey generates a default cache key for a given cache ID @@ -141,6 +142,17 @@ func (c *Compiler) extractCacheMemoryConfig(toolsConfig *ToolsConfig) (*CacheMem } } + // Parse scope field + if scope, exists := cacheMap["scope"]; exists { + if scopeStr, ok := scope.(string); ok { + entry.Scope = scopeStr + } + } + // Default to "workflow" scope if not specified + if entry.Scope == "" { + entry.Scope = "workflow" + } + config.Caches = append(config.Caches, entry) } } @@ -206,6 +218,17 @@ func (c *Compiler) extractCacheMemoryConfig(toolsConfig *ToolsConfig) (*CacheMem } } + // Parse scope field + if scope, exists := configMap["scope"]; exists { + if scopeStr, ok := scope.(string); ok { + entry.Scope = scopeStr + } + } + // Default to "workflow" scope if not specified + if entry.Scope == "" { + entry.Scope = "workflow" + } + config.Caches = []CacheMemoryEntry{entry} return config, nil } @@ -372,12 +395,42 @@ func generateCacheMemorySteps(builder *strings.Builder, data *WorkflowData) { cacheKey = cacheKey + runIdSuffix } - // Generate restore keys automatically by splitting the cache key on '-' + // Generate restore keys based on scope + // - "workflow" (default): Single restore key with workflow ID (secure) + // - "repo": Two restore keys - with and without workflow ID (allows cross-workflow sharing) var restoreKeys []string - keyParts := strings.Split(cacheKey, "-") - for i := len(keyParts) - 1; i > 0; i-- { - restoreKey := strings.Join(keyParts[:i], "-") + "-" + + // Determine scope (default to "workflow" for safety) + scope := cache.Scope + if scope == "" { + scope = "workflow" + } + + // First restore key: remove the run_id suffix as a single unit (don't split the key) + // The cacheKey always ends with "-${{ github.run_id }}" (ensured by code above) + if strings.HasSuffix(cacheKey, runIdSuffix) { + // Remove the run_id suffix to create the restore key + restoreKey := strings.TrimSuffix(cacheKey, "${{ github.run_id }}") // Keep the trailing "-" restoreKeys = append(restoreKeys, restoreKey) + } else { + // Fallback: split on last dash if run_id suffix not found + // This handles edge cases where the key format might be different + keyParts := strings.Split(cacheKey, "-") + if len(keyParts) >= 2 { + workflowLevelKey := strings.Join(keyParts[:len(keyParts)-1], "-") + "-" + restoreKeys = append(restoreKeys, workflowLevelKey) + } + } + + // For repo scope, add an additional restore key without the workflow ID + // This allows cache sharing across all workflows in the repository + if scope == "repo" { + // Remove both workflow and run_id to create a repo-wide restore key + // For example: "memory-chroma-${{ github.workflow }}-${{ github.run_id }}" -> "memory-chroma-" + repoKey := strings.TrimSuffix(cacheKey, "${{ github.workflow }}-${{ github.run_id }}") + if repoKey != cacheKey && repoKey != "" { + restoreKeys = append(restoreKeys, repoKey) + } } // Step name and action diff --git a/pkg/workflow/cache_memory_restore_keys_test.go b/pkg/workflow/cache_memory_restore_keys_test.go new file mode 100644 index 0000000000..28c5f8763b --- /dev/null +++ b/pkg/workflow/cache_memory_restore_keys_test.go @@ -0,0 +1,225 @@ +//go:build integration + +package workflow + +import ( + "os" + "path/filepath" + "regexp" + "strings" + "testing" + + "github.com/github/gh-aw/pkg/stringutil" + "github.com/github/gh-aw/pkg/testutil" +) + +// hasGenericRestoreKey checks if the lock file contains a generic restore key pattern +// that would match caches from other workflows. Returns true if found (which is bad). +func hasGenericRestoreKey(lockContent, prefix string) bool { + // Look for restore-keys sections + restoreKeysPattern := regexp.MustCompile(`restore-keys:\s*\|`) + matches := restoreKeysPattern.FindAllStringIndex(lockContent, -1) + + for _, match := range matches { + // Get the content after "restore-keys: |" + start := match[1] + // Find the next non-indented line (which marks the end of restore-keys) + lines := strings.Split(lockContent[start:], "\n") + for _, line := range lines { + // Check if this line is a restore key (starts with whitespace) + if strings.HasPrefix(line, " ") || strings.HasPrefix(line, " ") { + restoreKey := strings.TrimSpace(line) + // Check if this is a generic fallback (ends with just the prefix and nothing else) + // For example: "memory-" (bad) vs "memory-${{ github.workflow }}-" (good) + if restoreKey == prefix { + return true + } + } else if strings.TrimSpace(line) != "" { + // We've hit a non-restore-key line, stop checking this section + break + } + } + } + return false +} + +// TestCacheMemoryRestoreKeysNoGenericFallback verifies that cache-memory restore-keys +// do NOT include a generic fallback that would match caches from other workflows. +// This prevents cross-workflow cache poisoning attacks. +func TestCacheMemoryRestoreKeysNoGenericFallback(t *testing.T) { + tests := []struct { + name string + frontmatter string + expectedInLock []string + genericFallbacks []string // Generic restore key prefixes that should NOT be present + }{ + { + name: "default cache-memory should NOT have generic memory- fallback", + frontmatter: `--- +name: Test Cache Memory Restore Keys +on: workflow_dispatch +permissions: + contents: read +engine: claude +tools: + cache-memory: true + github: + allowed: [get_repository] +---`, + expectedInLock: []string{ + // Should have workflow-specific restore key + "restore-keys: |", + "memory-${{ github.workflow }}-", + }, + genericFallbacks: []string{"memory-"}, + }, + { + name: "cache-memory with custom ID should NOT have generic fallbacks", + frontmatter: `--- +name: Test Cache Memory Custom ID +on: workflow_dispatch +permissions: + contents: read + issues: read + pull-requests: read +engine: claude +tools: + cache-memory: + - id: chroma + key: memory-chroma-${{ github.workflow }} + github: + allowed: [get_repository] +---`, + expectedInLock: []string{ + // Custom key becomes memory-chroma-${{ github.workflow }}-${{ github.run_id }} + // Restore key should only remove run_id: memory-chroma-${{ github.workflow }}- + "restore-keys: |", + "memory-chroma-${{ github.workflow }}-", + }, + genericFallbacks: []string{"memory-chroma-", "memory-"}, + }, + { + name: "multiple cache-memory should NOT have generic fallbacks", + frontmatter: `--- +name: Test Multiple Cache Memory +on: workflow_dispatch +permissions: + contents: read + issues: read + pull-requests: read +engine: claude +tools: + cache-memory: + - id: default + key: memory-default-${{ github.workflow }} + - id: session + key: memory-session-${{ github.workflow }} + github: + allowed: [get_repository] +---`, + expectedInLock: []string{ + // Custom keys become memory-*-${{ github.workflow }}-${{ github.run_id }} + // Restore keys should only remove run_id + "memory-default-${{ github.workflow }}-", + "memory-session-${{ github.workflow }}-", + }, + genericFallbacks: []string{"memory-default-", "memory-session-", "memory-"}, + }, + { + name: "cache-memory with threat detection should NOT have generic fallback", + frontmatter: `--- +name: Test Cache Memory with Threat Detection +on: workflow_dispatch +permissions: + contents: read +engine: claude +tools: + cache-memory: true + github: + allowed: [get_repository] +safe-outputs: + create-issue: + threat-detection: true +---`, + expectedInLock: []string{ + // Should use restore action + "uses: actions/cache/restore@", + // Should have workflow-specific restore key + "restore-keys: |", + "memory-${{ github.workflow }}-", + }, + genericFallbacks: []string{"memory-"}, + }, + { + name: "cache-memory with repo scope should have two restore keys", + frontmatter: `--- +name: Test Cache Memory Repo Scope +on: workflow_dispatch +strict: false +permissions: + contents: read + issues: read + pull-requests: read +engine: claude +tools: + cache-memory: + - id: shared + key: shared-cache-${{ github.workflow }} + scope: repo + github: + allowed: [get_repository] +---`, + expectedInLock: []string{ + // Repo scope generates two restore keys: + // 1. With workflow ID (try same workflow first) + // 2. Without workflow ID (allows cross-workflow sharing) + "restore-keys: |", + "shared-cache-${{ github.workflow }}-", + "shared-cache-", + }, + genericFallbacks: []string{}, // No check - repo scope intentionally allows generic restore key + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a temporary directory + tmpDir := testutil.TempDir(t, "test-*") + + // Write the markdown file + mdPath := filepath.Join(tmpDir, "test-workflow.md") + content := tt.frontmatter + "\n\n# Test Workflow\n\nTest cache-memory restore-keys configuration.\n" + if err := os.WriteFile(mdPath, []byte(content), 0644); err != nil { + t.Fatalf("Failed to write test markdown file: %v", err) + } + + // Compile the workflow + compiler := NewCompiler() + if err := compiler.CompileWorkflow(mdPath); err != nil { + t.Fatalf("Failed to compile workflow: %v", err) + } + + // Read the generated lock file + lockPath := stringutil.MarkdownToLockFile(mdPath) + lockContent, err := os.ReadFile(lockPath) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + lockStr := string(lockContent) + + // Check expected strings + for _, expected := range tt.expectedInLock { + if !strings.Contains(lockStr, expected) { + t.Errorf("Expected to find '%s' in lock file but it was missing.\nLock file content:\n%s", expected, lockStr) + } + } + + // Check that generic fallback restore keys are NOT present using the helper + for _, genericFallback := range tt.genericFallbacks { + if hasGenericRestoreKey(lockStr, genericFallback) { + t.Errorf("Found generic restore key '%s' in lock file, which creates a security vulnerability.\nLock file content:\n%s", genericFallback, lockStr) + } + } + }) + } +} diff --git a/pkg/workflow/data/action_pins.json b/pkg/workflow/data/action_pins.json index c9ea38685d..3688f8f2b9 100644 --- a/pkg/workflow/data/action_pins.json +++ b/pkg/workflow/data/action_pins.json @@ -125,6 +125,11 @@ "version": "v2.0.3", "sha": "e95548e56dfa95d4e1a28d6f422fafe75c4c26fb" }, + "docker/build-push-action@v6": { + "repo": "docker/build-push-action", + "version": "v6", + "sha": "ee4ca427a2f43b6a16632044ca514c076267da23" + }, "docker/build-push-action@v6.18.0": { "repo": "docker/build-push-action", "version": "v6.18.0", diff --git a/pkg/workflow/strict_mode_validation.go b/pkg/workflow/strict_mode_validation.go index e579b8cca8..6bea468ada 100644 --- a/pkg/workflow/strict_mode_validation.go +++ b/pkg/workflow/strict_mode_validation.go @@ -178,6 +178,39 @@ func (c *Compiler) validateStrictTools(frontmatter map[string]any) error { } } + // Check if cache-memory is configured with scope: repo + cacheMemoryValue, hasCacheMemory := toolsMap["cache-memory"] + if hasCacheMemory { + // Helper function to check scope in a cache entry + checkScope := func(cacheMap map[string]any) error { + if scope, hasScope := cacheMap["scope"]; hasScope { + if scopeStr, ok := scope.(string); ok && scopeStr == "repo" { + strictModeValidationLog.Printf("Cache-memory repo scope validation failed") + return fmt.Errorf("strict mode: cache-memory with 'scope: repo' is not allowed for security reasons. Repo scope allows cache sharing across all workflows in the repository, which can enable cross-workflow cache poisoning attacks. Use 'scope: workflow' (default) instead, which isolates caches to individual workflows. See: https://github.github.com/gh-aw/reference/tools/#cache-memory") + } + } + return nil + } + + // Check if cache-memory is a map (object notation) + if cacheMemoryConfig, ok := cacheMemoryValue.(map[string]any); ok { + if err := checkScope(cacheMemoryConfig); err != nil { + return err + } + } + + // Check if cache-memory is an array (array notation) + if cacheMemoryArray, ok := cacheMemoryValue.([]any); ok { + for _, item := range cacheMemoryArray { + if cacheMap, ok := item.(map[string]any); ok { + if err := checkScope(cacheMap); err != nil { + return err + } + } + } + } + } + return nil } diff --git a/pkg/workflow/strict_mode_validation_test.go b/pkg/workflow/strict_mode_validation_test.go index 7fd4ed438b..ebc28607b2 100644 --- a/pkg/workflow/strict_mode_validation_test.go +++ b/pkg/workflow/strict_mode_validation_test.go @@ -551,3 +551,114 @@ func TestValidateStrictModeEdgeCases(t *testing.T) { }) } } + +// TestValidateStrictCacheMemoryScope tests that cache-memory with scope: repo is rejected in strict mode +func TestValidateStrictCacheMemoryScope(t *testing.T) { + tests := []struct { + name string + frontmatter map[string]any + expectError bool + errorMsg string + }{ + { + name: "cache-memory with workflow scope is allowed", + frontmatter: map[string]any{ + "on": "push", + "tools": map[string]any{ + "cache-memory": map[string]any{ + "key": "memory-test", + "scope": "workflow", + }, + }, + }, + expectError: false, + }, + { + name: "cache-memory without scope (defaults to workflow) is allowed", + frontmatter: map[string]any{ + "on": "push", + "tools": map[string]any{ + "cache-memory": map[string]any{ + "key": "memory-test", + }, + }, + }, + expectError: false, + }, + { + name: "cache-memory with repo scope is rejected", + frontmatter: map[string]any{ + "on": "push", + "tools": map[string]any{ + "cache-memory": map[string]any{ + "key": "memory-test", + "scope": "repo", + }, + }, + }, + expectError: true, + errorMsg: "strict mode: cache-memory with 'scope: repo' is not allowed for security reasons", + }, + { + name: "cache-memory array with repo scope is rejected", + frontmatter: map[string]any{ + "on": "push", + "tools": map[string]any{ + "cache-memory": []any{ + map[string]any{ + "id": "default", + "key": "memory-default", + "scope": "workflow", + }, + map[string]any{ + "id": "shared", + "key": "memory-shared", + "scope": "repo", + }, + }, + }, + }, + expectError: true, + errorMsg: "strict mode: cache-memory with 'scope: repo' is not allowed for security reasons", + }, + { + name: "cache-memory array with all workflow scope is allowed", + frontmatter: map[string]any{ + "on": "push", + "tools": map[string]any{ + "cache-memory": []any{ + map[string]any{ + "id": "default", + "key": "memory-default", + "scope": "workflow", + }, + map[string]any{ + "id": "logs", + "key": "memory-logs", + }, + }, + }, + }, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + compiler := NewCompiler() + compiler.strictMode = true + + err := compiler.validateStrictTools(tt.frontmatter) + + if tt.expectError && err == nil { + t.Error("Expected validation to fail but it succeeded") + } else if !tt.expectError && err != nil { + t.Errorf("Expected validation to succeed but it failed: %v", err) + } else if tt.expectError && err != nil && tt.errorMsg != "" { + if !strings.Contains(err.Error(), tt.errorMsg) { + t.Errorf("Expected error containing '%s', got '%s'", tt.errorMsg, err.Error()) + } + } + }) + } +}