fix(extension): resolve symlinks in containment check - #523
Conversation
✅MegaLinter analysis: Success
Notices📣 MegaLinter 9.5.0 is out! Discover the new features and security recommendations in the release announcement. (Skip this info by defining See detailed reports in MegaLinter artifacts MegaLinter is graciously provided by OX Security |
9e706d0 to
3607e9f
Compare
| await writeFile(payload, '#!/bin/sh\necho PAYLOAD RAN FROM OUTSIDE\n', { mode: 0o755 }) | ||
| await chmod(payload, 0o755) |
There was a problem hiding this comment.
| await writeFile(payload, '#!/bin/sh\necho PAYLOAD RAN FROM OUTSIDE\n', { mode: 0o755 }) | |
| await chmod(payload, 0o755) | |
| await writeFile(payload, '#!/bin/sh\necho PAYLOAD RAN FROM OUTSIDE\n', { mode: 0o755 }) |
nit: Setting mode twice is redundant.
There was a problem hiding this comment.
already fixed, mode is set once in the writeFile call now, no separate chmod after it.
…-containment # Conflicts: # src/extension/installer.ts # test/extension/installer.test.ts
|
addressed both flagged issues: @JoshMock the npm update path had the same gap as git pull, entrypoint wasn't reverified after upgrading. added the same assertWithinInstallDir check there, with a test that runs a real npm update against a symlinked entrypoint. on the ai review's ENOENT claim for createLocalExtension: not actually a bug, the scaffolded entrypoint file is written before assertWithinInstallDir runs (the write happens inside the if block, the check happens after it), so realpath never sees a missing file. the createLocalExtension test suite covers the no path default scaffold case and passes. rebased onto main to pick up ignore-scripts and the test run seam from #539, resolved conflicts by combining both. |
There was a problem hiding this comment.
nit: unintentional NOTICE regression?
a084bb9 to
4a12a26
Compare
|
The fix is well-motivated and the implementation is correct overall. A few real issues: Bug: symlink escape test for npm upgrade calls the wrong The test Real issue:
const realpath = promisify(realpathCb.native ?? realpathCb)The native variant is used specifically for Windows 8.3 short name expansion, so on non-Windows platforms falling back to the non-native version is semantically correct. Real issue: TOCTOU window between After Unnecessary dependency:
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'That said, if Windows CI was actually broken before this, the fix is pragmatic — just worth flagging. Minor: Removing the explicit Overall the core security fix (resolving symlinks before containment check) is correct and the tests cover the important cases. |
…-containment # Conflicts: # src/extension/installer.ts
|
The diff looks correct and well-reasoned. A few real issues worth noting:
The stated reason for adding
TOCTOU acknowledged but not mitigated in the npm upgrade path The comment in
The catch block in Test uses bare The test imports Nothing else is wrong with the logic, the security fix itself is sound, and the tests cover the intended cases. |
assertWithinInstallDircomparedresolve()d paths, which normalizes./..but doesn't follow symlinks, so a symlinked entrypoint inside the install directory passed the check while pointing anywhere on disk. Switches torealpathon both sides of the comparison and calls the check fromcreateLocalExtensiontoo (it previously only ran on theinstallpath).Closes #500.