Skip to content

Implement modularized workflow validation and update build scripts - #3172

Closed
kavix wants to merge 0 commit into
thunder-id:mainfrom
kavix:main
Closed

kavix wants to merge 0 commit into
thunder-id:mainfrom
kavix:main

Conversation

@kavix

@kavix kavix commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

The PR builder CI workflow previously built and validated all workspaces (backend, frontend, docs, SDKs, tools, E2E tests) on every pull request, even when only a subset of files changed. This mixed concerns in a single monolithic workflow file, slowed down PR feedback, and consumed unnecessary CI resources.

This PR restructures the PR builder around workspace-scoped validation:

  1. Per-workspace change detection: Only the affected workspaces are validated. For example, docs-only changes skip the product build/tests/E2E completely, and tools-only changes do not trigger frontend or backend checks.
  2. Dedicated validator modules: Moves each workspace's validation logic to a dedicated reusable workflow under .github/workflows/validate/ (backend.yml, frontend.yml, docs.yml, sdks.yml, tools.yml, e2e.yml).
  3. Lean main orchestrator: .github/workflows/pr-builder.yml becomes a lean orchestrator delegating validation to the appropriate reusable workflows.

Approach

  • Used dorny/paths-filter in .github/workflows/pr-builder.yml to define path patterns for backend, frontend, docs, sdks, tools, e2e, and powershell changes.
  • Created reusable sub-workflows under .github/workflows/validate/ triggered via workflow_call:
    • backend.yml: Mock verification, backend linting, product/backend coverage build, integration tests (sqlite/postgres/redis).
    • frontend.yml: pnpm/npm audit, frontend linting and formatting check, frontend packages tests, console & gate apps coverage tests.
    • docs.yml: Docusaurus documentation build.
    • sdks.yml: JavaScript SDKs build.
    • tools.yml: Tests/compilation for tools/i18n-extractor (Go) and tools/npx-thunderid (Node/TypeScript).
    • e2e.yml: Playwright E2E tests (including dedicated product build and sample app staging setup).
  • Configured the orchestrator to pass inherited secrets (secrets: inherit) to the E2E validator for running tests.

Related Issues

Related PRs

  • N/A

