




















@@ -69,6 +69,21 @@ describe("maybeRepairSandboxImages", () => {
6969};
7070}
717172+function createSandboxConfigWithDockerNetwork(network: string): OpenClawConfig {
73+return {
74+agents: {
75+defaults: {
76+sandbox: {
77+mode: "all",
78+docker: {
79+ network,
80+},
81+},
82+},
83+},
84+};
85+}
86+7287async function runSandboxRepair(params: {
7388mode: "off" | "all" | "non-main";
7489dockerAvailable: boolean;
@@ -131,6 +146,98 @@ describe("maybeRepairSandboxImages", () => {
131146);
132147expect(dockerUnavailableWarning).toBeUndefined();
133148});
149+150+it("warns when Codex bwrap namespaces are blocked on a sandboxed Linux host", async () => {
151+const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("linux");
152+runExec.mockImplementation(async (command: string, args: string[]) => {
153+if (command === "docker" && args[0] === "version") {
154+return { stdout: "24.0.0", stderr: "" };
155+}
156+if (command === "unshare") {
157+throw Object.assign(new Error("unshare failed"), {
158+stderr: "unshare: write failed /proc/self/uid_map: Operation not permitted",
159+});
160+}
161+return { stdout: "", stderr: "" };
162+});
163+164+try {
165+await maybeRepairSandboxImages(createSandboxConfig("all"), mockRuntime, mockPrompter);
166+} finally {
167+platformSpy.mockRestore();
168+}
169+170+expect(note).toHaveBeenCalledWith(
171+expect.stringContaining("Codex bwrap user namespace probe failed"),
172+"Sandbox",
173+);
174+expect(note).toHaveBeenCalledWith(
175+expect.stringContaining("kernel.apparmor_restrict_unprivileged_userns=0"),
176+"Sandbox",
177+);
178+});
179+180+it("checks Codex bwrap network namespaces only when Docker sandbox egress is offline", async () => {
181+const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("linux");
182+runExec.mockImplementation(async (command: string, args: string[]) => {
183+if (command === "docker" && args[0] === "version") {
184+return { stdout: "24.0.0", stderr: "" };
185+}
186+if (command === "unshare") {
187+if (args.includes("--net")) {
188+throw Object.assign(new Error("unshare failed"), {
189+stderr: "unshare: unshare failed: Operation not permitted",
190+});
191+}
192+return { stdout: "", stderr: "" };
193+}
194+return { stdout: "", stderr: "" };
195+});
196+197+try {
198+await maybeRepairSandboxImages(createSandboxConfig("all"), mockRuntime, mockPrompter);
199+} finally {
200+platformSpy.mockRestore();
201+}
202+203+expect(note).toHaveBeenCalledWith(
204+expect.stringContaining("Codex bwrap network namespace probe failed"),
205+"Sandbox",
206+);
207+expect(note).toHaveBeenCalledWith(
208+expect.stringContaining("bwrap: loopback: Failed RTM_NEWADDR"),
209+"Sandbox",
210+);
211+});
212+213+it("skips the Codex bwrap network namespace probe when Docker sandbox egress is enabled", async () => {
214+const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("linux");
215+runExec.mockImplementation(async (command: string, args: string[]) => {
216+if (command === "docker" && args[0] === "version") {
217+return { stdout: "24.0.0", stderr: "" };
218+}
219+if (command === "unshare") {
220+return { stdout: "", stderr: "" };
221+}
222+return { stdout: "", stderr: "" };
223+});
224+225+try {
226+await maybeRepairSandboxImages(
227+createSandboxConfigWithDockerNetwork("bridge"),
228+mockRuntime,
229+mockPrompter,
230+);
231+} finally {
232+platformSpy.mockRestore();
233+}
234+235+expect(
236+runExec.mock.calls.some(
237+([command, args]) => command === "unshare" && Array.isArray(args) && args.includes("--net"),
238+),
239+).toBe(false);
240+});
134241});
135242136243describe("maybeRepairSandboxRegistryFiles", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。