ci(security): improve security with adding permissions (#4619)

* ci(security): add scoped GITHUB_TOKEN permissions to all workflows

Every workflow now declares its token permissions explicitly instead of
inheriting the repository default, which satisfies OpenSSF Scorecard's
token-permissions check.

Multi-job and write-scoped workflows use `permissions: {}` as a
deny-by-default floor at the top level, with the actual grants declared
per job. Consolidating those grants at the workflow level would leak
scopes into jobs that don't need them — a `permissions` block replaces
rather than merges, so the empty top-level block also makes a forgotten
job block fail closed rather than inherit the default.

Single-job read-only package workflows keep a plain top-level
`contents: read`; there is no write scope to leak, so a second block per
file would only add noise.

Scopes were matched to what each job's steps actually do:

- linting-icons: `contents: read` per job. No `pull-requests: read` —
  tj-actions/changed-files only needs that with `use_rest_api: true`,
  and here it reads the local .git.
- lint-pr-title: `pull-requests: read`, which
  action-semantic-pull-request needs to read the PR title.
- close-stale-prs: `pull-requests: write` only; the job never checks out.
- comment-icon-preview: `actions: read` for the cross-run artifact
  download, plus `pull-requests: write` to post the comment.
- release: `contents: read` for the checkouts, `id-token: write` only on
  the publishing jobs that use npm provenance, and `contents: write` only
  on post-release, which uploads the release assets. The prepare job
  neither checks out nor calls the API, so its empty scope is explicit.
- ci: `contents: write` only on create-release; the reusable-workflow
  calls pass `id-token: write` and `contents: write` down to release.yml,
  since a called workflow can only reduce what the caller grants.

Verified with actionlint (no new findings) and zizmor (no
excessive-permissions findings).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ci): correct pull_request path filter in the lucide-vue workflow

The Vue package lives in packages/vue, not packages/lucide-vue, so the
filter never matched and the workflow did not run for changes to the
package itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Remove empty scope in prepare

