Please report privately through GitHub's "Report a vulnerability" (Security → Advisories) on this repository, or email hi@heyresearch.xyz with "security" in the subject. Do not open a public issue. We aim to acknowledge within 3 working days. There is no bug bounty for this repository.
hey-ship-action runs inside other people's workflows, so it is supply-chain code. What it does with untrusted input:
- Inputs are data, never shell. The action is a JavaScript action: inputs reach it as
environment variables (
INPUT_*) and are parsed in-process. It never runs a shell, a child process oreval, and no input is ever interpolated into a command. Every input is validated against a fixed shape (addresses, transaction hashes, a tag-like release name, relative paths) before it is used; anything else fails the step. Messages quote rejected values JSON-escaped and truncated, and log lines are escaped so a value cannot start a workflow command. - Files stay inside the workspace. Artifact, manifest and output paths must be relative to
GITHUB_WORKSPACE, without./..segments, backslashes or control characters. Every path component is checked withlstat; a symbolic link anywhere on the path is refused, files are opened withO_NOFOLLOWand re-checked withfstaton the open descriptor. Sizes are capped (1 GiB per hashed artifact, 1 MiB for the parsed manifest, 256 artifacts, 100 contracts). - Parsed JSON (the manifest) is size-capped and refuses
__proto__,constructorandprototypekeys anywhere in the document. - No network. The action makes no network call and never contacts HEY. The tests fail if a source file imports a network or process module, and if the committed bundle contains one.
- Runner variables (
GITHUB_*) are validated too: an unexpected shape fails the step rather than flowing into the evidence. pull_request_targetis refused. That event runs with the base repository's privileges on code from outside it; evidence written there would name the base repository for code it did not review. Onpull_request,commitis GitHub's merge commit andworkflowRun.eventsays so.
The action needs no token and reads none. Recommended permissions:
| Job | Permissions |
|---|---|
| Writes evidence only | contents: read |
| Writes and attests evidence | contents: read, id-token: write, attestations: write |
| Attaches the file to a release | contents: write, in a separate job without id-token |
Keep deployer keys and id-token: write in different jobs (see
examples/deploy-and-attest.yml). With attest: true the
action only checks that the runner offers an OIDC token request (the presence of
ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN); it never requests a token and
never logs either value. Signing is done by GitHub's actions/attest-build-provenance, not here.
In workflows: never use pull_request_target with this action, check out with
persist-credentials: false, pass secrets and event fields to run: steps through env: (never
${{ }} inside a script), and pin third-party actions — this one included — to a full commit SHA.
The action runs dist/index.js, a single file built from src/ with every dependency bundled
(zod and @noble/hashes). CI rebuilds it and fails if the committed copy differs, so the bundle
can be reviewed as the output of the reviewed source and the lockfile.
This project never needs HEY credentials, or any credential. It reads no token from the
environment and writes nothing but the evidence file and the step outputs. Never commit a .env
with values.
The latest 0.x minor receives fixes.