Is Аналитика и инструменты Авитолога - AvToolsPro safe?
Аналитика и инструменты Авитолога - AvToolsPro is high risk. Initializing its ad-cost tool on Avito pages, AvToolsPro requests your Avito OpenAPI client record, builds an OAuth token request with the client ID and credential, and posts that object to av-tools.pro/ext/request_avito.
Who publishes itИнструменты - 2 other listings from the same operator, 2 of them carrying a finding
Инструменты - 2 other listings from the same operator, 2 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
2 other listings published from this account, 10k+ users between them. 2 of them carry a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Avito OpenAPI credential is forwarded to av-tools.pro
Initializing its ad-cost tool on Avito pages, AvToolsPro requests your Avito OpenAPI client record, builds an OAuth token request with the client ID and credential, and posts that object to av-tools.pro/ext/request_avito.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You open or use AvToolsPro promotion controls while signed in to Avito.
The relevant component is loaded only on avito.ru and www.avito.ru pages.
The extension reads your Avito OpenAPI client credentials and forwards the token request through av-tools.pro.
The code constructs the token request locally, then posts the request object to the vendor proxy endpoint.
- Token request URLhttps://api.avito.ru/token
Shows which Avito API operation the extension is preparing on your behalf.
- Client IDclient_id=123456789 (illustrative)
Identifies the OpenAPI client associated with your Avito account or business integration.
- Client credentialclient_secret=<redacted>
Can be paired with the client ID to request Avito API access tokens for that OpenAPI client.
- Grant typegrant_type=client_credentials
Shows that the request is for a client-credentials access token.
The source code forwards the request object to this endpoint; no live body capture is available.
Credential source, token request, and proxy endpoint
Readable A_ class builds the token request and forwards it through request()
main.js class A_ { constructor(e, n) { this.client_id = e, this.client_secret = n, this.token = null, this.tokenExpiration = null, this.cache_cpxGetBids = {}; } _oauthStorageBase() { const e = encodeURIComponent(String(this.client_id ?? "")); let n = 0; const s = String(this.client_secret ?? ""); for (let r = 0; r < s.length; r++) n = Math.imul(31, n) + s.charCodeAt(r) | 0; return `avito_oauth_${e}_${(n>>>0).toString(36)}` } _tokenKey() { return `${this._oauthStorageBase()}_access` } _tokenExpKey() { return `${this._oauthStorageBase()}_exp` } async getToken() { const e = Number(localStorage.getItem(this._tokenExpKey())), n = localStorage.getItem(this._tokenKey()); if (!this.token && n && Date.now() < e) { this.token = n, this.tokenExpiration = e; return } const a = await (await this.request({ url: "https://api.avito.ru/token", method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, form: { client_id: this.client_id, client_secret: this.client_secret, grant_type: "client_credentials" } })).json(); this.token = a.access_token, this.tokenExpiration = Date.now() + a.expires_in * 1e3, localStorage.setItem(this._tokenKey(), this.token), localStorage.setItem(this._tokenExpKey(), String(this.tokenExpiration)); } async getVasPrices(e, n) { (!this.token || Date.now() > this.tokenExpiration) && await this.getToken(); const s = `https://api.avito.ru/core/v1/accounts/${e}/vas/prices`; return await (await this.request({ url: s, method: "POST", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, json: { itemIds: n } })).json() } async addVasServices(e, n) { (!this.token || Date.now() > this.tokenExpiration) && await this.getToken(); const s = `https://api.avito.ru/core/v2/items/${e}/vas/`; return await (await this.request({ url: s, method: "PUT", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, json: { slugs: n } })).json() } async cpxSetManualBid(e) { (!this.token || Date.now() > this.tokenExpiration) && await this.getToken(); const s = await this.request({ url: "https://api.avito.ru/cpxpromo/1/setManual", method: "POST", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, json: e }); if (!s.ok) throw new Error(await s.text()); return await s.json() } async cpxSetAutoBid(e) { (!this.token || Date.now() > this.tokenExpiration) && await this.getToken(); const s = await this.request({ url: "https://api.avito.ru/cpxpromo/1/setAuto", method: "POST", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, json: e }); if (!s.ok) throw new Error(await s.text()); return await s.json() } async cpxGetBids(e, n = !0) { return n || localStorage.removeItem("cache_cpxGetBids_" + e), (!this.token || Date.now() > this.tokenExpiration) && await this.getToken(), await oa("cpxGetBids_" + e, 60, async () => { const r = `https://api.avito.ru/cpxpromo/1/getBids/${e}`; return (await this.request({ url: r, method: "GET", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" } })).json() }, () => []) } async cpxStop(e) { (!this.token || Date.now() > this.tokenExpiration) && await this.getToken(); const s = await this.request({ url: "https://api.avito.ru/cpxpromo/1/remove", method: "POST", headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" }, json: { itemID: e } }); if (!s.ok) throw new Error(await s.text()); return await s.json() } async request(e) { const s = await fetch("https://av-tools.pro/ext/request_avito", { method: "POST", body: JSON.stringify(e) }); if (!s.ok) throw new Error(await s.text()); return s } }Readable AdvertsCost component reads clientId/clientSecret and calls cpx methods from the button flow
main.js vU = { class: "ext_avtools_dropdown__controls" }, mU = { key: 0, class: "ext_avtools_btn-dark", style: { width: "100%" }, disabled: "" }, gU = { key: 1, class: "ext_avtools_btn-dark", style: { width: "100%" }, disabled: "" }, _U = ["innerHTML"], xU = { __name: "AdvertsCost", setup(t) { const e = Q(!1); let n = Q(""); const s = Q([]), r = Q(0); let a = Q(null); Um("https://www.avito.ru/web/1/profile/openapi/clients", "GET", null, { "Content-Type": "application/json" }).then(p => { p.ok && p.json().then(h => { h.length > 0 && (a.value = new A_(h[0].clientId, h[0].clientSecret)); }); }); const o = Pn({ type: "manual", manual: { actionTypeID: 5, bidPenny: null, itemID: null, limitPenny: null, alg: "=" }, auto: { actionTypeID: 5, budgetPenny: null, itemID: null, budgetType: "1d" } }), l = Q(0), c = p => new Promise(h => setTimeout(h, p)), u = () => { const p = document.querySelectorAll("[class*=styles-module-controlledInput_checked]"); s.value = [], p && p.length > 0 && p.forEach(h => { try { const v = h.dataset.marker.replace(")", "").replace("select-offer(", "").replace("/toggle", ""); s.value.find(g => g === v) || s.value.push(v); } catch {} }); }, d = async () => { if (u(), s.value.length === 0) { n.value = "Выберите объявления галочками в списке объявлений!<br/>"; return } if (!a.value) { n.value = "API не инициализирован. Попробуйте позже.<br/>"; return } e.value = !0, n.value = "", l.value = 0; for (let p in s.value) { l.value += 1; let h = s.value[p]; try { n.value += h + ": Настраиваю объявление<br />"; let v = !1; try { if (!a.value) { n.value += h + ": API не доступно<br />"; continue } v = await a.value.cpxGetBids(parseInt(h), !1); } catch { n.value += h + ": Ошибка получения текущих настроек<br />"; continue } let g = {}; if (o.type === "manual") { let m = parseFloat(v?.manual?.bidPenny ?? 0); o.manual.alg === "=" && (m = parseFloat(r.value).toFixed(2) * 100), o.manual.alg === "+" && (m = m + parseFloat(r.value).toFixed(2) * 100), o.manual.alg === "-" && (m = m - parseFloat(r.value).toFixed(2) * 100), o.manual.alg === "+%" && (m = parseInt(m + m * (parseFloat(r.value) / 100))), o.manual.alg === "-%" && (m = parseInt(m - m * (parseFloat(r.value) / 100))), m <= 0 && (m = parseFloat(v?.manual?.bidPenny ?? 0)), g = { actionTypeID: v.actionTypeID ?? 5, bidPenny: m, itemID: parseInt(h), limitPenny: o?.manual?.limitPenny ? o?.manual?.limitPenny * 100 : v?.manual?.limitPenny ? v?.manual?.limitPenny : null }, await a.value.cpxSetManualBid(g); } o.type === "auto" && (g = { actionTypeID: parseInt(v.actionTypeID ?? 5), budgetPenny: parseInt(o.auto.budgetPenny ?? 0) * 100, itemID: parseInt(h), budgetType: o.auto.budgetType ?? "1d" }, await a.value.cpxSetAutoBid(g)), o.type === "stop" && await a.value.cpxStop(parseInt(h)), await a.value.cpxGetBids(parseInt(h), !1), n.value += h + ": Готово<br />", ss.emit("update_cps_advert_" + h), n.value += "Пауза...<br />", await c(1e3); } catch (v) { try { n.value += h + ": Ошибка " + JSON.parse(v.toString().replace("Error: ", "")) + "<br />"; } catch { n.value += h + ": Ошибка " + v.toString() + "<br />"; } } } l.value = 0, e.value = !1, n.value += "Выполнено"; }; return (p, h) => (k(), at(Xt, { "no-close": !0, onOpen: h[8] || (h[8] = v => u()) }, { button: Ke(() => h[9] || (h[9] = [f("img", { src: b_, alt: "", style: { width: "20px", height: "20px" } }, null, -1), _e(" Стоимость ЦД ")])), header: Ke(() => h[10] || (h[10] = [_e(" Настройка стоимости целевого действия ")])), content: Ke(() => [f("div", XN, [F(a) ? ee("", !0) : (k(), S("div", KN, " Инициализация API... ")), s.value.length === 0 ? (k(), S("div", YN, " Выберите объявления галочками в списке объявлений! ")) : ee("", !0), f("div", ZN, [h[11] || (h[11] = f("strong", null, "Объявлений выбрано:", -1)), _e(" " + W(s.value.length), 1)]), e.value ? (k(), S("div", JN, [h[12] || (h[12] = f("strong", null, "Выполнено:", -1)), _e(" " + W(l.value), 1)])) : ee("", !0), e.value ? ee("", !0) : (k(), S(ke, { key: 3 }, [f("div", QN, [f("div", eU, [h[15] || (h[15] = f("span", { style: { width: "130px", display: "inline-block" } }, "Как настраивать:", -1)), f("div", tU, [He(f("select", { "onUpdate:modelValue": h[0] || (h[0] = v => o.type = v) }, h[13] || (h[13] = [f("option", { value: "manual" }, "Вручную", -1), f("option", { value: "auto" }, "Автоматичеcки", -1), f("option", { value: "stop" }, "Убрать продвижение", -1)]), 512), [ [qt, o.type] ]), h[14] || (h[14] = f("img", { src: Sr, alt: "", style: { width: "16px", height: "16px" } }, null, -1))])])]), o.type === "manual" ? (k(), S(ke, { key: 0 }, [f("div", nU, [f("div", sU, [h[18] || (h[18] = f("span", { style: { width: "130px", display: "inline-block" } }, "Изменить ставку:", -1)), f("div", rU, [f("div", iU, [He(f("select", { "onUpdate:modelValue": h[1] || (h[1] = v => o.manual.alg = v) }, h[16] || (h[16] = [f("option", { value: "=" }, "=", -1), f("option", { value: "+" }, "+", -1), f("option", { value: "-" }, "-", -1), f("option", { value: "+%" }, "+%", -1), f("option", { value: "-%" }, "-%", -1)]), 512), [ [qt, o.manual.alg] ]), h[17] || (h[17] = f("img", { src: Sr, alt: "", style: { width: "16px", height: "16px" } }, null, -1))]), We(F(wi), { value: r.value, "onUpdate:value": h[2] || (h[2] = v => r.value = v), class: "ext_avtools_form-control", options: { precision: 2, prefix: "", suffix: "", decimal: ".", thousand: "", acceptNegative: !1, isInteger: !1 } }, null, 8, ["value"]), f("div", aU, [f("button", { onClick: h[3] || (h[3] = v => r.value = r.value + .1), class: "ext-text-xs ext-border-none bg-transparent cursor-pointer", style: { width: "20px", height: "18px", padding: "0" } }, "+"), f("button", { onClick: h[4] || (h[4] = v => r.value = r.value - .1), class: "ext-text-xs ext-border-none bg-transparent cursor-pointer", style: { width: "20px", height: "18px", padding: "0" } }, "-")])])]), h[19] || (h[19] = f("div", { class: "ext-text-xs mt-1 ext-text-gray-400", style: { "margin-left": "145px", "font-size": "0.8em", opacity: "0.8" } }, "Если не задать, то будет использована текущая настройка на объявлении", -1))]), f("div", oU, [f("div", lU, [h[20] || (h[20] = f("span", { style: { width: "130px", display: "inline-block" } }, "Лимит на день:", -1)), He(f("input", { type: "text", "onUpdate:modelValue": h[5] || (h[5] = v => o.manual.limitPenny = v), class: "ext_avtools_form-control", style: { width: "89%" } }, null, 512), [ [nt, o.manual.limitPenny] ])]), h[21] || (h[21] = f("div", { class: "ext-text-xs mt-1 ext-text-gray-400", style: { "margin-left": "145px", "font-size": "0.8em", opacity: "0.8" } }, "Если не задать, то будет использована текущая настройка на объявлении", -1))])], 64)) : ee("", !0), o.type === "auto" ? (k(), S(ke, { key: 1 }, [f("div", cU, [f("div", uU, [h[24] || (h[24] = f("span", { style: { width: "130px", display: "inline-block" } }, "Срок:", -1)), f("div", dU, [He(f("select", { "onUpdate:modelValue": h[6] || (h[6] = v => o.auto.budgetType = v) }, h[22] || (h[22] = [f("option", { value: "1d" }, "На день", -1), f("option", { value: "7d" }, "На неделю", -1), f("option", { value: "30d" }, "На месяц", -1)]), 512), [ [qt, o.auto.budgetType] ]), h[23] || (h[23] = f("img", { src: Sr, alt: "", style: { width: "16px", height: "16px" } }, null, -1))])])]), f("div", fU, [f("div", hU, [h[25] || (h[25] = f("span", { style: { width: "130px", display: "inline-block" } }, "Бюджет, руб.:", -1)), He(f("input", { type: "text", "onUpdate:modelValue": h[7] || (h[7] = v => o.auto.budgetPenny = v), class: "ext_avtools_form-control", style: { width: "89%" } }, null, 512), [ [nt, o.auto.budgetPenny] ])])])], 64)) : ee("", !0)], 64)), f("div", pU, [f("div", vU, [e.value ? (k(), S("button", mU, "Настраиваю...")) : ee("", !0), F(a) ? ee("", !0) : (k(), S("button", gU, "Инициализация API...")), !e.value && F(a) ? (k(), S("button", { key: 2, class: "ext_avtools_btn-dark", style: { width: "100%" }, onClick: d }, "Установить")) : ee("", !0)])]), F(n) !== "" ? (k(), S("div", { key: 4, class: "mt-3 mb-1 ext-border ext-border-solid ext-border-gray-300 ext-bg-gray-50 ext-text-xs ext-overflow-y-scroll ext-h-[100px] ext-rounded ext-shadow ext-p-1", innerHTML: F(n) }, null, 8, _U)) : ee("", !0)])]), _: 1 })) } },- av-tools.pro
Receives the JSON-serialized request object that includes the Avito token URL and credential form fields.