feat(lighthouse): add reusable lighthouse audit workflow#89
Conversation
📝 WalkthroughWalkthroughAdds a reusable GitHub Actions workflow for Lighthouse scans. It accepts a required target URL, optional extra URLs, and runner selection, runs the Lighthouse CI action, exposes the audit outcome, and writes a Markdown summary with per-URL scores and report links. ChangesReusable Lighthouse scan workflow
Code Review Effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/lighthouse-scan.yml:
- Around line 58-68: The Lighthouse report summary step is expanding step
outputs directly inside the shell script, which can create quoting/injection
issues. Update the “format report summary” step to pass
steps.audit.outputs.manifest and steps.audit.outputs.links through env, then
reference those env vars inside the run block instead of inline interpolation.
Keep the existing jq processing and summary formatting intact while using the
step name “format report summary” and steps.audit as the location anchors.
- Around line 38-48: The “build urls list” step in lighthouse-scan.yml is
vulnerable because inputs.target_url and inputs.urls are expanded directly into
the shell script; move those values into env for the step and have the script
read the env vars instead. Update the URLs construction logic in the same step
to pass the values to jq using --arg/related safe argument handling so `build
urls list` treats them as data, not executable shell content.
- Around line 30-34: The lighthouse-scan workflow job has an unnecessary
permission scope: remove the unused issues: write grant and keep the job at
least-privilege with contents: read only. Update the permissions block in
lighthouse-scan.yml accordingly, since the job only reports via
$GITHUB_STEP_SUMMARY and does not use any issue-writing actions.
- Line 52: The Lighthouse CI workflow step currently uses the mutable
treosh/lighthouse-ci-action@v12 tag, so update the action reference in the
lighthouse scan job to the v12.6.2 commit SHA instead. Keep the same uses entry
for treosh/lighthouse-ci-action, but replace the version tag with the pinned
commit so the workflow is reproducible and immutable.
- Line 36: Update the checkout step in the lighthouse scan workflow to use the
pinned actions/checkout commit for v6.0.3 instead of the mutable v6 tag, and
disable credential persistence for this job by setting persist-credentials to
false. Make the change in the actions/checkout usage so the workflow is
reproducible and does not leave the token in git config.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 80e9d665-b9d2-4ef2-bc2d-96d4e199e1a1
📒 Files selected for processing (1)
.github/workflows/lighthouse-scan.yml
| permissions: | ||
| contents: read | ||
| issues: write | ||
| outputs: | ||
| lighthouse_result: ${{ steps.audit.outcome }} |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Drop the unused issues: write permission.
The job never creates or comments on issues (it only writes to $GITHUB_STEP_SUMMARY), so issues: write is an unnecessary privilege escalation. Reducing it to contents: read keeps the job at least-privilege.
🔒 Proposed fix
permissions:
contents: read
- issues: writeAs per coding guidelines: "Apply minimal permissions principle to all workflow jobs - use contents: read by default and only escalate permissions when needed".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| permissions: | |
| contents: read | |
| issues: write | |
| outputs: | |
| lighthouse_result: ${{ steps.audit.outcome }} | |
| permissions: | |
| contents: read | |
| outputs: | |
| lighthouse_result: ${{ steps.audit.outcome }} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/lighthouse-scan.yml around lines 30 - 34, The
lighthouse-scan workflow job has an unnecessary permission scope: remove the
unused issues: write grant and keep the job at least-privilege with contents:
read only. Update the permissions block in lighthouse-scan.yml accordingly,
since the job only reports via $GITHUB_STEP_SUMMARY and does not use any
issue-writing actions.
Sources: Coding guidelines, Linters/SAST tools
| outputs: | ||
| lighthouse_result: ${{ steps.audit.outcome }} | ||
| steps: | ||
| - uses: actions/checkout@v6 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major
🧩 Analysis chain
🌐 Web query:
What is the latest release tag and commit SHA for actions/checkout v6?
💡 Result:
The latest release tag for actions/checkout v6 is v6.0.3 [1][2][3]. The commit SHA associated with the v6.0.3 tag is df4cb1c069e1874edd31b4311f1884172cec0e10 [4][5].
Citations:
- 1: https://github.com/actions/checkout/releases
- 2: https://github.com/actions/checkout/releases/tag/v6.0.3
- 3: amcheste/kagents@bc36736
- 4: actions/checkout@v6.0.2...v6.0.3
- 5: actions/checkout@de0fac2...df4cb1c
🏁 Script executed:
sed -n '1,160p' .github/workflows/lighthouse-scan.ymlRepository: tehw0lf/workflows
Length of output: 2518
🏁 Script executed:
python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/lighthouse-scan.yml')
print(p.read_text())
PYRepository: tehw0lf/workflows
Length of output: 2519
🏁 Script executed:
awk 'NR>=1 && NR<=200 {print NR ":" $0}' .github/workflows/lighthouse-scan.ymlRepository: tehw0lf/workflows
Length of output: 2713
Pin actions/checkout and disable credential persistence.
actions/checkout@v6 is a mutable tag; pin it to df4cb1c069e1874edd31b4311f1884172cec0e10 (v6.0.3). Since this job doesn’t push, set persist-credentials: false to keep the token out of .git/config.
🔒 Proposed fix
- - uses: actions/checkout@v6
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ persist-credentials: false📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - uses: actions/checkout@v6 | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false |
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 36-36: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 36-36: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/lighthouse-scan.yml at line 36, Update the checkout step
in the lighthouse scan workflow to use the pinned actions/checkout commit for
v6.0.3 instead of the mutable v6 tag, and disable credential persistence for
this job by setting persist-credentials to false. Make the change in the
actions/checkout usage so the workflow is reproducible and does not leave the
token in git config.
Source: Linters/SAST tools
| - name: build urls list | ||
| id: urls | ||
| run: | | ||
| BASE_URL="${{ inputs.target_url }}" | ||
| EXTRA='${{ inputs.urls }}' | ||
| if [ -n "$EXTRA" ]; then | ||
| URLS=$(echo "$EXTRA" | jq -c ". + [\"$BASE_URL\"]") | ||
| else | ||
| URLS="[\"$BASE_URL\"]" | ||
| fi | ||
| echo "urls=$URLS" >> $GITHUB_OUTPUT |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
Code injection: pass inputs via env, not template expansion.
${{ inputs.target_url }} and ${{ inputs.urls }} are expanded directly into the shell script before it runs, so a crafted URL (e.g. containing $(...) or backticks) executes arbitrary commands on the runner. Bind them to environment variables and feed them to jq with --arg so the values are treated as data.
🔒 Proposed fix
- name: build urls list
id: urls
+ env:
+ BASE_URL: ${{ inputs.target_url }}
+ EXTRA: ${{ inputs.urls }}
run: |
- BASE_URL="${{ inputs.target_url }}"
- EXTRA='${{ inputs.urls }}'
if [ -n "$EXTRA" ]; then
- URLS=$(echo "$EXTRA" | jq -c ". + [\"$BASE_URL\"]")
+ URLS=$(echo "$EXTRA" | jq -c --arg base "$BASE_URL" '. + [$base]')
else
- URLS="[\"$BASE_URL\"]"
+ URLS=$(jq -cn --arg base "$BASE_URL" '[$base]')
fi
echo "urls=$URLS" >> $GITHUB_OUTPUT📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: build urls list | |
| id: urls | |
| run: | | |
| BASE_URL="${{ inputs.target_url }}" | |
| EXTRA='${{ inputs.urls }}' | |
| if [ -n "$EXTRA" ]; then | |
| URLS=$(echo "$EXTRA" | jq -c ". + [\"$BASE_URL\"]") | |
| else | |
| URLS="[\"$BASE_URL\"]" | |
| fi | |
| echo "urls=$URLS" >> $GITHUB_OUTPUT | |
| - name: build urls list | |
| id: urls | |
| env: | |
| BASE_URL: ${{ inputs.target_url }} | |
| EXTRA: ${{ inputs.urls }} | |
| run: | | |
| if [ -n "$EXTRA" ]; then | |
| URLS=$(echo "$EXTRA" | jq -c --arg base "$BASE_URL" '. + [$base]') | |
| else | |
| URLS=$(jq -cn --arg base "$BASE_URL" '[$base]') | |
| fi | |
| echo "urls=$URLS" >> $GITHUB_OUTPUT |
🧰 Tools
🪛 zizmor (1.26.1)
[error] 41-41: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 42-42: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/lighthouse-scan.yml around lines 38 - 48, The “build urls
list” step in lighthouse-scan.yml is vulnerable because inputs.target_url and
inputs.urls are expanded directly into the shell script; move those values into
env for the step and have the script read the env vars instead. Update the URLs
construction logic in the same step to pass the values to jq using --arg/related
safe argument handling so `build urls list` treats them as data, not executable
shell content.
Source: Linters/SAST tools
|
|
||
| - name: run lighthouse | ||
| id: audit | ||
| uses: treosh/lighthouse-ci-action@v12 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major
🧩 Analysis chain
🌐 Web query:
Latest release tag and commit SHA for treosh/lighthouse-ci-action v12
💡 Result:
Latest release tag for treosh/lighthouse-ci-action v12 (as of 2026-06-24): 12.6.2 [1][2]. Commit SHA for tag 12.6.2: 3e7e23f8e4a1e7f1c9a9c8b8f6c9d8a7b6c5d4e3 (short SHA shown as 3e7e23f) [3].
Citations:
- 1: https://github.com/treosh/lighthouse-ci-action/releases/tag/12.6.2
- 2: https://github.com/treosh/lighthouse-ci-action/releases
- 3: chore(deps): bump treosh/lighthouse-ci-action from 12.6.1 to 12.6.2 facebook/docusaurus#11811
Pin treosh/lighthouse-ci-action to a commit SHA.
@v12 is a mutable tag; pin this step to the v12.6.2 commit instead.
🔒 Proposed fix
- uses: treosh/lighthouse-ci-action@v12
+ uses: treosh/lighthouse-ci-action@3e7e23f8e4a1e7f1c9a9c8b8f6c9d8a7b6c5d4e3 # v12.6.2📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| uses: treosh/lighthouse-ci-action@v12 | |
| uses: treosh/lighthouse-ci-action@3e7e23f8e4a1e7f1c9a9c8b8f6c9d8a7b6c5d4e3 # v12.6.2 |
🧰 Tools
🪛 zizmor (1.26.1)
[error] 52-52: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/lighthouse-scan.yml at line 52, The Lighthouse CI workflow
step currently uses the mutable treosh/lighthouse-ci-action@v12 tag, so update
the action reference in the lighthouse scan job to the v12.6.2 commit SHA
instead. Keep the same uses entry for treosh/lighthouse-ci-action, but replace
the version tag with the pinned commit so the workflow is reproducible and
immutable.
Source: Linters/SAST tools
| - name: format report summary | ||
| if: always() && steps.audit.outcome != 'skipped' | ||
| run: | | ||
| echo "### 🔦 Lighthouse Audit Results" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "| URL | Performance | Accessibility | Best Practices | SEO |" >> $GITHUB_STEP_SUMMARY | ||
| echo "|-----|-------------|---------------|----------------|-----|" >> $GITHUB_STEP_SUMMARY | ||
| echo '${{ steps.audit.outputs.manifest }}' | jq -r '.[] | "| \(.url) | \(.summary.performance * 100 | round)% | \(.summary.accessibility * 100 | round)% | \(.summary["best-practices"] * 100 | round)% | \(.summary.seo * 100 | round)% |"' >> $GITHUB_STEP_SUMMARY || true | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "Full reports: ${{ steps.audit.outputs.links }}" >> $GITHUB_STEP_SUMMARY | ||
| continue-on-error: true |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Move step outputs into env to avoid expansion into the script.
${{ steps.audit.outputs.manifest }} and ...links }} are expanded inline into the run script. While these originate from the action rather than the caller, binding them via env removes the injection surface flagged by static analysis and avoids quoting breakage if the values contain special characters.
🔒 Proposed fix
- name: format report summary
if: always() && steps.audit.outcome != 'skipped'
+ env:
+ MANIFEST: ${{ steps.audit.outputs.manifest }}
+ LINKS: ${{ steps.audit.outputs.links }}
run: |
echo "### 🔦 Lighthouse Audit Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| URL | Performance | Accessibility | Best Practices | SEO |" >> $GITHUB_STEP_SUMMARY
echo "|-----|-------------|---------------|----------------|-----|" >> $GITHUB_STEP_SUMMARY
- echo '${{ steps.audit.outputs.manifest }}' | jq -r '.[] | "| \(.url) | \(.summary.performance * 100 | round)% | \(.summary.accessibility * 100 | round)% | \(.summary["best-practices"] * 100 | round)% | \(.summary.seo * 100 | round)% |"' >> $GITHUB_STEP_SUMMARY || true
+ echo "$MANIFEST" | jq -r '.[] | "| \(.url) | \(.summary.performance * 100 | round)% | \(.summary.accessibility * 100 | round)% | \(.summary["best-practices"] * 100 | round)% | \(.summary.seo * 100 | round)% |"' >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY
- echo "Full reports: ${{ steps.audit.outputs.links }}" >> $GITHUB_STEP_SUMMARY
+ echo "Full reports: $LINKS" >> $GITHUB_STEP_SUMMARY
continue-on-error: true📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: format report summary | |
| if: always() && steps.audit.outcome != 'skipped' | |
| run: | | |
| echo "### 🔦 Lighthouse Audit Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| URL | Performance | Accessibility | Best Practices | SEO |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-----|-------------|---------------|----------------|-----|" >> $GITHUB_STEP_SUMMARY | |
| echo '${{ steps.audit.outputs.manifest }}' | jq -r '.[] | "| \(.url) | \(.summary.performance * 100 | round)% | \(.summary.accessibility * 100 | round)% | \(.summary["best-practices"] * 100 | round)% | \(.summary.seo * 100 | round)% |"' >> $GITHUB_STEP_SUMMARY || true | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Full reports: ${{ steps.audit.outputs.links }}" >> $GITHUB_STEP_SUMMARY | |
| continue-on-error: true | |
| - name: format report summary | |
| if: always() && steps.audit.outcome != 'skipped' | |
| env: | |
| MANIFEST: ${{ steps.audit.outputs.manifest }} | |
| LINKS: ${{ steps.audit.outputs.links }} | |
| run: | | |
| echo "### 🔦 Lighthouse Audit Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| URL | Performance | Accessibility | Best Practices | SEO |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-----|-------------|---------------|----------------|-----|" >> $GITHUB_STEP_SUMMARY | |
| echo "$MANIFEST" | jq -r '.[] | "| \(.url) | \(.summary.performance * 100 | round)% | \(.summary.accessibility * 100 | round)% | \(.summary["best-practices"] * 100 | round)% | \(.summary.seo * 100 | round)%"' >> $GITHUB_STEP_SUMMARY || true | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Full reports: $LINKS" >> $GITHUB_STEP_SUMMARY | |
| continue-on-error: true |
🧰 Tools
🪛 zizmor (1.26.1)
[info] 65-65: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[info] 67-67: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/lighthouse-scan.yml around lines 58 - 68, The Lighthouse
report summary step is expanding step outputs directly inside the shell script,
which can create quoting/injection issues. Update the “format report summary”
step to pass steps.audit.outputs.manifest and steps.audit.outputs.links through
env, then reference those env vars inside the run block instead of inline
interpolation. Keep the existing jq processing and summary formatting intact
while using the step name “format report summary” and steps.audit as the
location anchors.
Source: Linters/SAST tools
Summary
lighthouse-scan.ymlworkflow callable from any repoInputs
target_url(required): URL to auditurls(optional): additional URLs as JSON arrayrunner(optional): defaults toubuntu-latestTest plan
@mainSummary by CodeRabbit