ECLI-012: Privileged workflows execute branch-controlled npm lifecycle code
@margaretjgu is already working on this.
Since Aug 6, 2026.
Assessment
This issue has not been assessed yet.
Description
Severity: Medium
Problem
.github/workflows/regenerate-notice.yml:21–57 and .github/workflows/schema-auto-fix.yml:17–71 both use write-capable tokens. The former grants contents: write; the latter grants contents: write and pull-requests: write. Both check out branch or PR content and run npm ci, which can execute the root prepare script and dependency lifecycle hooks. schema-auto-fix.yml also invokes the branch-controlled build and build:schema scripts. Modified branch code therefore runs while a write token is available.
Both workflows have access controls:
regenerate-notice.ymlblocks fork PRs viagithub.event.pull_request.head.repo.full_name == github.repository.schema-auto-fix.ymlrequiresMEMBER/OWNER/COLLABORATORauthor association and rejects cross-repository PRs.
The exposure is limited to authorized collaborators and compromised internal branches, not arbitrary external contributors. That lowers the severity relative to an unguarded pull_request_target, but a compromised collaborator account or bad dependency merge can still execute code with write privileges.
Fix
Use different remediations for the two workflows. The required security property is that PR-controlled code never executes in a job with write permissions or secrets.
regenerate-notice.yml: remove PR auto-commit
The repository already runs npm run test:notice in read-only CI. Remove the write-capable pull_request path from regenerate-notice.yml and require contributors or dependency-update automation to run npm run generate:notice and commit NOTICE.txt when that check fails.
If automatic PR updates are required, use two separate workflows:
- A
pull_requestworkflow withcontents: read, no secrets, andpersist-credentials: falsegenerates and uploadsNOTICE.txt. - A privileged
workflow_runworkflow, whose definition comes from the default branch, downloads the artifact from the exact triggering run and commits it.
Do not put the privileged writer in the same PR-controlled workflow. The writer must verify that the triggering workflow succeeded, the PR is same-repository, and the PR head still equals the SHA used to generate the artifact.
schema-auto-fix.yml: split generation and commit jobs
Because issue_comment uses the workflow definition from the default branch, this workflow can safely use two jobs on separate fresh runners:
-
generate-schema:permissions: contents: read, pull-requests: read- No secrets.
- Resolve and record the exact PR
headRefOid; reject cross-repository PRs. - Check out that SHA with
persist-credentials: false. - Upload only
docs/cli/schema.jsonusing a SHA-pinnedupload-artifact.
-
commit-schema:needs: generate-schemapermissions: contents: write, pull-requests: write- Run on a fresh runner and do not invoke
npm,node, or any PR-controlled script. - Query the PR again and fail if its current head differs from the generation SHA.
- Check out the exact SHA using the write-capable token. Credential persistence is acceptable only in this isolated writer job because it executes no PR-controlled code; alternatively, perform the commit through an explicitly authenticated GitHub API operation.
- Require a regular JSON file, enforce a size limit, and validate it with
jq -e. - Copy it to the fixed
docs/cli/schema.jsondestination. - Assert that this is the only changed path.
- Commit and push using
--force-with-leaseagainst the validated SHA.
Add npm ci --ignore-scripts to unprivileged generation jobs where compatible. This is defense in depth: it does not stop explicitly invoked PR-controlled scripts such as build:schema. The primary control is running those scripts without write permissions or secrets.
Risk
Medium. Current guards keep this outside the external-contributor boundary. The remaining exposure is to trusted collaborators and compromised internal branches.
Copied from the security review in elastic/infosec#27626 (ECLI-012).
- Dominant language
- TypeScript
- Stars
- 43
- Forks
- 24
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 59
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from elastic/cli
-
cloud enhancement
Difficulty 1/5 1-3 hours Newbie friendliness 88/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Similar issues
-
Browser Waiting for: Product Owner
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
getsentry/sentry-javascript#24577 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
agilepathway/label-checker#640 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
copse-dev/agent-pane#2953 ·
-
agentic-workflows
Difficulty 1/5 Under an hour Newbie friendliness 85/100
githubnext/rig#534 ·
-
automation missing-model model-sync provider:pioneer
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
anomalyco/models.dev#7701 ·