security(commit-repo): replace execSync tar with node-tar#799
Merged
Conversation
pushArchiveToGitRepository() previously unpacked the downloaded
tarball by shelling out to
execSync(`tar -zxvf ${archivePath}${stripComponentsArg}`)
with archivePath interpolated directly into the shell command
string. archivePath is currently craft-constructed so the injection
isn't exploitable today, but the pattern is fragile against future
refactors — any artifact provider that returned a path containing
`;`, `$()`, backticks, or a space would become a command-injection
vector, and the failure mode would be silent arbitrary code
execution during release.
Switch to the `tar` npm package (already a dependency; used
elsewhere in src/utils/system.ts). `tar.x({ file, cwd, gzip,
strip })` takes the path as a parameter with no shell parsing at any
layer, so even adversarial input is treated as a literal filename.
Tests:
- Existing basic-functionality test updated to assert on the
tar.x mock instead of execSync.
- New regression test that passes an archivePath containing `;` and
verifies it reaches tar.x as a literal string (would have been a
shell injection vector under the old code).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the shell-interpolated
execSynctar invocation inpushArchiveToGitRepository()with thetarnpm package (already a dep). Closes a latent shell-injection hazard and makes the unpack step robust against adversarial artifact paths.The hazard
src/targets/commitOnGitRepository.tsused to unpack the downloaded tarball with:archivePathcomes fromthis.artifactProvider.downloadArtifact(...)so today it's Craft-constructed and not directly attacker-controllable. But the pattern is fragile: any future artifact provider that returns a path containing;,$(), backticks, spaces, or|becomes a silent arbitrary-command-execution vector during release.stripComponentsis zod-validated as a number so that vector is blocked, but the archive path is not so constrained.The fix
Use
tar.x({ file, cwd, gzip, strip })from thetarnpm package. It takes the path as a function parameter with no shell parsing at any layer, so even a path like/tmp/evil;touch /tmp/pwned.tgzis treated as a literal filename.tar7.x is already a dep (package.json);src/utils/system.tsalready usestar.extractelsewhere.Tests
src/targets/__tests__/commitOnGitRepository.test.ts:tar.xmock instead of the oldexecSyncspy.strip: 0path./tmp/evil;touch /tmp/CRAFT_INJECTION.tgzand assertstar.xreceived it literally. This would have been a shell injection under the old code.All 4 tests pass; lint / build clean.