diff --git a/.github/workflows/markdown_format.yml b/.github/workflows/markdown_format.yml index afd17b6..1d6d96e 100644 --- a/.github/workflows/markdown_format.yml +++ b/.github/workflows/markdown_format.yml @@ -10,6 +10,6 @@ jobs: runs-on: 'ubuntu-latest' steps: - name: 'Checkout Code' - uses: 'actions/checkout@v4' + uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 - name: 'Check Markdown Format' run: 'tools/mdformat --check --wrap 100 .' diff --git a/.github/workflows/publish_docs.yml b/.github/workflows/publish_docs.yml index c446cd1..70217c9 100644 --- a/.github/workflows/publish_docs.yml +++ b/.github/workflows/publish_docs.yml @@ -15,16 +15,16 @@ jobs: name: "Build and Deploy Docs" runs-on: 'ubuntu-latest' steps: - - uses: 'actions/checkout@v4' + - uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 - name: 'Generate HTML from Markdown' - uses: 'ldeluigi/markdown-docs@latest' + uses: 'ldeluigi/markdown-docs@e51f9a21070db6a21dfbe27ac92f065823e006a1' # ratchet:ldeluigi/markdown-docs@latest with: src: 'docs' dst: 'generated-pages' - name: 'Install rsync' run: 'apt-get update && apt-get install -y rsync' - name: 'Deploy Docs' - uses: JamesIves/github-pages-deploy-action@v4 + uses: JamesIves/github-pages-deploy-action@6c2d9db40f9296374acc17b90404b6e8864128c8 # ratchet:JamesIves/github-pages-deploy-action@v4 with: folder: generated-pages force: false diff --git a/.github/workflows/publish_docs_preview.yml b/.github/workflows/publish_docs_preview.yml index 13d6e3a..3fc9556 100644 --- a/.github/workflows/publish_docs_preview.yml +++ b/.github/workflows/publish_docs_preview.yml @@ -14,13 +14,13 @@ jobs: name: "Build PR Preview Docs" runs-on: 'ubuntu-latest' steps: - - uses: 'actions/checkout@v4' + - uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 - name: 'Generate HTML from Markdown' - uses: 'ldeluigi/markdown-docs@latest' + uses: 'ldeluigi/markdown-docs@e51f9a21070db6a21dfbe27ac92f065823e006a1' # ratchet:ldeluigi/markdown-docs@latest with: src: 'docs' dst: 'generated-pages' - name: 'Deploy GitHub Pages Preview' - uses: rossjrw/pr-preview-action@v1 + uses: rossjrw/pr-preview-action@df22037db54ab6ee34d3c1e2b8810ac040a530c6 # ratchet:rossjrw/pr-preview-action@v1 with: source-dir: './generated-pages/' diff --git a/.github/workflows/yaml_format.yml b/.github/workflows/yaml_format.yml index efc5017..7021be4 100644 --- a/.github/workflows/yaml_format.yml +++ b/.github/workflows/yaml_format.yml @@ -11,6 +11,6 @@ jobs: runs-on: 'ubuntu-latest' steps: - name: 'Checkout Code' - uses: 'actions/checkout@v4' + uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4 - name: 'Check Yaml Format' run: 'tools/yamlfmt --lint .' diff --git a/docs/semgrep-rules/actions-need-pinned-commits.md b/docs/semgrep-rules/actions-need-pinned-commits.md new file mode 100644 index 0000000..7915ec5 --- /dev/null +++ b/docs/semgrep-rules/actions-need-pinned-commits.md @@ -0,0 +1,31 @@ +# actions-need-pinned-commits + +For actions that look like: + +``` +uses: actions/checkout@v4 +``` + +GitHub uses the underlying git label v4 to fetch the action to run. As seen in the +[tj-actions/changed-files](https://semgrep.dev/blog/2025/popular-github-action-tj-actionschanged-files-is-compromised/) +vulnerabilty, these lables are not immutable and trivially changeable. So what you think is a nice +stable safe version, an attacker has changed behind your back to something nefarious. + +We are strongly encouraging use to use the full git commit hash instead to prevent this type of +attacks. + +## Ratchet + +[Ratchet](https://github.com/sethvargo/ratchet) provides an easy way to do this. + +You can pin all your workflow files like this: + +``` +ratchet pin .github/workflows/* +``` + +And upgrade them (as needed, under your control and not someone elses): + +``` +ratchet upgrade .github/workflows/action_to_upgrade.yml +``` diff --git a/semgrep-rules/actions/actions_need_pinned_commits.yaml b/semgrep-rules/actions/actions_need_pinned_commits.yaml new file mode 100644 index 0000000..bacec3a --- /dev/null +++ b/semgrep-rules/actions/actions_need_pinned_commits.yaml @@ -0,0 +1,18 @@ +rules: + - id: actions-need-pinned-commits + languages: + - yaml + severity: WARNING + message: 'Referencing an action to run by git tag is risky, due to the mutability of git tags. Prefer + to use full git SHAs instead. More information: https://google.github.io/github-team/semgrep-rules/actions-need-pinned-commits.html' + metadata: + category: best-practice + technology: + - github-actions + patterns: + - pattern-either: + - patterns: + - pattern-inside: "{steps: ...}" + # Match all uses patterns that don't contain the full SHA1 hash. Yes, short hashes exist but suffer from a similar (but slightly harder) attack vector by purposely crafting a colliding SHA. + - pattern: "uses: ..." + - pattern-not-regex: ".*@[0-9A-Fa-f]{40}.*" diff --git a/semgrep-tests/actions/actions_need_pinned_commits.test.yaml b/semgrep-tests/actions/actions_need_pinned_commits.test.yaml new file mode 100644 index 0000000..2349d21 --- /dev/null +++ b/semgrep-tests/actions/actions_need_pinned_commits.test.yaml @@ -0,0 +1,19 @@ +name: 'Test Actions Needing Pinned Commits' +on: + pull_request: +jobs: + do-stuff: + steps: + - name: 'Step 1' + # ruleid: actions-need-pinned-commits + uses: actions/checkout@v4 + - name: 'Step 2' + uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' + - name: 'Step 2.5' + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 + - name: 'Step 3' + # ruleid: actions-need-pinned-commits + uses: 'actions/checkout@11bd719' + - name: 'Step 4' + # ruleid: actions-need-pinned-commits + uses: 'actions/checkout@my_git_label' diff --git a/tools/ratchet b/tools/ratchet new file mode 100755 index 0000000..4bc8de7 --- /dev/null +++ b/tools/ratchet @@ -0,0 +1,2 @@ +#!/bin/sh +docker run -it --rm -v "${PWD}:${PWD}" -w "${PWD}" ghcr.io/sethvargo/ratchet:latest $* \ No newline at end of file