Appearance
GitHub Actions Security Review
Purpose: Security posture review of all GitHub Actions workflows in bedrock-docs — credential scope, OIDC usage, SHA pinning, and review cadence.
Owner: Rory Review frequency: Quarterly alongside compliance/annual-review-checklist.mdLast reviewed: May 2026
Why GHA Security Matters
Every GitHub Actions workflow is a trusted dependency with production credentials. Workflows can:
- Push to protected branches (auto-merge)
- Write to Azure Key Vault, deploy to Container Apps, modify Terraform state
- Access OIDC tokens that grant subscription-level access to Azure and AWS
A compromised workflow (via supply chain attack on an unpinned Action, or via a malicious PR) is a direct path to production infrastructure.
Security Principles
- SHA-pin all third-party Actions — never use a floating version tag (
@v4). Use a full commit SHA (@sha256:...or@<full-sha>). Floating tags can be moved to point at malicious code - Minimum necessary permissions — each workflow should declare only the
permissionsit actually needs - OIDC over stored secrets — use federated OIDC tokens for Azure and AWS auth. Never store cloud credentials as GitHub Secrets
- Restrict write access — workflows triggered on
pull_requestfrom forks must not have write permissions or access to secrets - Review PR workflows —
pull_request_targetis dangerous if it runs code from the PR branch with write permissions
Workflow Inventory
auto-merge.yml
Trigger: pull_request events, checks completed Purpose: Auto-merges kobe-bot PRs after CI passes Credentials: GitHub token (write: pull-requests, contents) OIDC: No Risk: Medium — can merge to main. Mitigated by requiring CI pass and only triggering on approved bot accounts SHA pinning: Verify all Actions are SHA-pinned
deploy-docs.yml
Trigger: Push to main Purpose: Builds VitePress site and deploys to Cloudflare Pages Credentials: Cloudflare API token (GitHub Secret: CLOUDFLARE_API_TOKEN) OIDC: No (Cloudflare uses token auth, not OIDC) Risk: Medium — Cloudflare token should be scoped to Pages deployment only, not account-level SHA pinning: Verify cloudflare/wrangler-action is SHA-pinned
pr-validation.yml
Trigger: pull_requestPurpose: Validates PR format, runs doc linting Credentials: GitHub read token only OIDC: No Risk: Low — read-only, triggered on PR (not pull_request_target) SHA pinning: Verify
baseline-scan.yml
Trigger: Push to main, schedule Purpose: Checkov HIPAA scan of IaC files Credentials: GitHub token (read) OIDC: No Risk: Low — read-only scan SHA pinning: Verify bridgecrewio/checkov-action
security-scan.yml
Trigger: Push to main, schedule Purpose: Trivy vulnerability scan Credentials: GitHub token (read) OIDC: No Risk: Low — read-only scan SHA pinning: Verify aquasecurity/trivy-action
auto-merge.yml / ingest-changes.yml
Trigger: Push to main Purpose: POST to SecOps /api/changes to create CM ticket after merge Credentials: SECOPS_API_KEY (GitHub Secret) OIDC: No Risk: Low — writes to SecOps only, not infrastructure Scope check: Verify the API key has only changes:write scope, not admin
palo-alto-backup-on-demand.yml / palo-alto-audit-on-demand.yml / palo-alto-quarterly-bpa.yml
Trigger: Manual dispatch, schedule Purpose: Backs up and audits Palo Alto configuration via Panorama Credentials: Panorama API key (GitHub Secret) OIDC: No Risk: Medium — Panorama API key can read all firewall config. Verify it cannot push config (read-only API key) SHA pinning: Verify
aztfexport-quarterly.yml
Trigger: Schedule (quarterly) Purpose: Runs aztfexport to capture unmanaged Azure resources Credentials: Azure OIDC via federated credential OIDC: Yes — Azure Risk: Medium — Reader access on subscriptions. Verify OIDC federated credential scope is Reader only, not Contributor SHA pinning: Verify azure/login
apply-windows-baseline.yml
Trigger: Manual dispatch Purpose: Applies Windows security baseline via Intune Credentials: Azure OIDC OIDC: Yes — Azure Risk: Medium-High — can modify Intune policies. Confirm OIDC role is scoped to Intune-specific permissions only SHA pinning: Verify
refresh-github-ips.yml
Trigger: Schedule Purpose: Updates Palo Alto rules with current GitHub IP ranges Credentials: Panorama API key + GitHub API OIDC: No Risk: Medium — can modify firewall rules. Verify the Panorama key has only rule-update scope
restart-syslog-vm.yml
Trigger: Manual dispatch Purpose: Restarts the syslog VM via AWS or Azure Credentials: OIDC (Azure or AWS) OIDC: Yes Risk: Medium — can restart a VM. Verify the OIDC role is scoped to the syslog VM resource group only
setup-github-runner.yml
Trigger: Manual dispatch Purpose: Provisions a self-hosted GitHub Actions runner Credentials: Azure OIDC (Contributor on runner resource group) OIDC: Yes — Azure Risk: High — Contributor access. Verify scope is limited to the runner resource group only. Self-hosted runners must run in isolated environments
SHA Pinning Audit
Run this check quarterly to verify all third-party Actions are SHA-pinned:
bash
# Check for non-SHA-pinned actions (version tags or branch refs)
grep -rn "uses:" .github/workflows/ | grep -v "@[0-9a-f]\{40\}" | grep -v "# sha-ok"Any result is a pinning gap. Update the workflow to use the full SHA:
- Find the Action on GitHub (
actions/checkout,cloudflare/wrangler-action, etc.) - Go to the specific version tag → copy the commit SHA
- Replace
@v4with@<full-sha> # v4.x.xin the workflow file
Secrets Inventory
GitHub Secrets in Cirius-Group-Inc/bedrock-docs:
| Secret | Used By | Scope | Rotation |
|---|---|---|---|
CLOUDFLARE_API_TOKEN | deploy-docs.yml | Cloudflare Pages deploy only | Annual (January) |
SECOPS_API_KEY | ingest-changes.yml | SecOps changes:write | Annual (January) |
PANORAMA_API_KEY | palo-alto-*.yml | Panorama read (verify read-only) | Annual (January) |
All cloud auth (Azure, AWS) uses OIDC — no cloud credentials stored as GitHub Secrets.
OIDC Federated Credential Audit
Verify OIDC federated credentials in Entra and AWS are correctly scoped:
bash
# Azure — list federated credentials for the github-deploy-main service principal
az ad sp list --display-name "github-deploy-main" --query "[].appId" -o tsv | \
xargs -I{} az ad app federated-credential list --id {}Each credential's subject must match the exact repo and branch:
- Format:
repo:Cirius-Group-Inc/bedrock-docs:ref:refs/heads/main - A wildcard subject (
repo:Cirius-Group-Inc/*) would allow any repo to claim the identity — verify this is not set
Quarterly Review Checklist
- [ ] Run SHA pinning audit — update any non-pinned Actions
- [ ] Review secrets inventory — are any secrets unused or over-scoped?
- [ ] Review OIDC federated credentials — correct repo and branch in subject?
- [ ] Review workflow permissions declarations — do any workflows have
permissions: write-all? - [ ] Check for new workflows added since last review — were they reviewed before merge?
- [ ] Verify Panorama API key is read-only (cannot push config changes)
- [ ] Verify
apply-windows-baseline.ymlOIDC role has minimum Intune scope - [ ] Check self-hosted runner (if any) is isolated and up to date
Related Documents
cicd/oidc-authentication.md— OIDC setup and federated credential configurationcicd/pipeline-overview.md— CI/CD pipeline architecturesecurity/supply-chain/— supply chain monitoring agentscompliance/annual-review-checklist.md— quarterly review schedule