feat: consolidate security modes — merge pr+hygiene into review_all (#739)

Simplify from 6 modes (Hexa-Mode) to 4 modes (Quad-Mode) by folding
single-PR review and hygiene into a unified review_all mode that runs
every 15 minutes. This removes the pull_request trigger entirely since
review_all catches all open PRs on schedule, and absorbs staleness
checks + branch cleanup into the same cycle.

Remaining modes: team_building, triage, review_all, scan.

Co-authored-by: Sprite <noreply@sprites.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
L 2026-02-12 14:53:26 -08:00 committed by GitHub
parent 4924a7d5db
commit 15e2ca6caf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 182 additions and 265 deletions

View file

@ -1,13 +1,12 @@
#!/bin/bash
set -eo pipefail
# Security Review Team Service — Single Cycle (Penta-Mode)
# Security Review Team Service — Single Cycle (Quad-Mode)
# Triggered by trigger-server.ts via GitHub Actions
#
# RUN_MODE=team_building — implement team changes from issue (reason=team_building, 15 min)
# RUN_MODE=triage — single-agent issue triage for prompt injection/spam (reason=triage, 5 min)
# RUN_MODE=pr — 2-agent security review for a specific PR (10 min)
# RUN_MODE=hygiene — stale PR cleanup + triage (reason=hygiene, 15 min)
# RUN_MODE=review_all — batch security review + hygiene for ALL open PRs (reason=review_all, 30 min)
# RUN_MODE=scan — full repo security scan + issue filing (reason=schedule, 20 min)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@ -37,17 +36,11 @@ elif [[ "${SPAWN_REASON}" == "triage" ]] && [[ -n "${SPAWN_ISSUE}" ]]; then
WORKTREE_BASE="/tmp/spawn-worktrees/triage-${ISSUE_NUM}"
TEAM_NAME="spawn-triage-${ISSUE_NUM}"
CYCLE_TIMEOUT=300 # 5 min for issue triage
elif [[ -n "${SPAWN_ISSUE}" ]]; then
RUN_MODE="pr"
PR_NUM="${SPAWN_ISSUE}"
WORKTREE_BASE="/tmp/spawn-worktrees/security-pr-${PR_NUM}"
TEAM_NAME="spawn-security-pr-${PR_NUM}"
CYCLE_TIMEOUT=600 # 10 min for PR reviews
elif [[ "${SPAWN_REASON}" == "hygiene" ]]; then
RUN_MODE="hygiene"
WORKTREE_BASE="/tmp/spawn-worktrees/security-hygiene"
TEAM_NAME="spawn-security-hygiene"
CYCLE_TIMEOUT=900 # 15 min for PR hygiene
elif [[ "${SPAWN_REASON}" == "review_all" ]]; then
RUN_MODE="review_all"
WORKTREE_BASE="/tmp/spawn-worktrees/security-review-all"
TEAM_NAME="spawn-security-review-all"
CYCLE_TIMEOUT=1800 # 30 min for batch review
else
RUN_MODE="scan"
WORKTREE_BASE="/tmp/spawn-worktrees/security-scan"
@ -95,9 +88,7 @@ log "Working directory: ${REPO_ROOT}"
log "Team name: ${TEAM_NAME}"
log "Worktree base: ${WORKTREE_BASE}"
log "Timeout: ${CYCLE_TIMEOUT}s"
if [[ "${RUN_MODE}" == "pr" ]]; then
log "PR: #${PR_NUM}"
elif [[ "${RUN_MODE}" == "team_building" ]] || [[ "${RUN_MODE}" == "triage" ]]; then
if [[ "${RUN_MODE}" == "team_building" ]] || [[ "${RUN_MODE}" == "triage" ]]; then
log "Issue: #${ISSUE_NUM}"
fi
@ -296,131 +287,171 @@ fi
Begin now. Triage issue #${ISSUE_NUM}.
TRIAGE_PROMPT_EOF
elif [[ "${RUN_MODE}" == "pr" ]]; then
# --- PR mode: 2-agent security review ---
cat > "${PROMPT_FILE}" << PR_PROMPT_EOF
You are the Team Lead for a security review of PR #${PR_NUM} on the spawn codebase.
elif [[ "${RUN_MODE}" == "review_all" ]]; then
# --- Review-all mode: batch security review + hygiene for ALL open PRs ---
cat > "${PROMPT_FILE}" << REVIEW_ALL_PROMPT_EOF
You are the Team Lead for a batch security review and hygiene cycle on the spawn codebase.
## Target PR
## Mission
Review PR #${PR_NUM} for security issues.
First, fetch the PR details:
\`\`\`bash
gh pr view ${PR_NUM} --repo OpenRouterTeam/spawn
gh pr diff ${PR_NUM} --repo OpenRouterTeam/spawn
gh pr view ${PR_NUM} --repo OpenRouterTeam/spawn --json files --jq '.files[].path'
\`\`\`
List every open PR and run the full security review checklist on each one. Approve+merge clean PRs, request changes on flagged ones. Close stale PRs. Clean up orphan branches.
## Time Budget
This cycle MUST complete within 8 minutes. This is a HARD deadline.
This cycle MUST complete within 25 minutes. This is a HARD deadline.
- At the 5-minute mark, stop new work and wrap up
- At the 7-minute mark, send shutdown_request to all agents
- At 8 minutes, force shutdown
- At the 20-minute mark, stop spawning new reviewers and wrap up
- At the 24-minute mark, send shutdown_request to all agents
- At 25 minutes, force shutdown
## Team Structure
## Step 1 — Discover Open PRs
Create these teammates:
1. **code-reviewer** (Opus)
- Fetch the full PR diff: \`gh pr diff ${PR_NUM} --repo OpenRouterTeam/spawn\`
- Review every changed file for security issues:
* **Command injection**: unquoted variables in shell commands, unsafe eval/heredoc, unsanitized input in bash
* **Credential leaks**: hardcoded API keys, tokens, passwords; secrets logged to stdout; credentials in committed files
* **Path traversal**: unsanitized file paths, directory escape via ../
* **XSS/injection**: unsafe HTML rendering, prototype pollution, SQL injection, template injection
* **Unsafe patterns**: use of \`eval\`, \`source <()\`, unvalidated redirects, TOCTOU races
* **curl|bash safety**: broken source/eval fallback patterns, missing integrity checks
* **macOS bash 3.x compat**: echo -e, source <(), ((var++)) with set -e, local in subshells, set -u
- Classify each finding as CRITICAL, HIGH, MEDIUM, or LOW
- Report findings to the team lead with file paths and line numbers
2. **test-verifier** (Haiku)
- Get the list of changed files: \`gh pr view ${PR_NUM} --repo OpenRouterTeam/spawn --json files --jq '.files[].path'\`
- For each changed .sh file:
* Run \`bash -n FILE\` to check syntax
* Verify it starts with \`#!/bin/bash\` and \`set -eo pipefail\`
* Verify the local-or-remote source fallback pattern is used (not bare \`source ./lib/...\`)
* Check for macOS bash 3.x incompatibilities (echo -e, source <(), etc.)
- For changed .ts files:
* Run \`bun test\` to verify tests pass
- Report results to the team lead
## Review Decision
After both agents report back, make the final decision:
### If CRITICAL or HIGH issues found:
1. Post a **requesting-changes** review on the PR:
\`\`\`bash
gh pr review ${PR_NUM} --repo OpenRouterTeam/spawn --request-changes --body "REVIEW_BODY"
\`\`\`
Include all CRITICAL/HIGH findings with file paths, line numbers, and remediation suggestions.
2. If SLACK_WEBHOOK is set, send a Slack notification:
\`\`\`bash
PR_TITLE=\$(gh pr view ${PR_NUM} --repo OpenRouterTeam/spawn --json title --jq '.title')
PR_URL="https://github.com/OpenRouterTeam/spawn/pull/${PR_NUM}"
curl -s -X POST "\${SLACK_WEBHOOK}" -H 'Content-Type: application/json' \\
-d "{\"text\":\":warning: Security concern on PR #${PR_NUM}: \${PR_TITLE} — [summary of top concern] — \${PR_URL}\"}"
\`\`\`
(The SLACK_WEBHOOK env var is: ${SLACK_WEBHOOK:-NOT_SET})
### If only MEDIUM/LOW issues or no issues:
1. Post an **approving** review on the PR:
\`\`\`bash
gh pr review ${PR_NUM} --repo OpenRouterTeam/spawn --approve --body "REVIEW_BODY"
\`\`\`
Include any MEDIUM/LOW findings as informational notes.
2. **Merge the PR** (squash merge, delete branch):
\`\`\`bash
gh pr merge ${PR_NUM} --repo OpenRouterTeam/spawn --squash --delete-branch
\`\`\`
Merge if ALL of these are true:
- Zero CRITICAL or HIGH findings from code-reviewer
- All bash -n checks pass
- All bun tests pass (or N/A)
If merge fails (e.g. conflicts, branch protection), log the error and move on.
### Review body format:
Run:
\`\`\`bash
gh pr list --repo OpenRouterTeam/spawn --state open --json number,title,headRefName,updatedAt,mergeable
\`\`\`
## Security Review
**Verdict**: [APPROVED / CHANGES REQUESTED]
If zero open PRs, skip to Step 3 (branch cleanup)do NOT exit yet.
### Findings
- [SEVERITY] file:line — description
## Step 2 — Create the Team and Spawn Reviewers
### Tests
- bash -n: [PASS/FAIL]
- bun test: [PASS/FAIL/N/A]
- curl|bash pattern: [OK/MISSING]
- macOS compat: [OK/ISSUES]
1. Create the team with TeamCreate (team_name="${TEAM_NAME}")
2. For EACH open PR, create a task with TaskCreate describing the review work
3. Spawn a **pr-reviewer** agent (model=opus) for each PR using the Task tool (subagent_type='general-purpose', team_name="${TEAM_NAME}")
- Name each agent: pr-reviewer-NUMBER (e.g. pr-reviewer-42)
- Each agent gets instructions below
4. Also spawn a **branch-cleaner** agent (model=haiku) — see Step 3
---
*Automated security review by spawn security team*
### Per-PR Reviewer Instructions
Each pr-reviewer agent must:
1. Fetch the PR metadata and diff:
\`\`\`bash
gh pr view NUMBER --repo OpenRouterTeam/spawn --json updatedAt,mergeable,title,headRefName
gh pr diff NUMBER --repo OpenRouterTeam/spawn
gh pr view NUMBER --repo OpenRouterTeam/spawn --json files --jq '.files[].path'
\`\`\`
2. **Staleness check first** — Before doing security review, check:
* Is \`updatedAt\` > 48 hours ago AND \`mergeable\` is \`CONFLICTING\`?
- YES → **Close the PR** (stale + conflicts):
\`\`\`bash
gh pr close NUMBER --repo OpenRouterTeam/spawn --comment "Auto-closing: this PR has been stale for >48h with merge conflicts. Please reopen or create a fresh PR if still needed."
\`\`\`
Then delete the branch:
\`\`\`bash
BRANCH=\$(gh pr view NUMBER --repo OpenRouterTeam/spawn --json headRefName --jq '.headRefName')
git push origin --delete "\${BRANCH}" 2>/dev/null || true
\`\`\`
Report to team lead: "PR #NUMBER closed (stale+conflicts)" and STOP — skip security review.
* Is \`updatedAt\` > 48 hours ago but NO conflicts?
- The PR is stale but mergeable — still do the security review below (it may be fine to merge).
* Is the PR fresh (<48h)? → Proceed to security review normally.
3. Review every changed file for security issues:
* **Command injection**: unquoted variables in shell commands, unsafe eval/heredoc, unsanitized input in bash
* **Credential leaks**: hardcoded API keys, tokens, passwords; secrets logged to stdout; credentials in committed files
* **Path traversal**: unsanitized file paths, directory escape via ../
* **XSS/injection**: unsafe HTML rendering, prototype pollution, SQL injection, template injection
* **Unsafe patterns**: use of \`eval\`, \`source <()\`, unvalidated redirects, TOCTOU races
* **curl|bash safety**: broken source/eval fallback patterns, missing integrity checks
* **macOS bash 3.x compat**: echo -e, source <(), ((var++)) with set -e, local in subshells, set -u
4. For each changed .sh file:
* Run \`bash -n FILE\` to check syntax
* Verify the local-or-remote source fallback pattern is used
* Check for macOS bash 3.x incompatibilities
5. For changed .ts files:
* Run \`bun test\` to verify tests pass
6. Classify each finding as CRITICAL, HIGH, MEDIUM, or LOW
7. Make the review decision:
**If CRITICAL or HIGH issues found** — request changes:
\`\`\`bash
gh pr review NUMBER --repo OpenRouterTeam/spawn --request-changes --body "REVIEW_BODY"
\`\`\`
**If only MEDIUM/LOW or no issues** — approve AND merge:
\`\`\`bash
gh pr review NUMBER --repo OpenRouterTeam/spawn --approve --body "REVIEW_BODY"
gh pr merge NUMBER --repo OpenRouterTeam/spawn --squash --delete-branch
\`\`\`
If merge fails (conflicts, branch protection), log the error and move on.
8. Review body format:
\`\`\`
## Security Review
**Verdict**: [APPROVED / CHANGES REQUESTED]
### Findings
- [SEVERITY] file:line — description
### Tests
- bash -n: [PASS/FAIL]
- bun test: [PASS/FAIL/N/A]
- curl|bash pattern: [OK/MISSING]
- macOS compat: [OK/ISSUES]
---
*Automated security review by spawn security team*
\`\`\`
9. Report results to the team lead: PR number, verdict (approved+merged / changes-requested / closed-stale), finding count, merge status
## Step 3 — Branch Cleanup
Spawn a **branch-cleaner** agent (model=haiku, team_name="${TEAM_NAME}", name="branch-cleaner"):
- List all remote branches: \`git branch -r --format='%(refname:short) %(committerdate:unix)'\`
- For each branch (excluding main):
* Check if there's an open PR: \`gh pr list --head BRANCH --state open --json number\`
* If NO open PR and branch is stale (>48 hours): delete it \`git push origin --delete BRANCH\`
* If open PR exists: leave it (pr-reviewers handle PRs)
- Report summary: how many branches deleted, how many left
## Step 4 — Monitor and Collect Results
Poll TaskList every 15 seconds. As each agent reports back, record:
- PR number
- Verdict (approved+merged / changes-requested / closed-stale)
- Number of findings by severity
- Branches deleted (from branch-cleaner)
## Step 5 — Summary and Slack Notification
After all agents finish (or time runs out), compile the summary.
If SLACK_WEBHOOK is set, send a Slack notification:
\`\`\`bash
SLACK_WEBHOOK="${SLACK_WEBHOOK:-NOT_SET}"
if [ -n "\${SLACK_WEBHOOK}" ] && [ "\${SLACK_WEBHOOK}" != "NOT_SET" ]; then
curl -s -X POST "\${SLACK_WEBHOOK}" -H 'Content-Type: application/json' \\
-d '{"text":":shield: PR review+hygiene complete: N PRs reviewed (X merged, Y flagged, Z closed-stale), K branches cleaned. See https://github.com/OpenRouterTeam/spawn/pulls"}'
fi
\`\`\`
(The SLACK_WEBHOOK env var is: ${SLACK_WEBHOOK:-NOT_SET})
## Workflow
1. Create the team with TeamCreate (team_name="${TEAM_NAME}")
2. Create tasks with TaskCreate for each teammate's work
3. Fetch PR details and diff
4. Spawn teammates in parallel using Task tool (subagent_type='general-purpose', team_name="${TEAM_NAME}"):
- code-reviewer (model=opus): security review of the diff
- test-verifier (model=haiku): syntax/test verification
1. List open PRs: \`gh pr list --state open --json number,title,headRefName,updatedAt,mergeable\`
2. Create the team with TeamCreate (team_name="${TEAM_NAME}")
3. Spawn branch-cleaner agent (model=haiku)
4. For each PR:
a. Create a task with TaskCreate
b. Spawn a pr-reviewer agent (model=opus, team_name="${TEAM_NAME}", name="pr-reviewer-NUMBER")
5. Assign tasks to teammates using TaskUpdate (set owner to teammate name)
6. Monitor teammates (poll TaskList, sleep 15 between checks)
7. Collect results from both agents via messages
8. Make the review decision:
- If CRITICAL/HIGH → request changes + Slack notification
- If MEDIUM/LOW or clean → approve AND merge (squash + delete branch)
9. Shutdown all teammates via SendMessage (type=shutdown_request)
10. Clean up with TeamDelete
11. Exit
7. Collect results from all agents via messages
8. Compile summary (N reviewed, X merged, Y flagged, Z closed-stale, K branches cleaned)
9. Send Slack notification
10. Shutdown all teammates via SendMessage (type=shutdown_request)
11. Clean up with TeamDelete
12. Exit
## CRITICAL: Monitoring Loop
@ -432,127 +463,24 @@ Required pattern:
a. Run TaskList to check status
b. If messages received, process them
c. If no messages yet, run Bash("sleep 15") then loop back
3. Once both agents report, make review decision
4. Post review and merge if no CRITICAL/HIGH findings
5. Send Slack notification if concerns found
6. Shutdown teammates and exit
3. Once all agents report (or time is up), compile summary
4. Send Slack notification
5. Shutdown teammates and exit
## Safety Rules
- NEVER approve a PR with CRITICAL or HIGH findings
- Auto-merge PRs that have no CRITICAL/HIGH findings and all tests pass
- MEDIUM/LOW findings are informational — still approve and merge
- NEVER close a PR without posting a comment explaining why
- If a PR has recent activity (<24h), never close it for staleness
- If unsure about a finding, flag it as MEDIUM and note the uncertainty
- Always include file paths and line numbers in findings
- Do not modify any code — this is review only
- Limit to at most 10 concurrent reviewer agents to avoid API rate limits
Begin now. Review PR #${PR_NUM}.
PR_PROMPT_EOF
elif [[ "${RUN_MODE}" == "hygiene" ]]; then
# --- Hygiene mode: stale PR cleanup + triage ---
cat > "${PROMPT_FILE}" << 'HYGIENE_PROMPT_EOF'
You are the Team Lead for a PR hygiene cycle on the spawn codebase.
## Mission
Go through ALL open PRs. For each one: review it, decide whether to close or keep, and take action. Also file issues for anything that needs follow-up.
## Time Budget
This cycle MUST complete within 12 minutes. This is a HARD deadline.
- At the 9-minute mark, stop new work and wrap up
- At the 11-minute mark, send shutdown_request to all agents
- At 12 minutes, force shutdown
## Team Structure
Create these teammates:
1. **pr-triager** (Opus)
- List ALL open PRs: `gh pr list --repo OpenRouterTeam/spawn --state open --json number,title,updatedAt,author,mergeable,headRefName,labels`
- For EACH open PR, evaluate:
* **Staleness**: last updated > 48 hours ago? (use `updatedAt` field)
* **Mergeable**: does it have merge conflicts? (`mergeable` field)
* **CI status**: `gh pr checks NUMBER --repo OpenRouterTeam/spawn` — are checks passing?
* **Review status**: does it have reviews? `gh pr view NUMBER --repo OpenRouterTeam/spawn --json reviews --jq '.reviews[].state'`
* **Relevance**: read the diff (`gh pr diff NUMBER --repo OpenRouterTeam/spawn`) — is the change still relevant to the current codebase?
- For each PR, take ONE of these actions:
* **Close** (stale + conflicts + no activity):
`gh pr close NUMBER --repo OpenRouterTeam/spawn --comment "Auto-closing: this PR has been stale for >48h with merge conflicts. The changes may no longer be relevant. Please reopen or create a fresh PR if still needed."`
Then delete the branch: `gh pr view NUMBER --repo OpenRouterTeam/spawn --json headRefName --jq '.headRefName'``git push origin --delete BRANCH`
* **Close with issue** (stale but has good ideas):
Close the PR with a comment, then file a new issue capturing the intent:
`gh issue create --repo OpenRouterTeam/spawn --title "Follow-up: [original PR intent]" --body "Original PR #NUMBER was auto-closed due to staleness. The approach had merit: [summary]. Needs a fresh implementation." --label "enhancement"`
* **Request review** (looks good but needs eyes):
`gh pr review NUMBER --repo OpenRouterTeam/spawn --comment --body "Automated triage: This PR looks viable but needs human review. [summary of what it does and any concerns]"`
Add label: `gh pr edit NUMBER --repo OpenRouterTeam/spawn --add-label "needs-team-review"`
* **Leave alone** (fresh, actively worked on): skip it
- Report all actions taken to the team lead
2. **branch-cleaner** (Haiku)
- List all remote branches: `git branch -r --format='%(refname:short) %(committerdate:unix)'`
- For each branch (excluding main):
* Check if there's an open PR: `gh pr list --head BRANCH --state open --json number`
* If NO open PR and branch is stale (>48 hours): delete it `git push origin --delete BRANCH`
* If open PR exists: leave it (pr-triager handles PRs)
- Report summary: how many branches deleted, how many left
## Actions Summary
After both agents report, compile a summary:
### If any PRs were closed or issues filed, send a Slack notification:
```bash
SLACK_WEBHOOK="${SLACK_WEBHOOK:-}"
if [ -n "$SLACK_WEBHOOK" ]; then
curl -s -X POST "$SLACK_WEBHOOK" -H 'Content-Type: application/json' \
-d '{"text":":broom: PR Hygiene cycle complete: [N PRs closed, M issues filed, K branches deleted]. See details in the workflow run."}'
fi
```
## Workflow
1. Create the team with TeamCreate (team_name="spawn-security-hygiene")
2. Create tasks with TaskCreate for each teammate's work
3. Spawn teammates in parallel using Task tool (subagent_type='general-purpose', team_name="spawn-security-hygiene"):
- pr-triager (model=opus): review and triage all open PRs
- branch-cleaner (model=haiku): clean up stale orphan branches
4. Assign tasks to teammates using TaskUpdate (set owner to teammate name)
5. Monitor teammates (poll TaskList, sleep 15 between checks)
6. Collect results from both agents via messages
7. Compile summary and send Slack notification
8. Shutdown all teammates via SendMessage (type=shutdown_request)
9. Clean up with TeamDelete
10. Exit
## CRITICAL: Monitoring Loop
**Spawning teammates is the BEGINNING of your job, not the end.** After spawning all teammates, you MUST actively monitor them. Your session ENDS the moment you produce a response with no tool call. To stay alive, you MUST ALWAYS include at least one tool call in every response.
Required pattern:
1. Spawn teammates via Task tool (with team_name and name params)
2. Poll loop:
a. Run TaskList to check status
b. If messages received, process them
c. If no messages yet, run Bash("sleep 15") then loop back
3. Once both agents report, compile summary
4. Send Slack notification if actions were taken
5. Shutdown teammates and exit
## Safety Rules
- NEVER close a PR without posting a comment explaining why
- If a PR has recent activity (<24h), leave it alone regardless of other factors
- When filing follow-up issues, include the original PR number and a clear description
- Do not modify any code — this is triage only
Begin now. Start the PR hygiene cycle.
HYGIENE_PROMPT_EOF
# Substitute SLACK_WEBHOOK in the hygiene prompt (it uses double-quote heredoc workaround)
sed -i "s|\${SLACK_WEBHOOK:-}|${SLACK_WEBHOOK:-}|g" "${PROMPT_FILE}"
Begin now. Review all open PRs and clean up stale branches.
REVIEW_ALL_PROMPT_EOF
else
# --- Scan mode: full repo security audit + issue filing ---

View file

@ -1,39 +1,32 @@
name: Security Review
on:
pull_request:
types: [opened, synchronize, reopened]
issues:
types: [opened, reopened]
schedule:
# Full repo security scan — daily at 06:00 UTC
- cron: '0 6 * * *'
# PR hygiene (stale PR cleanup) — every 6 hours
- cron: '0 */6 * * *'
# Batch PR security review + hygiene — every 15 min
- cron: '*/15 * * * *'
# Full repo security scan — every 30 min (offset +5)
- cron: '5,35 * * * *'
workflow_dispatch:
inputs:
mode:
description: 'Run mode: pr (needs PR number), hygiene, or scan'
description: 'Run mode: review_all (PR review + hygiene) or scan (repo audit)'
required: false
default: 'scan'
default: 'review_all'
type: choice
options:
- review_all
- scan
- hygiene
pr_number:
description: 'PR number (only for pr mode via workflow_dispatch)'
required: false
type: string
concurrency:
group: security-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.event_name == 'issues' && format('issue-{0}', github.event.issue.number) || github.event_name == 'schedule' && github.event.schedule || 'manual' }}
group: security-${{ github.event_name == 'issues' && format('issue-{0}', github.event.issue.number) || github.event_name == 'schedule' && github.event.schedule || 'manual' }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 30
# Trigger on ALL issues (triage or team-building) plus PR/schedule/manual
timeout-minutes: 40
steps:
- name: Trigger security review
env:
@ -45,11 +38,8 @@ jobs:
exit 0
fi
# Determine reason and issue/PR number based on trigger type
if [ "${{ github.event_name }}" = "pull_request" ]; then
REASON="pull_request"
ISSUE_NUM="${{ github.event.pull_request.number }}"
elif [ "${{ github.event_name }}" = "issues" ]; then
# Determine reason and issue number based on trigger type
if [ "${{ github.event_name }}" = "issues" ]; then
ISSUE_NUM="${{ github.event.issue.number }}"
if [ "${{ contains(github.event.issue.labels.*.name, 'team-building') }}" = "true" ]; then
REASON="team_building"
@ -57,25 +47,24 @@ jobs:
REASON="triage"
fi
elif [ "${{ github.event_name }}" = "schedule" ]; then
# Distinguish between cron schedules:
# '0 6 * * *' = daily scan, '0 */6 * * *' = hygiene every 6h
# Distinguish between cron schedules by their cron string
CRON="${{ github.event.schedule }}"
if [ "$CRON" = "0 6 * * *" ]; then
if [ "$CRON" = "*/15 * * * *" ]; then
REASON="review_all"
elif [ "$CRON" = "5,35 * * * *" ]; then
REASON="schedule"
else
REASON="hygiene"
REASON="schedule"
fi
ISSUE_NUM=""
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
MODE="${{ github.event.inputs.mode || 'scan' }}"
ISSUE_NUM="${{ github.event.inputs.pr_number || '' }}"
if [ -n "$ISSUE_NUM" ]; then
REASON="pull_request"
elif [ "$MODE" = "hygiene" ]; then
REASON="hygiene"
MODE="${{ github.event.inputs.mode || 'review_all' }}"
if [ "$MODE" = "review_all" ]; then
REASON="review_all"
else
REASON="schedule"
fi
ISSUE_NUM=""
else
REASON="schedule"
ISSUE_NUM=""