Skip to content

Linux: trust root CA for HTTPS custom domains#3268

Open
gavande1 wants to merge 11 commits intotrunkfrom
rsm-1299-linux-trust-root-ca-for-https-custom-domains
Open

Linux: trust root CA for HTTPS custom domains#3268
gavande1 wants to merge 11 commits intotrunkfrom
rsm-1299-linux-trust-root-ca-for-https-custom-domains

Conversation

@gavande1
Copy link
Copy Markdown
Contributor

@gavande1 gavande1 commented Apr 28, 2026

Related issues

How AI was used in this PR

The plan was generated by Claude. We started with minimal scope, then expanded after manual testing surfaced gaps. All code reviewed and manually tested by the author before pushing.

Proposed Changes

Wires up Trust Certificate on Linux. Clicking the button now installs the Studio root CA into the system trust store via @vscode/sudo-prompt (single polkit prompt), then best-effort imports it into per-user NSS DBs so Chromium-family browsers (incl. Snap-Chromium) trust it too.

  • New shared helper tools/common/lib/linux-trust-store.ts:
    • isCATrustedOnLinux()openssl verify against /etc/ssl/certs/ca-certificates.crt.
    • buildLinuxTrustInstallCommand()install -m 0644 … && update-ca-certificates.
    • getLinuxNssDbCandidates() / importCAIntoUserNssDbsLinux()certutil import into ~/.pki/nssdb (always) and ~/snap/chromium/current/.pki/nssdb (when Snap-Chromium is present). Best-effort, no sudo, swallows errors.
  • Both certificate-manager.ts files (CLI + renderer) gain a Linux branch in isRootCATrusted() and trustRootCA().
  • ipc-handlers.ts trustCertificate routes Linux to trustRootCA() instead of opening the file manager.
  • DEB packaging: new postrm.sh cleans the system CA on purge; MakerDeb depends adds ca-certificates and libnss3-tools.
  • 15 unit tests cover Linux branches, NSS path discovery, and post-sudo invocation order.

Out of scope

  • Firefox per-profile NSS — multiple profiles, Snap-Firefox sandbox, profile-may-not-exist. Manual workaround: about:preferences#privacy → View Certificates → Authorities → Import → select ~/.studio/certificates/studio-ca.crt → trust for websites. Tracked in RSM-1615 for docs coverage.
  • Distros other than Debian/Ubuntu (DEB only).
  • Docs site update at developer.wordpress.com/docs/developer-tools/studio/ssl-in-studio/ — RSM-1615.

Testing Instructions

