Skip to content

Commit 51e555d

Browse files
committed
Auto-merge upstream openclaw/openclaw
2 parents 3978fa6 + 03a7e5b commit 51e555d

42 files changed

Lines changed: 3635 additions & 4639 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/shared/OpenClawKit/Sources/OpenClawKit/NetworkInterfaceIPv4.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ public enum NetworkInterfaceIPv4 {
1717
let flags = Int32(ptr.pointee.ifa_flags)
1818
let isUp = (flags & IFF_UP) != 0
1919
let isLoopback = (flags & IFF_LOOPBACK) != 0
20-
let family = ptr.pointee.ifa_addr.pointee.sa_family
21-
if !isUp || isLoopback || family != UInt8(AF_INET) { continue }
20+
guard isUp, !isLoopback, let addrPtr = ptr.pointee.ifa_addr,
21+
addrPtr.pointee.sa_family == UInt8(AF_INET) else { continue }
2222

23-
var addr = ptr.pointee.ifa_addr.pointee
23+
var addr = addrPtr.pointee
2424
var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
2525
let result = getnameinfo(
2626
&addr,
27-
socklen_t(ptr.pointee.ifa_addr.pointee.sa_len),
27+
socklen_t(addrPtr.pointee.sa_len),
2828
&buffer,
2929
socklen_t(buffer.count),
3030
nil,

config/assertion-safety-baseline.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4263,6 +4263,6 @@ ui/src/pages/workboard/view-card-actions.ts 2
42634263
ui/src/pages/workboard/view-card-details.ts 1
42644264
ui/src/pages/workboard/view-card-modal.ts 1
42654265
ui/src/pages/workboard/view-card.ts 1
4266-
ui/src/pages/workboard/view.ts 2
4266+
ui/src/pages/workboard/view.ts 1
42674267
ui/src/pages/workboard/workboard-select.ts 1
42684268
ui/src/pages/worktrees/worktrees-page.ts 3

extensions/browser/src/browser/chrome.default-browser.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,52 @@ describe("browser default executable detection", () => {
232232
expect(exe?.path).toContain("Google Chrome.app/Contents/MacOS/Google Chrome");
233233
});
234234

235+
it("preserves vendor-first macOS browser discovery across system and user applications", () => {
236+
vi.mocked(fs.existsSync).mockReturnValue(false);
237+
238+
expect(
239+
resolveBrowserExecutableForPlatform(
240+
{} as Parameters<typeof resolveBrowserExecutableForPlatform>[0],
241+
"darwin",
242+
),
243+
).toBeNull();
244+
expect(
245+
vi
246+
.mocked(fs.existsSync)
247+
.mock.calls.map(([candidate]) => String(candidate))
248+
.filter((candidate) => candidate.includes(".app/Contents/MacOS/")),
249+
).toEqual([
250+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
251+
"/Users/test/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
252+
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
253+
"/Users/test/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
254+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
255+
"/Users/test/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
256+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
257+
"/Users/test/Applications/Chromium.app/Contents/MacOS/Chromium",
258+
"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
259+
"/Users/test/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
260+
]);
261+
});
262+
263+
it.each([
264+
["chrome", "Google Chrome"],
265+
["brave", "Brave Browser"],
266+
["edge", "Microsoft Edge"],
267+
["chromium", "Chromium"],
268+
["canary", "Google Chrome Canary"],
269+
])("preserves the %s kind for a user-installed macOS browser", (kind, appName) => {
270+
const expectedPath = `/Users/test/Applications/${appName}.app/Contents/MacOS/${appName}`;
271+
vi.mocked(fs.existsSync).mockImplementation((candidate) => String(candidate) === expectedPath);
272+
273+
expect(
274+
resolveBrowserExecutableForPlatform(
275+
{} as Parameters<typeof resolveBrowserExecutableForPlatform>[0],
276+
"darwin",
277+
),
278+
).toEqual({ kind, path: expectedPath });
279+
});
280+
235281
it("resolves an Opera default-browser launcher to the directly owned binary on Windows", () => {
236282
const installDir = "C:\\Users\\test\\AppData\\Local\\Programs\\Opera";
237283
const launcher = `${installDir}\\launcher.exe`;

extensions/browser/src/browser/chrome.executables.ts

Lines changed: 31 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -612,54 +612,20 @@ function readSortedDirNames(dir: string): string[] {
612612

613613
/** Find the best Chromium-family executable on macOS. */
614614
function findChromeExecutableMac(): BrowserExecutable | null {
615-
const candidates: Array<BrowserExecutable> = [
616-
{
617-
kind: "chrome",
618-
path: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
619-
},
620-
{
621-
kind: "chrome",
622-
path: path.join(os.homedir(), "Applications/Google Chrome.app/Contents/MacOS/Google Chrome"),
623-
},
624-
{
625-
kind: "brave",
626-
path: "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
627-
},
628-
{
629-
kind: "brave",
630-
path: path.join(os.homedir(), "Applications/Brave Browser.app/Contents/MacOS/Brave Browser"),
631-
},
632-
{
633-
kind: "edge",
634-
path: "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
635-
},
636-
{
637-
kind: "edge",
638-
path: path.join(
639-
os.homedir(),
640-
"Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
641-
),
642-
},
643-
{
644-
kind: "chromium",
645-
path: "/Applications/Chromium.app/Contents/MacOS/Chromium",
646-
},
647-
{
648-
kind: "chromium",
649-
path: path.join(os.homedir(), "Applications/Chromium.app/Contents/MacOS/Chromium"),
650-
},
651-
{
652-
kind: "canary",
653-
path: "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
654-
},
655-
{
656-
kind: "canary",
657-
path: path.join(
658-
os.homedir(),
659-
"Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
660-
),
661-
},
615+
const applications: Array<[BrowserExecutable["kind"], string]> = [
616+
["chrome", "Google Chrome"],
617+
["brave", "Brave Browser"],
618+
["edge", "Microsoft Edge"],
619+
["chromium", "Chromium"],
620+
["canary", "Google Chrome Canary"],
662621
];
622+
const roots = ["/Applications", path.join(os.homedir(), "Applications")];
623+
const candidates = applications.flatMap(([kind, name]) =>
624+
roots.map((root) => ({
625+
kind,
626+
path: path.join(root, `${name}.app`, "Contents", "MacOS", name),
627+
})),
628+
);
663629

664630
return findFirstExecutable(candidates, "darwin");
665631
}
@@ -721,68 +687,26 @@ function findGoogleChromeExecutableLinux(): BrowserExecutable | null {
721687
/** Find the best Chromium-family executable on Windows. */
722688
function findChromeExecutableWindows(): BrowserExecutable | null {
723689
const { localAppData, programFiles, programFilesX86 } = resolveWindowsBrowserInstallRoots();
724-
const joinWin = path.win32.join;
725-
const candidates: Array<BrowserExecutable> = [];
726-
727-
if (localAppData) {
728-
// Chrome (user install)
729-
candidates.push({
730-
kind: "chrome",
731-
path: joinWin(localAppData, "Google", "Chrome", "Application", "chrome.exe"),
732-
});
733-
// Brave (user install)
734-
candidates.push({
735-
kind: "brave",
736-
path: joinWin(localAppData, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
737-
});
738-
// Edge (user install)
739-
candidates.push({
740-
kind: "edge",
741-
path: joinWin(localAppData, "Microsoft", "Edge", "Application", "msedge.exe"),
742-
});
743-
// Chromium (user install)
744-
candidates.push({
745-
kind: "chromium",
746-
path: joinWin(localAppData, "Chromium", "Application", "chrome.exe"),
747-
});
748-
// Chrome Canary (user install)
749-
candidates.push({
750-
kind: "canary",
751-
path: joinWin(localAppData, "Google", "Chrome SxS", "Application", "chrome.exe"),
752-
});
690+
const browsers: Array<[BrowserExecutable["kind"], ...string[]]> = [
691+
["chrome", "Google", "Chrome", "Application", "chrome.exe"],
692+
["brave", "BraveSoftware", "Brave-Browser", "Application", "brave.exe"],
693+
["edge", "Microsoft", "Edge", "Application", "msedge.exe"],
694+
["chromium", "Chromium", "Application", "chrome.exe"],
695+
["canary", "Google", "Chrome SxS", "Application", "chrome.exe"],
696+
];
697+
const candidates: BrowserExecutable[] = localAppData
698+
? browsers.map(([kind, ...segments]) => ({
699+
kind,
700+
path: path.win32.join(localAppData, ...segments),
701+
}))
702+
: [];
703+
704+
for (const [kind, ...segments] of browsers.slice(0, 3)) {
705+
for (const root of [programFiles, programFilesX86]) {
706+
candidates.push({ kind, path: path.win32.join(root, ...segments) });
707+
}
753708
}
754709

755-
// Chrome (system install, 64-bit)
756-
candidates.push({
757-
kind: "chrome",
758-
path: joinWin(programFiles, "Google", "Chrome", "Application", "chrome.exe"),
759-
});
760-
// Chrome (system install, 32-bit on 64-bit Windows)
761-
candidates.push({
762-
kind: "chrome",
763-
path: joinWin(programFilesX86, "Google", "Chrome", "Application", "chrome.exe"),
764-
});
765-
// Brave (system install, 64-bit)
766-
candidates.push({
767-
kind: "brave",
768-
path: joinWin(programFiles, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
769-
});
770-
// Brave (system install, 32-bit on 64-bit Windows)
771-
candidates.push({
772-
kind: "brave",
773-
path: joinWin(programFilesX86, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
774-
});
775-
// Edge (system install, 64-bit)
776-
candidates.push({
777-
kind: "edge",
778-
path: joinWin(programFiles, "Microsoft", "Edge", "Application", "msedge.exe"),
779-
});
780-
// Edge (system install, 32-bit on 64-bit Windows)
781-
candidates.push({
782-
kind: "edge",
783-
path: joinWin(programFilesX86, "Microsoft", "Edge", "Application", "msedge.exe"),
784-
});
785-
786710
return findFirstExecutable(candidates, "win32");
787711
}
788712

extensions/browser/src/browser/routes/agent.storage.device.test.ts

Lines changed: 83 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { createBrowserRouteApp, createBrowserRouteResponse } from "./test-helper
33
import type { BrowserRequest } from "./types.js";
44

55
const routeState = vi.hoisted(() => ({
6+
cookiesGetViaPlaywright: vi.fn(async () => ({ cookies: [] })),
67
cookiesSetManyViaPlaywright: vi.fn(async () => ({ added: 2 })),
78
setDeviceViaPlaywright: vi.fn(async () => {}),
9+
setHttpCredentialsViaPlaywright: vi.fn(async () => {}),
810
withPlaywrightRouteContext: vi.fn(),
911
}));
1012

@@ -25,8 +27,10 @@ type PlaywrightRouteParams = {
2527
tab: { targetId: string };
2628
signal: AbortSignal;
2729
pw: {
30+
cookiesGetViaPlaywright: typeof routeState.cookiesGetViaPlaywright;
2831
cookiesSetManyViaPlaywright: typeof routeState.cookiesSetManyViaPlaywright;
2932
setDeviceViaPlaywright: typeof routeState.setDeviceViaPlaywright;
33+
setHttpCredentialsViaPlaywright: typeof routeState.setHttpCredentialsViaPlaywright;
3034
};
3135
}) => Promise<unknown>;
3236
};
@@ -39,25 +43,21 @@ function getPostHandler(route: string) {
3943
return handler;
4044
}
4145

42-
describe("browser device route", () => {
43-
beforeEach(() => {
44-
routeState.cookiesSetManyViaPlaywright.mockClear();
45-
routeState.setDeviceViaPlaywright.mockClear();
46-
routeState.withPlaywrightRouteContext
47-
.mockReset()
48-
.mockImplementation(async (params: PlaywrightRouteParams) => {
49-
await params.run({
50-
cdpUrl: "http://127.0.0.1:18800",
51-
tab: { targetId: "tab-1" },
52-
signal: params.req.signal ?? new AbortController().signal,
53-
pw: {
54-
cookiesSetManyViaPlaywright: routeState.cookiesSetManyViaPlaywright,
55-
setDeviceViaPlaywright: routeState.setDeviceViaPlaywright,
56-
},
57-
});
46+
beforeEach(() => {
47+
vi.clearAllMocks();
48+
routeState.withPlaywrightRouteContext
49+
.mockReset()
50+
.mockImplementation(async (params: PlaywrightRouteParams) => {
51+
await params.run({
52+
cdpUrl: "http://127.0.0.1:18800",
53+
tab: { targetId: "tab-1" },
54+
signal: params.req.signal ?? new AbortController().signal,
55+
pw: routeState,
5856
});
59-
});
57+
});
58+
});
6059

60+
describe("browser device route", () => {
6161
it("forwards the route lease signal into the atomic device transition", async () => {
6262
const controller = new AbortController();
6363
const response = createBrowserRouteResponse();
@@ -79,27 +79,36 @@ describe("browser device route", () => {
7979
signal: controller.signal,
8080
});
8181
expect(response.body).toEqual({ ok: true, targetId: "tab-1" });
82+
expect(routeState.withPlaywrightRouteContext).toHaveBeenCalledWith(
83+
expect.objectContaining({ feature: "device emulation" }),
84+
);
85+
expect(routeState.withPlaywrightRouteContext.mock.calls[0]?.[0]).not.toHaveProperty(
86+
"enforceCurrentUrlAllowed",
87+
);
8288
});
83-
});
8489

85-
describe("browser cookie batch route", () => {
86-
beforeEach(() => {
87-
routeState.cookiesSetManyViaPlaywright.mockClear();
88-
routeState.withPlaywrightRouteContext
89-
.mockReset()
90-
.mockImplementation(async (params: PlaywrightRouteParams) => {
91-
await params.run({
92-
cdpUrl: "http://127.0.0.1:18800",
93-
tab: { targetId: "tab-1" },
94-
signal: params.req.signal ?? new AbortController().signal,
95-
pw: {
96-
cookiesSetManyViaPlaywright: routeState.cookiesSetManyViaPlaywright,
97-
setDeviceViaPlaywright: routeState.setDeviceViaPlaywright,
98-
},
99-
});
100-
});
90+
it("never publishes a successful mutation after its route lease is canceled", async () => {
91+
const controller = new AbortController();
92+
const response = createBrowserRouteResponse();
93+
routeState.setDeviceViaPlaywright.mockImplementationOnce(async () => controller.abort());
94+
95+
await expect(
96+
getPostHandler("/set/device")?.(
97+
{
98+
params: {},
99+
query: {},
100+
body: { name: "iPhone 14" },
101+
signal: controller.signal,
102+
},
103+
response.res,
104+
),
105+
).rejects.toThrow();
106+
107+
expect(response.body).toBeUndefined();
101108
});
109+
});
102110

111+
describe("browser cookie batch route", () => {
103112
it("parses and injects a non-empty cookie batch", async () => {
104113
const controller = new AbortController();
105114
const response = createBrowserRouteResponse();
@@ -150,3 +159,43 @@ describe("browser cookie batch route", () => {
150159
expect(routeState.cookiesSetManyViaPlaywright).not.toHaveBeenCalled();
151160
});
152161
});
162+
163+
describe("browser storage route boundaries", () => {
164+
it("keeps cookie reads behind the current-tab URL guard", async () => {
165+
const { app, getHandlers } = createBrowserRouteApp();
166+
registerBrowserAgentStorageRoutes(app, {} as never);
167+
const response = createBrowserRouteResponse();
168+
169+
await getHandlers.get("/cookies")?.({ params: {}, query: {} }, response.res);
170+
171+
expect(routeState.withPlaywrightRouteContext).toHaveBeenCalledWith(
172+
expect.objectContaining({ feature: "cookies", enforceCurrentUrlAllowed: true }),
173+
);
174+
expect(response.body).toEqual({ ok: true, targetId: "tab-1", cookies: [] });
175+
});
176+
177+
it("applies HTTP credentials without ever returning the password", async () => {
178+
const response = createBrowserRouteResponse();
179+
180+
await getPostHandler("/set/credentials")?.(
181+
{
182+
params: {},
183+
query: {},
184+
body: { username: "browser-user", password: "sensitive-browser-password" },
185+
},
186+
response.res,
187+
);
188+
189+
expect(routeState.setHttpCredentialsViaPlaywright).toHaveBeenCalledWith({
190+
cdpUrl: "http://127.0.0.1:18800",
191+
targetId: "tab-1",
192+
username: "browser-user",
193+
password: "sensitive-browser-password",
194+
clear: false,
195+
});
196+
expect(response.body).toEqual({ ok: true, targetId: "tab-1" });
197+
expect(routeState.withPlaywrightRouteContext).toHaveBeenCalledWith(
198+
expect.objectContaining({ feature: "http credentials" }),
199+
);
200+
});
201+
});

0 commit comments

Comments
 (0)