Skip to content

Commit 5d000e4

Browse files
GeekTrainerCopilot
andcommitted
ci: pin lychee-action and split render workflow by least privilege
- Pin lycheeverse/lychee-action from the floating @v2 tag to commit SHA 8646ba30535128ac92d33dfc9133794bfdd9b411 (v2.6.1) in both pages.yml and render-markdown.yml. Resolves CodeQL findings about unpinned third-party actions on PR #140. - Split render-markdown.yml into a 'validate' job (contents: read, runs on every event) and a 'commit' job (contents: write, runs only on push to main, depends on validate). Previously the workflow granted contents: write at the workflow level, which also applied to PR runs even though those never push. Now PR runs execute with read-only contents permission, matching least-privilege guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3f59cd2 commit 5d000e4

2 files changed

Lines changed: 37 additions & 16 deletions

File tree

.github/workflows/pages.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ jobs:
5858
shell: bash
5959

6060
- name: Run lychee
61-
uses: lycheeverse/lychee-action@v2
61+
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.6.1
6262
with:
6363
args: >-
6464
--offline

.github/workflows/render-markdown.yml

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ name: Render workshop
44
# render error (missing partial, unresolved component, broken link, missing
55
# image) or lychee error. Does NOT commit anywhere.
66
#
7-
# On push to main: regenerate workshop/ and commit any diff back to main as
8-
# github-actions[bot]. The commit message contains [skip ci] to avoid
9-
# triggering this workflow recursively. pages.yml watches docs/**, not
10-
# workshop/**, so it also will not re-fire on the bot commit.
7+
# On push to main: re-runs validation, then regenerates workshop/ and commits
8+
# any diff back to main as github-actions[bot]. The commit message contains
9+
# [skip ci] to avoid triggering this workflow recursively. pages.yml watches
10+
# docs/**, not workshop/**, so it also will not re-fire on the bot commit.
11+
#
12+
# The validate and commit steps are split into separate jobs so that PR runs
13+
# (which never push) execute with read-only contents permission. Only the
14+
# main-branch commit job is granted contents:write.
1115

1216
on:
1317
pull_request:
@@ -24,19 +28,17 @@ on:
2428
workflow_dispatch:
2529

2630
permissions:
27-
contents: write # required to push the regenerated workshop/ back to main
31+
contents: read
2832

2933
jobs:
30-
render:
31-
name: Render workshop content
34+
validate:
35+
name: Validate render + links
3236
runs-on: ubuntu-latest
37+
permissions:
38+
contents: read
3339
steps:
3440
- name: Checkout
3541
uses: actions/checkout@v5
36-
with:
37-
# Default GITHUB_TOKEN; sufficient to push to the same repo unless
38-
# branch protection blocks bot pushes (see plan.md risks).
39-
token: ${{ secrets.GITHUB_TOKEN }}
4042

4143
- name: Set up Python
4244
uses: actions/setup-python@v6
@@ -47,13 +49,33 @@ jobs:
4749
run: python scripts/render-markdown.py
4850

4951
- name: Link check (lychee)
50-
uses: lycheeverse/lychee-action@v2
52+
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.6.1
5153
with:
5254
args: --offline --no-progress 'workshop/**/*.md'
5355
fail: true
5456

55-
- name: Commit regenerated workshop/ (main only)
56-
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
57+
commit:
58+
name: Regenerate workshop/ on main
59+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
60+
needs: validate
61+
runs-on: ubuntu-latest
62+
permissions:
63+
contents: write # only granted on the push-to-main path so the bot can push the regenerated workshop/
64+
steps:
65+
- name: Checkout
66+
uses: actions/checkout@v5
67+
with:
68+
token: ${{ secrets.GITHUB_TOKEN }}
69+
70+
- name: Set up Python
71+
uses: actions/setup-python@v6
72+
with:
73+
python-version: '3.13'
74+
75+
- name: Render workshop content
76+
run: python scripts/render-markdown.py
77+
78+
- name: Commit regenerated workshop/
5779
run: |
5880
if [[ -z "$(git status --porcelain workshop/)" ]]; then
5981
echo "workshop/ is already in sync — nothing to commit."
@@ -68,4 +90,3 @@ jobs:
6890
6991
[skip ci]"
7092
git push
71-

0 commit comments

Comments
 (0)