Fresh Ubuntu 22.04/24.04 VM. PR 1 (STU-1647) must be merged first so the proxy can bind 443.

  1. npm run makesudo apt install -y ./out/make/deb/*/studio_*_amd64.deb.
  2. Launch Studio, create a site with HTTPS + custom domain mysite.wp.local.
  3. Open https://mysite.wp.local in Chromium → confirm cert error (the bug).
  4. In Site Settings, click Trust Certificate. Single polkit prompt.
  5. Verify:
    • openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt ~/.studio/certificates/studio-ca.crtok.
    • certutil -d sql:$HOME/.pki/nssdb -L | grep Studio → present.
    • certutil -d sql:$HOME/snap/chromium/current/.pki/nssdb -L | grep Studio → present (when Snap-Chromium is installed).
    • The Trust Certificate button disappears.
  6. Reload https://mysite.wp.local in Chromium → green padlock. curl -v https://mysite.wp.local → handshakes without --insecure.
  7. macOS / Windows regression: click Trust Certificate, confirm no behavioral change.
  8. Purge: sudo apt purge studio/usr/local/share/ca-certificates/studio-ca.crt removed; openssl verify against the bundle now fails. (NSS entries persist by design — user-owned data.)
  9. Firefox still shows red — out of scope; manual import works (see Out of scope).
CleanShot.2026-04-28.at.22.22.00.mp4

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

The previous title 'no shell redirect' was misleading — the asserted
command still uses < and > /dev/null. The actual change is removing
the redirect into the privileged /etc/hosts file.
Wires up the 'Trust Certificate' button on Linux to install the Studio
root CA into the system trust store via @vscode/sudo-prompt. Mirrors
the existing macOS/Windows shape; system store only (per scope).

- isRootCATrusted() and trustRootCA() gain a linux branch in both the
  CLI and renderer-side certificate-manager, delegating to a shared
  helper in tools/common/lib/linux-trust-store.ts
- trustCertificate IPC handler routes Linux to trustRootCA() instead of
  falling through to openCertificateDialog()
- DEB postrm cleans up /usr/local/share/ca-certificates/studio-ca.crt
  on purge and refreshes the system bundle
- MakerDeb depends on ca-certificates
…ains-over-http' into rsm-1299-linux-trust-root-ca-for-https-custom-domains
System trust alone isn't enough on stock Ubuntu desktops because the
default Chromium ships as a Snap with sandboxed NSS, and Firefox uses
its own per-profile NSS. After the sudo-prompt system install succeeds,
trustRootCA now best-effort imports the CA into:

  - ~/.pki/nssdb (apt-installed Chromium-family browsers)
  - ~/snap/chromium/current/.pki/nssdb (Snap-Chromium, when present)

Each import runs as the user (no sudo), uses certutil, and swallows
errors with a console.warn. System trust install is still authoritative
- NSS imports are an additive convenience for browser UX.

- Adds importCAIntoUserNssDbsLinux + getLinuxNssDbCandidates to the
  shared helper
- Wires the call into both CLI and renderer trustRootCA Linux branches
- MakerDeb depends on libnss3-tools (provides certutil)
- Helper test asserts path discovery (always returns ~/.pki/nssdb;
  Snap-Chromium path only when ~/snap/chromium exists)
- CLI test asserts NSS import runs after sudo install succeeds, and
  is skipped when sudo install fails

Firefox per-profile NSS remains out of scope (profile discovery is
messy; users can import via about:preferences).
@gavande1 gavande1 marked this pull request as ready for review April 28, 2026 16:55
@gavande1 gavande1 requested review from a team and ivan-ottinger April 29, 2026 05:46
Base automatically changed from stu-1647-linux-enable-custom-domains-over-http to trunk April 29, 2026 06:36
…-root-ca-for-https-custom-domains

# Conflicts:
#	apps/studio/forge.config.ts
@wpmobilebot
Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing ce693a9 vs trunk

app-size

Metric trunk ce693a9 Diff Change
App Size (Mac) 1454.50 MB 1454.51 MB +0.01 MB ⚪ 0.0%

site-editor

Metric trunk ce693a9 Diff Change
load 1816 ms 1524 ms 292 ms 🟢 -16.1%

site-startup

Metric trunk ce693a9 Diff Change
siteCreation 8088 ms 8106 ms +18 ms ⚪ 0.0%
siteStartup 4947 ms 4951 ms +4 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

Copy link
Copy Markdown
Contributor

@ivan-ottinger ivan-ottinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, Rahul!

This PR took a bit longer to fully test and review. The changes look good and they work as expected. 👍🏼 On Firefox, the mentioned workaround also works.

Before workaround:

CleanShot.2026-04-29.at.12.22.56.mp4

After workaround:

CleanShot.2026-04-29.at.12.32.39.mp4

The only issue I noticed is the scenario where the user creates their site before installing a Chromium-family browser. In my case it was Snap-Chromium.

I let Claude summarize the issue in the text below - as it did much better job in explaining the details clearly.

What happens :

  1. User creates their first site → ensureRootCA() triggers trustRootCA() → polkit prompt → system bundle gets the CA, ~/.pki/nssdb gets it too. So far so good.
  2. At this point ~/snap/chromium/ doesn't exist, so getLinuxNssDbCandidates() doesn't include the snap NSS path. The snap DB is never touched.
  3. Later, the user installs Snap-Chromium and visits their site → Chromium shows the "your connection is not private" warning because Snap-Chromium reads from ~/snap/chromium/current/.pki/nssdb, which is empty.
  4. The user opens Studio site settings looking for the Trust Certificate button — but it's hidden, because isRootCATrusted() only checks the system bundle (isCATrustedOnLinux), which is still happy. There's no UI affordance to repair the gap; they'd have to know about certutil to fix it manually.

Reproduction

Starting from a clean Ubuntu VM:

  1. Make sure Snap-Chromium is not installed yet: sudo snap remove chromium (if present).
  2. Create a site in Studio with a custom domain + HTTPS. Approve the polkit prompt.
  3. Verify the system bundle has the cert: openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt ~/.studio/certificates/studio-ca.crtOK.
  4. Verify only the standard NSS DB has the cert: certutil -dsql:$HOME/.pki/nssdb -L | grep Studio shows WordPress Studio CA.
  5. Now install Snap-Chromium: sudo snap install chromium.
  6. Open the site in Chromium → privacy warning.
  7. Open Studio site settings → Trust Certificate button is
    missing.

Here's the diff with proposed fix:

diff --git a/apps/cli/lib/certificate-manager.ts b/apps/cli/lib/certificate-manager.ts
index d87b0978..7653e243 100644
--- a/apps/cli/lib/certificate-manager.ts
+++ b/apps/cli/lib/certificate-manager.ts
@@ -8,6 +8,7 @@ import { CERT_UNTRUSTED_ROOT, SERVER_AUTH_OID } from '@studio/common/constants';
 import {
 	buildLinuxTrustInstallCommand,
 	importCAIntoUserNssDbsLinux,
+	isCAImportedInUserNssDbsLinux,
 	isCATrustedOnLinux,
 } from '@studio/common/lib/linux-trust-store';
 import { getCertificatesPath } from '@studio/common/lib/well-known-paths';
@@ -183,7 +184,12 @@ export async function isRootCATrusted(): Promise< boolean > {
 			return false;
 		}
 	} else if ( process.platform === 'linux' ) {
-		return isCATrustedOnLinux( CA_CERT_PATH );
+		// The CA is fully trusted on Linux only when it lives in both the system
+		// bundle (covers curl/openssl/Node) AND every NSS DB candidate (covers
+		// Chromium-family browsers, including Snap-Chromium's sandboxed DB).
+		return (
+			( await isCATrustedOnLinux( CA_CERT_PATH ) ) && ( await isCAImportedInUserNssDbsLinux() )
+		);
 	}
 
 	return false;
@@ -220,22 +226,29 @@ export async function trustRootCA(): Promise< void > {
 				);
 			} );
 		} else if ( platform === 'linux' ) {
-			await new Promise< void >( ( resolve, reject ) => {
-				sudo.exec(
-					buildLinuxTrustInstallCommand( CA_CERT_PATH ),
-					{ name: 'WordPress Studio' },
-					( error ) => {
-						if ( error ) {
-							console.error( 'Error adding certificate to system trust store:', error );
-							reject( error );
-						} else {
-							console.log( 'Root CA trusted in Linux system trust store' );
-							resolve();
+			// Skip the sudo install when the system bundle is already trusted —
+			// otherwise we'd reprompt for the polkit password just to re-sync NSS
+			// (the common case when a Chromium-family browser is installed after
+			// the initial trust flow).
+			if ( ! ( await isCATrustedOnLinux( CA_CERT_PATH ) ) ) {
+				await new Promise< void >( ( resolve, reject ) => {
+					sudo.exec(
+						buildLinuxTrustInstallCommand( CA_CERT_PATH ),
+						{ name: 'WordPress Studio' },
+						( error ) => {
+							if ( error ) {
+								console.error( 'Error adding certificate to system trust store:', error );
+								reject( error );
+							} else {
+								console.log( 'Root CA trusted in Linux system trust store' );
+								resolve();
+							}
 						}
-					}
-				);
-			} );
-			// Chromium-family browsers don't consult the system bundle on Linux.
+					);
+				} );
+			}
+			// Always run NSS imports — they're idempotent (-D before -A) and don't
+			// need sudo, so re-running covers the install-browser-after-trust case.
 			await importCAIntoUserNssDbsLinux( CA_CERT_PATH );
 		} else {
 			console.error( 'Unsupported platform for automatic certificate trust:', platform );
diff --git a/apps/cli/lib/tests/certificate-manager.test.ts b/apps/cli/lib/tests/certificate-manager.test.ts
index 7aed2bda..e527151e 100644
--- a/apps/cli/lib/tests/certificate-manager.test.ts
+++ b/apps/cli/lib/tests/certificate-manager.test.ts
@@ -2,6 +2,7 @@ import fs from 'node:fs';
 import {
 	buildLinuxTrustInstallCommand,
 	importCAIntoUserNssDbsLinux,
+	isCAImportedInUserNssDbsLinux,
 	isCATrustedOnLinux,
 } from '@studio/common/lib/linux-trust-store';
 import sudo from '@vscode/sudo-prompt';
@@ -12,6 +13,7 @@ vi.mock( '@studio/common/lib/linux-trust-store', () => ( {
 	LINUX_TRUST_STORE_PATH: '/usr/local/share/ca-certificates/studio-ca.crt',
 	LINUX_NSS_NICKNAME: 'WordPress Studio CA',
 	isCATrustedOnLinux: vi.fn(),
+	isCAImportedInUserNssDbsLinux: vi.fn(),
 	buildLinuxTrustInstallCommand: vi.fn(),
 	importCAIntoUserNssDbsLinux: vi.fn(),
 } ) );
@@ -23,6 +25,7 @@ vi.mock( '@vscode/sudo-prompt', () => ( {
 } ) );
 
 const mockedIsCATrustedOnLinux = vi.mocked( isCATrustedOnLinux );
+const mockedIsCAImportedInUserNssDbsLinux = vi.mocked( isCAImportedInUserNssDbsLinux );
 const mockedBuildLinuxTrustInstallCommand = vi.mocked( buildLinuxTrustInstallCommand );
 const mockedImportCAIntoUserNssDbsLinux = vi.mocked( importCAIntoUserNssDbsLinux );
 const mockedSudoExec = vi.mocked( sudo.exec );
@@ -46,6 +49,7 @@ describe( 'certificate-manager (Linux)', () => {
 
 	beforeEach( () => {
 		mockedIsCATrustedOnLinux.mockReset();
+		mockedIsCAImportedInUserNssDbsLinux.mockReset();
 		mockedBuildLinuxTrustInstallCommand.mockReset();
 		mockedImportCAIntoUserNssDbsLinux.mockReset();
 		mockedSudoExec.mockReset();
@@ -53,6 +57,9 @@ describe( 'certificate-manager (Linux)', () => {
 			'install -m 0644 "/home/user/.studio/certificates/studio-ca.crt" "/usr/local/share/ca-certificates/studio-ca.crt" && update-ca-certificates'
 		);
 		mockedImportCAIntoUserNssDbsLinux.mockResolvedValue( undefined );
+		// Default: NSS DBs are populated. Override per-test for the
+		// install-browser-after-trust scenario.
+		mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( true );
 		existsSpy = vi.spyOn( fs, 'existsSync' ).mockReturnValue( true );
 	} );
 
@@ -62,21 +69,33 @@ describe( 'certificate-manager (Linux)', () => {
 	} );
 
 	describe( 'isRootCATrusted', () => {
-		it( 'delegates to isCATrustedOnLinux on Linux and returns its result', async () => {
+		it( 'returns true on Linux only when both system bundle AND NSS DBs are populated', async () => {
 			setPlatform( 'linux' );
 			mockedIsCATrustedOnLinux.mockResolvedValue( true );
+			mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( true );
 
-			const result = await isRootCATrusted();
-			expect( result ).toBe( true );
+			await expect( isRootCATrusted() ).resolves.toBe( true );
 
 			expect( mockedIsCATrustedOnLinux ).toHaveBeenCalledWith(
 				expect.stringContaining( 'studio-ca.crt' )
 			);
+			expect( mockedIsCAImportedInUserNssDbsLinux ).toHaveBeenCalled();
 		} );
 
-		it( 'returns false on Linux when isCATrustedOnLinux returns false', async () => {
+		it( 'returns false on Linux when the system bundle is missing the CA', async () => {
 			setPlatform( 'linux' );
 			mockedIsCATrustedOnLinux.mockResolvedValue( false );
+			mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( true );
+
+			await expect( isRootCATrusted() ).resolves.toBe( false );
+		} );
+
+		it( 'returns false on Linux when an NSS DB is missing the CA (install-browser-after-trust)', async () => {
+			// Repro: Snap-Chromium installed *after* the initial trust — system
+			// bundle is happy, but the new browser's NSS DB is empty.
+			setPlatform( 'linux' );
+			mockedIsCATrustedOnLinux.mockResolvedValue( true );
+			mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( false );
 
 			await expect( isRootCATrusted() ).resolves.toBe( false );
 		} );
@@ -87,6 +106,7 @@ describe( 'certificate-manager (Linux)', () => {
 
 			await expect( isRootCATrusted() ).resolves.toBe( false );
 			expect( mockedIsCATrustedOnLinux ).not.toHaveBeenCalled();
+			expect( mockedIsCAImportedInUserNssDbsLinux ).not.toHaveBeenCalled();
 		} );
 	} );
 
@@ -94,6 +114,7 @@ describe( 'certificate-manager (Linux)', () => {
 		it( 'invokes sudo.exec with the install command on Linux when not yet trusted', async () => {
 			setPlatform( 'linux' );
 			mockedIsCATrustedOnLinux.mockResolvedValue( false );
+			mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( false );
 			stubSudoExec();
 
 			await expect( trustRootCA() ).resolves.toBeUndefined();
@@ -111,6 +132,7 @@ describe( 'certificate-manager (Linux)', () => {
 		it( 'imports the CA into per-user NSS DBs after the system install succeeds', async () => {
 			setPlatform( 'linux' );
 			mockedIsCATrustedOnLinux.mockResolvedValue( false );
+			mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( false );
 			stubSudoExec();
 
 			await trustRootCA();
@@ -127,6 +149,7 @@ describe( 'certificate-manager (Linux)', () => {
 		it( 'does not import into NSS DBs when the system install fails', async () => {
 			setPlatform( 'linux' );
 			mockedIsCATrustedOnLinux.mockResolvedValue( false );
+			mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( false );
 			stubSudoExec( new Error( 'pkexec dismissed' ) );
 
 			await expect( trustRootCA() ).rejects.toThrow();
@@ -137,17 +160,37 @@ describe( 'certificate-manager (Linux)', () => {
 		it( 'rejects when sudo.exec reports an error', async () => {
 			setPlatform( 'linux' );
 			mockedIsCATrustedOnLinux.mockResolvedValue( false );
+			mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( false );
 			stubSudoExec( new Error( 'user dismissed pkexec prompt' ) );
 
 			await expect( trustRootCA() ).rejects.toThrow( 'user dismissed pkexec prompt' );
 		} );
 
-		it( 'short-circuits when the CA is already trusted', async () => {
+		it( 'short-circuits when the CA is fully trusted (system bundle + every NSS DB)', async () => {
+			setPlatform( 'linux' );
+			mockedIsCATrustedOnLinux.mockResolvedValue( true );
+			mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( true );
+
+			await expect( trustRootCA() ).resolves.toBeUndefined();
+			expect( mockedSudoExec ).not.toHaveBeenCalled();
+			expect( mockedImportCAIntoUserNssDbsLinux ).not.toHaveBeenCalled();
+		} );
+
+		it( 'skips sudo but still imports into NSS when system bundle is already trusted (install-browser-after-trust)', async () => {
+			// Repro: user clicks **Trust Certificate** after installing
+			// Snap-Chromium. System bundle is already populated, so polkit
+			// shouldn't reprompt — but NSS DBs need to be re-synced because the
+			// new browser's sandboxed DB started empty.
 			setPlatform( 'linux' );
 			mockedIsCATrustedOnLinux.mockResolvedValue( true );
+			mockedIsCAImportedInUserNssDbsLinux.mockResolvedValue( false );
 
 			await expect( trustRootCA() ).resolves.toBeUndefined();
+
 			expect( mockedSudoExec ).not.toHaveBeenCalled();
+			expect( mockedImportCAIntoUserNssDbsLinux ).toHaveBeenCalledWith(
+				expect.stringContaining( 'studio-ca.crt' )
+			);
 		} );
 	} );
 } );
diff --git a/apps/studio/src/lib/certificate-manager.ts b/apps/studio/src/lib/certificate-manager.ts
index 60e21256..c86288b3 100644
--- a/apps/studio/src/lib/certificate-manager.ts
+++ b/apps/studio/src/lib/certificate-manager.ts
@@ -8,6 +8,7 @@ import { CERT_UNTRUSTED_ROOT, SERVER_AUTH_OID } from '@studio/common/constants';
 import {
 	buildLinuxTrustInstallCommand,
 	importCAIntoUserNssDbsLinux,
+	isCAImportedInUserNssDbsLinux,
 	isCATrustedOnLinux,
 } from '@studio/common/lib/linux-trust-store';
 import { getCertificatesPath } from '@studio/common/lib/well-known-paths';
@@ -51,7 +52,12 @@ export async function isRootCATrusted(): Promise< boolean > {
 			return false;
 		}
 	} else if ( process.platform === 'linux' ) {
-		return isCATrustedOnLinux( CA_CERT_PATH );
+		// The CA is fully trusted on Linux only when it lives in both the system
+		// bundle (covers curl/openssl/Node) AND every NSS DB candidate (covers
+		// Chromium-family browsers, including Snap-Chromium's sandboxed DB).
+		return (
+			( await isCATrustedOnLinux( CA_CERT_PATH ) ) && ( await isCAImportedInUserNssDbsLinux() )
+		);
 	}
 
 	return false;
@@ -88,22 +94,29 @@ export async function trustRootCA(): Promise< void > {
 				);
 			} );
 		} else if ( platform === 'linux' ) {
-			await new Promise< void >( ( resolve, reject ) => {
-				sudo.exec(
-					buildLinuxTrustInstallCommand( CA_CERT_PATH ),
-					{ name: 'WordPress Studio' },
-					( error ) => {
-						if ( error ) {
-							console.error( 'Error adding certificate to system trust store:', error );
-							reject( error );
-						} else {
-							console.log( 'Root CA trusted in Linux system trust store' );
-							resolve();
+			// Skip the sudo install when the system bundle is already trusted —
+			// otherwise we'd reprompt for the polkit password just to re-sync NSS
+			// (the common case when a Chromium-family browser is installed after
+			// the initial trust flow).
+			if ( ! ( await isCATrustedOnLinux( CA_CERT_PATH ) ) ) {
+				await new Promise< void >( ( resolve, reject ) => {
+					sudo.exec(
+						buildLinuxTrustInstallCommand( CA_CERT_PATH ),
+						{ name: 'WordPress Studio' },
+						( error ) => {
+							if ( error ) {
+								console.error( 'Error adding certificate to system trust store:', error );
+								reject( error );
+							} else {
+								console.log( 'Root CA trusted in Linux system trust store' );
+								resolve();
+							}
 						}
-					}
-				);
-			} );
-			// Chromium-family browsers don't consult the system bundle on Linux.
+					);
+				} );
+			}
+			// Always run NSS imports — they're idempotent (-D before -A) and don't
+			// need sudo, so re-running covers the install-browser-after-trust case.
 			await importCAIntoUserNssDbsLinux( CA_CERT_PATH );
 		} else {
 			console.error( 'Unsupported platform for automatic certificate trust:', platform );
diff --git a/tools/common/lib/linux-trust-store.ts b/tools/common/lib/linux-trust-store.ts
index d138732c..24954f16 100644
--- a/tools/common/lib/linux-trust-store.ts
+++ b/tools/common/lib/linux-trust-store.ts
@@ -19,6 +19,24 @@ export async function isCATrustedOnLinux( caPath: string ): Promise< boolean > {
 	}
 }
 
+// Returns true only when every expected NSS DB contains the Studio CA. Used by
+// isRootCATrusted() so the **Trust Certificate** button reappears if a
+// Chromium-family browser (notably Snap-Chromium) is installed *after* the
+// initial system-bundle trust — that browser's sandboxed NSS DB starts empty
+// and only this check surfaces the gap.
+export async function isCAImportedInUserNssDbsLinux(
+	homeDir: string = os.homedir()
+): Promise< boolean > {
+	for ( const db of getLinuxNssDbCandidates( homeDir ) ) {
+		try {
+			await execFilePromise( 'certutil', [ '-d', `sql:${ db }`, '-L', '-n', LINUX_NSS_NICKNAME ] );
+		} catch {
+			return false;
+		}
+	}
+	return true;
+}
+
 export function buildLinuxTrustInstallCommand( caPath: string ): string {
 	return `install -m 0644 "${ caPath }" "${ LINUX_TRUST_STORE_PATH }" && update-ca-certificates`;
 }

@@ -0,0 +1,18 @@
#!/bin/sh
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great we are cleaning up the certificate on Studio app removal.

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.

3 participants