Is Publisher Discovery Extension safe?
Publisher Discovery Extension sends visited page URLs, account email, clicks, and non-password form values to its servers.
When a logged-in user changes tabs, navigates, or focuses a browser window, the extension records the active page URL and domain with the user's account email. Its content script also runs on every site, captures click labels and non-password input or select values, and forwards those analytics events to Publisher Discovery.
Who publishes itPublisher Discovery - no other listings under this identity
Publisher Discovery - no other listings under this identity
What this publisher told the store about itself, and the other listings that told it the same thing.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Form values and click labels sent to Publisher Discovery
We observed the all-sites content script collect click text, element identifiers, field names, and non-password form values, forwarding events to app.publisherdiscovery.com/api/tracking/events once logged in, batched every two seconds.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Dynamic sandbox
You click page content or change a non-password form field.
The content script runs on every website matched by the extension manifest.
The extension records click labels, element identifiers, field names, and non-password values, then queues them for transmission.
Dynamic analysis confirmed `input_change` events with a planted non-password input marker and `click` events with clicked page text.
- Your non-password form valuepd_form_canary_7f3c21
A value you type or select can reveal searches, account details, preferences, or other page-specific entries even when it is not a password.
- The field name or IDaccount_email
The field label can explain what the value represents, such as an email field, search box, coupon field, or account setting.
- The page contexthttps://example-shop.test/account/preferences
The page URL and domain connect the event to the site where you interacted.
- The clicked textpd_click_canary_7f3c21
Text from the element you clicked can show what option, link, or button you selected on the page.
- The element selector and ID#newsletter-signup
Selectors and IDs add page structure around the click or form event, making the interaction easier to interpret later.
Every 2 seconds
The content script flushes queued interaction events every two seconds while the page is active and sends any pending session event when the page loses focus.
Dynamic analysis confirmed a POST to this endpoint containing `input_change` with a planted non-password input marker and `click` with clicked page text.
- Accept
- application/json
- Content-Type
- application/json
- Authorization
- Bearer <redacted>
Shipped code that captures page interactions and forwards the batch
Readable interpretation of the content-script collection path
analytics.jsfunction initializeInteractionTracking() { captureCurrentPageContext(); document.addEventListener('click', event => { queueAnalyticsEvent('click', { element: selectorFor(event.target), text: event.target.innerText?.substring(0, 30) || 'no-text', id: event.target.id }); }); document.addEventListener('change', event => { const target = event.target; if (target.tagName !== 'INPUT' && target.tagName !== 'SELECT') return; if (target.type === 'password') return; queueAnalyticsEvent('input_change', { field: target.id || target.name, value: target.value }); }); const startTime = Date.now(); window.addEventListener('blur', () => { queueAnalyticsEvent('session_end', { duration: Date.now() - startTime }); flushQueuedEvents(); });}Readable interpretation of the two-second batch handoff
analytics.jsasync function flushQueuedEvents() { if (eventQueue.length === 0) return; if (isSaving) return; isSaving = true; const batch = [...eventQueue]; eventQueue = []; try { chrome.runtime.sendMessage({ type: 'SYNC_ANALYTICS', payload: batch }); } catch (error) { eventQueue = [...batch, ...eventQueue]; } finally { isSaving = false; }}Readable interpretation of the background forwarding path
background.jsfunction handleAnalyticsMessage(request, sendResponse) { if (request.type !== 'SYNC_ANALYTICS') return false; if (USER_TOKEN && request.payload && request.payload.length > 0) { pdApiCall('/api/tracking/events', 'POST', request.payload); } sendResponse({ success: true }); return true;}Readable interpretation of the JSON POST helper
background.jsasync function postToPublisherDiscovery(endpoint, body) { if (!USER_TOKEN) { const stored = await chrome.storage.local.get(['pd_token']); USER_TOKEN = stored.pd_token; if (!USER_TOKEN) return null; } const response = await fetch(`https://app.publisherdiscovery.com${endpoint}`, { method: 'POST', headers: { Authorization: `Bearer ${USER_TOKEN}`, 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify(body) }); if (!response.ok) return null; const text = await response.text(); return text ? JSON.parse(text) : {};}- app.publisherdiscovery.com
Publisher Discovery tracking API endpoint that receives the content-script event batch when the background worker has a user token.
Where it sends data
Destinations our analysis observed Publisher Discovery Extension contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.
- app.publisherdiscovery.com
Publisher Discovery Extension sends data to app.publisherdiscovery.com. No other extension we have analysed sends data here.