Skip to content

Commit 8fbdfc0

Browse files
committed
fix: validate browser geolocation numbers
1 parent 503d8d5 commit 8fbdfc0

2 files changed

Lines changed: 72 additions & 19 deletions

File tree

extensions/browser/src/cli/browser-cli-state.option-collisions.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,31 @@ describe("browser state option collisions", () => {
172172
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
173173
});
174174

175+
it("rejects invalid geolocation numbers before dispatch", async () => {
176+
await runBrowserCommand(["set", "geo", "48.208", "16.373", "--accuracy", "fast"]);
177+
178+
expect(mocks.callBrowserRequest).not.toHaveBeenCalled();
179+
expectErrorMessage("Invalid --accuracy: must be a finite number");
180+
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
181+
});
182+
183+
it("passes valid decimal geolocation numbers", async () => {
184+
const request = await runBrowserCommandAndGetRequest([
185+
"set",
186+
"geo",
187+
"48.2082",
188+
"16.3738",
189+
"--accuracy",
190+
"12.5",
191+
]);
192+
193+
expect(request.body).toMatchObject({
194+
latitude: 48.2082,
195+
longitude: 16.3738,
196+
accuracy: 12.5,
197+
});
198+
});
199+
175200
it("errors when headers JSON is missing", async () => {
176201
await runBrowserCommand(["set", "headers"]);
177202

extensions/browser/src/cli/browser-cli-state.ts

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ function parsePositiveInteger(value: unknown, label: string): number | undefined
2525
return parsed;
2626
}
2727

28+
function parseFiniteNumberOption(value: string | undefined, label: string): number | undefined {
29+
if (value === undefined) {
30+
return undefined;
31+
}
32+
const raw = value.trim();
33+
const parsed = /^[+-]?(?:(?:\d+\.?\d*)|(?:\.\d+))(?:e[+-]?\d+)?$/i.test(raw)
34+
? Number(raw)
35+
: Number.NaN;
36+
if (!Number.isFinite(parsed)) {
37+
defaultRuntime.error(danger(`Invalid ${label}: must be a finite number`));
38+
defaultRuntime.exit(1);
39+
return undefined;
40+
}
41+
return parsed;
42+
}
43+
2844
function runBrowserCommand(action: () => Promise<void>) {
2945
return runCommandWithRuntime(defaultRuntime, action, (err) => {
3046
defaultRuntime.error(danger(String(err)));
@@ -189,27 +205,39 @@ export function registerBrowserStateCommands(
189205
.command("geo")
190206
.description("Set geolocation (and grant permission)")
191207
.option("--clear", "Clear geolocation + permissions", false)
192-
.argument("[latitude]", "Latitude", (v: string) => Number(v))
193-
.argument("[longitude]", "Longitude", (v: string) => Number(v))
194-
.option("--accuracy <m>", "Accuracy in meters", (v: string) => Number(v))
208+
.argument("[latitude]", "Latitude")
209+
.argument("[longitude]", "Longitude")
210+
.option("--accuracy <m>", "Accuracy in meters")
195211
.option("--origin <origin>", "Origin to grant permissions for")
196212
.option("--target-id <id>", "CDP target id (or unique prefix)")
197-
.action(async (latitude: number | undefined, longitude: number | undefined, opts, cmd) => {
198-
const parent = parentOpts(cmd);
199-
await runBrowserSetRequest({
200-
parent,
201-
path: "/set/geolocation",
202-
body: {
203-
latitude: Number.isFinite(latitude) ? latitude : undefined,
204-
longitude: Number.isFinite(longitude) ? longitude : undefined,
205-
accuracy: Number.isFinite(opts.accuracy) ? opts.accuracy : undefined,
206-
origin: normalizeOptionalString(opts.origin),
207-
clear: Boolean(opts.clear),
208-
targetId: normalizeOptionalString(opts.targetId),
209-
},
210-
successMessage: opts.clear ? "geolocation cleared" : "geolocation set",
211-
});
212-
});
213+
.action(
214+
async (latitudeRaw: string | undefined, longitudeRaw: string | undefined, opts, cmd) => {
215+
const parent = parentOpts(cmd);
216+
const latitude = parseFiniteNumberOption(latitudeRaw, "latitude");
217+
const longitude = parseFiniteNumberOption(longitudeRaw, "longitude");
218+
const accuracy = parseFiniteNumberOption(opts.accuracy, "--accuracy");
219+
if (
220+
(latitudeRaw !== undefined && latitude === undefined) ||
221+
(longitudeRaw !== undefined && longitude === undefined) ||
222+
(opts.accuracy !== undefined && accuracy === undefined)
223+
) {
224+
return;
225+
}
226+
await runBrowserSetRequest({
227+
parent,
228+
path: "/set/geolocation",
229+
body: {
230+
latitude,
231+
longitude,
232+
accuracy,
233+
origin: normalizeOptionalString(opts.origin),
234+
clear: Boolean(opts.clear),
235+
targetId: normalizeOptionalString(opts.targetId),
236+
},
237+
successMessage: opts.clear ? "geolocation cleared" : "geolocation set",
238+
});
239+
},
240+
);
213241

214242
set
215243
.command("media")

0 commit comments

Comments
 (0)