Skip to content

fix: avoid shell interpolation in postinstall codesign - #87

Merged
abasiri merged 3 commits into
doctly:mainfrom
anupamme:fix-repo-switchboard-command-injection-postinstall
Sep 5, 2026
Merged

fix: avoid shell interpolation in postinstall codesign#87
abasiri merged 3 commits into
doctly:mainfrom
anupamme:fix-repo-switchboard-command-injection-postinstall

Conversation

@anupamme

@anupamme anupamme commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Replace the shell-based codesign invocation in scripts/postinstall.js with execFileSync() so filenames are passed as process arguments rather than interpolated into a shell command.

Changes

  • Replace execSync() with execFileSync() for the codesign invocation.
  • Preserve the existing findFiles() traversal and filenames unchanged.
  • Add regression coverage for filenames containing spaces, quotes, and backslashes.

Security

This removes shell interpretation of the discovered filename while preserving existing postinstall behavior.

…ss security vulnerability

Automated security fix generated by OrbisAI Security
@abasiri

abasiri commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Thanks for this. The execFileSync('codesign', ['--sign', '-', '--force', file], ...) change is useful: passing the filename as an argument removes the shell-interpolation risk.

Please drop the safeName changes and keep the original path.join(dir, entry.name) and entry.name.endsWith(suffix) traversal. Backslashes are valid characters in Unix filenames, so stripping them changes the path. I reproduced this with a file named native\addon.node: findFiles() returns a path to nativeaddon.node, which does not exist. The signing failure is then silently swallowed by the surrounding catch. Directory names containing backslashes would also cause their contents to be skipped.

The direct argument array already handles the quoting concern without modifying filenames. With the traversal restored, this should be a small, straightforward fix to land. Please also check filenames containing spaces, quotes and backslashes with a stubbed signer so the regression is covered without invoking the postinstall rebuild.

Drop the safeName sanitization added in the previous commit — backslash is
a valid filename character on Unix, so stripping it corrupted paths (e.g.
native\addon.node became nativeaddon.node) and skipped the contents of any
directory whose name contained a backslash. execFileSync already passes the
filename as a single argv element with no shell involved, so no sanitization
is needed. Also guard the top-level script body with require.main so tests
can require findFiles/codesignFile without running the real rebuild/codesign
flow, and add regression coverage for filenames with spaces, quotes, and
backslashes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@anupamme anupamme changed the title harden: sanitize child_process call in postinstall.js... fix: avoid shell interpolation in postinstall codesign Sep 5, 2026
@anupamme

anupamme commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for catching this and reproducing the backslash case. You're right — the filename sanitization is unnecessary once the argument is passed directly to execFileSync().

I've removed the safeName changes, restored the original path.join(dir, entry.name) traversal, and kept the fix focused on replacing the shell-based execSync() call with execFileSync().

I've also added regression coverage (test/postinstall.test.js) for filenames containing spaces, quotes, and backslashes using a stubbed signer, so the tests don't invoke the actual postinstall rebuild/codesign flow. To make that testable I added a require.main === module guard around the top-level script body — postinstall.js is only ever run directly via node scripts/postinstall.js (postinstall/build scripts), never required elsewhere, so this doesn't change runtime behavior.

@abasiri

abasiri commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Thanks @anupamme for the fix and for following up on the filename handling and regression tests!

I pushed a small test portability follow-up: the Unix-only filesystem cases now skip on Windows, while the stubbed signer tests use path strings without creating files and still run on every platform. I also added these tests to the build workflow so they run on all four CI targets.

All 199 tests pass locally with current main, and the postinstall tests have passed on Windows, macOS, and both Linux targets. The remaining builds are finishing; once they're green, I'll merge this. Thanks again for the contribution!

@abasiri
abasiri merged commit 63e30df into doctly:main Sep 5, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants