fix(ca): use .command file opened in Terminal to get sudo password prompt
CI / Build & Test (push) Successful in 2m36s
CI / Build & Test (push) Successful in 2m36s
Made-with: Cursor
This commit is contained in:
@@ -1498,7 +1498,7 @@
|
|||||||
This installs the Holesail root certificate authority into your system keychain, so Chrome trusts HTTPS for all <code style="font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--cyan);">*.hole.sail</code> virtual host domains.
|
This installs the Holesail root certificate authority into your system keychain, so Chrome trusts HTTPS for all <code style="font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--cyan);">*.hole.sail</code> virtual host domains.
|
||||||
</p>
|
</p>
|
||||||
<p class="modal-desc" style="color:var(--amber);">
|
<p class="modal-desc" style="color:var(--amber);">
|
||||||
You will be prompted for your administrator password. This is required to add the certificate to the system trust store.
|
A Terminal window will open and ask for your password. Enter it and wait for the success message, then close the Terminal window.
|
||||||
</p>
|
</p>
|
||||||
<p class="modal-desc" style="color:var(--fg-muted);font-size:11px;">
|
<p class="modal-desc" style="color:var(--fg-muted);font-size:11px;">
|
||||||
After installing, fully quit and reopen Chrome (Cmd+Q) for the trust to take effect.
|
After installing, fully quit and reopen Chrome (Cmd+Q) for the trust to take effect.
|
||||||
|
|||||||
@@ -173,27 +173,69 @@ function installRootCA(callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addToSystemKeychain(done) {
|
function addToSystemKeychain(done) {
|
||||||
// The native host runs as a background daemon (spawned by Chrome) with no
|
// The native host is a background daemon with no GUI session.
|
||||||
// GUI session, so osascript "with administrator privileges" silently fails.
|
// osascript "with administrator privileges" and launchctl asuser both
|
||||||
// Fix: use `launchctl asuser <uid> osascript` to run in the user's GUI
|
// fail without root. Solution: write a .command file and open it with
|
||||||
// session — this correctly shows the macOS password dialog.
|
// `open` — macOS launches it in Terminal.app inside the user's GUI
|
||||||
const uid = process.getuid ? process.getuid() : 0;
|
// session, which can show the sudo password prompt. Poll a flag file
|
||||||
const inner = `security add-trusted-cert -d -r trustRoot -k ${systemKeychain} ${caPath}`;
|
// to know when it completes.
|
||||||
const appleScript = `do shell script "${inner.replace(/"/g, '\\"')}" with administrator privileges`;
|
const home = process.env.HOME || '';
|
||||||
const command = `launchctl asuser ${uid} osascript -e '${appleScript.replace(/'/g, "'\"'\"'")}'`;
|
const tmpCmd = path.join(home, '.holesail-install-ca.command');
|
||||||
runCommand(command, (err, _stdout, stderr) => {
|
const tmpDone = path.join(home, '.holesail-ca-done');
|
||||||
if (!err) {
|
const tmpErr = path.join(home, '.holesail-ca-err');
|
||||||
logInfo('CA', 'Root CA installed to System keychain (trusted by all apps including Chrome).');
|
|
||||||
done(null);
|
try { fs.unlinkSync(tmpDone); } catch (_) {}
|
||||||
|
try { fs.unlinkSync(tmpErr); } catch (_) {}
|
||||||
|
|
||||||
|
const script = [
|
||||||
|
'#!/bin/bash',
|
||||||
|
`sudo security add-trusted-cert -d -r trustRoot -k "${systemKeychain}" "${caPath}"`,
|
||||||
|
'if [ $? -eq 0 ]; then',
|
||||||
|
` echo ok > "${tmpDone}"`,
|
||||||
|
' echo ""',
|
||||||
|
' echo "Holesail Browser CA installed successfully. You can close this window."',
|
||||||
|
'else',
|
||||||
|
` echo fail > "${tmpErr}"`,
|
||||||
|
' echo ""',
|
||||||
|
' echo "Installation failed. Please try again."',
|
||||||
|
'fi',
|
||||||
|
'sleep 3',
|
||||||
|
`rm -f "${tmpCmd}"`,
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(tmpCmd, script, { mode: 0o755 });
|
||||||
|
} catch (e) {
|
||||||
|
done(new Error('Could not write install script: ' + e.message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const msg = (stderr || err.message || '').trim();
|
|
||||||
logError('CA', 'launchctl osascript failed: ' + msg);
|
runCommand(`open "${tmpCmd}"`, (errOpen) => {
|
||||||
if (msg.includes('cancelled') || msg.includes('cancel') || msg.includes('-128')) {
|
if (errOpen) {
|
||||||
done(new Error('Installation cancelled.'));
|
try { fs.unlinkSync(tmpCmd); } catch (_) {}
|
||||||
} else {
|
done(new Error('Could not open Terminal to install CA: ' + errOpen.message));
|
||||||
done(new Error('Could not install CA: ' + msg));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Poll for completion flag (up to 120s for user to enter password)
|
||||||
|
let waited = 0;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
waited += 1000;
|
||||||
|
if (fs.existsSync(tmpDone)) {
|
||||||
|
clearInterval(interval);
|
||||||
|
try { fs.unlinkSync(tmpDone); } catch (_) {}
|
||||||
|
logInfo('CA', 'Root CA installed to System keychain.');
|
||||||
|
done(null);
|
||||||
|
} else if (fs.existsSync(tmpErr)) {
|
||||||
|
clearInterval(interval);
|
||||||
|
try { fs.unlinkSync(tmpErr); } catch (_) {}
|
||||||
|
done(new Error('CA installation failed. Make sure you entered the correct password.'));
|
||||||
|
} else if (waited >= 120000) {
|
||||||
|
clearInterval(interval);
|
||||||
|
try { fs.unlinkSync(tmpCmd); } catch (_) {}
|
||||||
|
done(new Error('Timed out waiting for CA installation.'));
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user