Is Arizona Helper by Sakai safe?
Arizona Helper by Sakai sends saved VK tokens and Discord webhook URLs to a complaint-transfer proxy.
When complaint transfer is used, the extension sends saved VK group tokens or Discord webhook URLs with generated complaint text to vktransfer-helper-proxy.onrender.com. Its logsparser feature can also send login IP addresses to ip-api.com over cleartext HTTP to fetch geolocation details.
Who publishes itNo other listings under this identity, 2 shared hostnames
No other listings under this identity, 2 shared hostnames
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 2 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Complaint transfer sends VK and Discord credentials to proxy
Code analysis shows Arizona Helper by Sakai's complaint transfer reads the VK group token or Discord webhook URL from settings and sends it to vktransfer-helper-proxy.onrender.com with the complaint message and thread URL.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You click the complaint-transfer control after configuring VK or Discord delivery.
The content script prepares the complaint message from the current forum or logs workflow.
The extension sends the saved posting credential and complaint content to a helper proxy.
The background worker chooses the VK token path first, then the Discord webhook path if no VK token is saved.
- VK group tokenvk1.a.AQAAcV8m4x7s9QK-example-token
Lets the proxy submit VK messages using the group token you saved in the extension settings.
- Discord webhook URLhttps://discord.com/api/webhooks/123456789012345678/AbCdEfGhIjKlMnOpQrStUvWxYz0123456789
Lets the proxy post to the Discord webhook you connected for complaint delivery.
- Complaint thread URLhttps://forum.arizona-rp.com/threads/1234567/
Shows which forum thread you were working from when the transfer was sent.
- Forum nicknamesSender_Nick -> Admin_Nick
Links the complaint transfer to the sender nickname and the administrator nickname in the page workflow.
- Comment and selected log textКомментарий к жалобе: проверка доказательств; Строка из логов: [12:34:56] Admin_Nick action
Carries the complaint text you entered or selected from the logs page.
- Content-Type
- application/json
- Content-Type
- application/json
The settings, trigger, and proxy-sending code paths are in the distributed JavaScript.
Proxy endpoints declared by the background worker
background.jsconst API_BASE_URL = 'https://helper.admin-wednesday.com';const SHINOA_TECH_URL = 'https://logs.shinoa.tech';const VK_TRANSFER_PROXY_URL = "https://vktransfer-helper-proxy.onrender.com/api/sendvk";const DISCORD_TRANSFER_PROXY_URL = "https://vktransfer-helper-proxy.onrender.com/api/senddiscord";Complaint-transfer handler reads saved credentials
background.js case 'transfer_complaint': (async () => { try { const { arizonaHelperSettings } = await chrome.storage.local.get('arizonaHelperSettings'); const storedSettings = arizonaHelperSettings || {}; const sendToChat = request.vkSendToChat ?? storedSettings.vkSendToChat ?? false; const chatId = request.vkChatId ?? storedSettings.vkChatId ?? ''; if (storedSettings.vkGroupToken) { const response = await transferComplaintViaVkApi(storedSettings.vkGroupToken, request.admin.vk, request.messageData, sendToChat, chatId); sendResponse(response); return; } if (storedSettings.discordWebhookUrl) { const response = await transferComplaintViaDiscordWebhook(storedSettings.discordWebhookUrl, request.messageData, storedSettings.discordUserMappings); sendResponse(response); return; }Settings UI stores credentials and the page script sends complaint data
script.js <div class="fn-xfer-field"> <span class="fn-xfer-field-label">Токен группы VK</span> <div class="fn-xfer-input-wrap"> <span class="fn-xfer-input-icon">${createIconHTML('key')}</span> <input type="password" id="vk-group-token-input" class="fn-xfer-input" value="${esc(settings.vkGroupToken || '')}" placeholder="Введите токен группы..." autocomplete="off"> </div> <div class="fn-xfer-field"> <span class="fn-xfer-field-label">Webhook URL</span> <div class="fn-xfer-input-wrap"> <span class="fn-xfer-input-icon">${createIconHTML('link')}</span> <input type="password" id="discord-webhook-url" class="fn-xfer-input" value="${esc(settings.discordWebhookUrl || '')}" placeholder="https://discord.com/api/webhooks/..." autocomplete="off"> </div> settings.vkGroupToken = document.getElementById('vk-group-token-input').value.trim(); settings.vkSendToChat = document.getElementById('vk-send-to-chat').checked; settings.vkChatId = document.getElementById('vk-chat-id-input').value.trim(); settings.vkComplaintTemplate = document.getElementById('vk-complaint-template').value; settings.discordWebhookUrl = document.getElementById('discord-webhook-url').value.trim(); settings.discordComplaintTemplate = document.getElementById('discord-complaint-template').value; settings.discordUserMappings = document.getElementById('discord-user-mappings').value; const messagePayload = { vkTemplate: currentSettings.vkComplaintTemplate, discordTemplate: currentSettings.discordComplaintTemplate, tags: { thread: window.location.href, your_nick: senderNick, nick_name: admin.nick, discord: admin.discord }, comment: comment, selectedLog: selectedLog }; try { const response = await chrome.runtime.sendMessage({ action: 'transfer_complaint', admin: admin, messageData: messagePayload, vkSendToChat: currentSettings.vkSendToChat, vkChatId: currentSettings.vkChatId });Functions that construct and send the proxy POSTs
background.jsasync function transferComplaintViaVkApi(token, vkId, messageData, sendToChat, chatId) { const useChat = sendToChat === true; const chatIdentifier = (typeof chatId === 'string') ? chatId.trim() : ''; const userIdentifier = (typeof vkId === 'string') ? vkId.trim() : ''; if (useChat) { if (!chatIdentifier) { throw new Error("Отправка в беседу включена, но ID беседы не указан в настройках."); } } else { if (!userIdentifier) { throw new Error("Не удалось получить VK ID администратора для отправки сообщения."); } } if (!messageData || !messageData.vkTemplate || !messageData.tags) { throw new Error("Отсутствуют данные для формирования сообщения."); } let peerId = ''; if (useChat) { const parsedChatId = parseInt(chatIdentifier, 10); if (!isNaN(parsedChatId) && parsedChatId < 2000000000) { peerId = (2000000000 + parsedChatId).toString(); } else { peerId = chatIdentifier; } } else { let finalVkId = userIdentifier; if (finalVkId.startsWith('id') && !isNaN(parseInt(finalVkId.substring(2)))) { finalVkId = finalVkId.substring(2); } else if (isNaN(parseInt(finalVkId))) { try { const resolveUrl = `https://api.vk.ru/method/users.get?user_ids=${finalVkId}&access_token=${token}&v=5.199`; const resolveResponse = await fetch(resolveUrl); const resolveData = await resolveResponse.json(); if (resolveData.response && resolveData.response[0] && resolveData.response[0].id) { finalVkId = resolveData.response[0].id; } else { throw new Error(resolveData.error ? resolveData.error.error_msg : 'Неизвестная ошибка при получении ID.'); } } catch (e) { throw new Error(`Не удалось преобразовать короткое имя VK "${finalVkId}" в ID. Ошибка: ${e.message}`); } } peerId = finalVkId; } let finalMessage = messageData.vkTemplate; finalMessage = finalMessage.replace(/{thread}/g, messageData.tags.thread); finalMessage = finalMessage.replace(/{your_nick}/g, messageData.tags.your_nick); finalMessage = finalMessage.replace(/{nick_name}/g, messageData.tags.nick_name); if (messageData.comment) { finalMessage += `\n\n🗒 Комментарий к жалобе: ${messageData.comment}`; } if (messageData.selectedLog) { finalMessage += `\n\n⛓️💥 Строка из логов:\n${messageData.selectedLog}`; } const payload = { token: token, message: finalMessage, vkId: peerId }; const response = await fetch(VK_TRANSFER_PROXY_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || `Сервер-посредник ответил с кодом ${response.status}`); } const data = await response.json(); if (!data.success) { throw new Error(data.message); } return { success: true };}async function transferComplaintViaDiscordWebhook(webhookUrl, messageData, mappings) { if (!messageData || !messageData.discordTemplate || !messageData.tags) { throw new Error("Отсутствуют данные для формирования сообщения Discord."); } const userMappingsText = mappings || ''; const userMappings = {}; userMappingsText.split('\n').forEach(line => { if (line.includes('=')) { const parts = line.split('='); const nick = parts[0].trim().replace(/"/g, ''); const id = parts[1].trim().replace(/"/g, ''); if (nick && id) { userMappings[nick] = id; } } }); let content = messageData.discordTemplate; content = content.replace(/{thread}/g, messageData.tags.thread); content = content.replace(/{your_nick}/g, messageData.tags.your_nick); content = content.replace(/{nick_name}/g, messageData.tags.nick_name); const recipientNick = messageData.tags.nick_name; const discordId = userMappings[recipientNick]; const pingText = discordId ? `<@${discordId}>` : recipientNick; content = content.replace(/<@{discord_ping}>/g, pingText); const payload = { username: "Передача жалоб", avatar_url: "https://i.imgur.com/b5r3v2y.png", content: content, embeds: [], "allowed_mentions": { "parse": ["users"] } }; if (messageData.comment) { payload.embeds.push({ color: 15158332, fields: [{ name: "🗒️ Комментарий к жалобе:", value: messageData.comment }] }); } if (messageData.selectedLog) { if (payload.embeds.length === 0) { payload.embeds.push({ color: 15158332, fields: [] }); } payload.embeds[0].fields.push({ name: "⛓️💥 Строка из логов:", value: "```" + messageData.selectedLog + "```" }); } const response = await fetch(DISCORD_TRANSFER_PROXY_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ webhookUrl, payload }) }); if (response.status !== 204) { const errorData = await response.json(); throw new Error(errorData.message || `Сервер-посредник ответил с кодом ${response.status}`); } return { success: true };}- vktransfer-helper-proxy.onrender.com
Receives the VK group token or Discord webhook URL together with generated complaint content.
- api.vk.ru
May be contacted directly by the extension to resolve a VK screen name before the proxy request is sent.
Where it sends data
Destinations our analysis observed Arizona Helper by Sakai 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.
- ip-api.comwidely used
Arizona Helper by Sakai sends data to ip-api.com. A widely used service: 62 other extensions we have analysed send data here.
- vktransfer-helper-proxy.onrender.com
Arizona Helper by Sakai sends data to vktransfer-helper-proxy.onrender.com. No other extension we have analysed sends data here.