Is WPS PDF - Read, Edit, Fill, Convert, and AI Chat PDF with Ease safe?
WPS PDF transmits persistent device and user identifiers to Google Analytics on every extension event.
Each time the extension is installed, activated, or used to view a PDF, background.js collects a user_id, device_id, install_id, chrome_instance_id, country code, OS, and browser fingerprint and sends them via POST to Google Analytics. The extension also requests broad permissions — <all_urls>, webRequest, cookies, and nativeMessaging — which allow it to intercept HTTP and file:// PDF downloads across every website. An opt-out preference (sendUsageData) exists but is not surfaced prominently.
Who publishes itwww.wps.com - 1 other listing from the same operator, none carrying a finding
www.wps.com - 1 other listing from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
1 other listing published from this account, 6.0M+ users between them, none of them carrying a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Server-Controlled Script Injection on Competitor PDF Sites
WPS PDF's service worker POSTs to ap.wps.com on start for promotion rules.
Campaign 8130 targeted 12 competitor sites (ilovepdf.com, smallpdf.com, etc) plus 130 Google/Bing patterns; the server sets targets at runtime, none in manifest.
You open your browser with WPS PDF installed.
The extension's service worker starts and fetches promotion configuration from the WPS server before any tab is visited.
Content scripts are registered at runtime on competitor PDF sites and search pages using match patterns supplied by the server.
During dynamic analysis, the server directed registration on six competitor PDF services (ilovepdf.com, smallpdf.com, pdf24.org, sejda.com, pdfcandy.com, pdfgear.com) and 130 Google and Bing search URL patterns, none of which are listed in the extension's manifest.
| Content-Type | application/json |
{
"channel_code": "WPWP1001",
"platform": 8
}Dynamic content script registration using server-supplied match patterns (url_match type)
// Constants (background.js):
// Ut = 'url-match-promotion-dynamic'
// ot = 'https://ap.wps.com/tiance/dce/exec/api/market/activity'
// nt = 'promotion_config_cache'
async function registerUrlMatchScripts(rules) {
// Keep only url_match rules that are enabled
const urlMatchRules = (rules || []).filter(
r => r.type === 'url_match' && r.enabled
);
if (!urlMatchRules.length) {
// No rules — remove any previously registered script
try {
await chrome.scripting.unregisterContentScripts(
{ ids: ['url-match-promotion-dynamic'] }
);
} catch {}
return;
}
// Build match patterns from server-supplied fields.
// Priority: match_patterns field → fallback to domains/sites fields.
const matchPatterns = [...new Set(
urlMatchRules.flatMap(rule =>
Array.isArray(rule.match_patterns) && rule.match_patterns.length > 0
? rule.match_patterns // server explicit patterns
: buildMatchPatterns(rule.domains || rule.sites || []) // Ft()
)
)];
if (matchPatterns.length) {
// Replace old registration with new server-supplied patterns
try {
await chrome.scripting.unregisterContentScripts(
{ ids: ['url-match-promotion-dynamic'] }
);
} catch {}
// Inject promotion.js on every server-specified URL — not from manifest
await chrome.scripting.registerContentScripts([{
id: 'url-match-promotion-dynamic',
matches: matchPatterns, // <-- entirely server-controlled
js: ['content-scripts/promotion.js'],
runAt: 'document_end'
}]);
}
}- ap.wps.com
WPS Office server supplying promotion rules, controlling which sites get content-script injection. Rules cache up to 6 hours, applied on each page visit.
- ilovepdf.com
Competitor PDF service. Received promotion.js injection via url-match-promotion-dynamic during observed session.
- smallpdf.com
Competitor PDF service. Received promotion.js injection via url-match-promotion-dynamic during observed session.
- pdf24.org
Competitor PDF service. Received promotion.js injection via url-match-promotion-dynamic during observed session.
- sejda.com
Competitor PDF service. Received promotion.js injection via url-match-promotion-dynamic during observed session.
- pdfcandy.com
Competitor PDF service. Received promotion.js injection via url-match-promotion-dynamic during observed session.
- pdfgear.com
Competitor PDF service. Received promotion.js injection via url-match-promotion-dynamic during observed session.
- google.com
Search pages. Received search-promotion.js via keyword-match-promotion-dynamic. 36 country Google TLDs (co.uk, co.jp, de, fr, etc.) appear, covering /search* and /webhp* paths.
- bing.com
Search pages. Received search-promotion.js injection via keyword-match-promotion-dynamic. Patterns cover *.bing.com, cn.bing.com, and global.bing.com on /search* paths.
Queries the Chrome scripting API to reveal any dynamically registered content scripts that WPS PDF has installed at runtime. These scripts are not declared in the extension manifest and are not visible in the Chrome Web Store listing.
/**
* detect-wps-dynamic-scripts.js
*
* Reveals content scripts dynamically registered by WPS PDF (kdpelmjpfafjppnhbloffcjpeomlnpah)
* that are not declared in the extension manifest.
*
* Run from the WPS PDF service worker DevTools console:
* 1. Open chrome://extensions
* 2. Enable Developer Mode (top-right toggle)
* 3. Click "service worker" under WPS PDF
* 4. Paste this script into the Console and press Enter
*/
(async () => {
const DYNAMIC_IDS = [
'url-match-promotion-dynamic',
'keyword-match-promotion-dynamic'
];
let registered;
try {
registered = await chrome.scripting.getRegisteredContentScripts();
} catch (e) {
console.error('[WPS PDF detector] Could not query content scripts:', e.message);
return;
}
const dynamic = registered.filter(s => DYNAMIC_IDS.includes(s.id));
if (dynamic.length === 0) {
console.log('[WPS PDF detector] No dynamic promotion scripts currently registered.');
console.log(' (Rules may have been cleared, or the server returned no rules this session.)');
return;
}
console.log(`[WPS PDF detector] Found ${dynamic.length} dynamic script registration(s):`);
for (const script of dynamic) {
console.group(` id: ${script.id}`);
console.log(' js:', script.js);
console.log(` match patterns (${script.matches.length} total):`);
script.matches.forEach((p, i) => console.log(` [${i}] ${p}`));
console.groupEnd();
}
// Also show the cached promotion config to see the originating campaign
try {
const stored = await chrome.storage.local.get(['promotion_config_cache']);
const cache = stored['promotion_config_cache'];
if (cache) {
console.log('\n[WPS PDF detector] Cached promotion config:');
console.log(' campaign_id:', cache.campaign_id);
console.log(' rule count:', (cache.rules || []).length);
console.log(' cached at:', new Date(cache.timestamp).toISOString());
console.log(' expires at:', new Date(cache.timestamp + 216e5).toISOString(), '(6-hour TTL)');
} else {
console.log('\n[WPS PDF detector] No cached promotion config found in storage.');
}
} catch (e) {
console.warn('[WPS PDF detector] Could not read storage:', e.message);
}
})();- 1Open chrome://extensions → enable Developer Mode → click 'service worker' under WPS PDF → paste into the Console tab and press Enter.
Browser Notifications Used as Server-Controlled Ad Channel
When you visit flagged pages, WPS PDF's worker fetches config from ap.wps.com and fires a notification whose title, message, button come from that response.
A Google 'pdf' search triggered a matching notification, capped at 2/category/day.
You search for "pdf" on Google (or visit another page matching a server-supplied keyword or URL rule).
The extension's background worker had already fetched and cached the current promotion rules before you navigated.
The extension fires a native Chrome notification whose title, message, and button text are pulled directly from the server's rule object.
In a captured session, the notification read "Local Conversion, No Formatting Loss / Perfectly preserve layouts and tables with blazing fast speed" with a "Try it now" button, matching the cached rule verbatim.
| Content-Type | application/json |
{
"channel_code": "WPWP1001",
"platform": 8
}Notification content comes from the server rule, not a built-in string
// background.js — bound to runtime message action 'showPromotionPopup'
const showPromotionPopup = async (msg) => {
const { rule_id } = msg;
try {
// fetchOrCachedPromotionConfig() — reads promotion_config_cache,
// re-fetches from ap.wps.com if the 6-hour TTL has expired
const config = await fetchOrCachedPromotionConfig();
if (!config?.rules) return;
const rule = config.rules.find(r => r.id === rule_id);
if (!rule) return;
const notificationId = `promotion_${rule_id}`;
const options = {
type: "basic",
iconUrl: rule.image_url, // <-- server-controlled
title: rule.title, // <-- server-controlled
message: rule.body, // <-- server-controlled
buttons: [{ title: rule.button_text || "OK" }], // <-- server-controlled
priority: 2,
requireInteraction: true
};
chrome.notifications.clear(notificationId, () => {
chrome.notifications.create(notificationId, options, () => { /* ... */ });
});
} catch (e) {}
};A per-category, per-style frequency counter (chrome.storage.local key promotion_frequency_state) caps notifications at 2 per day and 7 per week, with a minimum 2-hour gap between any two, before the extension will show another one. The counter resets on a rolling daily/weekly window computed client-side, independent of whether the server sends new rules.
Reads the cached promotion configuration and frequency-tracking state from WPS PDF's own extension storage to show which notification rules are currently armed and how close each category is to its daily/weekly cap.
/**
* detect-wps-notification-rules.js
*
* Reveals cached, server-controlled notification rules and frequency-cap
* state inside WPS PDF (kdpelmjpfafjppnhbloffcjpeomlnpah).
*
* Run from the WPS PDF service worker DevTools console:
* 1. Open chrome://extensions
* 2. Enable Developer Mode (top-right toggle)
* 3. Click "service worker" under WPS PDF
* 4. Paste this script into the Console and press Enter
*/
(async () => {
const stored = await chrome.storage.local.get([
'promotion_config_cache',
'promotion_frequency_state'
]);
const cache = stored['promotion_config_cache'];
if (!cache) {
console.log('[WPS PDF detector] No cached promotion config found.');
return;
}
console.log('[WPS PDF detector] campaign_id:', cache.campaign_id);
console.log('[WPS PDF detector] cached at:', new Date(cache.timestamp).toISOString());
console.log('[WPS PDF detector] expires at:', new Date(cache.timestamp + 216e5).toISOString(), '(6h TTL)');
const notifRules = (cache.rules || []).filter(r => r.popup_style === 'notification');
console.log(`\n[WPS PDF detector] ${notifRules.length} notification rule(s) cached:`);
for (const r of notifRules) {
console.group(` id: ${r.id} (category: ${r.category})`);
console.log(' title:', r.title);
console.log(' message:', r.body);
console.log(' button_text:', r.button_text);
console.log(' button_url:', r.button_url);
console.log(' keywords:', r.keywords || r.match_patterns || '(url_match rule)');
console.groupEnd();
}
console.log('\n[WPS PDF detector] Frequency-cap state:');
console.log(JSON.stringify(stored['promotion_frequency_state'] || {}, null, 2));
})();- 1Open chrome://extensions, enable Developer Mode.
- 2Click "service worker" under WPS PDF.
- 3Paste script, press Enter.
- 4Review the rule list and frequency counters for armed notifications.
What it can do
Permissions this extension asks for, as declared in version 1.0.0.52. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 1.0.0.61, which we have not unpacked yet.
Read and change your data on every site you visit
<all_urls>
Watch every request your browser makes
webRequest
See every page you navigate to, as you navigate to it
webNavigation
Store data in your browser
storage
See the address and title of every tab you have open
tabs
Talk to a program installed on your computer, outside the browser's sandbox
nativeMessaging
Block and redirect the requests your browser makes
declarativeNetRequest
Receive push messages from its developer's servers
gcm
Read and change cookies, including the ones that keep you signed in
cookies
Where it sends data
Destinations our analysis observed WPS PDF 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.
- www.google-analytics.comwidely used
WPS PDF sends data to www.google-analytics.com. A widely used service: 346 other extensions we have analysed send data here.