pr-test
Facilitates end-to-end manual testing of PRs using Docker, ensuring thorough validation with detailed reporting and evidence.
Install this skill
Security score
The pr-test skill was audited on May 29, 2026 and we found 87 security issues across 5 threat categories, including 2 high-severity. Review the findings below before installing.
Categories Tested
Security Issues
Template literal with variable interpolation in command context
| 122 | ```bash |
Template literal with variable interpolation in command context
| 138 | ```bash |
Template literal with variable interpolation in command context
| 234 | ```bash |
Template literal with variable interpolation in command context
| 281 | ```bash |
Template literal with variable interpolation in command context
| 305 | ```bash |
Template literal with variable interpolation in command context
| 322 | ```bash |
Template literal with variable interpolation in command context
| 477 | ```bash |
Template literal with variable interpolation in command context
| 830 | ```bash |
Template literal with variable interpolation in command context
| 892 | ```bash |
Template literal with variable interpolation in command context
| 933 | - \`$(basename "$failed")\` (local path: \`$failed\`)" |
Template literal with variable interpolation in command context
| 1018 | ```bash |
Template literal with variable interpolation in command context
| 1039 | ```bash |
Curl to non-GitHub URL
| 69 | BEFORE=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/credits | jq '.credits') |
Curl to non-GitHub URL
| 75 | AFTER=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/credits | jq '.credits') |
Curl to non-GitHub URL
| 93 | API_CREDITS=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/credits | jq '.credits') |
Curl to non-GitHub URL
| 447 | if [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8006/docs 2>/dev/null)" = "200" ]; then |
Curl to non-GitHub URL
| 465 | if [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:3000 2>/dev/null)" = "200" ]; then |
Curl to non-GitHub URL
| 494 | BACKEND=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8006/docs 2>/dev/null) |
Curl to non-GitHub URL
| 495 | FRONTEND=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null) |
Curl to non-GitHub URL
| 512 | RESULT=$(curl -s -X POST 'http://localhost:8000/auth/v1/signup' \ |
Curl to non-GitHub URL
| 522 | RESULT=$(curl -s -X POST 'http://localhost:8000/auth/v1/signup' \ |
Curl to non-GitHub URL
| 529 | TOKEN=$(curl -s -X POST 'http://localhost:8000/auth/v1/token?grant_type=password' \ |
Curl to non-GitHub URL
| 537 | curl -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/... |
Curl to non-GitHub URL
| 584 | curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/graphs | jq . | head -20 |
Curl to non-GitHub URL
| 587 | curl -s -X POST http://localhost:8006/api/graphs \ |
Curl to non-GitHub URL
| 593 | curl -s -X POST "http://localhost:8006/api/graphs/{graph_id}/execute" \ |
Curl to non-GitHub URL
| 606 | BEFORE_STATE=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/{resource} | jq '{relevant_fields}') |
Curl to non-GitHub URL
| 614 | AFTER_STATE=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/{resource} | jq '{relevant_fields}') |
Curl to non-GitHub URL
| 711 | SESSION_ID=$(curl -s -X POST 'http://localhost:8006/api/chat/sessions' \ |
Curl to non-GitHub URL
| 717 | curl -N -X POST "http://localhost:8006/api/chat/sessions/$SESSION_ID/stream" \ |
Access to hidden dotfiles in home directory
| 357 | - **Linux/WSL**: `~/.claude/.credentials.json` |
Access to hidden dotfiles in home directory
| 360 | It sets `CLAUDE_CODE_OAUTH_TOKEN`, `CLAUDE_CODE_REFRESH_TOKEN`, and `CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=true` in the `.env` file. On container startup, the backend auto-provisions `~/.claude/.credentia |
Access to hidden dotfiles in home directory
| 1122 | **Fix:** Re-extract the OAuth token from macOS keychain (see step 3b, Option 1) and recreate the container (`docker compose up -d copilot_executor`). The backend auto-provisions `~/.claude/.credential |
Access to .env file
| 329 | ### 3a. Copy .env files from the root worktree |
Access to .env file
| 331 | The root worktree (`$REPO_ROOT`) has the canonical `.env` files with all API keys. Copy them to the target worktree: |
Access to .env file
| 334 | # CRITICAL: .env files are NOT checked into git. They must be copied manually. |
Access to .env file
| 335 | cp $REPO_ROOT/autogpt_platform/.env $PLATFORM_DIR/.env |
Access to .env file
| 336 | cp $REPO_ROOT/autogpt_platform/backend/.env $BACKEND_DIR/.env |
Access to .env file
| 337 | cp $REPO_ROOT/autogpt_platform/frontend/.env $FRONTEND_DIR/.env |
Access to .env file
| 348 | Run the helper script to extract tokens from your host and auto-update `backend/.env` (works on macOS, Linux, and Windows/WSL): |
Access to .env file
| 351 | # Extracts OAuth tokens and writes CLAUDE_CODE_OAUTH_TOKEN + CLAUDE_CODE_REFRESH_TOKEN into .env |
Access to .env file
| 352 | bash $BACKEND_DIR/scripts/refresh_claude_token.sh --env-file $BACKEND_DIR/.env |
Access to .env file
| 360 | It sets `CLAUDE_CODE_OAUTH_TOKEN`, `CLAUDE_CODE_REFRESH_TOKEN`, and `CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=true` in the `.env` file. On container startup, the backend auto-provisions `~/.claude/.credentia |
Access to .env file
| 362 | **Note:** The OAuth token expires (~24h). If copilot returns auth errors, re-run the script and restart: `$BACKEND_DIR/scripts/refresh_claude_token.sh --env-file $BACKEND_DIR/.env && docker compose up |
Access to .env file
| 369 | # In $BACKEND_DIR/.env, ensure these are set: |
Access to .env file
| 371 | CHAT_API_KEY=<value of OPEN_ROUTER_API_KEY from the same .env> |
Access to .env file
| 378 | ORKEY=$(grep "^OPEN_ROUTER_API_KEY=" $BACKEND_DIR/.env | cut -d= -f2) |
Access to .env file
| 379 | [ -n "$ORKEY" ] || { echo "ERROR: OPEN_ROUTER_API_KEY is missing in $BACKEND_DIR/.env"; exit 1; } |
Access to .env file
| 380 | perl -i -pe 's/CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=true/CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=false/' $BACKEND_DIR/.env |
Access to .env file
| 382 | grep -q "^CHAT_API_KEY=" $BACKEND_DIR/.env && perl -i -pe "s|^CHAT_API_KEY=.*|CHAT_API_KEY=$ORKEY|" $BACKEND_DIR/.env || echo "CHAT_API_KEY=$ORKEY" >> $BACKEND_DIR/.env |
Access to .env file
| 383 | grep -q "^CHAT_BASE_URL=" $BACKEND_DIR/.env && perl -i -pe 's|^CHAT_BASE_URL=.*|CHAT_BASE_URL=https://openrouter.ai/api/v1|' $BACKEND_DIR/.env || echo "CHAT_BASE_URL=https://openrouter.ai/api/v1" >> $ |
Access to .env file
| 508 | ANON_KEY=$(grep "NEXT_PUBLIC_SUPABASE_ANON_KEY=" $FRONTEND_DIR/.env | sed 's/.*NEXT_PUBLIC_SUPABASE_ANON_KEY=//' | tr -d '[:space:]') |
Access to system keychain/keyring
| 356 | - **macOS**: system keychain (`"Claude Code-credentials"`) |
Access to system keychain/keyring
| 1122 | **Fix:** Re-extract the OAuth token from macOS keychain (see step 3b, Option 1) and recreate the container (`docker compose up -d copilot_executor`). The backend auto-provisions `~/.claude/.credential |
External URL reference
| 69 | BEFORE=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/credits | jq '.credits') |
External URL reference
| 75 | AFTER=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/credits | jq '.credits') |
External URL reference
| 93 | API_CREDITS=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/credits | jq '.credits') |
External URL reference
| 372 | CHAT_BASE_URL=https://openrouter.ai/api/v1 |
External URL reference
| 383 | grep -q "^CHAT_BASE_URL=" $BACKEND_DIR/.env && perl -i -pe 's|^CHAT_BASE_URL=.*|CHAT_BASE_URL=https://openrouter.ai/api/v1|' $BACKEND_DIR/.env || echo "CHAT_BASE_URL=https://openrouter.ai/api/v1" >> $ |
External URL reference
| 447 | if [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8006/docs 2>/dev/null)" = "200" ]; then |
External URL reference
| 465 | if [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:3000 2>/dev/null)" = "200" ]; then |
External URL reference
| 494 | BACKEND=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8006/docs 2>/dev/null) |
External URL reference
| 495 | FRONTEND=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null) |
External URL reference
| 512 | RESULT=$(curl -s -X POST 'http://localhost:8000/auth/v1/signup' \ |
External URL reference
| 522 | RESULT=$(curl -s -X POST 'http://localhost:8000/auth/v1/signup' \ |
External URL reference
| 529 | TOKEN=$(curl -s -X POST 'http://localhost:8000/auth/v1/token?grant_type=password' \ |
External URL reference
| 537 | curl -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/... |
External URL reference
| 547 | "http://localhost:8006/api/onboarding/step?step=VISIT_COPILOT" \ |
External URL reference
| 553 | "http://localhost:8006/api/onboarding/completed" \ |
External URL reference
| 568 | | Frontend | 3000 | http://localhost:3000 | |
External URL reference
| 569 | | Backend REST | 8006 | http://localhost:8006 | |
External URL reference
| 570 | | Supabase Auth (via Kong) | 8000 | http://localhost:8000 | |
External URL reference
| 571 | | Executor | 8002 | http://localhost:8002 | |
External URL reference
| 572 | | Copilot Executor | 8008 | http://localhost:8008 | |
External URL reference
| 573 | | WebSocket | 8001 | http://localhost:8001 | |
External URL reference
| 574 | | Database Manager | 8005 | http://localhost:8005 | |
External URL reference
| 584 | curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/graphs | jq . | head -20 |
External URL reference
| 587 | curl -s -X POST http://localhost:8006/api/graphs \ |
External URL reference
| 593 | curl -s -X POST "http://localhost:8006/api/graphs/{graph_id}/execute" \ |
External URL reference
| 600 | "http://localhost:8006/api/graphs/{graph_id}/executions/{exec_id}" | jq . |
External URL reference
| 606 | BEFORE_STATE=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/{resource} | jq '{relevant_fields}') |
External URL reference
| 614 | AFTER_STATE=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/{resource} | jq '{relevant_fields}') |
External URL reference
| 632 | agent-browser --session-name pr-test open 'http://localhost:3000/login' --timeout 15000 |
External URL reference
| 647 | agent-browser --session-name pr-test open 'http://localhost:3000/copilot' --timeout 10000 |
External URL reference
| 711 | SESSION_ID=$(curl -s -X POST 'http://localhost:8006/api/chat/sessions' \ |
External URL reference
| 717 | curl -N -X POST "http://localhost:8006/api/chat/sessions/$SESSION_ID/stream" \ |
External URL reference
| 726 | agent-browser --session-name pr-test open 'http://localhost:3000/copilot' --timeout 10000 |