**Related issue:** Resolves #48007 # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/` - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. ## What When Dependabot opens a code-dependency PR (Go or npm), a GitHub Actions workflow now auto-creates a QA tracking issue assigned to @AndreyKizimenko and places it on the `:help-qa` board in "Ready" status. Also exempts `dependabot[bot]` from the PR template check. ### Files changed - **`.github/workflows/dependabot-qa-ticket.yml`** (new) - The core workflow. Triggers on `pull_request_target[opened]`, filters for code-dependency Dependabot PRs (Go modules + npm, excluding website/slackbot), creates a QA tracking issue, comments on the PR, and adds the issue to the `:help-qa` project board. - **`.github/workflows/check-pr-template.yml`** - Exempts `dependabot[bot]` from the PR description template check (Dependabot PRs have auto-generated descriptions). - **`.github/zizmor-gate.yml`** - Suppresses two expected zizmor findings: `dangerous-triggers` (uses `pull_request_target` but never checks out PR code) and `bot-conditions` (gates on `user.login` which is not spoofable for same-repo Dependabot PRs). - **`changes/48007-dependabot-qa-ticket`** - Release note. ## Security review This workflow uses `pull_request_target`, a known attack surface for GitHub Actions. A dedicated security audit was performed covering all standard threat vectors: | Area | Status | Details | |------|--------|---------| | **Code checkout injection** | SAFE | No `actions/checkout` or `git clone`. The workflow only calls the GitHub API via `actions/github-script`. The inline JavaScript is embedded in the base-branch YAML, not loaded from PR code. | | **Script/template injection** | SAFE | PR data (`pr.title`, `pr.head.ref`) is used in JavaScript template literals passed to the GitHub API as strings. There is no shell interpolation, no `eval()`, no `${{ }}` expressions in `run:` blocks. | | **Bot identity spoofing** | SAFE | Gates on `github.event.pull_request.user.login == 'dependabot[bot]'`, which is server-set by GitHub and cannot be spoofed. Uses `user.login` (immutable) rather than `github.actor` (which can differ on re-runs). The `[bot]` suffix is reserved for GitHub App accounts. | | **Secret exposure** | SAFE | `FLEET_GITHUB_TOKEN_PROJECTS` is passed via `env:` block, accessed as `process.env.PROJECT_TOKEN`, and used only as an argument to `getOctokit()`. Never logged, never interpolated into output. | | **Permission scope** | MINIMAL | Top-level: `contents: read`. Job-level: `contents: read` + `issues: write`. No `pull-requests: write` (PR comments use the Issues API). | | **Idempotency** | SAFE | Before creating an issue, searches for an existing open issue with the same title and `dependabot` label. Prevents duplicates on workflow re-runs. | | **Markdown injection** | LOW (theoretical) | `pr.title` is interpolated into issue title/body. Dependabot controls the title format ("Bump X from Y to Z") and package names are registry-constrained. Not exploitable without compromising Dependabot itself. | | **Denial of service** | LOW (theoretical) | Bounded by Dependabot's own rate limits (default 5 PRs/ecosystem/day) plus the idempotency guard. | | **Zizmor waivers** | JUSTIFIED | `dangerous-triggers`: no code checkout, API-only. `bot-conditions`: uses `user.login` not `github.actor`. Both include inline rationale comments. | | **check-pr-template.yml** | SAFE | `AUTHOR` is passed via `env:` (not `${{ }}` in shell), double-quoted in comparison. `user.login` is GitHub-constrained to alphanumeric + hyphens. | **Actions are SHA-pinned** with version comments: `step-security/harden-runner@9af8...` (v2.19.4) and `actions/github-script@60a0...` (v7.0.1). ## Testing - [x] QA'd all new/changed functionality manually Since this is a GitHub Actions workflow that triggers on Dependabot PR opens, it cannot be fully end-to-end tested without a real Dependabot PR. The following offline validations were performed: ### 1. YAML syntax validation ``` $ ruby -e "require 'yaml'; YAML.safe_load(File.read('.github/workflows/dependabot-qa-ticket.yml')); puts 'YAML: valid'" YAML: valid ``` ### 2. Verified hardcoded GraphQL IDs against the live GitHub API **Project board ID** (`PVT_kwDOBDAnic4A5q11`): ``` $ gh api graphql -f query='query { node(id: "PVT_kwDOBDAnic4A5q11") { ... on ProjectV2 { title number url } } }' -> title: ":help-qa", number: 85, url: "https://github.com/orgs/fleetdm/projects/85" ``` **Status field ID** (`PVTSSF_lADOBDAnic4A5q11zgubZ6Y`) and option ID (`f75ad846`): ``` $ gh api graphql -f query='query { node(id: "PVTSSF_lADOBDAnic4A5q11zgubZ6Y") { ... on ProjectV2SingleSelectField { name options { id name } } } }' -> field name: "Status", option f75ad846 = "Ready" ✅ ``` ### 3. Verified assignee and label exist ``` $ gh api users/AndreyKizimenko --jq '.login' -> AndreyKizimenko ✅ $ gh label list --search dependabot -> dependabot ✅ ``` ### 4. Branch filtering logic unit tested Extracted the JavaScript filter logic and ran it against 8 test cases covering single-package updates, grouped updates, and exclusions: ``` $ node -e "<test script>" PASS | dependabot/go_modules/golang.org/x/crypto-0.24.0 | match (Go single) PASS | dependabot-go_modules-google.golang.org-grpc-1.82.1 | match (Go grouped) PASS | dependabot/npm_and_yarn/typescript-5.5.0 | match (npm single) PASS | dependabot-npm_and_yarn-postcss-8.5.18 | match (npm grouped) PASS | dependabot/npm_and_yarn/website/next-14.0.0 | skip (website single) PASS | dependabot-npm_and_yarn-website-next-14.0.0 | skip (website grouped) PASS | dependabot/npm_and_yarn/fleet-slackbot/axios-1.7.0 | skip (slackbot single) PASS | dependabot/github_actions/actions/checkout-4 | skip (GitHub Actions) 8/8 passed ``` ### 5. Validated filter against real Dependabot PRs Checked the 20 most recent Dependabot PRs (`gh pr list --author 'app/dependabot' --state all --limit 20`) to confirm real branch names match the filter patterns. This revealed three bugs that were fixed: 1. **Go branch prefix was wrong**: Real branches use `dependabot/go_modules/`, not `dependabot/gomod/`. Fixed. 2. **Grouped updates use dashes**: Real grouped branches like `dependabot-go_modules-...` and `dependabot-npm_and_yarn-...` were not matched. Added dash-variant prefix checks and exclusions. 3. **Status label mismatch**: The constant was named `AWAITING_QA` but the option ID maps to "Ready" on the board (there is no "Awaiting QA" status). Renamed to `READY` and updated comments. ### 6. CI checks All CI checks pass (CodeQL, zizmor, dependency-review, build-binaries, publish). **Note:** The project board integration requires a `FLEET_GITHUB_TOKEN_PROJECTS` repo secret with Organization Projects read/write scope. Without it, the issue is still created but not added to the board (a warning is logged). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an automated GitHub Actions workflow for eligible Dependabot update pull requests. * Creates (and deduplicates) labeled QA issues with pull request details and checklist content, and posts the issue back to the pull request. * Optionally links new QA issues to project tracking with an “Awaiting QA” status when configured. * **Chores** * Hardened the automation workflow’s runner and permissions. * Updated safety-gate configuration to waive checks for the new Dependabot QA workflow. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
86 lines
4.0 KiB
YAML
86 lines
4.0 KiB
YAML
# zizmor "blocking gate" configuration.
|
|
#
|
|
# This config is used by the zizmor job in .github/workflows/zizmor.yml.
|
|
#
|
|
# This file is passed explicitly via the action's `config:` input rather than being named
|
|
# .github/zizmor.yml, so a plain `zizmor .` run still reports the full backlog locally.
|
|
|
|
rules:
|
|
# The misfeature rule stays ENABLED so new workflows are still checked; we only waive the
|
|
# specific Windows signing/build workflows below. Their findings are all `shell: cmd` notices:
|
|
# zizmor cannot introspect CMD scripts, so it emits a Low-severity analysis-limitation note
|
|
# rather than a real finding, and these workflows require the Windows CMD shell. Matching is by
|
|
# filename, so keep these names unique.
|
|
misfeature:
|
|
ignore:
|
|
- build-fleetctl-msi.yml
|
|
- code-sign-windows.yml
|
|
- e2e-agent.yml
|
|
- fleet-and-orbit.yml
|
|
- generate-osqueryd-targets.yml
|
|
- goreleaser-fleet.yaml
|
|
# The template-injection rule stays ENABLED so new workflows are checked, and we waive only the
|
|
# workflows that carry the existing backlog (listed below). We audited every current finding by
|
|
# trigger and source: none are reachable by untrusted input. There are no pull_request_target /
|
|
# workflow_run / issue_comment workflows; the one issues-triggered workflow
|
|
# (auto-tag-unreleased-bugs.yml) reads the issue body via actions/github-script's context object,
|
|
# never interpolated into a shell. The remaining findings draw only from maintainer-/runner-
|
|
# controlled sources (static matrix values, workflow_dispatch inputs gated behind write access,
|
|
# push/tag refs, github.sha, and internal step outputs), so they are not externally exploitable.
|
|
# This waiver freezes the backlog: any template-injection in a NEW workflow, or any NEW finding
|
|
# in a workflow not listed here, fails the gate.
|
|
#
|
|
# Matching is by filename only (zizmor does not match on path), so keep these names unique.
|
|
# NOTE: `action.yml` matches every composite-action manifest. Today only .github/actions/r2-upload
|
|
# carries findings; a future finding in another composite action (e.g. eng-metrics) would also be
|
|
# suppressed. Revisit if more composite actions are added.
|
|
# dependabot-qa-ticket.yml uses pull_request_target for write access but never checks out
|
|
# PR code -- it only calls the GitHub API via actions/github-script, so there is no code-
|
|
# injection vector.
|
|
dangerous-triggers:
|
|
ignore:
|
|
- dependabot-qa-ticket.yml
|
|
# dependabot-qa-ticket.yml gates on github.event.pull_request.user.login (the PR author),
|
|
# not github.actor. zizmor still flags the pull_request_target bot condition; waived because
|
|
# user.login is not spoofable for same-repo Dependabot PRs.
|
|
bot-conditions:
|
|
ignore:
|
|
- dependabot-qa-ticket.yml
|
|
template-injection:
|
|
ignore:
|
|
- action.yml # .github/actions/r2-upload/action.yml (bash; env/inputs from trusted callers)
|
|
- build-fleetctl-msi.yml
|
|
- build-fleetctl-pkg.yml
|
|
- build-fleetd-base-msi.yml
|
|
- build-fleetd-base-pkg.yml
|
|
- build-fleetdm-fleetctl-check-vulnerabilities.yml
|
|
- check-bomutils-vulnerabilities.yml
|
|
- check-vulnerabilities-in-released-docker-images.yml
|
|
- check-wix-vulnerabilities.yml
|
|
- code-sign-windows.yml
|
|
- db-upgrade-test.yml
|
|
- dogfood-automated-policy-updates.yml
|
|
- dogfood-update-testing-qa-apps.yml
|
|
- e2e-agent.yml
|
|
- fleet-and-orbit.yml
|
|
- generate-desktop-targets.yml
|
|
- golangci-lint.yml
|
|
- goreleaser-fleet.yaml
|
|
- goreleaser-orbit.yaml
|
|
- goreleaser-snapshot-fleet.yaml
|
|
- ingest-maintained-apps.yml
|
|
- loadtest-infra.yml
|
|
- loadtest-osquery-perf.yml
|
|
- product-eng-handbook-summary.yml
|
|
- publish-go-module.yml
|
|
- randokiller-go.yml
|
|
- release-fleetd-base.yml
|
|
- release-fleetd-chrome-beta.yml
|
|
- release-fleetd-chrome.yml
|
|
- tag-aging-bugs.yml
|
|
- test-db-changes.yml
|
|
- test-fma-darwin-pr-only.yml
|
|
- test-fma-windows-pr-only.yml
|
|
- test-go-suite.yaml
|
|
- verify-fleetd-base.yml
|