* Patch WeaknessCWE-506

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Eric Fennis
2026-08-06 09:57:00 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent 1d7da164bb
commit caa622b4ea
26 changed files with 89 additions and 14 deletions
+3
View File
@@ -7,6 +7,9 @@ on:
- tools/build-icons/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
+11 -3
View File
@@ -23,9 +23,7 @@ on:
- beta
- alpha
permissions:
id-token: write # Required for OIDC
contents: write
permissions: {}
jobs:
check-dispatch-gate:
@@ -33,6 +31,8 @@ jobs:
startsWith(github.event.head_commit.message, 'feat(icons)') &&
github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
RELEASE_ENVIRONMENT: ${{ steps.environment.outputs.RELEASE_ENVIRONMENT }}
steps:
@@ -60,6 +60,8 @@ jobs:
needs: check-dispatch-gate
environment: ${{ needs.check-dispatch-gate.outputs.RELEASE_ENVIRONMENT }}
runs-on: ubuntu-latest
permissions:
contents: write # Required to create the release and tag
outputs:
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
steps:
@@ -111,6 +113,9 @@ jobs:
needs: create-release
uses: './.github/workflows/release.yml'
secrets: inherit
permissions:
id-token: write # Required for OIDC (npm provenance)
contents: write # Required to upload release assets
with:
version: ${{ needs.create-release.outputs.VERSION }}
@@ -118,6 +123,9 @@ jobs:
if: github.event_name == 'workflow_dispatch'
uses: './.github/workflows/release.yml'
secrets: inherit
permissions:
id-token: write # Required for OIDC (npm provenance)
contents: write # Required to upload release assets
with:
version: ${{ inputs.version }}
tag: ${{ inputs.tag }}
@@ -4,10 +4,16 @@ on:
issues:
types: [opened]
permissions: {}
env:
GH_TOKEN: ${{ github.token }}
jobs:
block_phrases:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
@@ -43,6 +49,3 @@ jobs:
exit 0
fi
done
env:
GH_TOKEN: ${{ github.token }}
+2
View File
@@ -3,6 +3,8 @@ on:
schedule:
- cron: '45 1 * * *'
permissions: {}
jobs:
stale:
runs-on: ubuntu-latest
@@ -6,9 +6,15 @@ on:
types:
- completed
permissions: {}
jobs:
upload:
runs-on: ubuntu-latest
permissions:
contents: read
actions: read # Required to download the artifact from the triggering run
pull-requests: write
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
+3
View File
@@ -9,6 +9,9 @@ on:
- tools/rollup-plugins/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
+2
View File
@@ -2,6 +2,8 @@ name: 'Pull Request Labeler'
on:
- pull_request_target
permissions: {}
jobs:
triage:
permissions:
+3
View File
@@ -3,6 +3,9 @@ name: Linting PR code
on:
pull_request:
permissions:
contents: read
jobs:
lint-code:
runs-on: ubuntu-latest
+4
View File
@@ -7,6 +7,10 @@ on:
- edited
- reopened
permissions:
contents: read
pull-requests: read # Required to read the PR title
jobs:
lint-pr-title:
name: PR Title Lint
+3 -1
View File
@@ -9,6 +9,8 @@ on:
- 'icons/*'
- 'lab/*'
permissions: {}
jobs:
lint-filenames:
name: Lint Filenames
@@ -29,7 +31,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v46
uses: tj-actions/changed-files@v46.0.1
with:
files: |
icons/*
+3
View File
@@ -11,6 +11,9 @@ on:
- tools/build-icons/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
+3
View File
@@ -9,6 +9,9 @@ on:
- tools/build-font/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
lucide-font:
runs-on: ubuntu-latest
+3
View File
@@ -12,6 +12,9 @@ on:
- tools/rollup-plugins/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
lucide-preact:
runs-on: ubuntu-latest
@@ -12,6 +12,9 @@ on:
- tools/rollup-plugins/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
+3
View File
@@ -13,6 +13,9 @@ on:
- scripts/generateNextJSAliases.mjs
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
+3
View File
@@ -9,6 +9,9 @@ on:
- packages/shared/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
+3
View File
@@ -12,6 +12,9 @@ on:
- tools/rollup-plugins/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
+3
View File
@@ -7,6 +7,9 @@ on:
- tools/build-icons/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
lucide-static:
runs-on: ubuntu-latest
+3
View File
@@ -12,6 +12,9 @@ on:
- tools/rollup-plugins/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
+4 -1
View File
@@ -6,12 +6,15 @@ on:
- next
pull_request:
paths:
- packages/lucide-vue/**
- packages/vue/**
- packages/shared/**
- tools/build-icons/**
- tools/rollup-plugins/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
+3
View File
@@ -11,6 +11,9 @@ on:
- tools/rollup-plugins/**
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
@@ -5,6 +5,8 @@ on:
paths:
- 'icons/*.svg'
permissions: {}
jobs:
generate-changed-icons-comment:
name: Generate Changed Icons Comment
@@ -5,6 +5,8 @@ on:
paths:
- 'icons/*.json'
permissions: {}
jobs:
pull-request-metadata-suggestions:
name: Pull Request Metadata Suggestions
+4 -6
View File
@@ -16,9 +16,7 @@ on:
default: 'latest'
type: string
permissions:
id-token: write # Required for OIDC
contents: write
permissions: {}
jobs:
prepare:
@@ -26,7 +24,6 @@ jobs:
runs-on: ubuntu-latest
outputs:
VERSION: ${{ steps.get_version.outputs.VERSION }}
steps:
- name: Get the version
id: get_version
@@ -122,6 +119,8 @@ jobs:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: prepare
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
@@ -152,8 +151,7 @@ jobs:
runs-on: ubuntu-latest
needs: [prepare, lucide-font]
permissions:
id-token: write # Required for OIDC
contents: write
contents: write # Required to upload the release assets
steps:
- uses: actions/checkout@v6
- uses: actions/download-artifact@v4
+2
View File
@@ -4,8 +4,10 @@ on:
# Run this workflow every Monday at 07:00 UTC
schedule:
- cron: '0 7 * * 1'
permissions:
contents: read
jobs:
weekly-summary:
runs-on: ubuntu-latest
+2
View File
@@ -5,6 +5,8 @@ on:
paths:
- icons/*.svg
permissions: {}
jobs:
request-review:
if: github.event.pull_request.draft == false