diff --git a/cli/package.json b/cli/package.json index 558e8bc..5a8bc62 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@bproxy/cli", - "version": "0.9.2", + "version": "0.9.3", "private": true, "type": "module", "bin": { diff --git a/extension/package.json b/extension/package.json index 54dd9c8..759b455 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1,6 +1,6 @@ { "name": "@bproxy/extension", - "version": "0.9.2", + "version": "0.9.3", "private": true, "type": "module", "scripts": { diff --git a/extension/src/background/__tests__/dispatcher.test.ts b/extension/src/background/__tests__/dispatcher.test.ts index 3a78359..bd1b95f 100644 --- a/extension/src/background/__tests__/dispatcher.test.ts +++ b/extension/src/background/__tests__/dispatcher.test.ts @@ -97,13 +97,35 @@ describe("parseForwardedRequest", () => { JSON.stringify( makeRequest({ action: "links", - params: { selector: "#search", visibleOnly: true, limit: 10 }, + params: { + selector: "#search", + visibleOnly: true, + limit: 10, + hrefContains: "/in/", + offset: 5, + }, }), ), ); expect(parsed.success).toBe(true); }); + it("rejects negative links offset at the forwarded request boundary", () => { + const parsed = parseForwardedRequest( + JSON.stringify( + makeRequest({ + action: "links", + params: { limit: 10, offset: -1 }, + }), + ), + ); + expect(parsed).toMatchObject({ + success: false, + id: "req-1", + error: "params are invalid for action links", + }); + }); + it("accepts click and hover params for forwarded DOM interactions", () => { expect( parseForwardedRequest( diff --git a/extension/src/background/forwarded-params.ts b/extension/src/background/forwarded-params.ts index 5279ca1..0c0f623 100644 --- a/extension/src/background/forwarded-params.ts +++ b/extension/src/background/forwarded-params.ts @@ -45,11 +45,15 @@ function isSelectorParams(value: unknown): value is ForwardedActionParams["text" } function isLinksParams(value: unknown): value is ForwardedActionParams["links"] { + if (!isStrictObject(value, ["selector", "visibleOnly", "limit", "hrefContains", "offset"])) { + return false; + } return ( - isStrictObject(value, ["selector", "visibleOnly", "limit"]) && - (value["selector"] === undefined || typeof value["selector"] === "string") && - (value["visibleOnly"] === undefined || typeof value["visibleOnly"] === "boolean") && - (value["limit"] === undefined || isInteger(value["limit"])) + optionalFieldMatches(value, "selector", isString) && + optionalFieldMatches(value, "visibleOnly", isBoolean) && + optionalFieldMatches(value, "limit", isInteger) && + optionalFieldMatches(value, "hrefContains", isString) && + optionalFieldMatches(value, "offset", isNonNegativeInteger) ); } @@ -232,10 +236,31 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } +function optionalFieldMatches( + value: Record, + key: string, + validator: (field: unknown) => boolean, +): boolean { + const field = value[key]; + return field === undefined || validator(field); +} + +function isString(value: unknown): value is string { + return typeof value === "string"; +} + +function isBoolean(value: unknown): value is boolean { + return typeof value === "boolean"; +} + function isInteger(value: unknown): value is number { return typeof value === "number" && Number.isInteger(value); } +function isNonNegativeInteger(value: unknown): value is number { + return isInteger(value) && value >= 0; +} + export function isTarget(value: unknown): value is BproxyForwardedRequest["target"] { return isStrictObject(value, ["tabId"]) && (value["tabId"] === null || isInteger(value["tabId"])); } diff --git a/package.json b/package.json index ecd8437..aa59500 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "bproxy", "private": true, "type": "module", - "version": "0.9.2", + "version": "0.9.3", "description": "Human-in-the-loop browser proxy for AI agents.", "license": "MIT", "packageManager": "pnpm@9.15.0", diff --git a/service/package.json b/service/package.json index 050b356..b8808ba 100644 --- a/service/package.json +++ b/service/package.json @@ -1,6 +1,6 @@ { "name": "@bproxy/service", - "version": "0.9.2", + "version": "0.9.3", "private": true, "type": "module", "bin": { diff --git a/shared/package.json b/shared/package.json index 9ccbdaa..19a38a3 100644 --- a/shared/package.json +++ b/shared/package.json @@ -1,6 +1,6 @@ { "name": "@bproxy/shared", - "version": "0.9.2", + "version": "0.9.3", "private": true, "type": "module", "exports": { diff --git a/shared/src/version.ts b/shared/src/version.ts index 7da4792..2e11739 100644 --- a/shared/src/version.ts +++ b/shared/src/version.ts @@ -6,7 +6,7 @@ */ /** Current bproxy package version (semver). */ -export const VERSION = "0.9.2"; +export const VERSION = "0.9.3"; /** Protocol version for the daemon↔CLI↔extension wire format. */ export const PROTOCOL_VERSION = 2; diff --git a/skills/bproxy/SKILL.md b/skills/bproxy/SKILL.md index 7355518..e20d5d0 100644 --- a/skills/bproxy/SKILL.md +++ b/skills/bproxy/SKILL.md @@ -9,7 +9,7 @@ description: >- compatibility: Node >=24, bproxy installed (npm install -g @dimdasci/bproxy), daemon running, extension paired license: MIT metadata: - version: "0.9.2" + version: "0.9.3" --- # bproxy diff --git a/views/package.json b/views/package.json index cf82cb7..c5a0292 100644 --- a/views/package.json +++ b/views/package.json @@ -1,7 +1,7 @@ { "name": "@bproxy/views", "private": true, - "version": "0.9.2", + "version": "0.9.3", "type": "module", "scripts": { "dev": "astro dev",