Created
June 12, 2026 05:12
-
-
Save Isolyth/af68397c5d39864b1cb545199df35815 to your computer and use it in GitHub Desktop.
kickbacks.ai realistic multi-window saturation test with --forever mode and token auto-refresh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Massed, realistic-timing, parallel fraud test against the REAL kickbacks backend. | |
| // | |
| // This tests whether new server-side detections (per-account aggregate checks, | |
| // duty-cycle heuristics, concurrency limits, etc.) can stop a multi-window | |
| // saturation attack that is *byte-for-byte indistinguishable* from the real | |
| // VS Code extension's traffic. | |
| // | |
| // What it does: | |
| // * Authenticates as YOUR account via KICKBACKS_TOKEN (from signin.mjs or | |
| // signin-email.mjs), or auto-refreshes the token from Firebase email/password. | |
| // * Pulls the live ad portfolio to learn queue depth K + session_tokens. | |
| // * Spawns K parallel "windows" — one per ad — each emitting the exact event | |
| // sequence the real extension uses, with realistic choppy cadence. | |
| // * Tracks credited events vs. rejected/throttled/capped in real time. | |
| // | |
| // Realism (faithful to cliTick.ts / statusBarAd.ts / portfolio/client.ts): | |
| // * Shows are SHORT and irregular — mostly <5s (zero view_ticks), some | |
| // medium, rare long — not a metronome. | |
| // * view_tick intervals have positive jitter (>= 5000ms), never early. | |
| // * visible_ms on view_tick is true accrued wall-clock time, not fabricated. | |
| // * impression_viewable closes a show with an ODD duration, never a clean | |
| // multiple of 5000. | |
| // * Gaps between shows mimic think -> idle transitions. | |
| // * Surfaces alternate (statusbar / statusline) across windows. | |
| // * A 60s continuous show cap + 20s rest cycle is enforced per window. | |
| // | |
| // Requires explicit opt-in so it can never run by accident: | |
| // export KICKBACKS_TOKEN='...' | |
| // node attack-real.mjs --yes-i-own-this-account | |
| // | |
| // Forever mode (runs until Ctrl-C, auto-refreshes Firebase token every 20-60 min): | |
| // export KICKBACKS_EMAIL='...' | |
| // export KICKBACKS_PASSWORD='...' | |
| // node attack-real.mjs --yes-i-own-this-account --forever | |
| // | |
| // Environment: | |
| // KICKBACKS_TOKEN bearer token (required unless --forever + email/pw) | |
| // KICKBACKS_EMAIL email for Firebase auto-refresh | |
| // KICKBACKS_PASSWORD password for Firebase auto-refresh | |
| // KICKBACKS_FIREBASE_KEY Firebase API key (default built-in) | |
| // KICKBACKS_BASE backend base URL (default: live) | |
| // DURATION_MS test duration in ms (default 120_000 = 2 min) | |
| // KICKBACKS_WINDOW_COUNT window count (default 4) | |
| // KICKBACKS_QUIET "1" to suppress per-event logging | |
| import { randomUUID } from "node:crypto"; | |
| // ── safety: explicit opt-in ───────────────────────────────────────────────── | |
| if (!process.argv.includes("--yes-i-own-this-account")) { | |
| console.error("refusing to run without --yes-i-own-this-account"); | |
| process.exit(2); | |
| } | |
| const FOREVER = process.argv.includes("--forever"); | |
| let TOKEN = process.env.KICKBACKS_TOKEN; | |
| const EMAIL = process.env.KICKBACKS_EMAIL; | |
| const PASSWORD = process.env.KICKBACKS_PASSWORD; | |
| const FIREBASE_KEY = process.env.KICKBACKS_FIREBASE_KEY | |
| || "AIzaSyAhvdYkzjBP6ZaTk_e_cxHoGPx_39ljCvA"; | |
| if (!TOKEN && !FOREVER) { | |
| console.error("set KICKBACKS_TOKEN or use --forever with KICKBACKS_EMAIL + KICKBACKS_PASSWORD"); | |
| process.exit(2); | |
| } | |
| if (FOREVER && (!EMAIL || !PASSWORD)) { | |
| console.error("--forever requires KICKBACKS_EMAIL and KICKBACKS_PASSWORD for token auto-refresh"); | |
| process.exit(2); | |
| } | |
| const BASE = process.env.KICKBACKS_BASE | |
| || "https://kickbacks-backend-gmdaqm2c7q-uw.a.run.app"; | |
| const CC_VERSION = process.env.KICKBACKS_CC_VERSION || "2.1.173"; | |
| const EXT_VERSION = process.env.KICKBACKS_EXT_VERSION || "0.4.0"; | |
| const DURATION_MS = Number(process.env.DURATION_MS || 120_000); | |
| const FORCED_K = process.env.KICKBACKS_WINDOW_COUNT | |
| ? Number(process.env.KICKBACKS_WINDOW_COUNT) : 4; | |
| const QUIET = process.env.KICKBACKS_QUIET === "1"; | |
| const CLIENT_ID = `install-${randomUUID().slice(0, 4)}`; | |
| // ── faithful constants (cliTick.ts / statusBarAd.ts) ───────────────────────── | |
| const POLL_MS = 1_000; | |
| const VIEW_TICK_MS = 5_000; | |
| const FRESH_ACTIVITY_MS = 4_000; | |
| const VISIBLE_GAP_CAP_MS = 2_000; // suspend clamp per poll tick | |
| const AD_SHOW_MAX_MS = 60_000; // max continuous show | |
| const AD_REST_MS = 20_000; // rest after hitting max | |
| const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); | |
| const tickGap = () => VIEW_TICK_MS + Math.random() * 800; // always >= 5000ms | |
| // ── token refresh (Firebase email/password) ───────────────────────────────── | |
| async function refreshTokenFromFirebase() { | |
| const url = "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=" | |
| + encodeURIComponent(FIREBASE_KEY); | |
| const r = await fetch(url, { | |
| method: "POST", | |
| headers: { "content-type": "application/json" }, | |
| body: JSON.stringify({ email: EMAIL, password: PASSWORD, returnSecureToken: true }), | |
| }); | |
| const j = await r.json(); | |
| if (!r.ok) { | |
| throw new Error(`Firebase sign-in HTTP ${r.status}: ${j?.error?.message || JSON.stringify(j)}`); | |
| } | |
| TOKEN = j.idToken; | |
| console.log(`[token-refresh] new token acquired (expiresIn=${j.expiresIn}s)`); | |
| } | |
| async function tokenRefreshLoop() { | |
| if (!FOREVER) return; | |
| // First token from Firebase if none was supplied via env. | |
| if (!TOKEN) { | |
| console.log("[token-refresh] fetching initial token from Firebase..."); | |
| await refreshTokenFromFirebase(); | |
| } | |
| while (true) { | |
| // Random interval: 20 minutes (1,200,000 ms) to 1 hour (3,600,000 ms) | |
| const delayMs = 1_200_000 + Math.random() * 2_400_000; | |
| const nextAt = new Date(Date.now() + delayMs); | |
| console.log(`[token-refresh] next refresh at ${nextAt.toISOString()}`); | |
| await sleep(delayMs); | |
| try { | |
| await refreshTokenFromFirebase(); | |
| } catch (e) { | |
| console.error(`[token-refresh] FAILED: ${e.message}`); | |
| console.error("[token-refresh] retrying in 5 minutes..."); | |
| await sleep(300_000); | |
| } | |
| } | |
| } | |
| // ── helpers ───────────────────────────────────────────────────────────────── | |
| const authed = (extra = {}) => ({ authorization: `Bearer ${TOKEN}`, ...extra }); | |
| async function getPortfolio() { | |
| const r = await fetch(`${BASE}/v1/portfolio?claude_code_version=${CC_VERSION}`, { | |
| headers: authed(), | |
| }); | |
| if (!r.ok) throw new Error(`portfolio HTTP ${r.status}`); | |
| return r.json(); | |
| } | |
| async function getEarnings() { | |
| const r = await fetch(`${BASE}/v1/earnings`, { headers: authed() }); | |
| if (!r.ok) return null; | |
| return r.json(); | |
| } | |
| async function fireEvent(ad, type, { visibleMs, surface = "statusbar" } = {}) { | |
| const body = { | |
| event_type: type, | |
| ad_id: ad.ad_id, | |
| campaign_id: ad.campaign_id, | |
| client_id: CLIENT_ID, | |
| ts: new Date().toISOString(), | |
| claude_code_version: CC_VERSION, | |
| extension_version: EXT_VERSION, | |
| nonce: randomUUID(), | |
| surface, | |
| ...(visibleMs != null | |
| ? { | |
| visible_ms: Math.round(visibleMs), | |
| view_pct: 100, | |
| viewable: true, | |
| } | |
| : {}), | |
| session_token: ad.session_token, | |
| }; | |
| const t0 = performance.now(); | |
| const r = await fetch(`${BASE}/v1/metrics`, { | |
| method: "POST", | |
| headers: authed({ "content-type": "application/json" }), | |
| body: JSON.stringify(body), | |
| }); | |
| const latency = performance.now() - t0; | |
| let j; | |
| try { j = await r.json(); } catch { j = {}; } | |
| const didCredit = !!j.credited; | |
| const reason = j.reason || (r.ok ? "ok" : `HTTP_${r.status}`); | |
| if (!QUIET) { | |
| const tag = didCredit ? "CREDITED" : reason; | |
| console.log( | |
| ` [${surface}] ${type.padEnd(20)} ad=${ad.ad_id.padEnd(10)} ` + | |
| `ms=${String(body.visible_ms ?? "—").padStart(6)} ` + | |
| `lat=${latency.toFixed(0)}ms ${tag}` | |
| ); | |
| } | |
| return { didCredit, reason, latency, status: r.status }; | |
| } | |
| // ── realistic show-length distribution ─────────────────────────────────────── | |
| function sampleShowLen() { | |
| const r = Math.random(); | |
| if (r < 0.55) return 400 + Math.random() * 4_000; // <5s => 0 view_ticks | |
| if (r < 0.88) return 5_000 + Math.random() * 18_000; // medium | |
| return 20_000 + Math.random() * 35_000; // long (will hit cap) | |
| } | |
| // ── one "window" pinned to one ad ──────────────────────────────────────────── | |
| async function runWindow(ad, surface, deadline, counters) { | |
| let showCount = 0; | |
| while (Date.now() < deadline) { | |
| const showLen = sampleShowLen(); | |
| const showStart = Date.now(); | |
| const showDeadline = showStart + showLen; | |
| // continuous-show cap: if this show exceeds 60s, cut at 60s then rest 20s | |
| const effectiveShowMax = Math.min(showDeadline, showStart + AD_SHOW_MAX_MS); | |
| await fireEvent(ad, "impression_rendered", { surface }); | |
| counters.rendered++; | |
| let lastTick = showStart; | |
| let nextGap = tickGap(); | |
| let accruedMs = 0; | |
| while (Date.now() < effectiveShowMax && Date.now() < deadline) { | |
| await sleep(180); // ~ the 1s poll, finer grain | |
| const now = Date.now(); | |
| const delta = now - lastTick; | |
| // simulate the VISIBLE_GAP_CAP_MS clamp the real client applies | |
| const cappedDelta = Math.min(delta, VISIBLE_GAP_CAP_MS); | |
| accruedMs += cappedDelta; | |
| lastTick = now; | |
| if (now - showStart >= nextGap) { | |
| const res = await fireEvent(ad, "view_tick", { surface, visibleMs: accruedMs }); | |
| counters.viewTick++; | |
| if (res.didCredit) counters.credited++; | |
| if (!res.didCredit) counters.rejected++; | |
| if (res.reason && res.reason !== "credited" && res.reason !== "ok") { | |
| counters.reasons[res.reason] = (counters.reasons[res.reason] || 0) + 1; | |
| } | |
| nextGap += tickGap(); | |
| } | |
| } | |
| const actualShowMs = Date.now() - showStart; | |
| const res = await fireEvent(ad, "impression_viewable", { | |
| surface, | |
| visibleMs: actualShowMs, | |
| }); | |
| counters.viewable++; | |
| if (res.didCredit) counters.credited++; | |
| if (!res.didCredit) counters.rejected++; | |
| if (res.reason && res.reason !== "credited" && res.reason !== "ok") { | |
| counters.reasons[res.reason] = (counters.reasons[res.reason] || 0) + 1; | |
| } | |
| showCount++; | |
| // if we hit the 60s cap, enforce the 20s rest | |
| const hitCap = actualShowMs >= AD_SHOW_MAX_MS - 500; | |
| if (hitCap) { | |
| const restUntil = Date.now() + AD_REST_MS; | |
| while (Date.now() < restUntil && Date.now() < deadline) await sleep(200); | |
| } else { | |
| // normal think -> idle gap | |
| const idleMs = 800 + Math.random() * 3_200; | |
| const idleUntil = Date.now() + idleMs; | |
| while (Date.now() < idleUntil && Date.now() < deadline) await sleep(200); | |
| } | |
| } | |
| counters.shows += showCount; | |
| } | |
| // ── main ───────────────────────────────────────────────────────────────────── | |
| async function main() { | |
| console.log(`\n[attack-real] base = ${BASE}`); | |
| console.log(`[attack-real] mode = ${FOREVER ? "FOREVER (Ctrl-C to stop)" : `${DURATION_MS}ms`}`); | |
| console.log(`[attack-real] client_id = ${CLIENT_ID}`); | |
| // Start background token refresh right away so it is ready before any fetch. | |
| if (FOREVER) { | |
| tokenRefreshLoop().catch((e) => { | |
| console.error("[token-refresh] background loop crashed:", e); | |
| process.exit(1); | |
| }); | |
| // Give the first token fetch a moment to complete if we have no env token. | |
| if (!process.env.KICKBACKS_TOKEN) { | |
| let waited = 0; | |
| while (!TOKEN && waited < 15_000) { | |
| await sleep(100); | |
| waited += 100; | |
| } | |
| if (!TOKEN) { | |
| console.error("[attack-real] token refresh never produced a token — aborting"); | |
| process.exit(1); | |
| } | |
| } | |
| } | |
| const before = await getEarnings(); | |
| if (!before) { console.error("[attack-real] could not read earnings — is the token valid?"); process.exit(1); } | |
| console.log(`[attack-real] before lifetime=$${before.lifetime_usd} today=$${before.today_usd}\n`); | |
| const portfolio = await getPortfolio(); | |
| const ads = portfolio.ads || []; | |
| if (!ads.length) { console.error("[attack-real] empty portfolio"); process.exit(1); } | |
| const K = FORCED_K ?? ads.length; | |
| const windows = ads.slice(0, K); | |
| console.log(`[attack-real] queue depth K = ${ads.length} windows = ${K}`); | |
| console.log(`[attack-real] ads = [${windows.map((a) => a.ad_id).join(", ")}]\n`); | |
| const counters = { | |
| rendered: 0, viewTick: 0, viewable: 0, | |
| credited: 0, rejected: 0, shows: 0, reasons: {}, | |
| }; | |
| const deadline = FOREVER ? Infinity : Date.now() + DURATION_MS; | |
| const startWall = Date.now(); | |
| console.log("[attack-real] firing windows …\n"); | |
| // run all windows concurrently on the SAME account | |
| await Promise.all( | |
| windows.map((ad, i) => | |
| runWindow(ad, i % 2 ? "statusline" : "statusbar", deadline, counters) | |
| ) | |
| ); | |
| // ── the code below only executes in bounded mode (FOREVER = false) ───────── | |
| const wallElapsed = Date.now() - startWall; | |
| // settle + final earnings | |
| console.log("\n[attack-real] polling earnings to let async credits settle …"); | |
| let after = before; | |
| for (let i = 0; i < 6; i++) { | |
| await sleep(2_000); | |
| const e = await getEarnings(); | |
| if (e) after = e; | |
| } | |
| const lifetimeDelta = | |
| parseFloat(after.lifetime_usd || "0") - parseFloat(before.lifetime_usd || "0"); | |
| const todayDelta = | |
| parseFloat(after.today_usd || "0") - parseFloat(before.today_usd || "0"); | |
| const eventTotal = counters.rendered + counters.viewTick + counters.viewable; | |
| const creditRate = counters.credited / Math.max(1, wallElapsed / 1000); | |
| const estimatedPerHour = lifetimeDelta / Math.max(1, wallElapsed / 3_600_000); | |
| console.log("\n" + "═".repeat(72)); | |
| console.log(" RESULTS"); | |
| console.log("═".repeat(72)); | |
| console.log(` wall-clock elapsed : ${(wallElapsed / 1000).toFixed(1)}s`); | |
| console.log(` events sent : ${eventTotal}`); | |
| console.log(` impression_rendered : ${counters.rendered}`); | |
| console.log(` view_tick : ${counters.viewTick}`); | |
| console.log(` impression_viewable : ${counters.viewable}`); | |
| console.log(` shows completed : ${counters.shows}`); | |
| console.log(` credited events : ${counters.credited}`); | |
| console.log(` rejected/ignored : ${counters.rejected}`); | |
| console.log(` credit rate : ${creditRate.toFixed(2)} events/sec`); | |
| console.log(` ──────────────────────────────────────────────────────────────────────`); | |
| console.log(` lifetime before : $${before.lifetime_usd}`); | |
| console.log(` lifetime after : $${after.lifetime_usd}`); | |
| console.log(` lifetime Δ : $${lifetimeDelta.toFixed(6)}`); | |
| console.log(` today Δ : $${todayDelta.toFixed(6)}`); | |
| console.log(` extrapolated /hour : ~$${estimatedPerHour.toFixed(4)}`); | |
| console.log(" ──────────────────────────────────────────────────────────────────────"); | |
| if (Object.keys(counters.reasons).length) { | |
| console.log(" rejection reasons:"); | |
| for (const [reason, count] of Object.entries(counters.reasons).sort((a, b) => b[1] - a[1])) { | |
| console.log(` ${reason.padEnd(18)} : ${count}`); | |
| } | |
| } | |
| // verdict | |
| const pass = counters.credited > 0; | |
| const throttled = (counters.reasons.cooldown || 0) > 0; | |
| const capped = (counters.reasons.daily_cap || 0) > 0; | |
| console.log(" ──────────────────────────────────────────────────────────────────────"); | |
| if (capped) { | |
| console.log(" VERDICT: DAILY CAP hit — the economic brake is engaged."); | |
| } else if (throttled && counters.credited > 0) { | |
| console.log(" VERDICT: COOLDOWN active but credits still flow (expected behavior)."); | |
| } else if (!throttled && !capped && counters.rejected > 0) { | |
| console.log(" VERDICT: events were REJECTED with no cooldown/cap — a new detection may be active."); | |
| } else if (pass) { | |
| console.log(" VERDICT: CREDITS FLOW — no new detection blocked the attack."); | |
| } else { | |
| console.log(" VERDICT: ZERO credits — either cooldown saturation or a new block."); | |
| } | |
| console.log("═".repeat(72) + "\n"); | |
| process.exit(pass ? 0 : 1); | |
| } | |
| main().catch((e) => { console.error(e); process.exit(1); }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment