| """Task 4: Workflow Secrets and Permissions — MEDIUM. |
| |
| Agent fixes secret wiring, env variable mapping, and permission issues |
| in GitHub Actions workflows: |
| - Missing env block for Docker secrets |
| - Wrong secret syntax (${ vs ${{) |
| - Missing permissions for GITHUB_TOKEN |
| - GHCR login using wrong credentials |
| - Missing write permission for packages |
| """ |
|
|
|
|
|
|
| from server.models import TaskDifficulty |
| from server.tasks.base import BaseTask |
|
|
|
|
| class WorkflowSecretsPermissionsTask(BaseTask): |
| NAME = "Workflow Secrets and Permissions" |
| DESCRIPTION = "Fix secret wiring, env usage, and permissions in workflows" |
| DIFFICULTY = TaskDifficulty.MEDIUM |
| AVAILABLE_SECRETS = ["DOCKER_USERNAME", "DOCKER_PASSWORD", "GITHUB_TOKEN"] |
|
|
| SCENARIOS = [ |
| |
| { |
| "id": "missing_env_secrets", |
| "files": [ |
| { |
| "path": ".github/workflows/build.yml", |
| "type": "workflow", |
| "content": ( |
| "name: Build and Push\n" |
| "on: push\n" |
| "\n" |
| "jobs:\n" |
| " build:\n" |
| " runs-on: ubuntu-latest\n" |
| " steps:\n" |
| " - uses: actions/checkout@v4\n" |
| " - name: Login to DockerHub\n" |
| " run: echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin\n" |
| " - name: Build and push\n" |
| " run: |\n" |
| " docker build -t myuser/myapp:${{ github.sha }} .\n" |
| " docker push myuser/myapp:${{ github.sha }}" |
| ), |
| }, |
| { |
| "path": "Dockerfile", |
| "type": "dockerfile", |
| "content": ( |
| "FROM python:3.9-slim\n" |
| "WORKDIR /app\n" |
| "COPY . .\n" |
| "RUN pip install -r requirements.txt\n" |
| 'CMD ["python", "app.py"]' |
| ), |
| }, |
| ], |
| "error": { |
| "phase": "workflow_parse", |
| "message": "Error: Cannot perform an interactive login from a non TTY device", |
| "exit_code": 1, |
| "failed_step": "Login to DockerHub", |
| }, |
| "expected_fixes": [ |
| { |
| "file": ".github/workflows/build.yml", |
| "type": "contains", |
| "expected": "DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}", |
| "hint": "Secrets must be passed via env block", |
| }, |
| { |
| "file": ".github/workflows/build.yml", |
| "type": "contains", |
| "expected": "DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}", |
| "hint": "Both username and password need to be passed as env vars", |
| }, |
| ], |
| }, |
|
|
| |
| { |
| "id": "wrong_secret_syntax", |
| "files": [ |
| { |
| "path": ".github/workflows/deploy.yml", |
| "type": "workflow", |
| "content": ( |
| "name: Deploy\n" |
| "on:\n" |
| " push:\n" |
| " branches: [main]\n" |
| "\n" |
| "jobs:\n" |
| " deploy:\n" |
| " runs-on: ubuntu-latest\n" |
| " steps:\n" |
| " - uses: actions/checkout@v4\n" |
| " - name: Deploy to server\n" |
| " run: |\n" |
| " echo \"Deploying version ${ github.sha }\"\n" |
| " curl -H \"Authorization: Bearer ${ secrets.DEPLOY_TOKEN }\" https://api.example.com/deploy\n" |
| " env:\n" |
| " DEPLOY_TOKEN: ${ secrets.DEPLOY_TOKEN }" |
| ), |
| }, |
| ], |
| "error": { |
| "phase": "workflow_parse", |
| "message": ( |
| "Unrecognized expression syntax. " |
| "Use ${{ expression }} with double braces for GitHub Actions expressions." |
| ), |
| "exit_code": 1, |
| }, |
| "expected_fixes": [ |
| { |
| "file": ".github/workflows/deploy.yml", |
| "type": "contains", |
| "expected": "${{ secrets.DEPLOY_TOKEN }}", |
| "hint": "GitHub Actions uses ${{ }} (double braces), not ${ } (single brace)", |
| }, |
| { |
| "file": ".github/workflows/deploy.yml", |
| "type": "contains", |
| "expected": "${{ github.sha }}", |
| "hint": "All GitHub expressions require double braces: ${{ github.sha }}", |
| }, |
| ], |
| }, |
|
|
| |
| { |
| "id": "missing_token_permissions", |
| "files": [ |
| { |
| "path": ".github/workflows/publish.yml", |
| "type": "workflow", |
| "content": ( |
| "name: Publish Package\n" |
| "on:\n" |
| " push:\n" |
| " tags: ['v*']\n" |
| "\n" |
| "jobs:\n" |
| " publish:\n" |
| " runs-on: ubuntu-latest\n" |
| " steps:\n" |
| " - uses: actions/checkout@v4\n" |
| " - name: Login to GHCR\n" |
| " run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin\n" |
| " - name: Build and push\n" |
| " run: |\n" |
| " docker build -t ghcr.io/${{ github.repository }}:${{ github.ref_name }} .\n" |
| " docker push ghcr.io/${{ github.repository }}:${{ github.ref_name }}" |
| ), |
| }, |
| { |
| "path": "Dockerfile", |
| "type": "dockerfile", |
| "content": ( |
| "FROM python:3.11-slim\n" |
| "WORKDIR /app\n" |
| "COPY . .\n" |
| 'CMD ["python", "app.py"]' |
| ), |
| }, |
| ], |
| "error": { |
| "phase": "push", |
| "message": ( |
| "denied: permission_denied: write_package — " |
| "GITHUB_TOKEN does not have packages:write permission" |
| ), |
| "exit_code": 1, |
| "failed_step": "Build and push", |
| }, |
| "expected_fixes": [ |
| { |
| "file": ".github/workflows/publish.yml", |
| "type": "contains", |
| "expected": "packages: write", |
| "hint": "Add 'permissions: packages: write' at job or workflow level to allow pushing to GHCR", |
| }, |
| ], |
| }, |
|
|
| |
| { |
| "id": "secret_not_in_env", |
| "files": [ |
| { |
| "path": ".github/workflows/notify.yml", |
| "type": "workflow", |
| "content": ( |
| "name: Notify\n" |
| "on:\n" |
| " push:\n" |
| " branches: [main]\n" |
| "\n" |
| "jobs:\n" |
| " notify:\n" |
| " runs-on: ubuntu-latest\n" |
| " steps:\n" |
| " - uses: actions/checkout@v4\n" |
| " - name: Send Slack notification\n" |
| " run: |\n" |
| " curl -X POST -H 'Content-Type: application/json' \\\n" |
| " -d '{\"text\": \"Deployed ${{ github.sha }}\"}' \\\n" |
| " $SLACK_WEBHOOK_URL" |
| ), |
| }, |
| ], |
| "error": { |
| "phase": "workflow_parse", |
| "message": "SLACK_WEBHOOK_URL is empty — secret not available in shell environment", |
| "exit_code": 1, |
| "failed_step": "Send Slack notification", |
| }, |
| "expected_fixes": [ |
| { |
| "file": ".github/workflows/notify.yml", |
| "type": "contains", |
| "expected": "SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}", |
| "hint": "Map the secret to an environment variable using env: block", |
| }, |
| ], |
| }, |
|
|
| |
| { |
| "id": "ghcr_wrong_credentials", |
| "files": [ |
| { |
| "path": ".github/workflows/ghcr.yml", |
| "type": "workflow", |
| "content": ( |
| "name: Push to GHCR\n" |
| "on:\n" |
| " push:\n" |
| " branches: [main]\n" |
| "\n" |
| "jobs:\n" |
| " push:\n" |
| " runs-on: ubuntu-latest\n" |
| " permissions:\n" |
| " packages: write\n" |
| " steps:\n" |
| " - uses: actions/checkout@v4\n" |
| " - name: Login to GHCR\n" |
| " run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin\n" |
| " - name: Push image\n" |
| " run: |\n" |
| " docker build -t ghcr.io/${{ github.repository }}:latest .\n" |
| " docker push ghcr.io/${{ github.repository }}:latest" |
| ), |
| }, |
| { |
| "path": "Dockerfile", |
| "type": "dockerfile", |
| "content": ( |
| "FROM python:3.11-slim\n" |
| "WORKDIR /app\n" |
| "COPY . .\n" |
| 'CMD ["python", "app.py"]' |
| ), |
| }, |
| ], |
| "error": { |
| "phase": "push", |
| "message": ( |
| "Error: denied: installation not allowed to Create organization package — " |
| "GHCR requires GITHUB_TOKEN, not DOCKER_PASSWORD" |
| ), |
| "exit_code": 1, |
| "failed_step": "Login to GHCR", |
| }, |
| "expected_fixes": [ |
| { |
| "file": ".github/workflows/ghcr.yml", |
| "type": "contains", |
| "expected": "secrets.GITHUB_TOKEN", |
| "hint": "GHCR uses GITHUB_TOKEN for authentication, not DOCKER_PASSWORD", |
| }, |
| ], |
| }, |
| ] |
|
|