diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 41ba91bfed..6b39835f91 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,6 +12,13 @@ on: - 'release-v*' - 'feat/*' +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + env: GO_VERSION: 1.26.2 NODE_VERSION: 20.19.0 @@ -20,6 +27,7 @@ env: jobs: go: runs-on: ubuntu-24.04 + timeout-minutes: 30 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/setup-go@v3 @@ -31,6 +39,7 @@ jobs: plugin: runs-on: ubuntu-24.04 + timeout-minutes: 30 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/setup-go@v3 @@ -41,6 +50,7 @@ jobs: web: runs-on: ubuntu-24.04 + timeout-minutes: 30 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/setup-node@v3 @@ -63,6 +73,7 @@ jobs: chart: runs-on: ubuntu-24.04 + timeout-minutes: 15 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: diff --git a/.github/workflows/cherry_pick.yaml b/.github/workflows/cherry_pick.yaml index e5ae63d044..7f5ddd7933 100644 --- a/.github/workflows/cherry_pick.yaml +++ b/.github/workflows/cherry_pick.yaml @@ -11,9 +11,15 @@ on: description: 'release version (e.g. v0.48.1)' required: true type: string + +permissions: + contents: write + pull-requests: write + jobs: tool: runs-on: ubuntu-24.04 + timeout-minutes: 15 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index ff4148d535..95d2e9f41c 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -5,9 +5,15 @@ on: schedule: - cron: '00 10 * * 1' +permissions: + contents: read + security-events: write + actions: read + jobs: analyze: runs-on: ubuntu-24.04 + timeout-minutes: 120 strategy: fail-fast: false diff --git a/.github/workflows/first-time-contributor.yaml b/.github/workflows/first-time-contributor.yaml index cd5eba69c4..352bafc153 100644 --- a/.github/workflows/first-time-contributor.yaml +++ b/.github/workflows/first-time-contributor.yaml @@ -9,7 +9,8 @@ permissions: jobs: welcome: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 + timeout-minutes: 5 steps: - name: Welcome first-time contributor uses: actions/github-script@v7 diff --git a/.github/workflows/gen.yaml b/.github/workflows/gen.yaml index f689531942..074042f60d 100644 --- a/.github/workflows/gen.yaml +++ b/.github/workflows/gen.yaml @@ -4,9 +4,17 @@ on: pull_request: branches: [ master ] +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: code: runs-on: ubuntu-24.04 + timeout-minutes: 15 steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 diff --git a/.github/workflows/prerelease.yaml b/.github/workflows/prerelease.yaml index 372c9fbcb6..020e0d7802 100644 --- a/.github/workflows/prerelease.yaml +++ b/.github/workflows/prerelease.yaml @@ -8,9 +8,13 @@ on: paths: - 'RELEASE' +permissions: + contents: write + jobs: gh-release: runs-on: ubuntu-24.04 + timeout-minutes: 10 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: diff --git a/.github/workflows/publish_image_chart.yaml b/.github/workflows/publish_image_chart.yaml index c16fe3057d..1b9388fabe 100644 --- a/.github/workflows/publish_image_chart.yaml +++ b/.github/workflows/publish_image_chart.yaml @@ -15,6 +15,7 @@ env: jobs: artifacts: runs-on: oracle-vm-8cpu-32gb-x86-64 + timeout-minutes: 60 permissions: contents: read packages: write diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7b7c1ed5c9..61281a93d0 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -7,9 +7,15 @@ on: description: 'release version (e.g. v0.48.1)' required: true type: string + +permissions: + contents: write + pull-requests: write + jobs: tool: runs-on: ubuntu-24.04 + timeout-minutes: 15 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index 321a598289..2ed59aecc5 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -1,11 +1,17 @@ name: close_stale_issues_and_PRs + on: schedule: - cron: '0 0 * * *' +permissions: + issues: write + pull-requests: write + jobs: stale: runs-on: ubuntu-24.04 + timeout-minutes: 10 steps: - uses: actions/stale@v8 with: @@ -21,5 +27,4 @@ jobs: close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity. Feel free to reopen if still applicable.' days-before-pr-stale: 30 days-before-pr-close: 7 - delete-branch: true - exempt-pr-labels: 'not-auto-close' \ No newline at end of file + exempt-pr-labels: 'not-auto-close' diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 33ed76433e..a96b2a90d9 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -11,9 +11,13 @@ on: - master - 'release-v*' - 'feat/*' +permissions: + contents: read + concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} + env: GO_VERSION: 1.26.2 NODE_VERSION: 20.19.0 @@ -97,6 +101,7 @@ jobs: integration: runs-on: ubuntu-24.04 + timeout-minutes: 60 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/setup-go@v3 diff --git a/.github/workflows/thank-you.yaml b/.github/workflows/thank-you.yaml index 782c4a68f4..1c8db8c797 100644 --- a/.github/workflows/thank-you.yaml +++ b/.github/workflows/thank-you.yaml @@ -11,13 +11,14 @@ permissions: jobs: thank-you: - # Only run if the PR was actually merged - if: github.event.pull_request.merged - runs-on: ubuntu-latest + if: github.event.pull_request.merged == true + runs-on: ubuntu-24.04 + timeout-minutes: 5 steps: - # We must check out the code to read the MAINTAINERS.json file - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.event.pull_request.base.sha }} - name: Leave a Thank You Comment uses: actions/github-script@v7 @@ -26,29 +27,24 @@ jobs: const fs = require('fs'); const creator = context.payload.pull_request.user.login; - // Skip bots to avoid comment loops if (creator.endsWith('[bot]')) { console.log('Skipping bot account.'); return; } - // Read maintainers list from JSON file const maintainersData = JSON.parse(fs.readFileSync('MAINTAINERS.json', 'utf8')); const maintainers = maintainersData.maintainers.map(m => m.toLowerCase()); - // Skip core maintainers to avoid spamming the team if (maintainers.includes(creator.toLowerCase())) { console.log(`Skipping maintainer account: ${creator}`); return; } - const message = 'Thank you for contributing to PipeCD! The changes in this pull request will be part of the upcoming release!'; + const message = `Thank you for contributing to PipeCD, @${creator}! The changes in this pull request will be part of the upcoming release!`; - // Post the comment to the PR await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, body: message }); - \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 7624567e64..cc951bcb29 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ -# Documentation +# Documentation XYZ CHANGE FOR TESTING The source files for the documentation is placing in [content](https://github.com/pipe-cd/pipecd/tree/master/docs/content) directory. diff --git a/workflow-audit/issues/maintenance-action-pinning-inconsistency.md b/workflow-audit/issues/maintenance-action-pinning-inconsistency.md new file mode 100644 index 0000000000..083ad217b3 --- /dev/null +++ b/workflow-audit/issues/maintenance-action-pinning-inconsistency.md @@ -0,0 +1,26 @@ +# Maintenance: Inconsistent action pinning across workflows + +## Problem + +The repo pins security-sensitive Docker actions to SHA (good) but leaves many other actions on mutable tags. Dependabot tracks `github-actions` weekly for version bumps but does not enforce SHA pinning. + +## Unpinned Actions (not SHA-pinned) + +| Action | Used in | +|---|---| +| `actions/setup-go@v3` | build.yaml, test.yaml, publish_binary.yaml, plugin_release.yaml, publish_pipedv1_exp.yaml | +| `actions/setup-node@v3` | build.yaml, test.yaml, publish_site.yaml | +| `azure/setup-helm@v4` | build.yaml, lint.yaml, publish_image_chart.yaml, publish_site.yaml, publish_pipedv1_exp.yaml | +| `actions/labeler@v4` | labeler.yaml | +| `actions/stale@v8` | stale.yaml | +| `actions/github-script@v7` | first-time-contributor.yaml, thank-you.yaml | +| `codecov/codecov-action@v3` | test.yaml | +| `pipe-cd/actions-gh-release@v2.6.0` | prerelease.yaml | +| `github/codeql-action/*@v3` | codeql-analysis.yaml | +| `peter-evans/create-pull-request@v6` | release.yaml, publish_image_chart.yaml, publish_pipedv1_exp.yaml | +| `actions/checkout@v4` | thank-you.yaml (all others are SHA-pinned) | + +## Priority + +Highest risk: `ca-dp/code-butler@v1` (see separate security issue), `codecov/codecov-action`, `peter-evans/create-pull-request`. +Lower risk: GitHub-owned actions (`setup-go`, `setup-node`, `labeler`, `stale`) where Dependabot provides version tracking. diff --git a/workflow-audit/issues/maintenance-missing-concurrency-controls.md b/workflow-audit/issues/maintenance-missing-concurrency-controls.md new file mode 100644 index 0000000000..3d8652bee1 --- /dev/null +++ b/workflow-audit/issues/maintenance-missing-concurrency-controls.md @@ -0,0 +1,19 @@ +# Maintenance: build.yaml and gen.yaml are missing concurrency controls + +**Files:** `.github/workflows/build.yaml`, `.github/workflows/gen.yaml` + +## Problem + +`lint.yaml` and `test.yaml` both use the concurrency pattern: + +```yaml +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} +``` + +`build.yaml` and `gen.yaml` do not. On active PRs with multiple pushes, stale build and codegen-validation jobs queue up and waste CI minutes. On push to master the cancel-in-progress is false so those runs complete safely. + +## Fix + +Add the same `concurrency:` block to `build.yaml` and `gen.yaml`. diff --git a/workflow-audit/issues/maintenance-missing-timeouts.md b/workflow-audit/issues/maintenance-missing-timeouts.md new file mode 100644 index 0000000000..1d0b06ec18 --- /dev/null +++ b/workflow-audit/issues/maintenance-missing-timeouts.md @@ -0,0 +1,22 @@ +# Maintenance: Several workflows and jobs are missing timeout-minutes + +**Files:** `build.yaml`, `test.yaml` (integration), `codeql-analysis.yaml`, `gen.yaml`, `publish_image_chart.yaml` (artifacts job), `release.yaml`, `stale.yaml`, `prerelease.yaml` + +## Problem + +Jobs with no `timeout-minutes` can hang indefinitely. On self-hosted runners (`oracle-vm-8cpu-32gb-x86-64`) this is especially dangerous — a hung Docker build holds the runner and blocks all subsequent jobs. + +`build_tool.yaml` uses `timeout-minutes: 15` and `publish_tool.yaml` uses `timeout-minutes: 30` — these are the right reference points. + +## Recommended Timeouts + +| Job | Suggested timeout | +|---|---| +| `build.yaml` go/plugin/web/chart | 30 minutes | +| `test.yaml` integration | 60 minutes | +| `codeql-analysis.yaml` analyze | 120 minutes | +| `publish_image_chart.yaml` artifacts | 60 minutes | +| `gen.yaml` code | 15 minutes | +| `release.yaml` tool | 15 minutes | +| `stale.yaml` stale | 10 minutes | +| `prerelease.yaml` gh-release | 10 minutes | diff --git a/workflow-audit/issues/security-missing-permissions-declarations.md b/workflow-audit/issues/security-missing-permissions-declarations.md new file mode 100644 index 0000000000..d2dfdc2f70 --- /dev/null +++ b/workflow-audit/issues/security-missing-permissions-declarations.md @@ -0,0 +1,20 @@ +# Security: Multiple workflows missing explicit permissions declarations + +**Files:** `cherry_pick.yaml`, `codeql-analysis.yaml`, `stale.yaml`, `release.yaml`, `build.yaml`, `test.yaml` + +## Problem + +Workflows without explicit `permissions:` blocks inherit the repository-level default, which may be `contents: write` in some configurations. This violates the principle of least privilege and makes the security posture dependent on a repo setting rather than workflow intent. + +Most critically: `codeql-analysis.yaml` is missing `security-events: write`, which is **required** for CodeQL to upload SARIF results to the Security tab. Without it, the scan runs every Monday but findings may be silently discarded. + +## Required Permissions per Workflow + +| Workflow | Minimum permissions needed | +|---|---| +| `codeql-analysis.yaml` | `contents: read`, `security-events: write`, `actions: read` | +| `stale.yaml` | `issues: write`, `pull-requests: write` | +| `cherry_pick.yaml` | `contents: write`, `pull-requests: write` | +| `release.yaml` | `contents: write`, `pull-requests: write` | +| `build.yaml` | `contents: read` | +| `test.yaml` | `contents: read` |