Skip to content

fix(lint): enforce SHA pinning for externals, warnings for internals#170

Merged
bedatty merged 7 commits intodevelopfrom
fix/pin-refs-v1.20.0-beta.1
Mar 25, 2026
Merged

fix(lint): enforce SHA pinning for externals, warnings for internals#170
bedatty merged 7 commits intodevelopfrom
fix/pin-refs-v1.20.0-beta.1

Conversation

@bedatty
Copy link
Contributor

@bedatty bedatty commented Mar 25, 2026

Lerian

GitHub Actions Shared Workflows


Description

Pin pr-validation composite refs to v1.20.0-beta.1 and overhaul the action pinning enforcement:

  • Pinned Actions Check: external actions must be pinned by commit SHA (not tag); internal (LerianStudio/) accepts any semver ref including pre-releases and develop/main branches
  • CodeQL Reporter: suppress actions/unpinned-tag findings from PR comments — handled by our own pinned-actions check with org-aware logic
  • PR Lint Reporter: surface warning-level annotations in a collapsible "⚠️ Warnings" section in the PR lint comment

Type of Change

  • feat: New workflow or new input/output/step in an existing workflow
  • fix: Bug fix in a workflow (incorrect behavior, broken step, wrong condition)
  • perf: Performance improvement (e.g. caching, parallelism, reduced steps)
  • refactor: Internal restructuring with no behavior change
  • docs: Documentation only (README, docs/, inline comments)
  • ci: Changes to self-CI (workflows under .github/workflows/ that run on this repo)
  • chore: Dependency bumps, config updates, maintenance
  • test: Adding or updating tests
  • BREAKING CHANGE: Callers must update their configuration after this PR

Breaking Changes