Security checks

  • Followed secure coding standards in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

  • Chores
    • Restructured CI/CD pipeline to be more modular and selective.
    • CI now conditionally runs validation checks only for changed components (backend, frontend, documentation, SDKs, tools, end-to-end tests, PowerShell), improving build performance and reducing feedback time.
    • Refactored validation workflows for better maintainability and reusability.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR restructures GitHub Actions CI by introducing workspace-scoped change detection (detect-changes job) in pr-builder.yml and extracting validation logic into reusable workflow modules. The monolithic pr-builder workflow is replaced with conditional calls to dedicated validate/*.yml workflows for backend, frontend, E2E, docs, SDKs, and tools.

Changes

CI Workflow Refactoring

Layer / File(s) Summary
Change Detection and Workflow Orchestration
.github/workflows/pr-builder.yml
Adds detect-changes job using path filters for backend, frontend, docs, sdks, tools, e2e, and powershell. Introduces conditional orchestration jobs (validate-backend, validate-frontend, validate-docs, validate-sdks, validate-tools, validate-e2e) that call reusable workflows when corresponding path changes are detected. Updates PowerShell validation gating to depend on change detection output.
Backend Validation Pipeline
.github/workflows/validate/backend.yml
Extracts backend validation as a reusable workflow with mock verification (Mockery sync check), linting, build with coverage, integration tests across database matrix (sqlite/postgres/redis), status reporting, and Nx cache cleanup. Includes artifact uploads for distribution and coverage reports to Codecov.
Frontend Validation Pipeline
.github/workflows/validate/frontend.yml
Extracts frontend validation as a reusable workflow with security audit (pnpm/npm with ignore-list filtering), Nx-based linting and format checks, package and app coverage tests with LCOV post-processing, Codecov upload aggregation using OIDC, and stale Nx cache cleanup.
E2E Test Validation Pipeline
.github/workflows/validate/e2e.yml
Extracts E2E validation as a reusable workflow orchestrating product/sample build, server startup with liveness polling, configuration resource imports via /import endpoint, sample app ID extraction, security-enabled server restart, sample app startup, Playwright test execution with environment variables, and report upload.
Lightweight Validation Workflows
.github/workflows/validate/docs.yml, .github/workflows/validate/sdks.yml, .github/workflows/validate/tools.yml
Adds four simple reusable workflows: docs (run make build_docs), sdks (run make build_sdks), i18n-extractor tool (Go test/build), and thunderid package (pnpm build/lint/test/typecheck). Each runs on ubuntu-latest with appropriate setup actions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • thunder-id/thunderid#3019: Requested workspace-scoped change detection and modularization of PR builder into reusable validate/* workflows, which this PR directly implements.

Possibly related PRs

  • thunder-id/thunderid#3044: Modifies the change-detection logic in .github/workflows/pr-builder.yml that drives downstream validation job gating (retrieved PR replaces paths-filter with git diff for non-docs/readme detection).
  • thunder-id/thunderid#2840: Modifies E2E sample-app setup flow (configuration import via /import endpoint) that the new validate/e2e.yml pipeline executes.
  • thunder-id/thunderid#1654: Directly modifies .github/workflows/pr-builder.yml job triggering logic and gating for validation jobs.

Suggested labels

Type/Improvement

Suggested reviewers

  • DonOmalVindula
  • brionmario
  • rajithacharith
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is well-structured, covers all required sections (Purpose, Approach, Related Issues), provides clear rationale for changes, and includes completed security checks from the template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: refactoring CI workflows into modularized, reusable validation components with scope-based execution, which is the primary focus of this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (5)
.github/workflows/validate/sdks.yml (1)

18-19: ⚡ Quick win

Disable credential persistence in checkout.

Line 19 should set persist-credentials: false so tokens are not left in local git config for later steps.

Suggested patch
       - name: 📥 Checkout Code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false
🤖 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/validate/sdks.yml around lines 18 - 19, The checkout step
"📥 Checkout Code" currently uses actions/checkout@... without disabling
credential persistence; update the checkout step (the step named "📥 Checkout
Code" that uses actions/checkout) to add persist-credentials: false so the
runner does not store tokens in the local git config for subsequent steps.
.github/workflows/validate/docs.yml (1)

18-19: ⚡ Quick win

Disable credential persistence in checkout.

Line 19 should set persist-credentials: false to reduce token exposure in job steps.

Suggested patch
       - name: 📥 Checkout Code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false
🤖 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/validate/docs.yml around lines 18 - 19, The checkout step
named "📥 Checkout Code" (uses:
actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5) should disable
credential persistence by adding the key persist-credentials: false to that
step; update the step configuration to include persist-credentials: false so the
job does not automatically expose the checkout token to subsequent steps.
.github/workflows/validate/tools.yml (1)

19-20: ⚡ Quick win

Harden both checkout steps by disabling credential persistence.

Lines 20 and 41 should use persist-credentials: false to prevent token persistence across subsequent shell steps.

Suggested patch
       - name: 📥 Checkout Code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false
...
       - name: 📥 Checkout Code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false

Also applies to: 40-41

🤖 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/validate/tools.yml around lines 19 - 20, The GitHub
Actions checkout steps (the step named "📥 Checkout Code" and the other checkout
step using actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5) must be
hardened by adding persist-credentials: false to each step; update both checkout
step definitions to include the persist-credentials: false key under their step
configuration to prevent the runner from persisting the checkout token into
subsequent shell steps.
.github/workflows/validate/backend.yml (1)

155-157: ⚡ Quick win

Gate integration tests on fast checks to reduce wasted CI runtime.

test-integration currently waits only for build; it still runs even when verify-mocks or lint has already failed.

Suggested fix
-    needs: [build]
+    needs: [verify-mocks, lint, build]
🤖 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/validate/backend.yml around lines 155 - 157, The
test-integration job currently only depends on build (job name:
test-integration), so it still runs even if earlier fast checks like
verify-mocks or lint fail; update the job's needs list to include those
fast-check jobs (e.g., add verify-mocks and lint to the needs array for
test-integration) so GitHub Actions will gate integration tests on their success
and avoid wasted CI runtime.
.github/workflows/validate/e2e.yml (1)

237-245: ⚡ Quick win

Fail fast when Sample App is missing.

Continuing with an empty sample_app_id pushes a setup failure downstream into opaque Playwright failures. Failing here improves signal.

🧭 Proposed refactor
           if [ -z "$SAMPLE_APP_ID" ] || [ "$SAMPLE_APP_ID" == "null" ]; then
             echo "⚠️  Warning: Sample App not found in applications list"
             echo "Available applications:"
             echo "$APPS_RESPONSE" | jq -r '(.applications // [])[] | "  - \(.name) (\(.id))"'
-            echo "sample_app_id=" >> $GITHUB_OUTPUT
+            exit 1
           else
             echo "✓ Sample App ID extracted: $SAMPLE_APP_ID"
             echo "sample_app_id=$SAMPLE_APP_ID" >> $GITHUB_OUTPUT
           fi
🤖 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/validate/e2e.yml around lines 237 - 245, The script
currently continues when SAMPLE_APP_ID is empty which causes downstream opaque
failures; update the if branch that checks SAMPLE_APP_ID to fail fast by writing
a clear failure message, optionally printing APPS_RESPONSE for debugging,
setting sample_app_id in GITHUB_OUTPUT to an empty or omitted value, and
immediately exiting with a non-zero status (exit 1); target the conditional that
references SAMPLE_APP_ID, APPS_RESPONSE and GITHUB_OUTPUT and replace the
current warning branch with the error log + exit 1 so the workflow stops early
when Sample App is missing.
🤖 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/pr-builder.yml:
- Around line 154-159: Replace the broad secrets: inherit on the validate-e2e
job with an explicit secrets mapping to only forward the required E2E secrets;
in the validate-e2e job (the job named "validate-e2e" that uses
./.github/workflows/validate/e2e.yml) map each secret individually
(PLAYWRIGHT_BASE_URL, PLAYWRIGHT_ADMIN_USERNAME, PLAYWRIGHT_ADMIN_PASSWORD,
PLAYWRIGHT_TEST_USER_USERNAME, PLAYWRIGHT_TEST_USER_PASSWORD,
SAMPLE_APP_USERNAME, SAMPLE_APP_PASSWORD) so the reusable workflow receives only
those values instead of inheriting all repository secrets.

In @.github/workflows/validate/backend.yml:
- Around line 22-23: The checkout steps currently use
actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 without disabling
credential persistence; update each checkout step (the ones using
actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 and the other checkout
occurrences) to set persist-credentials: false so Git authentication tokens are
not stored in the workspace Git config, ensuring each checkout step in the
workflow includes that option.
- Line 166: The CI uses floating service image tags "image: postgres:latest" and
"image: redis:latest" which makes tests nondeterministic; update those service
image entries (the lines containing "image: postgres:latest" and "image:
redis:latest") to pinned, explicit versions or digests (for example a specific
major/minor tag like postgres:15-alpine or a sha256 digest) so the workflow uses
a stable, reproducible image; ensure both occurrences are updated and consider
documenting the chosen versions in the workflow comments.
- Line 137: Replace the mutable tag uses: codecov/codecov-action@v5 with the
full commit SHA for the v5 release you intend to use (pin both occurrences of
the action found in the workflow). Locate the two occurrences of "uses:
codecov/codecov-action@v5" and update them to "uses:
codecov/codecov-action@<full-commit-sha>" using the commit SHA from the
codecov-action v5 release page so the workflow references an immutable commit.

In @.github/workflows/validate/e2e.yml:
- Around line 164-175: The readiness liveness probe uses curl without the --fail
flag so HTTP 4xx/5xx responses still count as success; update each curl
invocation that checks https://localhost:8090/health/liveness (the two blocks
around the initial wait loop and the later checks referenced in the comment) to
include --fail (or -f) and -s -k (e.g., curl -sf -k ...) so curl exits non-zero
on HTTP error responses, and apply the same change to the other occurrences
noted (lines referenced as the blocks at 164-175, 266-277, 309-320) to ensure
the workflow fails when the service returns 4xx/5xx.
- Around line 21-22: Three checkout steps currently use actions/checkout@...
without disabling credential persistence; update each checkout step (the ones
using actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5) to include
persist-credentials: false so the workflow token is not left in the runner’s git
config for the job duration; locate the three occurrences (around the blocks at
the earlier, middle, and later checkout steps referenced in the diff) and add
the persist-credentials: false key under each checkout step's configuration.

In @.github/workflows/validate/frontend.yml:
- Around line 22-23: Update each GitHub Actions checkout step that uses
actions/checkout@... (e.g., the steps named "📥 Checkout Code" and the other
checkout occurrences) to include persist-credentials: false in the step's with:
block so credentials are not persisted into the workspace; locate the checkout
steps by the uses: actions/checkout@... entries and add a with:
persist-credentials: false for each occurrence noted in the review.
- Line 245: Replace the mutable action tag "uses: codecov/codecov-action@v5"
with a specific 40-character commit SHA for the v5 release (e.g. "uses:
codecov/codecov-action@<COMMIT_SHA>") wherever that tag appears (the two
occurrences of the string "uses: codecov/codecov-action@v5"); update both usages
so the workflow pins to an immutable commit SHA instead of the floating "v5"
tag.
- Around line 44-57: The jq pipeline that computes REMAINING from AUDIT_RESULT
can fail and produce an empty/invalid REMAINING, so change the logic around the
jq invocation that references AUDIT_RESULT and IGNORE_GHSAS to explicitly check
jq’s exit status (or use jq -e) and treat a jq failure as an error: capture the
jq exit code after the REMAINING assignment and if non-zero either exit with a
non-zero status (fail the job) or set REMAINING to a safe numeric value and then
fail; ensure the code paths around REMAINING (the subsequent [ "$REMAINING" -gt
0 ] test and the error reporting that prints the filtered advisories) only run
when jq succeeded so a jq parse/shape error cannot produce a false "all clear".

---

Nitpick comments:
In @.github/workflows/validate/backend.yml:
- Around line 155-157: The test-integration job currently only depends on build
(job name: test-integration), so it still runs even if earlier fast checks like
verify-mocks or lint fail; update the job's needs list to include those
fast-check jobs (e.g., add verify-mocks and lint to the needs array for
test-integration) so GitHub Actions will gate integration tests on their success
and avoid wasted CI runtime.

In @.github/workflows/validate/docs.yml:
- Around line 18-19: The checkout step named "📥 Checkout Code" (uses:
actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5) should disable
credential persistence by adding the key persist-credentials: false to that
step; update the step configuration to include persist-credentials: false so the
job does not automatically expose the checkout token to subsequent steps.

In @.github/workflows/validate/e2e.yml:
- Around line 237-245: The script currently continues when SAMPLE_APP_ID is
empty which causes downstream opaque failures; update the if branch that checks
SAMPLE_APP_ID to fail fast by writing a clear failure message, optionally
printing APPS_RESPONSE for debugging, setting sample_app_id in GITHUB_OUTPUT to
an empty or omitted value, and immediately exiting with a non-zero status (exit
1); target the conditional that references SAMPLE_APP_ID, APPS_RESPONSE and
GITHUB_OUTPUT and replace the current warning branch with the error log + exit 1
so the workflow stops early when Sample App is missing.

In @.github/workflows/validate/sdks.yml:
- Around line 18-19: The checkout step "📥 Checkout Code" currently uses
actions/checkout@... without disabling credential persistence; update the
checkout step (the step named "📥 Checkout Code" that uses actions/checkout) to
add persist-credentials: false so the runner does not store tokens in the local
git config for subsequent steps.

In @.github/workflows/validate/tools.yml:
- Around line 19-20: The GitHub Actions checkout steps (the step named "📥
Checkout Code" and the other checkout step using
actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5) must be hardened by
adding persist-credentials: false to each step; update both checkout step
definitions to include the persist-credentials: false key under their step
configuration to prevent the runner from persisting the checkout token into
subsequent shell steps.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 84857f0f-475b-4a00-b59a-18565ae7ec83

📥 Commits

Reviewing files that changed from the base of the PR and between 07cfd84 and 90662bd.

📒 Files selected for processing (8)
  • .github/workflows/pr-builder.yml
  • .github/workflows/validate/backend.yml
  • .github/workflows/validate/docs.yml
  • .github/workflows/validate/e2e.yml
  • .github/workflows/validate/frontend.yml
  • .github/workflows/validate/sdks.yml
  • .github/workflows/validate/tools.yml
  • build.ps1

Comment thread .github/workflows/pr-builder.yml Outdated
Comment on lines +154 to +159
validate-e2e:
name: 🎭 E2E
needs: detect-changes
if: ${{ needs.detect-changes.outputs.backend == 'true' || needs.detect-changes.outputs.frontend == 'true' || needs.detect-changes.outputs.e2e == 'true' }}
uses: ./.github/workflows/validate/e2e.yml
secrets: inherit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify which secrets are actually referenced by the reusable E2E workflow.
rg -n '\${{\s*secrets\.' .github/workflows/validate/e2e.yml

Repository: thunder-id/thunderid

Length of output: 166


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/validate/e2e.yml"

echo "== File =="
ls -la "$FILE"

echo
echo "== Literal references to reusable secrets (e.g. \${{ secrets.NAME }} ) =="
rg -nF '${{ secrets.' "$FILE" -C 2 || true

echo
echo "== All occurrences of 'secrets.' in the file (broader pattern) =="
rg -n 'secrets\.' "$FILE" -C 1 || true

echo
echo "== Extract unique secret names referenced =="
python3 - <<'PY'
import re, pathlib
p = pathlib.Path(".github/workflows/validate/e2e.yml")
txt = p.read_text()
names = sorted(set(re.findall(r"\$\{\{\s*secrets\.([A-Za-z0-9_]+)\s*\}\}", txt)))
print("\n".join(names) if names else "(none found)")
PY

Repository: thunder-id/thunderid

Length of output: 2259


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/validate/e2e.yml"
python3 - <<'PY'
import re, pathlib
txt = pathlib.Path("$FILE").read_text()
# Match patterns like: ${{ secrets.NAME || ... }} and ${ { secrets.NAME }}
names = sorted(set(re.findall(r"\$\{\{\s*secrets\.([A-Za-z0-9_]+)", txt)))
print("\n".join(names) if names else "(none found)")
PY

Repository: thunder-id/thunderid

Length of output: 596


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE_E2E=".github/workflows/validate/e2e.yml"
FILE_PR=".github/workflows/pr-builder.yml"

echo "== Reusable workflow secrets references (unique names) =="
python3 - <<PY
import re, pathlib
txt = pathlib.Path("${FILE_E2E}").read_text()
names = sorted(set(re.findall(r"\$\{\{\s*secrets\.([A-Za-z0-9_]+)", txt)))
print("\n".join(names) if names else "(none found)")
PY

echo
echo "== Literal references to reusable secrets in e2e.yml =="
rg -nF '${{ secrets.' "$FILE_E2E" -C 1

echo
echo "== Confirm pr-builder.yml around validate-e2e =="
rg -n 'validate-e2e:' "$FILE_PR" -n -C 3

Repository: thunder-id/thunderid

Length of output: 1414


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE_E2E=".github/workflows/validate/e2e.yml"
FILE_PR=".github/workflows/pr-builder.yml"

echo "== All literal occurrences of \${{ secrets. in e2e.yml =="
rg -nF '${{ secrets.' "$FILE_E2E"

echo
echo "== pr-builder.yml validate-e2e job secrets forwarding =="
rg -n "validate-e2e:|secrets:" "$FILE_PR" -n -C 2

Repository: thunder-id/thunderid

Length of output: 1220


Scope E2E secrets instead of inheriting all secrets.

File: .github/workflows/pr-builder.yml (lines 154-159)

  validate-e2e:
    name: 🎭 E2E
    needs: detect-changes
    if: ${{ needs.detect-changes.outputs.backend == 'true' || needs.detect-changes.outputs.frontend == 'true' || needs.detect-changes.outputs.e2e == 'true' }}
    uses: ./.github/workflows/validate/e2e.yml
    secrets: inherit

secrets: inherit (line 159) forwards every available secret to the reusable E2E workflow; that workflow only consumes: PLAYWRIGHT_BASE_URL, PLAYWRIGHT_ADMIN_USERNAME, PLAYWRIGHT_ADMIN_PASSWORD, PLAYWRIGHT_TEST_USER_USERNAME, PLAYWRIGHT_TEST_USER_PASSWORD, SAMPLE_APP_USERNAME, SAMPLE_APP_PASSWORD. Prefer explicit secret mapping for least privilege.

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 154-159: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)


[warning] 158-158: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow

(secrets-inherit)

🤖 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/pr-builder.yml around lines 154 - 159, Replace the broad
secrets: inherit on the validate-e2e job with an explicit secrets mapping to
only forward the required E2E secrets; in the validate-e2e job (the job named
"validate-e2e" that uses ./.github/workflows/validate/e2e.yml) map each secret
individually (PLAYWRIGHT_BASE_URL, PLAYWRIGHT_ADMIN_USERNAME,
PLAYWRIGHT_ADMIN_PASSWORD, PLAYWRIGHT_TEST_USER_USERNAME,
PLAYWRIGHT_TEST_USER_PASSWORD, SAMPLE_APP_USERNAME, SAMPLE_APP_PASSWORD) so the
reusable workflow receives only those values instead of inheriting all
repository secrets.

Comment thread .github/workflows/validate/backend.yml Outdated
Comment on lines +22 to +23
- name: 📥 Checkout Code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden checkout by disabling credential persistence.

These jobs execute repository code after checkout; keeping Git auth in local config is avoidable exposure.

Suggested fix
       - name: 📥 Checkout Code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false

Also applies to: 57-58, 74-75, 188-189

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 22-23: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 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/validate/backend.yml around lines 22 - 23, The checkout
steps currently use actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
without disabling credential persistence; update each checkout step (the ones
using actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 and the other
checkout occurrences) to set persist-credentials: false so Git authentication
tokens are not stored in the workspace Git config, ensuring each checkout step
in the workflow includes that option.

Comment thread .github/workflows/validate/backend.yml Outdated
if-no-files-found: error

- name: 📊 Upload Unit Test Coverage Report to Codecov
uses: codecov/codecov-action@v5

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and inspect the content around lines 137 and 232
wc -l .github/workflows/validate/backend.yml

Repository: thunder-id/thunderid

Length of output: 107


🏁 Script executed:

# Read the specific lines mentioned in the review
sed -n '130,145p' .github/workflows/validate/backend.yml
sed -n '225,240p' .github/workflows/validate/backend.yml

Repository: thunder-id/thunderid

Length of output: 1234


🏁 Script executed:

# Search for all occurrences of codecov-action in the file to understand context
rg 'codecov-action' .github/workflows/validate/backend.yml -B 2 -A 2

Repository: thunder-id/thunderid

Length of output: 396


🌐 Web query:

codecov codecov-action v5 release full commit SHA github

💡 Result:

For codecov/codecov-action version v5 (e.g., the v5.0.0 release), the release tag v5.0.0 corresponds to the commit full SHA: - v5.0.0 full commit SHA: 1ea6f4f4f6a5a0d6c7e4d6f0e3c3d5c7c1e2d3f4 [1] Notes: - The official GitHub releases page for tag v5.0.0 is the authoritative place to map the tag to the underlying commit. [1] If you meant a different v5.x.y release (e.g., v5.4.0), tell me the exact tag and I’ll return its full commit SHA as well.

Citations:


Pin codecov/codecov-action to a full commit SHA instead of @v5.

Version tags like @v5 are mutable and can change over time, weakening supply-chain guarantees in CI. Pin both occurrences (lines 137 and 232) to the full commit SHA of the v5 release you intend to use. For example, v5.0.0 resolves to commit 1ea6f4f4f6a5a0d6c7e4d6f0e3c3d5c7c1e2d3f4. Check the codecov-action releases page for the current v5 release and its corresponding commit SHA.

Suggested fix
-        uses: codecov/codecov-action@v5
+        uses: codecov/codecov-action@<full_commit_sha>

(Apply to both line 137 and line 232)

📝 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.

Suggested change
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@<actual_commit_sha_here>
🧰 Tools
🪛 zizmor (1.25.2)

[error] 137-137: 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/validate/backend.yml at line 137, Replace the mutable tag
uses: codecov/codecov-action@v5 with the full commit SHA for the v5 release you
intend to use (pin both occurrences of the action found in the workflow). Locate
the two occurrences of "uses: codecov/codecov-action@v5" and update them to
"uses: codecov/codecov-action@<full-commit-sha>" using the commit SHA from the
codecov-action v5 release page so the workflow references an immutable commit.

Comment thread .github/workflows/validate/backend.yml Outdated
fail-fast: false
services:
postgres:
image: postgres:latest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/validate/backend.yml"
echo "== Checking file exists =="
ls -la "$FILE"

echo
echo "== Showing lines around 160-190 =="
nl -ba "$FILE" | sed -n '140,210p'

echo
echo "== Searching for ':latest' in the workflow =="
rg -n --fixed-string ":latest" "$FILE" || true

echo
echo "== Searching for postgres/redis image references =="
rg -n "image:\s*(postgres|redis):" "$FILE" || true

Repository: thunder-id/thunderid

Length of output: 277


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/validate/backend.yml"

echo "== Showing lines around 160-190 (with line numbers via awk) =="
awk 'NR>=140 && NR<=210 {printf "%d\t%s\n", NR, $0}' "$FILE"

echo
echo "== Searching for ':latest' in the workflow =="
rg -n --fixed-string ":latest" "$FILE" || true

echo
echo "== Searching for postgres/redis image references =="
rg -n "image:\s*(postgres|redis):" "$FILE" || true

Repository: thunder-id/thunderid

Length of output: 3124


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/validate/backend.yml"

echo "== Searching for all ':latest' occurrences in the workflow =="
rg -n ":latest" "$FILE" || true

echo
echo "== Showing exact service blocks for postgres/redis (lines 160-190) =="
awk 'NR>=155 && NR<=190 {printf "%d\t%s\n", NR, $0}' "$FILE"

Repository: thunder-id/thunderid

Length of output: 1359


Pin Postgres/Redis service images in integration tests (avoid :latest).

.github/workflows/validate/backend.yml uses floating tags at lines 166 (postgres:latest) and 179 (redis:latest), which can make CI nondeterministic as upstream images change.

Suggested fix
-        image: postgres:latest
+        image: postgres:<version>`@sha256`:<digest>
...
-        image: redis:latest
+        image: redis:<version>`@sha256`:<digest>
🧰 Tools
🪛 zizmor (1.25.2)

[error] 166-166: unpinned image references (unpinned-images): container image is pinned to latest

(unpinned-images)

🤖 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/validate/backend.yml at line 166, The CI uses floating
service image tags "image: postgres:latest" and "image: redis:latest" which
makes tests nondeterministic; update those service image entries (the lines
containing "image: postgres:latest" and "image: redis:latest") to pinned,
explicit versions or digests (for example a specific major/minor tag like
postgres:15-alpine or a sha256 digest) so the workflow uses a stable,
reproducible image; ensure both occurrences are updated and consider documenting
the chosen versions in the workflow comments.

Comment thread .github/workflows/validate/e2e.yml Outdated
Comment on lines +21 to +22
- name: 📥 Checkout Code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show the relevant parts of the workflow with line numbers
FILE=".github/workflows/validate/e2e.yml"
echo "== File: $FILE =="
if [ -f "$FILE" ]; then
  nl -ba "$FILE" | sed -n '1,220p'
else
  echo "Missing: $FILE"
  exit 1
fi

# 2) Find all checkout steps using the pinned v4 SHA in this repo (and confirm if persist-credentials is already set)
echo
echo "== All actions/checkout occurrences with the pinned SHA in .github/workflows =="
rg -n "actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5" .github/workflows -S || true

echo
echo "== Any checkout steps in this file that already set persist-credentials =="
rg -n "actions/checkout@|persist-credentials" "$FILE" -S || true

# 3) Check for any git operations that might require credentials/token after checkout
echo
echo "== git push / auth / GITHUB_TOKEN usage in this file =="
rg -n "git push|GITHUB_TOKEN|persist-credentials|checkout.*token|token:" "$FILE" -S || true

Repository: thunder-id/thunderid

Length of output: 172


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/validate/e2e.yml"
echo "== File: $FILE =="

if [ ! -f "$FILE" ]; then
  echo "Missing: $FILE"
  exit 1
fi

# Show enough of the workflow to include all checkout steps and nearby context
cat -n "$FILE" | sed -n '1,220p'

echo
echo "== All actions/checkout occurrences with the pinned SHA in .github/workflows =="
rg -n "actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5" .github/workflows -S || true

echo
echo "== Any checkout steps in this file that already set persist-credentials =="
rg -n "persist-credentials" "$FILE" -S || true
rg -n "uses: actions/checkout@" "$FILE" -S || true

echo
echo "== Any git push / auth / GITHUB_TOKEN usage in this file =="
rg -n "git push|GITHUB_TOKEN|persist-credentials|checkout.*token|token:" "$FILE" -S || true

Repository: thunder-id/thunderid

Length of output: 16148


🌐 Web query:

actions/checkout persist-credentials default value

💡 Result:

The default value for the persist-credentials input in the actions/checkout GitHub Action is true [1][2][3]. When this setting is enabled (the default), the action configures the repository's local Git configuration to include the authentication token or SSH key used for the checkout [1][4]. This allows subsequent steps in your workflow to execute authenticated Git commands (such as git fetch or git push) automatically [1][3]. The action is designed to remove these credentials from the Git configuration during post-job cleanup [1][4]. If you wish to prevent the action from configuring these credentials in the local Git configuration, you can explicitly set persist-credentials to false [1][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/validate/e2e.yml"

echo "== Line count =="
wc -l "$FILE"

echo
echo "== actions/checkout@... occurrences in e2e.yml =="
rg -n "uses:\s*actions/checkout@" "$FILE" -S

echo
echo "== persist-credentials occurrences in e2e.yml =="
rg -n "persist-credentials" "$FILE" -S || true

Repository: thunder-id/thunderid

Length of output: 462


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/validate/e2e.yml"

echo "== git-related commands in e2e.yml =="
rg -n "(^|\s)git\s+(push|fetch|pull|clone|config)|GITHUB_TOKEN|token:" "$FILE" -S || true

echo
echo "== persist-credentials in the repo (to see if any pattern exists) =="
rg -n "persist-credentials\s*:\s*(false|true)" .github/workflows -S || true

Repository: thunder-id/thunderid

Length of output: 172


Disable credential persistence for all actions/checkout steps in .github/workflows/validate/e2e.yml

.github/workflows/validate/e2e.yml uses actions/checkout@... # v4 in three places without persist-credentials, so the action falls back to its default (persist-credentials: true). Adding persist-credentials: false reduces how long the workflow token is configured in the runner’s local git settings while the job executes repo-provided commands.

🔒 Proposed fix
       - name: 📥 Checkout Code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false
@@
       - name: 📥 Checkout Code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false
@@
       - name: 📥 Checkout Code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false

Also applies to: 88-89, 116-117

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 21-22: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 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/validate/e2e.yml around lines 21 - 22, Three checkout
steps currently use actions/checkout@... without disabling credential
persistence; update each checkout step (the ones using
actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5) to include
persist-credentials: false so the workflow token is not left in the runner’s git
config for the job duration; locate the three occurrences (around the blocks at
the earlier, middle, and later checkout steps referenced in the diff) and add
the persist-credentials: false key under each checkout step's configuration.

Comment thread .github/workflows/validate/e2e.yml Outdated
Comment on lines +164 to +175
if curl -s -k https://localhost:8090/health/liveness > /dev/null; then
echo "Server is UP!"
break
fi
echo "Still waiting ($i/60)..."
sleep 2
done

if ! curl -s -k https://localhost:8090/health/liveness > /dev/null; then
echo "Server failed to start within 2 minutes"
exit 1
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/validate/e2e.yml"

echo "== File exists? =="
ls -la "$FILE" || true

echo
echo "== Snippet around lines 150-190 =="
nl -ba "$FILE" | sed -n '150,190p'

echo
echo "== Snippet around lines 250-300 (covers 266-277) =="
nl -ba "$FILE" | sed -n '250,300p'

echo
echo "== Snippet around lines 300-340 (covers 309-320) =="
nl -ba "$FILE" | sed -n '300,340p'

echo
echo "== Find all curl health/liveness probes =="
rg -n --fixed-string "health/liveness" "$FILE" || true

echo
echo "== Find all curl probes to localhost:3000 (if any) =="
rg -n --fixed-string "https://localhost:3000" "$FILE" || true

Repository: thunder-id/thunderid

Length of output: 266


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/validate/e2e.yml"

echo "== Snippet around lines 150-190 =="
awk 'NR>=150 && NR<=190 {printf "%5d  %s\n", NR, $0}' "$FILE"

echo
echo "== Snippet around lines 250-300 (covers 266-277) =="
awk 'NR>=250 && NR<=300 {printf "%5d  %s\n", NR, $0}' "$FILE"

echo
echo "== Snippet around lines 300-340 (covers 309-320) =="
awk 'NR>=300 && NR<=340 {printf "%5d  %s\n", NR, $0}' "$FILE"

echo
echo "== Find all curl health/liveness probes =="
rg -n "curl .*health/liveness" "$FILE" || true

echo
echo "== Find all curl probes to localhost:3000 =="
rg -n "curl .*https://localhost:3000" "$FILE" || true

echo
echo "== Check whether any uses curl -sSf anywhere in this workflow file =="
rg -n "curl .* -sSf" "$FILE" || true

Repository: thunder-id/thunderid

Length of output: 6906


Fail readiness probes on HTTP 4xx/5xx

Current probes use curl -s -k without -f, so HTTP error responses (4xx/5xx) can still satisfy the if condition and let the workflow proceed before the service is actually ready.

File: .github/workflows/validate/e2e.yml
Lines: 164-175

            if curl -s -k https://localhost:8090/health/liveness > /dev/null; then
              echo "Server is UP!"
              break
            fi
            echo "Still waiting ($i/60)..."
            sleep 2
          done
          
          if ! curl -s -k https://localhost:8090/health/liveness > /dev/null; then
            echo "Server failed to start within 2 minutes"
            exit 1
          fi

Also applies to: 266-277, 309-320

✅ Proposed fix
-            if curl -s -k https://localhost:8090/health/liveness > /dev/null; then
+            if curl -sSf -k https://localhost:8090/health/liveness > /dev/null; then
@@
-          if ! curl -s -k https://localhost:8090/health/liveness > /dev/null; then
+          if ! curl -sSf -k https://localhost:8090/health/liveness > /dev/null; then
@@
-            if curl -s -k https://localhost:8090/health/liveness > /dev/null; then
+            if curl -sSf -k https://localhost:8090/health/liveness > /dev/null; then
@@
-          if ! curl -s -k https://localhost:8090/health/liveness > /dev/null; then
+          if ! curl -sSf -k https://localhost:8090/health/liveness > /dev/null; then
@@
-            if curl -s -k https://localhost:3000 > /dev/null 2>&1; then
+            if curl -sSf -k https://localhost:3000 > /dev/null 2>&1; then
@@
-          if ! curl -s -k https://localhost:3000 > /dev/null 2>&1; then
+          if ! curl -sSf -k https://localhost:3000 > /dev/null 2>&1; then
🤖 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/validate/e2e.yml around lines 164 - 175, The readiness
liveness probe uses curl without the --fail flag so HTTP 4xx/5xx responses still
count as success; update each curl invocation that checks
https://localhost:8090/health/liveness (the two blocks around the initial wait
loop and the later checks referenced in the comment) to include --fail (or -f)
and -s -k (e.g., curl -sf -k ...) so curl exits non-zero on HTTP error
responses, and apply the same change to the other occurrences noted (lines
referenced as the blocks at 164-175, 266-277, 309-320) to ensure the workflow
fails when the service returns 4xx/5xx.

Comment thread .github/workflows/validate/frontend.yml Outdated
Comment on lines +22 to +23
- name: 📥 Checkout Code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Set persist-credentials: false on checkout steps.

All these jobs run project scripts after checkout; disabling persisted git credentials is a safer default.

Suggested fix
       - name: 📥 Checkout Code
         uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false

Also applies to: 71-74, 107-108, 143-144, 185-186, 229-230

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 22-23: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 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/validate/frontend.yml around lines 22 - 23, Update each
GitHub Actions checkout step that uses actions/checkout@... (e.g., the steps
named "📥 Checkout Code" and the other checkout occurrences) to include
persist-credentials: false in the step's with: block so credentials are not
persisted into the workspace; locate the checkout steps by the uses:
actions/checkout@... entries and add a with: persist-credentials: false for each
occurrence noted in the review.

Comment thread .github/workflows/validate/frontend.yml Outdated
Comment on lines +44 to +57
AUDIT_RESULT=$(npm audit --json --audit-level=high 2>&1) || true
REMAINING=$(echo "$AUDIT_RESULT" | jq --argjson ignored "$IGNORED_GHSAS" '
[.vulnerabilities[].via[] | select(type == "object")
| select(.severity == "high" or .severity == "critical")
| select(.url | test($ignored | join("|")) | not)
] | length
')
if [ "$REMAINING" -gt 0 ]; then
echo "❌ Found $REMAINING unignored high/critical advisories in $app:"
echo "$AUDIT_RESULT" | jq --argjson ignored "$IGNORED_GHSAS" '
[.vulnerabilities[].via[] | select(type == "object")
| select(.severity == "high" or .severity == "critical")
| select(.url | test($ignored | join("|")) | not)
] | unique_by(.url) | .[] | "\(.severity): \(.name) - \(.url)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the workflow file around the referenced lines
FILE=".github/workflows/validate/frontend.yml"
echo "== File: $FILE =="
python3 - <<'PY'
import itertools
file_path=".github/workflows/validate/frontend.yml"
start=1
end=200
with open(file_path,'r',encoding='utf-8') as f:
    for i,line in enumerate(f,1):
        if 40 <= i <= 120:
            print(f"{i:4d}: {line.rstrip()}")
        if i>120:
            break
PY

# Extract any other jq/npm audit usage in the repo (to understand broader context)
echo
echo "== Search for npm audit --json usage =="
rg -n "npm audit --json" .github/workflows || true

echo
echo "== Search for jq parsing of .vulnerabilities[].via[] =="
rg -n "\\.vulnerabilities\\[\\]\\.via\\[\\]" .github/workflows || true

Repository: thunder-id/thunderid

Length of output: 4010


🏁 Script executed:

#!/bin/bash
set -euo pipefail

JQ_EXPR='
  [.vulnerabilities[].via[] | select(type == "object")
   | select(.severity == "high" or .severity == "critical")
   | select(.url | test($ignored | join("|")) | not)
  ] | length
'

# Reuse same jq invocation style as the workflow
JQ_BASE=(jq --argjson ignored '["ghsa-ignored-example"]' "$JQ_EXPR")

echo "== Case 1: Valid JSON with empty vulnerabilities =="
payload='{"vulnerabilities":{}}'
printf '%s\n' "$payload" | "${JQ_BASE[@]}"
echo "exit=$?"

echo "== Case 2: Missing vulnerabilities key =="
payload='{"meta":{}}'
printf '%s\n' "$payload" | "${JQ_BASE[@]}"
echo "exit=$?"

echo "== Case 3: vulnerabilities is null =="
payload='{"vulnerabilities":null}'
printf '%s\n' "$payload" | "${JQ_BASE[@]}"
echo "exit=$?"

echo "== Case 4: via is missing/empty =="
payload='{"vulnerabilities":{"a":{"via":[]}}}'
printf '%s\n' "$payload" | "${JQ_BASE[@]}"
echo "exit=$?"

echo "== Case 5: via is a string (shape mismatch) =="
payload='{"vulnerabilities":{"a":{"via":"not-an-array"}}}'
printf '%s\n' "$payload" | "${JQ_BASE[@]}"
echo "exit=$?"

echo "== Case 6: Invalid JSON (npm audit error text) =="
payload='npm audit failed'
# show jq exit code without stopping
set +e
printf '%s\n' "$payload" | "${JQ_BASE[@]}"
echo "jq_exit=$?"
set -e

echo "== Case 7: via contains object(s) with high/critical but url missing =="
payload='{"vulnerabilities":{"a":{"via":[{"severity":"high","name":"x"}]}}}'
printf '%s\n' "$payload" | "${JQ_BASE[@]}"
echo "exit=$?"

Repository: thunder-id/thunderid

Length of output: 243


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/validate/frontend.yml"

echo "== Show relevant workflow context around the audit loop =="
python3 - <<'PY'
import itertools
path=".github/workflows/validate/frontend.yml"
start=1
end=90
with open(path,'r',encoding='utf-8') as f:
    for i,line in enumerate(f,1):
        if 25 <= i <= 80:
            print(f"{i:4d}: {line.rstrip()}")
PY

echo
echo "== Re-run jq shape tests, capturing exit codes without aborting =="
JQ_EXPR='
  [.vulnerabilities[].via[] | select(type == "object")
   | select(.severity == "high" or .severity == "critical")
   | select(.url | test($ignored | join(\"|\")) | not)
  ] | length
'
IGNORED='["ghsa-ignored-example"]'

run_case () {
  local label="$1"
  local payload="$2"
  echo
  echo "-- $label --"
  set +e
  printf '%s\n' "$payload" | jq --argjson ignored "$IGNORED" "$JQ_EXPR"
  local code=$?
  echo "exit=$code"
  set -e
}

run_case "Case 1: valid JSON with empty vulnerabilities" '{"vulnerabilities":{}}'
run_case "Case 2: missing vulnerabilities key" '{"meta":{}}'
run_case "Case 3: vulnerabilities is null" '{"vulnerabilities":null}'
run_case "Case 4: via is []" '{"vulnerabilities":{"a":{"via":[]}}}'
run_case "Case 5: via is string (shape mismatch)" '{"vulnerabilities":{"a":{"via":"not-an-array"}}}'
run_case "Case 6: via contains object with high but url missing" '{"vulnerabilities":{"a":{"via":[{"severity":"high","name":"x"}]}}}'

echo
echo "== Invalid npm audit text (not JSON) -> jq behavior =="
set +e
printf '%s\n' "npm audit failed" | jq --argjson ignored "$IGNORED" "$JQ_EXPR" >/dev/null
echo "exit=$?"
set -e

Repository: thunder-id/thunderid

Length of output: 4763


🏁 Script executed:

#!/bin/bash
set -euo pipefail

IGNORED='["ghsa-ignored-example"]'

# Exact jq program from workflow (no extra escaping)
JQ_PROG='
  [.vulnerabilities[].via[] | select(type == "object")
   | select(.severity == "high" or .severity == "critical")
   | select(.url | test($ignored | join("|")) | not)
  ] | length
'

echo "== jq exit codes for shape variants (standalone) =="
run_jq () {
  local label="$1"
  local payload="$2"
  echo
  echo "-- $label --"
  set +e
  out="$(printf '%s\n' "$payload" | jq --argjson ignored "$IGNORED" "$JQ_PROG" 2>&1)"
  code=$?
  printf '%s\n' "$out"
  echo "exit=$code"
  set -e
}

run_jq "Case 1: vulnerabilities = {}" '{"vulnerabilities":{}}'
run_jq "Case 2: vulnerabilities missing" '{"meta":{}}'
run_jq "Case 3: vulnerabilities = null" '{"vulnerabilities":null}'
run_jq "Case 4: via = []" '{"vulnerabilities":{"a":{"via":[]}}}'
run_jq "Case 5: via = not array" '{"vulnerabilities":{"a":{"via":"not-an-array"}}}'
run_jq "Case 6: high/critical via object but missing url" '{"vulnerabilities":{"a":{"via":[{"severity":"high","name":"x"}]}}}'
run_jq "Case 7: invalid JSON text" 'npm audit failed'

echo
echo "== bash -e behavior when jq fails in command substitution =="
set +e
set -e

test_script () {
  local payload="$1"
  echo
  echo "-- payload: $payload --"
  set +e
  # emulates: REMAINING=$(echo "$AUDIT_RESULT" | jq ... )
  REMAINING=$(printf '%s\n' "$payload" | jq --argjson ignored "$IGNORED" "$JQ_PROG" )
  jq_code=$?
  echo "jq_exit=$jq_code REMAINING='${REMAINING}'"
  set -e
  echo "script continued"
}

set +e
test_script '{"vulnerabilities":{}}'
test_script '{"meta":{}}'
test_script '{"vulnerabilities":null}'
test_script '{"vulnerabilities":{"a":{"via":"not-an-array"}}}'
test_script 'npm audit failed'

set -e
echo
echo "done"

Repository: thunder-id/thunderid

Length of output: 1468


🏁 Script executed:

#!/bin/bash
set -e

echo "case: REMAINING empty"
REMAINING=''
if [ "$REMAINING" -gt 0 ]; then
  echo "in if"
else
  echo "in else (no exit)"
fi
echo "after if (should only print if not exited)"

Repository: thunder-id/thunderid

Length of output: 217


Fix sample-app npm audit parsing so jq failures don’t lead to a false “all clear”
In .github/workflows/validate/frontend.yml (lines ~44-60), REMAINING=$(echo "$AUDIT_RESULT" | jq ...) doesn’t handle jq parse/shape errors. When jq fails, REMAINING ends up empty; the subsequent [ "$REMAINING" -gt 0 ] doesn’t reliably stop the step, so the job can incorrectly print the ✅ success message (false pass).

Suggested fix
-              AUDIT_RESULT=$(npm audit --json --audit-level=high 2>&1) || true
-              REMAINING=$(echo "$AUDIT_RESULT" | jq --argjson ignored "$IGNORED_GHSAS" '
-                [.vulnerabilities[].via[] | select(type == "object")
-                 | select(.severity == "high" or .severity == "critical")
-                 | select(.url | test($ignored | join("|")) | not)
-                ] | length
-              ')
+              AUDIT_RESULT=$(npm audit --json --audit-level=high 2>&1 || true)
+              REMAINING=$(echo "$AUDIT_RESULT" | jq --argjson ignored "$IGNORED_GHSAS" -r '
+                def expected_vulns:
+                  if (.vulnerabilities | type) == "object" then .vulnerabilities
+                  else error("unexpected npm audit JSON: missing/invalid .vulnerabilities")
+                  end;
+
+                [
+                  (expected_vulns | to_entries[] | .value
+                    | if (.via | type) == "array" then .via[] else empty end
+                  )
+                  | select(type == "object")
+                  | select((.severity == "high") or (.severity == "critical"))
+                  | select((.url // "") | test($ignored | join("|")) | not)
+                ] | length
+              ') || {
+                echo "❌ Unable to parse npm audit JSON for $app"
+                echo "$AUDIT_RESULT"
+                exit 1
+              }
🤖 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/validate/frontend.yml around lines 44 - 57, The jq
pipeline that computes REMAINING from AUDIT_RESULT can fail and produce an
empty/invalid REMAINING, so change the logic around the jq invocation that
references AUDIT_RESULT and IGNORE_GHSAS to explicitly check jq’s exit status
(or use jq -e) and treat a jq failure as an error: capture the jq exit code
after the REMAINING assignment and if non-zero either exit with a non-zero
status (fail the job) or set REMAINING to a safe numeric value and then fail;
ensure the code paths around REMAINING (the subsequent [ "$REMAINING" -gt 0 ]
test and the error reporting that prints the filtered advisories) only run when
jq succeeded so a jq parse/shape error cannot produce a false "all clear".

Comment thread .github/workflows/validate/frontend.yml Outdated
path: coverage/gate

- name: 📊 Upload `@thunderid/console` Unit Test Coverage Report to Codecov
uses: codecov/codecov-action@v5

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/validate/frontend.yml"

echo "== Exists? =="
ls -l "$FILE" || exit 1

echo
echo "== Matching codecov action usage =="
rg -n --hidden --no-ignore-vcs "uses:\s*codecov/codecov-action@" "$FILE" || true

echo
echo "== Show lines around 245 and 257 =="
nl -ba "$FILE" | sed -n '230,270p'

Repository: thunder-id/thunderid

Length of output: 391


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/validate/frontend.yml"

echo "== Show lines around 245 and 257 =="
# Prefer cat -n; fallback to sed+grep for line numbers if needed.
if command -v cat >/dev/null 2>&1 && cat -n "$FILE" >/dev/null 2>&1; then
  cat -n "$FILE" | sed -n '235,265p'
else
  sed -n '235,265p' "$FILE" | nl -ba -w2 -s': ' || true
fi

echo
echo "== Full matching lines (with context) =="
rg -n "uses:\s*codecov/codecov-action@" "$FILE" -n -C 3

Repository: thunder-id/thunderid

Length of output: 1945


🌐 Web query:

codecov/codecov-action v5 full_commit_sha

💡 Result:

To set Codecov’s “commit SHA” used by codecov/codecov-action v5, you use the action input override_commit (not full_commit_sha). In action v5, override_commit is defined as: “Commit SHA (with 40 chars)” [1]. The repository input table also lists override_commit as an optional input with that description [2]. Example: - uses: codecov/codecov-action@v5 with: override_commit: ${{ github.sha }} Related (parent SHA) input: commit_parent lets you override the parent SHA: “SHA (with 40 chars) of what should be the parent of this commit.” [3][2]. If you saw “wrong commit sha” behavior, there is an open issue reporting that Action v5 may consider a different SHA than expected even without setting commit_parent or override_commit [4]. So, for your query “codecov/codecov-action v5 full_commit_sha”, the authoritative answer from the v5 action definition is: there is no documented input named full_commit_sha; the documented input to override the commit SHA is override_commit. [1][2]

Citations:


🌐 Web query:

codecov/codecov-action tag v5 commit SHA

💡 Result:

The v5.0.0 release of codecov/codecov-action is associated with the commit SHA 2a61338c201d4a0468305041048b25d48797f748 [1]. While you can reference tags like v5 in your GitHub Actions workflow file (e.g., uses: codecov/codecov-action@v5), it is considered a best practice to pin your actions to a specific commit SHA to ensure immutability and security [2]. If you intend to pin your workflow to the initial v5.0.0 release, you should use the SHA provided above [1]. For later versions within the v5 release line, you can find the corresponding commit SHAs by viewing the repository's tags or release history on GitHub [3]. Note that the v5 series has been succeeded by v6.0.0 and later versions as of 2026 [4][5].

Citations:


🌐 Web query:

https://github.com/codecov/codecov-action/tree/v5 commit SHA

💡 Result:

The codecov/codecov-action repository does not have a single, static commit SHA for all of v5 because v5 is a major version (or "tag") that receives updates [1][2]. GitHub Action tags are mutable references; they point to the latest commit associated with that specific tag version [3][4]. To find the specific commit SHA for a v5 release: 1. Visit the official GitHub repository releases page for codecov-action at https://github.com/codecov/codecov-action/releases [1]. 2. Locate the specific v5 release (e.g., v5.0.0, v5.5.2, etc.) [1][2]. 3. Click the tag link or the release commit link to view the associated full 40-character commit SHA [3][5]. Because v5 has evolved through multiple patch releases (e.g., v5.0.1, v5.0.7, v5.5.2) [6][7][2], the commit SHA changes whenever the maintainers update the v5 tag [3]. If you require immutability for security or consistency, you should identify the exact SHA of the specific v5 release version you intend to use and pin your workflow to that 40-character identifier [3][4][5]. For example, an issue recorded in the repository shows a user pinning to a specific v5 SHA: codecov/codecov-action@9688725 [8]. Note that this specific SHA corresponds to an earlier point in the v5 lifecycle and may not represent the current latest v5 release [8][2]. You can verify the current SHA for any tag by using the GitHub CLI command: gh api repos/codecov/codecov-action/git/refs/tags/v5.x.x --jq '.object.sha' [3].

Citations:


Pin codecov/codecov-action to an immutable commit SHA

.github/workflows/validate/frontend.yml uses the mutable tag codecov/codecov-action@v5 for both uploads (lines 245 and 257). Pin to a specific 40-character commit SHA (for the exact v5.x.y you want) instead of the floating v5 tag.

Suggested fix
-        uses: codecov/codecov-action@v5
+        uses: codecov/codecov-action@<40_char_commit_sha_for_v5_x_y>
...
-        uses: codecov/codecov-action@v5
+        uses: codecov/codecov-action@<40_char_commit_sha_for_v5_x_y>

Also applies to: 257-257

🧰 Tools
🪛 zizmor (1.25.2)

[error] 245-245: 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/validate/frontend.yml at line 245, Replace the mutable
action tag "uses: codecov/codecov-action@v5" with a specific 40-character commit
SHA for the v5 release (e.g. "uses: codecov/codecov-action@<COMMIT_SHA>")
wherever that tag appears (the two occurrences of the string "uses:
codecov/codecov-action@v5"); update both usages so the workflow pins to an
immutable commit SHA instead of the floating "v5" tag.

@brionmario

Copy link
Copy Markdown
Member

Hey @kavix,

Thanks a lot for the PR.
Have you tested this on your fork by any chance?

If not, lets trigger few PRs agains your forks main with different combinations to see if the pr-builder is behaving as expected.

@brionmario brionmario changed the title feat: implement modularized workflow validation and update build scripts Implement modularized workflow validation and update build scripts Jun 5, 2026
@kavix kavix closed this Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants