From 753d7da33c636a732d5dead916abad035a08c50b Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 13 Jan 2026 21:14:55 +0100 Subject: [PATCH 1/4] Initial commit with task details Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/link-foundation/browser-commander/issues/23 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..906beb1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/link-foundation/browser-commander/issues/23 +Your prepared branch: issue-23-4ea2027e76ed +Your prepared working directory: /tmp/gh-issue-solver-1768335294589 + +Proceed. From 6ef35e4ab8540163875a3338875637f80641650a Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 13 Jan 2026 21:17:29 +0100 Subject: [PATCH 2/4] Fix normalizeSelector to validate input type and reject arrays Previously, normalizeSelector had a fallback that returned any selector unchanged if it didn't match expected types. This caused issues when arrays were accidentally passed, leading to downstream querySelectorAll errors with invalid selector syntax (trailing comma). Changes: - Add explicit type validation at the start of normalizeSelector - Return null with a warning for arrays and invalid object types - Only accept strings and valid Puppeteer text selector objects - Add tests for array, number, and invalid object rejection - Add test for valid Puppeteer text selector object acceptance Fixes #23 Co-Authored-By: Claude Opus 4.5 --- js/src/elements/selectors.js | 21 +++++++++++++++ js/tests/unit/elements/selectors.test.js | 34 ++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/js/src/elements/selectors.js b/js/src/elements/selectors.js index b5b6dbc..0b00d1b 100644 --- a/js/src/elements/selectors.js +++ b/js/src/elements/selectors.js @@ -162,6 +162,25 @@ export async function normalizeSelector(options = {}) { throw new Error('selector is required in options'); } + // DEFENSIVE: Validate selector type - must be string or Puppeteer text selector object + // Arrays and other invalid types should be rejected to prevent downstream querySelectorAll errors + if (Array.isArray(selector)) { + console.warn( + `normalizeSelector received invalid selector type: array. Expected string or text selector object.` + ); + return null; + } + + if ( + typeof selector !== 'string' && + (typeof selector !== 'object' || !selector._isPuppeteerTextSelector) + ) { + console.warn( + `normalizeSelector received invalid selector type: ${typeof selector}. Expected string or text selector object.` + ); + return null; + } + // Handle Playwright text selectors (strings containing :has-text or :text-is) // These are valid for Playwright's locator API but NOT for document.querySelectorAll if ( @@ -267,6 +286,8 @@ export async function normalizeSelector(options = {}) { } } + // This line should be unreachable after validation, but kept as a safeguard + // istanbul ignore next return selector; } diff --git a/js/tests/unit/elements/selectors.test.js b/js/tests/unit/elements/selectors.test.js index f92fc97..b1c848f 100644 --- a/js/tests/unit/elements/selectors.test.js +++ b/js/tests/unit/elements/selectors.test.js @@ -207,11 +207,41 @@ describe('selectors', () => { assert.strictEqual(result, 'button'); }); - it('should return non-text-selector object unchanged', async () => { + it('should return null for invalid object selector', async () => { const page = createMockPlaywrightPage(); const obj = { someKey: 'value' }; const result = await normalizeSelector({ page, selector: obj }); - assert.strictEqual(result, obj); + assert.strictEqual(result, null); + }); + + it('should return null for array selector', async () => { + const page = createMockPlaywrightPage(); + const arr = ['[data-qa="test"]', []]; + const result = await normalizeSelector({ page, selector: arr }); + assert.strictEqual(result, null); + }); + + it('should return null for number selector', async () => { + const page = createMockPlaywrightPage(); + const result = await normalizeSelector({ page, selector: 123 }); + assert.strictEqual(result, null); + }); + + it('should accept valid Puppeteer text selector object', async () => { + const page = createMockPuppeteerPage(); + page.evaluate = async () => '[data-qa="test"]'; + const textSelector = { + _isPuppeteerTextSelector: true, + baseSelector: 'button', + text: 'Click me', + exact: false, + }; + const result = await normalizeSelector({ + page, + engine: 'puppeteer', + selector: textSelector, + }); + assert.strictEqual(result, '[data-qa="test"]'); }); }); From e9043cc74101ff2938ca6332b89e4461c6424f98 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 13 Jan 2026 21:18:04 +0100 Subject: [PATCH 3/4] Add changeset for normalizeSelector fix Co-Authored-By: Claude Opus 4.5 --- .../fix-normalize-selector-array-validation.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 js/.changeset/fix-normalize-selector-array-validation.md diff --git a/js/.changeset/fix-normalize-selector-array-validation.md b/js/.changeset/fix-normalize-selector-array-validation.md new file mode 100644 index 0000000..3b42ad9 --- /dev/null +++ b/js/.changeset/fix-normalize-selector-array-validation.md @@ -0,0 +1,11 @@ +--- +'browser-commander': patch +--- + +Fix normalizeSelector to validate input type and reject arrays + +When `normalizeSelector` receives an invalid type (array, number, or non-text-selector object), it now returns `null` with a warning instead of returning the invalid value unchanged. + +This prevents downstream `querySelectorAll` errors with invalid selector syntax (like trailing commas when arrays are accidentally passed). + +Fixes #23 From 764df0900862247b99370313221de54fafc850a6 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 13 Jan 2026 21:23:42 +0100 Subject: [PATCH 4/4] Revert "Initial commit with task details" This reverts commit 753d7da33c636a732d5dead916abad035a08c50b. --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 906beb1..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/link-foundation/browser-commander/issues/23 -Your prepared branch: issue-23-4ea2027e76ed -Your prepared working directory: /tmp/gh-issue-solver-1768335294589 - -Proceed.