External actions pinned by tag (e.g., @v6) will now fail the Pinned Actions Check. They must be pinned by commit SHA with a version comment (e.g., @abc123 # v6). Internal (LerianStudio/) actions are unaffected.

Testing

  • YAML syntax validated locally
  • Triggered a real workflow run on a caller repository using @develop or the beta tag
  • Verified all existing inputs still work with default values
  • Confirmed no secrets or tokens are printed in logs
  • Checked that unrelated workflows are not affected

Caller repo / workflow run:

Related Issues

Summary by CodeRabbit

  • Chores

    • Updated validation and notification workflows to newer reusable action versions.
  • Bug Fixes

    • Internal action pin checks now accept semver-like prerelease and branch refs to reduce false warnings.
    • External action refs now require commit-SHA pinning to strengthen validation.
    • Security reporting suppresses an unpinned-action finding, changing reported issue counts and generated config.
  • New Features

    • PR lint reporter adds a dedicated warnings section that aggregates and displays warning annotations alongside failures.

@bedatty bedatty requested a review from a team as a code owner March 25, 2026 21:23
@coderabbitai
Copy link

coderabbitai bot commented Mar 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 16b6059e-8b65-420c-bf3e-3a4fc71afc1a

📥 Commits

Reviewing files that changed from the base of the PR and between 630865c and c8385d4.

📒 Files selected for processing (1)
  • src/lint/pinned-actions/action.yml

Walkthrough

Bumped reusable workflow refs in .github/workflows/pr-validation.yml; relaxed internal pinned-action ref validation and require commit SHAs for external actions; added suppression of actions/unpinned-tag findings in CodeQL report generation and config; extended PR lint reporter to include and group warning annotations alongside failures.

Changes

Cohort / File(s) Summary
Workflow refs
\.github/workflows/pr-validation.yml
Updated reusable workflow uses: refs from @v1.19.1-beta.2 to @v1.20.0-beta.1. Inputs, step ids, conditions, and job control flow unchanged.
Pinned-actions validation
src/lint/pinned-actions/action.yml
Relaxed internal ref acceptance to semver-like tags (v<major>[.<minor>[.<patch>][-suffix]) plus develop/main; changed external refs to require full commit SHA (40–64 lowercase hex). Updated messages and input descriptions.
CodeQL reporter
src/security/codeql-reporter/action.yml, src/security/codeql-config/action.yml
Pinned actions/github-script to a specific commit SHA; report generation now filters out actions/unpinned-tag via SUPPRESSED_RULES; CodeQL config now includes query-filters with an exclude for actions/unpinned-tag.
PR lint reporter — warnings added
src/notify/pr-lint-reporter/action.yml
Pinned actions/github-script to a commit SHA; expanded annotation fetch to include checks with result of failure or success; separated failure vs warning annotation maps and added a new collapsed ⚠️ Warnings (...) section grouping warning messages by file/line alongside existing failures.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: stricter SHA pinning for external actions and relaxed enforcement for internal ones, matching the core payload of the PR.
Description check ✅ Passed The description covers required sections: clear description of changes, correct type-of-change selection (feat and fix), explicit breaking change disclosure, and testing checklist completion. All essential information is present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pin-refs-v1.20.0-beta.1

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

@lerian-studio lerian-studio added size/XS PR changes < 50 lines workflow Changes to one or more reusable workflow files labels Mar 25, 2026
@lerian-studio
Copy link

lerian-studio commented Mar 25, 2026

🔍 Lint Analysis

Check Files Scanned Status
YAML Lint 5 file(s) ✅ success
Action Lint 1 file(s) ✅ success
Pinned Actions 5 file(s) ✅ success
Markdown Link Check no changes ⏭️ skipped
Spelling Check 5 file(s) ✅ success
Shell Check 5 file(s) ✅ success
README Check 5 file(s) ✅ success
Composite Schema 4 file(s) ✅ success

🔍 View full scan logs

@lerian-studio
Copy link

lerian-studio commented Mar 25, 2026

🛡️ CodeQL Analysis Results

Languages analyzed: actions

✅ No security issues found.


🔍 View full scan logs | 🛡️ Security tab

bedatty added 2 commits March 25, 2026 18:33
…unpinned-tag

Pinned Actions Check:
- External actions must be pinned by commit SHA (40-64 hex chars)
- Internal actions (LerianStudio/) accept any semver ref including pre-releases
- Internal also allows develop/main branch refs

CodeQL Reporter:
- Suppress actions/unpinned-tag findings from PR comments — handled by
  our own pinned-actions check with org-aware logic
coderabbitai[bot]
coderabbitai bot previously requested changes Mar 25, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lint/pinned-actions/action.yml`:
- Line 61: The semver prerelease part of the regex in the conditional that
validates refs (the printf/grep line) is too permissive and allows invalid forms
while rejecting valid hyphenated identifiers; update the prerelease pattern in
that regex (the one currently using [a-zA-Z0-9.]+) to the stricter RFC-like
pattern [0-9A-Za-z-]+(\.[0-9A-Za-z-]+)* so prerelease identifiers like beta-1
are accepted and malformed sequences like .. are rejected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ae2f0007-b179-487c-b926-e71eb8bb5c3a

📥 Commits

Reviewing files that changed from the base of the PR and between a1179b4 and 708452b.

📒 Files selected for processing (1)
  • src/lint/pinned-actions/action.yml

Fetch warning-level annotations from all checks (not just failures)
and display them in a collapsible "⚠️ Warnings" section in the PR
lint comment. This surfaces internal pinned-actions warnings (e.g.,
pre-release tags) without cluttering the main summary table.
@lerian-studio lerian-studio added size/S PR changes 50–199 lines and removed size/XS PR changes < 50 lines labels Mar 25, 2026
@bedatty bedatty changed the title fix(pr-validation): pin composite refs to v1.20.0-beta.1 fix(lint): enforce SHA pinning for externals, warnings for internals Mar 25, 2026
coderabbitai[bot]
coderabbitai bot previously requested changes Mar 25, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lint/pinned-actions/action.yml`:
- Around line 67-68: The SHA validation is case-sensitive and rejects uppercase
hex; update the grep check that inspects "$ref" (the line using grep -Eq
'^[0-9a-f]{40,64}$') to allow uppercase hex as well by using a character class
that includes A-F (e.g., [0-9a-fA-F]) so both lowercase and uppercase SHAs pass
validation.
- Around line 66-72: The validation currently rejects any external action not
pinned to a full hex SHA (the block that checks printf '%s\n' "$ref" | grep -Eq
'^[0-9a-f]{40,64}$' and emits the "::error ... External action not pinned by
SHA" message), but your repo policy allows third-party actions pinned to
semantic-version tags; update the logic to treat valid semver tags as
acceptable: expand the conditional to also accept refs that match semver/tag
patterns (e.g. /^v[0-9]+(\.[0-9]+){0,2}([-.+][0-9A-Za-z.-]+)?$/ or other
approved tag forms), so "$ref" and "$normalized" that are semver tags bypass the
SHA-only error, and adjust the error text to reflect the policy (mention
acceptable formats) while still counting violations via the violations variable.

In `@src/security/codeql-reporter/action.yml`:
- Around line 128-133: Replace the hardcoded SUPPRESSED_RULES array with a
configurable input: add a new action input (e.g., suppressed-rules) defaulting
to "actions/unpinned-tag" and read it at runtime
(process.env.SUPPRESSED_RULES_INPUT) in the script that builds the report; parse
the string as a comma-separated list (trim entries and ignore empties) to
produce SUPPRESSED_RULES and then apply it when filtering readSarifFiles() so
callers can override the default suppression when the pinned-actions check is
not present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 35156765-5e38-4ed3-95b1-5dab2f7be9ba

📥 Commits

Reviewing files that changed from the base of the PR and between 708452b and 3dc7367.

📒 Files selected for processing (2)
  • src/lint/pinned-actions/action.yml
  • src/security/codeql-reporter/action.yml

coderabbitai[bot]
coderabbitai bot previously requested changes Mar 25, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/notify/pr-lint-reporter/action.yml`:
- Around line 224-255: The warnings block duplicates the failure rendering
logic; extract the grouping-and-formatting logic into a single helper (e.g.,
renderAnnotationsGroupedByFile or formatAnnotationSection) that accepts an array
of annotations and returns the markdown string, and then replace the duplicated
code in the warnings flow (the block using jobWarningAnnotations, byFile, and
iterating w.start_line) and the failure rendering flow to call that helper;
ensure the helper preserves existing behavior (group by a.path or '__general__',
render `**\`file\`**`, include line info when start_line exists, and emit plain
messages for '__general__') so both sections remain identical.
- Around line 162-166: The current call to
github.rest.actions.listJobsForWorkflowRun only fetches the first page (default
30) and truncates jobs; replace this with a paginated fetch (e.g., use
github.paginate or an explicit paging loop) to collect all pages into a single
jobs array before doing annotation lookup. Specifically, change the usage around
listJobsForWorkflowRun / const { data: { jobs } } to call
github.paginate(github.rest.actions.listJobsForWorkflowRun, { owner:
context.repo.owner, repo: context.repo.repo, run_id: context.runId }) (or
equivalent paging loop) and assign the flattened result to the jobs variable so
subsequent annotation processing (which iterates jobs) covers all workflow jobs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 72e910f7-a7da-4f7c-96b6-0586520a1b82

📥 Commits

Reviewing files that changed from the base of the PR and between 3dc7367 and 6b1e9bb.

📒 Files selected for processing (1)
  • src/notify/pr-lint-reporter/action.yml

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
src/security/codeql-reporter/action.yml (1)

128-133: ⚠️ Potential issue | 🟠 Major

Make suppressed SARIF rules configurable.

Line 133 filters actions/unpinned-tag before findings-count, has-findings, and the fail-on-findings gate are computed. Any caller that does not also run the org-aware pinned-actions check will silently under-report real findings and can pass the gate incorrectly.

Proposed fix
 inputs:
   github-token:
     description: GitHub token with pull-requests:write and issues:write permissions
     required: true
   sarif-path:
     description: Path to the CodeQL SARIF output directory
     required: false
     default: '../results'
   languages:
     description: Comma-separated list of languages analyzed (for display purposes)
     required: true
   fail-on-findings:
     description: 'Fail the step with exit code 1 when security findings are detected (true/false)'
     required: false
     default: 'false'
+  suppressed-rules:
+    description: 'Comma-separated SARIF rule IDs to suppress from the PR report'
+    required: false
+    default: 'actions/unpinned-tag'
@@
       env:
         SARIF_PATH: ${{ inputs.sarif-path }}
         LANGUAGES: ${{ inputs.languages }}
+        SUPPRESSED_RULES_INPUT: ${{ inputs.suppressed-rules }}
@@
-          const SUPPRESSED_RULES = ['actions/unpinned-tag'];
+          const SUPPRESSED_RULES = (process.env.SUPPRESSED_RULES_INPUT || '')
+            .split(',')
+            .map((rule) => rule.trim())
+            .filter(Boolean);

As per coding guidelines, every composite action must be self-contained with sensible defaults in action.yml.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/security/codeql-reporter/action.yml` around lines 128 - 133, The SARIF
suppression list (SUPPRESSED_RULES) is hard-coded and applied before computing
findings-count/has-findings/fail-on-findings, which can under-report results;
change action.yml to expose a new string input (e.g., suppressed_rules) with a
sensible default that includes "actions/unpinned-tag", parse that input where
readSarifFiles() is called, and remove the static SUPPRESSED_RULES constant;
also ensure filtering using the parsed suppressed_rules is deferred so that
findings-count, has-findings, and fail-on-findings are computed from the
unfiltered findings and the suppression is only applied when generating the
final report (reference SUPPRESSED_RULES, readSarifFiles(), findings filter, and
the findings-count/has-findings/fail-on-findings gates).
src/notify/pr-lint-reporter/action.yml (1)

162-166: ⚠️ Potential issue | 🟠 Major

Paginate the workflow job fetch before annotation lookup.

Line 162 still loads only one page of jobs. Now that this block collects annotations for both failed and successful checks, later jobs in larger runs will be skipped entirely, so both the failures section and the new warnings section can be incomplete.

Proposed fix
-          const { data: { jobs } } = await github.rest.actions.listJobsForWorkflowRun({
-            owner:  context.repo.owner,
-            repo:   context.repo.repo,
-            run_id: context.runId,
-          });
+          const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, {
+            owner:    context.repo.owner,
+            repo:     context.repo.repo,
+            run_id:   context.runId,
+            per_page: 100,
+          });
GitHub REST API `actions.listJobsForWorkflowRun` pagination: does a single call return only one page of jobs, and is `github.paginate(github.rest.actions.listJobsForWorkflowRun, { owner, repo, run_id, per_page: 100 })` the correct way to collect all jobs for a workflow run?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/notify/pr-lint-reporter/action.yml` around lines 162 - 166, The code
currently calls github.rest.actions.listJobsForWorkflowRun and only retrieves a
single page of jobs (assigned to jobs), which misses later jobs in large runs;
replace the single-page call with GitHub REST pagination by using
github.paginate against github.rest.actions.listJobsForWorkflowRun (pass the
same parameters owner, repo, run_id and per_page: 100) to collect all jobs
before you iterate for annotations so both failures and warnings include every
job; update references to the resulting jobs collection where annotations are
looked up.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/notify/pr-lint-reporter/action.yml`:
- Around line 162-166: The code currently calls
github.rest.actions.listJobsForWorkflowRun and only retrieves a single page of
jobs (assigned to jobs), which misses later jobs in large runs; replace the
single-page call with GitHub REST pagination by using github.paginate against
github.rest.actions.listJobsForWorkflowRun (pass the same parameters owner,
repo, run_id and per_page: 100) to collect all jobs before you iterate for
annotations so both failures and warnings include every job; update references
to the resulting jobs collection where annotations are looked up.

In `@src/security/codeql-reporter/action.yml`:
- Around line 128-133: The SARIF suppression list (SUPPRESSED_RULES) is
hard-coded and applied before computing
findings-count/has-findings/fail-on-findings, which can under-report results;
change action.yml to expose a new string input (e.g., suppressed_rules) with a
sensible default that includes "actions/unpinned-tag", parse that input where
readSarifFiles() is called, and remove the static SUPPRESSED_RULES constant;
also ensure filtering using the parsed suppressed_rules is deferred so that
findings-count, has-findings, and fail-on-findings are computed from the
unfiltered findings and the suppression is only applied when generating the
final report (reference SUPPRESSED_RULES, readSarifFiles(), findings filter, and
the findings-count/has-findings/fail-on-findings gates).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ba818843-dfa1-4596-8836-e5ea588873f6

📥 Commits

Reviewing files that changed from the base of the PR and between 6b1e9bb and 7143fd5.

📒 Files selected for processing (2)
  • src/notify/pr-lint-reporter/action.yml
  • src/security/codeql-reporter/action.yml

@bedatty bedatty dismissed stale reviews from coderabbitai[bot], coderabbitai[bot], and coderabbitai[bot] March 25, 2026 22:05

All comments addressed in commit c8385d4.

@bedatty bedatty merged commit bd3661f into develop Mar 25, 2026
17 checks passed
@github-actions github-actions bot deleted the fix/pin-refs-v1.20.0-beta.1 branch March 25, 2026 22:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S PR changes 50–199 lines workflow Changes to one or more reusable workflow files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants