fix(desktop): preserve Linux sandboxing wherever the host allows it (#4447)

* fix(desktop): preserve Linux sandboxing wherever the host allows it

Chromium checks its Linux sandbox before main.js, while package installation probes run as root and miss AppArmor restrictions on desktop users. Choose the launch flags before exec and always install the SUID alternative for native packages. Test published artifacts as an ordinary user with namespaces both denied and allowed; never repair permissions in the smoke harness.

* fix(desktop): retain the Linux release build baseline

* fix(ci): allow canceled desktop smoke uploads to stop

* test(desktop): verify the installed RPM sandbox under AppArmor

* fix(ci): resolve packaged desktop dependencies before installation

* chore(desktop): keep QA evidence out of the repository
This commit is contained in:
Mohamed Boudra
2026-09-11 09:05:26 +02:00
committed by GitHub
parent 0110302b6f
commit 772ff8d6f3
21 changed files with 597 additions and 69 deletions
@@ -335,3 +335,20 @@ export function readLegacySkillSelection(): Promise<AgentSkillSelection | null>
export async function deleteLegacySkillSelection(): Promise<void> {
await invokeDesktopCommand("delete_legacy_skill_selection");
}
export interface DesktopSandboxDiagnostics {
enabled: boolean;
reason: string;
}
export async function getDesktopSandboxDiagnostics(): Promise<DesktopSandboxDiagnostics> {
const result: unknown = await invokeDesktopCommand("desktop_sandbox_diagnostics");
if (
!isRecord(result) ||
typeof result.enabled !== "boolean" ||
typeof result.reason !== "string"
) {
throw new Error("Unexpected desktop sandbox diagnostics response.");
}
return { enabled: result.enabled, reason: result.reason };
}
@@ -6,6 +6,7 @@ import {
function makeSources(): DesktopDiagnosticSources {
return {
getSandboxDiagnostics: async () => ({ enabled: true, reason: "user namespaces available" }),
getStatus: async () => ({
serverId: "server-1",
status: "running",
@@ -147,3 +148,20 @@ describe("desktop diagnostic report", () => {
expect(report).toContain("Desktop app log tail\n Error: app log unavailable");
});
});
test("reports sandbox degradation and its reason even if daemon diagnostics fail", async () => {
const result = await collectDesktopDiagnosticSections({
...makeSources(),
getStatus: async () => {
throw new Error("daemon unavailable");
},
getSandboxDiagnostics: async () => ({
enabled: false,
reason: "user namespaces unavailable; no usable SUID helper",
}),
});
expect(result.sections.join("\n")).toContain(
"Chromium sandbox\n State: Disabled\n Reason: user namespaces unavailable; no usable SUID helper",
);
expect(result.status).toBe("failed");
});
@@ -1,4 +1,6 @@
import {
getDesktopSandboxDiagnostics,
type DesktopSandboxDiagnostics,
getDesktopAppLogs,
getDesktopDaemonLogs,
getDesktopDaemonStatus,
@@ -19,6 +21,7 @@ export interface DesktopDiagnosticCollectionResult {
}
export interface DesktopDiagnosticSources {
getSandboxDiagnostics: () => Promise<DesktopSandboxDiagnostics>;
getStatus: () => Promise<DesktopDaemonStatus>;
getDaemonLogs: () => Promise<DesktopDaemonLogs>;
getAppLogs: () => Promise<DesktopAppLogs>;
@@ -26,6 +29,7 @@ export interface DesktopDiagnosticSources {
}
const DEFAULT_DESKTOP_DIAGNOSTIC_SOURCES: DesktopDiagnosticSources = {
getSandboxDiagnostics: getDesktopSandboxDiagnostics,
getStatus: getDesktopDaemonStatus,
getDaemonLogs: getDesktopDaemonLogs,
getAppLogs: getDesktopAppLogs,
@@ -38,12 +42,33 @@ export async function collectDesktopDiagnosticSections(
const sections: string[] = [];
let failed = false;
const [daemonResult, appLogsResult, updaterResult] = await Promise.allSettled([
const [daemonResult, appLogsResult, updaterResult, sandboxResult] = await Promise.allSettled([
Promise.all([sources.getStatus(), sources.getDaemonLogs()]),
sources.getAppLogs(),
sources.getUpdaterDiagnostics(),
sources.getSandboxDiagnostics(),
]);
if (sandboxResult.status === "fulfilled") {
const sandbox = sandboxResult.value;
sections.push(
formatDiagnosticSection("Chromium sandbox", [
{
label: "State",
value: sandbox.enabled ? "Enabled" : "Disabled",
},
{ label: "Reason", value: sandbox.reason },
]),
);
} else {
failed = true;
sections.push(
formatDiagnosticSection("Chromium sandbox", [
{ label: "Error", value: toMessage(sandboxResult.reason) },
]),
);
}
if (daemonResult.status === "fulfilled") {
const [status, daemonLogs] = daemonResult.value;
const appLogs = appLogsResult.status === "fulfilled" ? appLogsResult.value : null;