Is VEO Automation - Auto VEO & Nano Banana on Google Flow safe?
VEO Automation is high risk. The extension requests a Google Flow config from configs.kylenguyen.me. The worker caches JSON only with a selectors object; content-script code clicks Flow controls and reads names. Server selectors let a response retarget the automation.
Who publishes itTruong Nguyen - 4 other listings from the same operator, 1 of them carrying a finding
Truong Nguyen - 4 other listings from the same operator, 1 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
4 other listings published from this account, 186k+ users between them. 1 of them carries a finding.
Shared hosts - 1 hostname
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
Remote config controls Google Flow selectors
The extension requests a Google Flow config from configs.kylenguyen.me.
The worker caches JSON only with a selectors object; content-script code clicks Flow controls and reads names.
Server selectors let a response retarget the automation.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Dynamic sandbox
You start the extension's Google Flow automation or character scan.
The extension asks its service worker for server-provided selector settings before interacting with the page.
Those settings determine which Google Flow controls the automation clicks or reads.
Observed during dynamic analysis: the response was JSON containing a selectors object, including a selector consumed as selectors.charactersTabButton.
- client credential header
- <redacted>
- Character tab targetselectors.charactersTabButton
Controls which Google Flow tab the extension opens before reading available characters.
- Character name targetselectors.charactersNameSelector
Controls which page elements are read to build the list of character names shown to you.
- Agent-mode controlsselectors.enableAgentModeButton / selectors.disableAgentModeButton
Controls which page elements are clicked to enable or disable Flow's agent mode during automation.
- Media cleanup controlselectors.removeSelectedImagesButton
Controls which page element is clicked to remove selected images and videos before generating output.
The fetch, cache, and selector-use path
Remote config helper fetches and caches selector JSON
assets/remoteConfig-CbIdrXch.jsfunction t(t, n) { return t.version.split(",").map(t => t.trim()).filter(Boolean).includes(n.trim())}let n = null;async function e(t) { const n = await fetch(`${t}/config/flow-automation`, { method: "GET", headers: { "client credential header": "<redacted>" } }); if (!n.ok) throw new Error(`HTTP ${n.status}`); const e = await n.json(); if (!e?.selectors) throw new Error("Invalid config shape"); return e}async function r() { if (n) return n; try { return n = await e("https://configs.kylenguyen.me"), n } catch (t) {} try { return n = await e("https://extension-config.onegreen.workers.dev"), n } catch (t) { return null }}export { r as g, t as i};Service worker returns that config to callers
assets/index.ts-DoSGWp_j.jscase "GET_REMOTE_CONFIG": return e().then(e => m(e)).catch(() => m(null)), !0;Prompt queue loads remote config before processing automation work
assets/index.ts-CjccIiMW.jsasync function oe(e) { const t = await new Promise(e => { chrome.runtime.sendMessage({ type: "GET_REMOTE_CONFIG" }, t => e(t ?? null)) }); if (!t) return e.status = "error", re(e), Z.shift(), e.sendResponse({ success: !1, error: "Cannot connect to server. Extension may be unauthorized.", results: [] }), te = null, void ne(1); for (;;) { if (e.isCancelling) { e.status = "cancelled"; const t = e.results; return Z.shift(), re(e), e.sendResponse({ success: !1, cancelled: !0, results: t }), te = null, void ne(1) } if (e.pendingIndexes.length > 0) { const n = []; for (let r = 0; r < 1; r++) n.push(X(e, t, ee, re).catch(e => {})); await Promise.all(n) } for (; ee.some(t => t.id === e.id);) await K(200); if (0 === e.pendingIndexes.length) { e.status = "completed"; const t = e.results.every(e => e.success), n = e.results; return Z.shift(), re(e), e.sendResponse({ success: t, results: n }), te = null, void ne(1) } }}Character scan consumes selector fields from the returned config
assets/index.ts-CjccIiMW.jscase "SCAN_CHARACTERS": return async function() { const e = await new Promise(e => { chrome.runtime.sendMessage({ type: "GET_REMOTE_CONFIG" }, t => e(t ?? null)) }); if (!e) return []; if (i(e.selectors.charactersTabButton).length > 0) { await w(e.selectors.charactersTabButton, "Click characters tab"), await v(e.selectors.charactersNameSelector); return i(e.selectors.charactersNameSelector).map((e, t) => i(t).attr("alt") || i(t).text() || "").get() } return [] }().then(e => n({ characters: e })), !0;- configs.kylenguyen.me
Primary flow-automation config host in shipped code; dynamic analysis observed the extension requesting selector JSON from this host.
- extension-config.onegreen.workers.dev
Fallback flow-automation config host in shipped code if the primary config request fails.