From d950f6c5c01dab98015e2032a91b1844948e86ed Mon Sep 17 00:00:00 2001 From: Derek Misler Date: Thu, 26 Mar 2026 16:13:20 -0400 Subject: [PATCH 1/4] feat: fetch GitHub App credentials from AWS Secrets Manager via OIDC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace org-secret lookups with OIDC + Secrets Manager in all reusable consumer-facing workflows. Each affected job now: 1. Assumes the docker-agent-action IAM role via OIDC (id-token: write) 2. Fetches { app_id, private_key, org_membership_token } from the docker-agent-action/github-app Secrets Manager secret 3. Passes those values to downstream steps instead of inherited secrets Affected workflows: - review-pr.yml (auto-review, manual-review, reply-to-feedback jobs) - reply-to-feedback.yml (reply job) - pr-describe.yml (generate-description job) - security-scan.yml (security-scan job) - self-review-pr.yml (auto-review, manual-review jobs) HAS_APP_SECRETS env var changed from dynamic secret check to hardcoded 'true' — credentials are always available via Secrets Manager. Existing secrets: declarations on workflow_call kept as required: false for backward compatibility with consumer repos not yet migrated. TODO_ROLE_ARN must be replaced with the actual IAM role ARN after the Terraform in docker/infra-terraform is applied. Assisted-By: docker-agent refactor: remove HAS_APP_SECRETS — credentials are always available via OIDC Assisted-By: docker-agent --- .github/workflows/pr-describe.yml | 32 ++++++- .github/workflows/reply-to-feedback.yml | 41 ++++++-- .github/workflows/review-pr.yml | 119 ++++++++++++++++++++---- .github/workflows/security-scan.yml | 32 ++++++- .github/workflows/self-review-pr.yml | 71 +++++++++++--- 5 files changed, 247 insertions(+), 48 deletions(-) diff --git a/.github/workflows/pr-describe.yml b/.github/workflows/pr-describe.yml index 30ad370..a0b4fce 100644 --- a/.github/workflows/pr-describe.yml +++ b/.github/workflows/pr-describe.yml @@ -12,14 +12,37 @@ jobs: # Only run if comment contains /describe and is on a PR if: ${{ (github.event.issue.pull_request && contains(github.event.comment.body, '/describe')) }} runs-on: ubuntu-latest - env: - HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} permissions: contents: read pull-requests: write issues: write checks: write + id-token: write steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: TODO_ROLE_ARN + aws-region: us-east-1 + + - name: Fetch GitHub App credentials from Secrets Manager + id: app-credentials + shell: bash + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/github-app \ + --query SecretString --output text) + APP_ID=$(echo "$SECRET" | jq -r .app_id) + ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) + PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + echo "::add-mask::$APP_ID" + echo "::add-mask::$ORG_TOKEN" + echo "::add-mask::$PRIVATE_KEY" + echo "app-id=$APP_ID" >> $GITHUB_OUTPUT + echo "org-membership-token=$ORG_TOKEN" >> $GITHUB_OUTPUT + DELIM="$(openssl rand -hex 8)" + { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Check out Git repository uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 @@ -48,12 +71,11 @@ jobs: # Generate GitHub App token so actions appear as the custom app (optional - falls back to github.token) - name: Get GitHub App token id: app-token - if: env.HAS_APP_SECRETS == 'true' continue-on-error: true # Don't fail workflow if token generation fails uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 with: - app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} - private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} + app_id: ${{ steps.app-credentials.outputs.app-id }} + private_key: ${{ steps.app-credentials.outputs.private-key }} - name: Validate PR and add reaction id: validate_pr diff --git a/.github/workflows/reply-to-feedback.yml b/.github/workflows/reply-to-feedback.yml index aae4671..3c3d5e9 100644 --- a/.github/workflows/reply-to-feedback.yml +++ b/.github/workflows/reply-to-feedback.yml @@ -19,16 +19,45 @@ permissions: pull-requests: write issues: write actions: read # Required to download artifacts from the triggering run + id-token: write jobs: reply: # Only run if the triggering workflow succeeded (artifact was uploaded) if: github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest - env: - HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} + permissions: + contents: read + pull-requests: write + issues: write + actions: read + id-token: write steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: TODO_ROLE_ARN + aws-region: us-east-1 + + - name: Fetch GitHub App credentials from Secrets Manager + id: app-credentials + shell: bash + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/github-app \ + --query SecretString --output text) + APP_ID=$(echo "$SECRET" | jq -r .app_id) + ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) + PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + echo "::add-mask::$APP_ID" + echo "::add-mask::$ORG_TOKEN" + echo "::add-mask::$PRIVATE_KEY" + echo "app-id=$APP_ID" >> $GITHUB_OUTPUT + echo "org-membership-token=$ORG_TOKEN" >> $GITHUB_OUTPUT + DELIM="$(openssl rand -hex 8)" + { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + # ---------------------------------------------------------------- # Download artifact from the triggering workflow run # ---------------------------------------------------------------- @@ -157,7 +186,7 @@ jobs: env: USERNAME: ${{ steps.meta.outputs.author }} with: - github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN }} + github-token: ${{ steps.app-credentials.outputs.org-membership-token }} script: | const org = 'docker'; const username = process.env.USERNAME; @@ -301,13 +330,13 @@ jobs: ref: refs/pull/${{ steps.meta.outputs.pr_number }}/head - name: Generate GitHub App token - if: steps.meta.outputs.proceed == 'true' && steps.auth.outputs.authorized == 'true' && env.HAS_APP_SECRETS == 'true' + if: steps.meta.outputs.proceed == 'true' && steps.auth.outputs.authorized == 'true' id: app-token continue-on-error: true uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 with: - app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} - private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} + app_id: ${{ steps.app-credentials.outputs.app-id }} + private_key: ${{ steps.app-credentials.outputs.private-key }} - name: Run reply if: steps.meta.outputs.proceed == 'true' && steps.auth.outputs.authorized == 'true' && steps.checkout.outcome == 'success' && steps.thread.outcome == 'success' diff --git a/.github/workflows/review-pr.yml b/.github/workflows/review-pr.yml index 8466abd..a68e91a 100644 --- a/.github/workflows/review-pr.yml +++ b/.github/workflows/review-pr.yml @@ -124,12 +124,39 @@ jobs: inputs.pr-number != '' ) runs-on: ubuntu-latest - env: - HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} + permissions: + contents: read + pull-requests: write + issues: write + id-token: write outputs: exit-code: ${{ steps.run-review.outputs.exit-code }} steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: TODO_ROLE_ARN + aws-region: us-east-1 + + - name: Fetch GitHub App credentials from Secrets Manager + id: app-credentials + shell: bash + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/github-app \ + --query SecretString --output text) + APP_ID=$(echo "$SECRET" | jq -r .app_id) + ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) + PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + echo "::add-mask::$APP_ID" + echo "::add-mask::$ORG_TOKEN" + echo "::add-mask::$PRIVATE_KEY" + echo "app-id=$APP_ID" >> $GITHUB_OUTPUT + echo "org-membership-token=$ORG_TOKEN" >> $GITHUB_OUTPUT + DELIM="$(openssl rand -hex 8)" + { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Get PR number id: get-pr shell: bash @@ -167,7 +194,7 @@ jobs: PR_DRAFT: ${{ steps.pr-info.outputs.draft }} PR_AUTHOR: ${{ steps.pr-info.outputs.author }} with: - github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN }} + github-token: ${{ steps.app-credentials.outputs.org-membership-token }} script: | const org = '${{ inputs.auto-review-org }}'; @@ -216,13 +243,13 @@ jobs: # Generate GitHub App token for custom app identity (optional - falls back to github.token) - name: Generate GitHub App token - if: steps.membership.outputs.is_member == 'true' && env.HAS_APP_SECRETS == 'true' + if: steps.membership.outputs.is_member == 'true' id: app-token continue-on-error: true # Don't fail workflow if token generation fails uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 with: - app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} - private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} + app_id: ${{ steps.app-credentials.outputs.app-id }} + private_key: ${{ steps.app-credentials.outputs.private-key }} - name: Run PR Review if: steps.membership.outputs.is_member == 'true' @@ -254,17 +281,44 @@ jobs: startsWith(github.event.comment.body, '/review') && (github.event.comment.user.type != 'Bot' || github.event.comment.user.login == 'docker-agent[bot]') runs-on: ubuntu-latest - env: - HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} + permissions: + contents: read + pull-requests: write + issues: write + id-token: write outputs: exit-code: ${{ steps.run-review.outputs.exit-code }} steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: TODO_ROLE_ARN + aws-region: us-east-1 + + - name: Fetch GitHub App credentials from Secrets Manager + id: app-credentials + shell: bash + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/github-app \ + --query SecretString --output text) + APP_ID=$(echo "$SECRET" | jq -r .app_id) + ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) + PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + echo "::add-mask::$APP_ID" + echo "::add-mask::$ORG_TOKEN" + echo "::add-mask::$PRIVATE_KEY" + echo "app-id=$APP_ID" >> $GITHUB_OUTPUT + echo "org-membership-token=$ORG_TOKEN" >> $GITHUB_OUTPUT + DELIM="$(openssl rand -hex 8)" + { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Check if commenter is org member id: membership uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN }} + github-token: ${{ steps.app-credentials.outputs.org-membership-token }} script: | const org = '${{ inputs.auto-review-org }}'; const username = context.payload.comment.user.login; @@ -303,13 +357,13 @@ jobs: # Generate GitHub App token first so the check run is created under the app's identity # (prevents GitHub from nesting it under unrelated pull_request-triggered workflows) - name: Generate GitHub App token - if: steps.membership.outputs.is_member == 'true' && env.HAS_APP_SECRETS == 'true' + if: steps.membership.outputs.is_member == 'true' id: app-token continue-on-error: true # Don't fail workflow if token generation fails uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 with: - app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} - private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} + app_id: ${{ steps.app-credentials.outputs.app-id }} + private_key: ${{ steps.app-credentials.outputs.private-key }} - name: Create check run if: steps.membership.outputs.is_member == 'true' @@ -358,7 +412,7 @@ jobs: add-prompt-files: ${{ inputs.add-prompt-files }} model: ${{ inputs.model }} github-token: ${{ steps.app-token.outputs.token || github.token }} - trusted-bot-app-id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} + trusted-bot-app-id: ${{ steps.app-credentials.outputs.app-id }} anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} openai-api-key: ${{ secrets.OPENAI_API_KEY }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} @@ -455,10 +509,37 @@ jobs: github.event.comment.in_reply_to_id && github.event.comment.user.type != 'Bot' runs-on: ubuntu-latest - env: - HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} + permissions: + contents: read + pull-requests: write + issues: write + id-token: write steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: TODO_ROLE_ARN + aws-region: us-east-1 + + - name: Fetch GitHub App credentials from Secrets Manager + id: app-credentials + shell: bash + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/github-app \ + --query SecretString --output text) + APP_ID=$(echo "$SECRET" | jq -r .app_id) + ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) + PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + echo "::add-mask::$APP_ID" + echo "::add-mask::$ORG_TOKEN" + echo "::add-mask::$PRIVATE_KEY" + echo "app-id=$APP_ID" >> $GITHUB_OUTPUT + echo "org-membership-token=$ORG_TOKEN" >> $GITHUB_OUTPUT + DELIM="$(openssl rand -hex 8)" + { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Check if reply is to agent comment id: check shell: bash @@ -526,7 +607,7 @@ jobs: id: auth shell: bash env: - GH_TOKEN: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN }} + GH_TOKEN: ${{ steps.app-credentials.outputs.org-membership-token }} ORG: ${{ inputs.auto-review-org }} USERNAME: ${{ github.event.comment.user.login }} run: | @@ -682,13 +763,13 @@ jobs: # Generate GitHub App token for custom app identity (optional - falls back to github.token) - name: Generate GitHub App token - if: steps.check.outputs.is_agent == 'true' && steps.auth.outputs.authorized == 'true' && env.HAS_APP_SECRETS == 'true' + if: steps.check.outputs.is_agent == 'true' && steps.auth.outputs.authorized == 'true' id: app-token continue-on-error: true uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 with: - app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} - private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} + app_id: ${{ steps.app-credentials.outputs.app-id }} + private_key: ${{ steps.app-credentials.outputs.private-key }} - name: Run reply if: steps.check.outputs.is_agent == 'true' && steps.auth.outputs.authorized == 'true' diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 93a6cbb..5a96ef5 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -19,12 +19,35 @@ jobs: security-scan: name: Security Scan with Docker Agent runs-on: ubuntu-latest - env: - HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} permissions: contents: read issues: write + id-token: write steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: TODO_ROLE_ARN + aws-region: us-east-1 + + - name: Fetch GitHub App credentials from Secrets Manager + id: app-credentials + shell: bash + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/github-app \ + --query SecretString --output text) + APP_ID=$(echo "$SECRET" | jq -r .app_id) + ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) + PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + echo "::add-mask::$APP_ID" + echo "::add-mask::$ORG_TOKEN" + echo "::add-mask::$PRIVATE_KEY" + echo "app-id=$APP_ID" >> $GITHUB_OUTPUT + echo "org-membership-token=$ORG_TOKEN" >> $GITHUB_OUTPUT + DELIM="$(openssl rand -hex 8)" + { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Check out Git repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -33,12 +56,11 @@ jobs: # Generate GitHub App token so issues appear as the custom app (optional - falls back to github.token) - name: Get GitHub App token id: app-token - if: env.HAS_APP_SECRETS == 'true' continue-on-error: true # Don't fail workflow if token generation fails uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 with: - app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} - private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} + app_id: ${{ steps.app-credentials.outputs.app-id }} + private_key: ${{ steps.app-credentials.outputs.private-key }} - name: Get commits from past week id: commits diff --git a/.github/workflows/self-review-pr.yml b/.github/workflows/self-review-pr.yml index 49602b9..770d562 100644 --- a/.github/workflows/self-review-pr.yml +++ b/.github/workflows/self-review-pr.yml @@ -22,6 +22,7 @@ permissions: issues: write checks: write actions: read + id-token: write jobs: # ========================================================================== @@ -45,10 +46,32 @@ jobs: github.event.workflow_run.head_repository.full_name != github.repository ) runs-on: ubuntu-latest - env: - HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: TODO_ROLE_ARN + aws-region: us-east-1 + + - name: Fetch GitHub App credentials from Secrets Manager + id: app-credentials + shell: bash + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/github-app \ + --query SecretString --output text) + APP_ID=$(echo "$SECRET" | jq -r .app_id) + ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) + PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + echo "::add-mask::$APP_ID" + echo "::add-mask::$ORG_TOKEN" + echo "::add-mask::$PRIVATE_KEY" + echo "app-id=$APP_ID" >> $GITHUB_OUTPUT + echo "org-membership-token=$ORG_TOKEN" >> $GITHUB_OUTPUT + DELIM="$(openssl rand -hex 8)" + { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + # For workflow_run events (fork PRs), download the artifact saved by pr-review-trigger.yml # to get the PR number. For pull_request events, read it directly from the event payload. - name: Get PR number @@ -91,7 +114,7 @@ jobs: id: membership uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN }} + github-token: ${{ steps.app-credentials.outputs.org-membership-token }} script: | const org = 'docker'; @@ -153,13 +176,13 @@ jobs: # Generate GitHub App token for custom app identity (optional - falls back to github.token) - name: Generate GitHub App token - if: steps.membership.outputs.is_member == 'true' && env.HAS_APP_SECRETS == 'true' + if: steps.membership.outputs.is_member == 'true' id: app-token continue-on-error: true # Don't fail workflow if token generation fails uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 with: - app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} - private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} + app_id: ${{ steps.app-credentials.outputs.app-id }} + private_key: ${{ steps.app-credentials.outputs.private-key }} - name: Run PR Review if: steps.membership.outputs.is_member == 'true' @@ -188,15 +211,37 @@ jobs: startsWith(github.event.comment.body, '/review') && (github.event.comment.user.type != 'Bot' || github.event.comment.user.login == 'docker-agent[bot]') runs-on: ubuntu-latest - env: - HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: TODO_ROLE_ARN + aws-region: us-east-1 + + - name: Fetch GitHub App credentials from Secrets Manager + id: app-credentials + shell: bash + run: | + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/github-app \ + --query SecretString --output text) + APP_ID=$(echo "$SECRET" | jq -r .app_id) + ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) + PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + echo "::add-mask::$APP_ID" + echo "::add-mask::$ORG_TOKEN" + echo "::add-mask::$PRIVATE_KEY" + echo "app-id=$APP_ID" >> $GITHUB_OUTPUT + echo "org-membership-token=$ORG_TOKEN" >> $GITHUB_OUTPUT + DELIM="$(openssl rand -hex 8)" + { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Check if commenter is org member id: membership uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN }} + github-token: ${{ steps.app-credentials.outputs.org-membership-token }} script: | const org = 'docker'; const username = context.payload.comment.user.login; @@ -235,13 +280,13 @@ jobs: # Generate GitHub App token first so the check run is created under the app's identity # (prevents GitHub from nesting it under unrelated pull_request-triggered workflows) - name: Generate GitHub App token - if: steps.membership.outputs.is_member == 'true' && env.HAS_APP_SECRETS == 'true' + if: steps.membership.outputs.is_member == 'true' id: app-token continue-on-error: true # Don't fail workflow if token generation fails uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2 with: - app_id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} - private_key: ${{ secrets.CAGENT_REVIEWER_APP_PRIVATE_KEY }} + app_id: ${{ steps.app-credentials.outputs.app-id }} + private_key: ${{ steps.app-credentials.outputs.private-key }} - name: Create check run if: steps.membership.outputs.is_member == 'true' @@ -287,7 +332,7 @@ jobs: pr-number: ${{ github.event.issue.number }} comment-id: ${{ github.event.comment.id }} github-token: ${{ steps.app-token.outputs.token || github.token }} - trusted-bot-app-id: ${{ secrets.CAGENT_REVIEWER_APP_ID }} + trusted-bot-app-id: ${{ steps.app-credentials.outputs.app-id }} anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} openai-api-key: ${{ secrets.OPENAI_API_KEY }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} From c844fc75bd0db3c1ffd16dce7bfeac628a0d112d Mon Sep 17 00:00:00 2001 From: Derek Misler Date: Thu, 26 Mar 2026 19:37:36 -0400 Subject: [PATCH 2/4] fix: add fail-fast validation to Secrets Manager credential extraction Assisted-By: docker-agent --- .github/workflows/pr-describe.yml | 15 +++++++++ .github/workflows/reply-to-feedback.yml | 15 +++++++++ .github/workflows/review-pr.yml | 45 +++++++++++++++++++++++++ .github/workflows/security-scan.yml | 15 +++++++++ .github/workflows/self-review-pr.yml | 30 +++++++++++++++++ 5 files changed, 120 insertions(+) diff --git a/.github/workflows/pr-describe.yml b/.github/workflows/pr-describe.yml index a0b4fce..d338b73 100644 --- a/.github/workflows/pr-describe.yml +++ b/.github/workflows/pr-describe.yml @@ -29,12 +29,27 @@ jobs: id: app-credentials shell: bash run: | + set -euo pipefail SECRET=$(aws secretsmanager get-secret-value \ --secret-id docker-agent-action/github-app \ --query SecretString --output text) APP_ID=$(echo "$SECRET" | jq -r .app_id) ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + + if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then + echo "::error::Failed to extract app_id from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$ORG_TOKEN" ] || [ "$ORG_TOKEN" = "null" ]; then + echo "::error::Failed to extract org_membership_token from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$PRIVATE_KEY" ] || [ "$PRIVATE_KEY" = "null" ]; then + echo "::error::Failed to extract private_key from secret docker-agent-action/github-app" + exit 1 + fi + echo "::add-mask::$APP_ID" echo "::add-mask::$ORG_TOKEN" echo "::add-mask::$PRIVATE_KEY" diff --git a/.github/workflows/reply-to-feedback.yml b/.github/workflows/reply-to-feedback.yml index 3c3d5e9..1bbf753 100644 --- a/.github/workflows/reply-to-feedback.yml +++ b/.github/workflows/reply-to-feedback.yml @@ -44,12 +44,27 @@ jobs: id: app-credentials shell: bash run: | + set -euo pipefail SECRET=$(aws secretsmanager get-secret-value \ --secret-id docker-agent-action/github-app \ --query SecretString --output text) APP_ID=$(echo "$SECRET" | jq -r .app_id) ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + + if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then + echo "::error::Failed to extract app_id from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$ORG_TOKEN" ] || [ "$ORG_TOKEN" = "null" ]; then + echo "::error::Failed to extract org_membership_token from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$PRIVATE_KEY" ] || [ "$PRIVATE_KEY" = "null" ]; then + echo "::error::Failed to extract private_key from secret docker-agent-action/github-app" + exit 1 + fi + echo "::add-mask::$APP_ID" echo "::add-mask::$ORG_TOKEN" echo "::add-mask::$PRIVATE_KEY" diff --git a/.github/workflows/review-pr.yml b/.github/workflows/review-pr.yml index a68e91a..df10f29 100644 --- a/.github/workflows/review-pr.yml +++ b/.github/workflows/review-pr.yml @@ -143,12 +143,27 @@ jobs: id: app-credentials shell: bash run: | + set -euo pipefail SECRET=$(aws secretsmanager get-secret-value \ --secret-id docker-agent-action/github-app \ --query SecretString --output text) APP_ID=$(echo "$SECRET" | jq -r .app_id) ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + + if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then + echo "::error::Failed to extract app_id from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$ORG_TOKEN" ] || [ "$ORG_TOKEN" = "null" ]; then + echo "::error::Failed to extract org_membership_token from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$PRIVATE_KEY" ] || [ "$PRIVATE_KEY" = "null" ]; then + echo "::error::Failed to extract private_key from secret docker-agent-action/github-app" + exit 1 + fi + echo "::add-mask::$APP_ID" echo "::add-mask::$ORG_TOKEN" echo "::add-mask::$PRIVATE_KEY" @@ -300,12 +315,27 @@ jobs: id: app-credentials shell: bash run: | + set -euo pipefail SECRET=$(aws secretsmanager get-secret-value \ --secret-id docker-agent-action/github-app \ --query SecretString --output text) APP_ID=$(echo "$SECRET" | jq -r .app_id) ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + + if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then + echo "::error::Failed to extract app_id from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$ORG_TOKEN" ] || [ "$ORG_TOKEN" = "null" ]; then + echo "::error::Failed to extract org_membership_token from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$PRIVATE_KEY" ] || [ "$PRIVATE_KEY" = "null" ]; then + echo "::error::Failed to extract private_key from secret docker-agent-action/github-app" + exit 1 + fi + echo "::add-mask::$APP_ID" echo "::add-mask::$ORG_TOKEN" echo "::add-mask::$PRIVATE_KEY" @@ -526,12 +556,27 @@ jobs: id: app-credentials shell: bash run: | + set -euo pipefail SECRET=$(aws secretsmanager get-secret-value \ --secret-id docker-agent-action/github-app \ --query SecretString --output text) APP_ID=$(echo "$SECRET" | jq -r .app_id) ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + + if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then + echo "::error::Failed to extract app_id from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$ORG_TOKEN" ] || [ "$ORG_TOKEN" = "null" ]; then + echo "::error::Failed to extract org_membership_token from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$PRIVATE_KEY" ] || [ "$PRIVATE_KEY" = "null" ]; then + echo "::error::Failed to extract private_key from secret docker-agent-action/github-app" + exit 1 + fi + echo "::add-mask::$APP_ID" echo "::add-mask::$ORG_TOKEN" echo "::add-mask::$PRIVATE_KEY" diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 5a96ef5..b0750fe 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -34,12 +34,27 @@ jobs: id: app-credentials shell: bash run: | + set -euo pipefail SECRET=$(aws secretsmanager get-secret-value \ --secret-id docker-agent-action/github-app \ --query SecretString --output text) APP_ID=$(echo "$SECRET" | jq -r .app_id) ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + + if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then + echo "::error::Failed to extract app_id from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$ORG_TOKEN" ] || [ "$ORG_TOKEN" = "null" ]; then + echo "::error::Failed to extract org_membership_token from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$PRIVATE_KEY" ] || [ "$PRIVATE_KEY" = "null" ]; then + echo "::error::Failed to extract private_key from secret docker-agent-action/github-app" + exit 1 + fi + echo "::add-mask::$APP_ID" echo "::add-mask::$ORG_TOKEN" echo "::add-mask::$PRIVATE_KEY" diff --git a/.github/workflows/self-review-pr.yml b/.github/workflows/self-review-pr.yml index 770d562..b24329f 100644 --- a/.github/workflows/self-review-pr.yml +++ b/.github/workflows/self-review-pr.yml @@ -58,12 +58,27 @@ jobs: id: app-credentials shell: bash run: | + set -euo pipefail SECRET=$(aws secretsmanager get-secret-value \ --secret-id docker-agent-action/github-app \ --query SecretString --output text) APP_ID=$(echo "$SECRET" | jq -r .app_id) ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + + if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then + echo "::error::Failed to extract app_id from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$ORG_TOKEN" ] || [ "$ORG_TOKEN" = "null" ]; then + echo "::error::Failed to extract org_membership_token from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$PRIVATE_KEY" ] || [ "$PRIVATE_KEY" = "null" ]; then + echo "::error::Failed to extract private_key from secret docker-agent-action/github-app" + exit 1 + fi + echo "::add-mask::$APP_ID" echo "::add-mask::$ORG_TOKEN" echo "::add-mask::$PRIVATE_KEY" @@ -223,12 +238,27 @@ jobs: id: app-credentials shell: bash run: | + set -euo pipefail SECRET=$(aws secretsmanager get-secret-value \ --secret-id docker-agent-action/github-app \ --query SecretString --output text) APP_ID=$(echo "$SECRET" | jq -r .app_id) ORG_TOKEN=$(echo "$SECRET" | jq -r .org_membership_token) PRIVATE_KEY=$(echo "$SECRET" | jq -r .private_key) + + if [ -z "$APP_ID" ] || [ "$APP_ID" = "null" ]; then + echo "::error::Failed to extract app_id from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$ORG_TOKEN" ] || [ "$ORG_TOKEN" = "null" ]; then + echo "::error::Failed to extract org_membership_token from secret docker-agent-action/github-app" + exit 1 + fi + if [ -z "$PRIVATE_KEY" ] || [ "$PRIVATE_KEY" = "null" ]; then + echo "::error::Failed to extract private_key from secret docker-agent-action/github-app" + exit 1 + fi + echo "::add-mask::$APP_ID" echo "::add-mask::$ORG_TOKEN" echo "::add-mask::$PRIVATE_KEY" From 11e66c03b9599d8f50172953d37e1532bb823e3b Mon Sep 17 00:00:00 2001 From: Derek Misler Date: Fri, 27 Mar 2026 10:27:47 -0400 Subject: [PATCH 3/4] feat: fetch AI API keys from Secrets Manager Move ANTHROPIC_API_KEY and OPENAI_API_KEY out of GitHub secrets and into AWS Secrets Manager (docker-agent-action/ai-api-keys). Keys are now fetched at runtime via the existing OIDC role, consistent with how GitHub App credentials are already fetched. Affected workflows: review-pr.yml, reply-to-feedback.yml, self-review-pr.yml, security-scan.yml, pr-describe.yml Assisted-By: docker-agent --- .github/workflows/pr-describe.yml | 17 +++++++- .github/workflows/reply-to-feedback.yml | 19 ++++++++- .github/workflows/review-pr.yml | 57 ++++++++++++++++++++++--- .github/workflows/security-scan.yml | 17 +++++++- .github/workflows/self-review-pr.yml | 38 +++++++++++++++-- 5 files changed, 134 insertions(+), 14 deletions(-) diff --git a/.github/workflows/pr-describe.yml b/.github/workflows/pr-describe.yml index d338b73..64b34d0 100644 --- a/.github/workflows/pr-describe.yml +++ b/.github/workflows/pr-describe.yml @@ -58,6 +58,21 @@ jobs: DELIM="$(openssl rand -hex 8)" { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Fetch AI API keys from Secrets Manager + id: ai-api-keys + shell: bash + run: | + set -euo pipefail + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/ai-api-keys \ + --query SecretString --output text) + ANTHROPIC_KEY=$(echo "$SECRET" | jq -r '.anthropic_api_key // ""') + OPENAI_KEY=$(echo "$SECRET" | jq -r '.openai_api_key // ""') + [ -n "$ANTHROPIC_KEY" ] && echo "::add-mask::$ANTHROPIC_KEY" + [ -n "$OPENAI_KEY" ] && echo "::add-mask::$OPENAI_KEY" + echo "anthropic-api-key=$ANTHROPIC_KEY" >> $GITHUB_OUTPUT + echo "openai-api-key=$OPENAI_KEY" >> $GITHUB_OUTPUT + - name: Check out Git repository uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 @@ -217,7 +232,7 @@ jobs: **Diff:** $(cat pr.diff) - anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} + anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} github-token: ${{ steps.app-token.outputs.token || github.token }} timeout: 300 # 5 minutes diff --git a/.github/workflows/reply-to-feedback.yml b/.github/workflows/reply-to-feedback.yml index 1bbf753..b774aca 100644 --- a/.github/workflows/reply-to-feedback.yml +++ b/.github/workflows/reply-to-feedback.yml @@ -73,6 +73,21 @@ jobs: DELIM="$(openssl rand -hex 8)" { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Fetch AI API keys from Secrets Manager + id: ai-api-keys + shell: bash + run: | + set -euo pipefail + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/ai-api-keys \ + --query SecretString --output text) + ANTHROPIC_KEY=$(echo "$SECRET" | jq -r '.anthropic_api_key // ""') + OPENAI_KEY=$(echo "$SECRET" | jq -r '.openai_api_key // ""') + [ -n "$ANTHROPIC_KEY" ] && echo "::add-mask::$ANTHROPIC_KEY" + [ -n "$OPENAI_KEY" ] && echo "::add-mask::$OPENAI_KEY" + echo "anthropic-api-key=$ANTHROPIC_KEY" >> $GITHUB_OUTPUT + echo "openai-api-key=$OPENAI_KEY" >> $GITHUB_OUTPUT + # ---------------------------------------------------------------- # Download artifact from the triggering workflow run # ---------------------------------------------------------------- @@ -361,8 +376,8 @@ jobs: with: thread-context: ${{ steps.thread.outputs.prompt }} comment-id: ${{ steps.meta.outputs.comment_id }} - anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} - openai-api-key: ${{ secrets.OPENAI_API_KEY }} + anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} diff --git a/.github/workflows/review-pr.yml b/.github/workflows/review-pr.yml index df10f29..57f133a 100644 --- a/.github/workflows/review-pr.yml +++ b/.github/workflows/review-pr.yml @@ -172,6 +172,21 @@ jobs: DELIM="$(openssl rand -hex 8)" { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Fetch AI API keys from Secrets Manager + id: ai-api-keys + shell: bash + run: | + set -euo pipefail + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/ai-api-keys \ + --query SecretString --output text) + ANTHROPIC_KEY=$(echo "$SECRET" | jq -r '.anthropic_api_key // ""') + OPENAI_KEY=$(echo "$SECRET" | jq -r '.openai_api_key // ""') + [ -n "$ANTHROPIC_KEY" ] && echo "::add-mask::$ANTHROPIC_KEY" + [ -n "$OPENAI_KEY" ] && echo "::add-mask::$OPENAI_KEY" + echo "anthropic-api-key=$ANTHROPIC_KEY" >> $GITHUB_OUTPUT + echo "openai-api-key=$OPENAI_KEY" >> $GITHUB_OUTPUT + - name: Get PR number id: get-pr shell: bash @@ -277,8 +292,8 @@ jobs: add-prompt-files: ${{ inputs.add-prompt-files }} model: ${{ inputs.model }} github-token: ${{ steps.app-token.outputs.token || github.token }} - anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} - openai-api-key: ${{ secrets.OPENAI_API_KEY }} + anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} @@ -344,6 +359,21 @@ jobs: DELIM="$(openssl rand -hex 8)" { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Fetch AI API keys from Secrets Manager + id: ai-api-keys + shell: bash + run: | + set -euo pipefail + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/ai-api-keys \ + --query SecretString --output text) + ANTHROPIC_KEY=$(echo "$SECRET" | jq -r '.anthropic_api_key // ""') + OPENAI_KEY=$(echo "$SECRET" | jq -r '.openai_api_key // ""') + [ -n "$ANTHROPIC_KEY" ] && echo "::add-mask::$ANTHROPIC_KEY" + [ -n "$OPENAI_KEY" ] && echo "::add-mask::$OPENAI_KEY" + echo "anthropic-api-key=$ANTHROPIC_KEY" >> $GITHUB_OUTPUT + echo "openai-api-key=$OPENAI_KEY" >> $GITHUB_OUTPUT + - name: Check if commenter is org member id: membership uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 @@ -443,8 +473,8 @@ jobs: model: ${{ inputs.model }} github-token: ${{ steps.app-token.outputs.token || github.token }} trusted-bot-app-id: ${{ steps.app-credentials.outputs.app-id }} - anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} - openai-api-key: ${{ secrets.OPENAI_API_KEY }} + anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} @@ -585,6 +615,21 @@ jobs: DELIM="$(openssl rand -hex 8)" { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Fetch AI API keys from Secrets Manager + id: ai-api-keys + shell: bash + run: | + set -euo pipefail + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/ai-api-keys \ + --query SecretString --output text) + ANTHROPIC_KEY=$(echo "$SECRET" | jq -r '.anthropic_api_key // ""') + OPENAI_KEY=$(echo "$SECRET" | jq -r '.openai_api_key // ""') + [ -n "$ANTHROPIC_KEY" ] && echo "::add-mask::$ANTHROPIC_KEY" + [ -n "$OPENAI_KEY" ] && echo "::add-mask::$OPENAI_KEY" + echo "anthropic-api-key=$ANTHROPIC_KEY" >> $GITHUB_OUTPUT + echo "openai-api-key=$OPENAI_KEY" >> $GITHUB_OUTPUT + - name: Check if reply is to agent comment id: check shell: bash @@ -823,8 +868,8 @@ jobs: with: thread-context: ${{ steps.thread.outputs.prompt }} comment-id: ${{ github.event.comment.id }} - anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} - openai-api-key: ${{ secrets.OPENAI_API_KEY }} + anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index b0750fe..0703389 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -63,6 +63,21 @@ jobs: DELIM="$(openssl rand -hex 8)" { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Fetch AI API keys from Secrets Manager + id: ai-api-keys + shell: bash + run: | + set -euo pipefail + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/ai-api-keys \ + --query SecretString --output text) + ANTHROPIC_KEY=$(echo "$SECRET" | jq -r '.anthropic_api_key // ""') + OPENAI_KEY=$(echo "$SECRET" | jq -r '.openai_api_key // ""') + [ -n "$ANTHROPIC_KEY" ] && echo "::add-mask::$ANTHROPIC_KEY" + [ -n "$OPENAI_KEY" ] && echo "::add-mask::$OPENAI_KEY" + echo "anthropic-api-key=$ANTHROPIC_KEY" >> $GITHUB_OUTPUT + echo "openai-api-key=$OPENAI_KEY" >> $GITHUB_OUTPUT + - name: Check out Git repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -162,7 +177,7 @@ jobs: with: agent: agentcatalog/github-action-security-scanner prompt: ${{ steps.commits.outputs.prompt }} - anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} + anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} timeout: 300 # 5 minutes - name: Validate reported file paths diff --git a/.github/workflows/self-review-pr.yml b/.github/workflows/self-review-pr.yml index b24329f..155d198 100644 --- a/.github/workflows/self-review-pr.yml +++ b/.github/workflows/self-review-pr.yml @@ -87,6 +87,21 @@ jobs: DELIM="$(openssl rand -hex 8)" { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Fetch AI API keys from Secrets Manager + id: ai-api-keys + shell: bash + run: | + set -euo pipefail + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/ai-api-keys \ + --query SecretString --output text) + ANTHROPIC_KEY=$(echo "$SECRET" | jq -r '.anthropic_api_key // ""') + OPENAI_KEY=$(echo "$SECRET" | jq -r '.openai_api_key // ""') + [ -n "$ANTHROPIC_KEY" ] && echo "::add-mask::$ANTHROPIC_KEY" + [ -n "$OPENAI_KEY" ] && echo "::add-mask::$OPENAI_KEY" + echo "anthropic-api-key=$ANTHROPIC_KEY" >> $GITHUB_OUTPUT + echo "openai-api-key=$OPENAI_KEY" >> $GITHUB_OUTPUT + # For workflow_run events (fork PRs), download the artifact saved by pr-review-trigger.yml # to get the PR number. For pull_request events, read it directly from the event payload. - name: Get PR number @@ -207,8 +222,8 @@ jobs: with: pr-number: ${{ steps.get-pr.outputs.pr-number }} github-token: ${{ steps.app-token.outputs.token || github.token }} - anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} - openai-api-key: ${{ secrets.OPENAI_API_KEY }} + anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} @@ -267,6 +282,21 @@ jobs: DELIM="$(openssl rand -hex 8)" { echo "private-key<<$DELIM"; echo "$PRIVATE_KEY"; echo "$DELIM"; } >> $GITHUB_OUTPUT + - name: Fetch AI API keys from Secrets Manager + id: ai-api-keys + shell: bash + run: | + set -euo pipefail + SECRET=$(aws secretsmanager get-secret-value \ + --secret-id docker-agent-action/ai-api-keys \ + --query SecretString --output text) + ANTHROPIC_KEY=$(echo "$SECRET" | jq -r '.anthropic_api_key // ""') + OPENAI_KEY=$(echo "$SECRET" | jq -r '.openai_api_key // ""') + [ -n "$ANTHROPIC_KEY" ] && echo "::add-mask::$ANTHROPIC_KEY" + [ -n "$OPENAI_KEY" ] && echo "::add-mask::$OPENAI_KEY" + echo "anthropic-api-key=$ANTHROPIC_KEY" >> $GITHUB_OUTPUT + echo "openai-api-key=$OPENAI_KEY" >> $GITHUB_OUTPUT + - name: Check if commenter is org member id: membership uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 @@ -363,8 +393,8 @@ jobs: comment-id: ${{ github.event.comment.id }} github-token: ${{ steps.app-token.outputs.token || github.token }} trusted-bot-app-id: ${{ steps.app-credentials.outputs.app-id }} - anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }} - openai-api-key: ${{ secrets.OPENAI_API_KEY }} + anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} From cfe6c7084424c6d99a533afd14a681e8309bae33 Mon Sep 17 00:00:00 2001 From: Derek Misler Date: Fri, 27 Mar 2026 10:30:39 -0400 Subject: [PATCH 4/4] fix: use consumer secrets as override, Secrets Manager as fallback For ANTHROPIC_API_KEY, OPENAI_API_KEY, and CAGENT_ORG_MEMBERSHIP_TOKEN, apply precedence: consumer-defined GitHub secret wins; Secrets Manager value is used only if the consumer hasn't provided one. Uses GitHub Actions || short-circuit: secrets.X || steps.y.outputs.x Assisted-By: docker-agent --- .github/workflows/pr-describe.yml | 2 +- .github/workflows/reply-to-feedback.yml | 6 +++--- .github/workflows/review-pr.yml | 18 +++++++++--------- .github/workflows/security-scan.yml | 2 +- .github/workflows/self-review-pr.yml | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/pr-describe.yml b/.github/workflows/pr-describe.yml index 64b34d0..dde070c 100644 --- a/.github/workflows/pr-describe.yml +++ b/.github/workflows/pr-describe.yml @@ -232,7 +232,7 @@ jobs: **Diff:** $(cat pr.diff) - anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY || steps.ai-api-keys.outputs.anthropic-api-key }} github-token: ${{ steps.app-token.outputs.token || github.token }} timeout: 300 # 5 minutes diff --git a/.github/workflows/reply-to-feedback.yml b/.github/workflows/reply-to-feedback.yml index b774aca..db81674 100644 --- a/.github/workflows/reply-to-feedback.yml +++ b/.github/workflows/reply-to-feedback.yml @@ -216,7 +216,7 @@ jobs: env: USERNAME: ${{ steps.meta.outputs.author }} with: - github-token: ${{ steps.app-credentials.outputs.org-membership-token }} + github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN || steps.app-credentials.outputs.org-membership-token }} script: | const org = 'docker'; const username = process.env.USERNAME; @@ -376,8 +376,8 @@ jobs: with: thread-context: ${{ steps.thread.outputs.prompt }} comment-id: ${{ steps.meta.outputs.comment_id }} - anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} - openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY || steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ secrets.OPENAI_API_KEY || steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} diff --git a/.github/workflows/review-pr.yml b/.github/workflows/review-pr.yml index 57f133a..eda52c3 100644 --- a/.github/workflows/review-pr.yml +++ b/.github/workflows/review-pr.yml @@ -224,7 +224,7 @@ jobs: PR_DRAFT: ${{ steps.pr-info.outputs.draft }} PR_AUTHOR: ${{ steps.pr-info.outputs.author }} with: - github-token: ${{ steps.app-credentials.outputs.org-membership-token }} + github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN || steps.app-credentials.outputs.org-membership-token }} script: | const org = '${{ inputs.auto-review-org }}'; @@ -292,8 +292,8 @@ jobs: add-prompt-files: ${{ inputs.add-prompt-files }} model: ${{ inputs.model }} github-token: ${{ steps.app-token.outputs.token || github.token }} - anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} - openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY || steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ secrets.OPENAI_API_KEY || steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} @@ -378,7 +378,7 @@ jobs: id: membership uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - github-token: ${{ steps.app-credentials.outputs.org-membership-token }} + github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN || steps.app-credentials.outputs.org-membership-token }} script: | const org = '${{ inputs.auto-review-org }}'; const username = context.payload.comment.user.login; @@ -473,8 +473,8 @@ jobs: model: ${{ inputs.model }} github-token: ${{ steps.app-token.outputs.token || github.token }} trusted-bot-app-id: ${{ steps.app-credentials.outputs.app-id }} - anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} - openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY || steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ secrets.OPENAI_API_KEY || steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} @@ -697,7 +697,7 @@ jobs: id: auth shell: bash env: - GH_TOKEN: ${{ steps.app-credentials.outputs.org-membership-token }} + GH_TOKEN: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN || steps.app-credentials.outputs.org-membership-token }} ORG: ${{ inputs.auto-review-org }} USERNAME: ${{ github.event.comment.user.login }} run: | @@ -868,8 +868,8 @@ jobs: with: thread-context: ${{ steps.thread.outputs.prompt }} comment-id: ${{ github.event.comment.id }} - anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} - openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY || steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ secrets.OPENAI_API_KEY || steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 0703389..7607b96 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -177,7 +177,7 @@ jobs: with: agent: agentcatalog/github-action-security-scanner prompt: ${{ steps.commits.outputs.prompt }} - anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY || steps.ai-api-keys.outputs.anthropic-api-key }} timeout: 300 # 5 minutes - name: Validate reported file paths diff --git a/.github/workflows/self-review-pr.yml b/.github/workflows/self-review-pr.yml index 155d198..0d3b92e 100644 --- a/.github/workflows/self-review-pr.yml +++ b/.github/workflows/self-review-pr.yml @@ -144,7 +144,7 @@ jobs: id: membership uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - github-token: ${{ steps.app-credentials.outputs.org-membership-token }} + github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN || steps.app-credentials.outputs.org-membership-token }} script: | const org = 'docker'; @@ -222,8 +222,8 @@ jobs: with: pr-number: ${{ steps.get-pr.outputs.pr-number }} github-token: ${{ steps.app-token.outputs.token || github.token }} - anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} - openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY || steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ secrets.OPENAI_API_KEY || steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }} @@ -301,7 +301,7 @@ jobs: id: membership uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - github-token: ${{ steps.app-credentials.outputs.org-membership-token }} + github-token: ${{ secrets.CAGENT_ORG_MEMBERSHIP_TOKEN || steps.app-credentials.outputs.org-membership-token }} script: | const org = 'docker'; const username = context.payload.comment.user.login; @@ -393,8 +393,8 @@ jobs: comment-id: ${{ github.event.comment.id }} github-token: ${{ steps.app-token.outputs.token || github.token }} trusted-bot-app-id: ${{ steps.app-credentials.outputs.app-id }} - anthropic-api-key: ${{ steps.ai-api-keys.outputs.anthropic-api-key }} - openai-api-key: ${{ steps.ai-api-keys.outputs.openai-api-key }} + anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY || steps.ai-api-keys.outputs.anthropic-api-key }} + openai-api-key: ${{ secrets.OPENAI_API_KEY || steps.ai-api-keys.outputs.openai-api-key }} google-api-key: ${{ secrets.GOOGLE_API_KEY }} aws-bearer-token-bedrock: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} xai-api-key: ${{ secrets.XAI_API_KEY }}