Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix an assertion failure at exit in the macOS app sandbox #33944

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/node.cc
Expand Up @@ -737,7 +737,10 @@ void ResetStdio() {
err = tcsetattr(fd, TCSANOW, &s.termios);
while (err == -1 && errno == EINTR); // NOLINT
CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sa, nullptr));
CHECK_EQ(0, err);

// Normally we expect err == 0. But if macOS App Sandbox is enabled,
// tcsetattr will fail with err == -1 and errno == EPERM.
CHECK_IMPLIES(err != 0, err == -1 && errno == EPERM);
}
}
#endif // __POSIX__
Expand Down
24 changes: 24 additions & 0 deletions test/fixtures/macos-app-sandbox/Info.plist
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>node</string>
<key>CFBundleIdentifier</key>
<string>org.nodejs.test.node_sandboxed</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>node_sandboxed</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
8 changes: 8 additions & 0 deletions test/fixtures/macos-app-sandbox/node_sandboxed.entitlements
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
</dict>
</plist>
67 changes: 67 additions & 0 deletions test/parallel/test-macos-app-sandbox.js
@@ -0,0 +1,67 @@
'use strict';
const common = require('../common');
if (process.platform !== 'darwin')
common.skip('App Sandbox is only avaliable on Darwin');

const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const assert = require('assert');
const child_process = require('child_process');
const path = require('path');
const fs = require('fs');
const os = require('os');

const nodeBinary = path.resolve(
__dirname, '..', '..',
`out/${common.buildType}/node`);
branchseer marked this conversation as resolved.
Show resolved Hide resolved

tmpdir.refresh();

const appBundlePath = path.join(tmpdir.path, 'node_sandboxed.app');
const appBundleContentPath = path.join(appBundlePath, 'Contents');
const appExecutablePath = path.join(
appBundleContentPath, 'MacOS', 'node');

// Construct the app bundle and put the node executable in it:
// node_sandboxed.app/
// └── Contents
// ├── Info.plist
// ├── MacOS
// │ └── node
fs.mkdirSync(appBundlePath);
fs.mkdirSync(appBundleContentPath);
fs.mkdirSync(path.join(appBundleContentPath, 'MacOS'));
fs.copyFileSync(
fixtures.path('macos-app-sandbox', 'Info.plist'),
path.join(appBundleContentPath, 'Info.plist'));
fs.copyFileSync(
nodeBinary,
appExecutablePath);


// Sign the app bundle with sandbox entitlements:
assert.strictEqual(
child_process.spawnSync('/usr/bin/codesign', [
'--entitlements', fixtures.path(
'macos-app-sandbox', 'node_sandboxed.entitlements'),
'-s', '-',
appBundlePath
]).status,
0);

// Sandboxed app shouldn't be able to read the home dir
assert.notStrictEqual(
child_process.spawnSync(appExecutablePath, [
'-e', 'fs.readdirSync(process.argv[1])', os.homedir()
]).status,
0);

if (process.stdin.isTTY) {
// Run the sandboxed node instance with inherited tty stdin
const spawnResult = child_process.spawnSync(
appExecutablePath, ['-e', ''],
{ stdio: 'inherit' }
);

assert.strictEqual(spawnResult.signal, null);
}