import fs from "fs"; import readline from "readline"; import vm from "vm"; const KWF_PATH = new URL("./kwf-0.0.2.2cee19b4b7dec496.js", import.meta.url); let fixedNow = process.env.KS_H5_KWW_NOW ? Number(process.env.KS_H5_KWW_NOW) : null; let rngState = (process.env.KS_H5_KWW_SEED ? Number(process.env.KS_H5_KWW_SEED) : Date.now()) >>> 0; let traceEvents = null; let traceCounts = null; const TRACE_TYPE_LIMITS = { "Math.floor": 256, "String.fromCharCode": 512, "Object.assign": 64, "Array.join": 64, }; function trace(type, detail = {}) { if (!traceEvents) return; if (traceCounts) traceCounts[type] = (traceCounts[type] || 0) + 1; const limit = TRACE_TYPE_LIMITS[type] || 2000; if (traceCounts && traceCounts[type] > limit) return; if (traceEvents.length >= 4000) return; traceEvents.push({ type, ...detail }); } function previewArgs(values, limit = 32) { const arr = Array.from(values || []); return { count: arr.length, first: arr.slice(0, limit), truncated: arr.length > limit, }; } function seededRandom() { rngState = (Math.imul(rngState, 1664525) + 1013904223) >>> 0; const value = rngState / 0x100000000; trace("random", { value }); return value; } function currentNow() { const value = fixedNow === null || Number.isNaN(fixedNow) ? Date.now() : fixedNow; trace("now", { value }); return value; } function FakeDate(...args) { if (new.target) { return args.length > 0 ? new Date(...args) : new Date(currentNow()); } return new Date(args.length > 0 ? args[0] : currentNow()).toString(); } Object.setPrototypeOf(FakeDate, Date); FakeDate.prototype = Date.prototype; FakeDate.now = () => currentNow(); FakeDate.parse = Date.parse; FakeDate.UTC = Date.UTC; const fakeMath = Object.create(Math); fakeMath.random = () => seededRandom(); fakeMath.floor = (value) => { const result = Math.floor(value); trace("Math.floor", { value, result }); return result; }; const NativeString = String; function TracedString(...args) { return NativeString(...args); } Object.setPrototypeOf(TracedString, NativeString); TracedString.prototype = NativeString.prototype; TracedString.fromCharCode = (...codes) => { const result = NativeString.fromCharCode(...codes); trace("String.fromCharCode", { args: previewArgs(codes), result: preview(result), }); return result; }; const NativeObject = Object; function previewObject(value) { if (!value || typeof value !== "object") return preview(value); const keys = NativeObject.keys(value); const sample = {}; for (const key of keys.slice(0, 16)) sample[key] = preview(value[key]); return { keys: keys.slice(0, 32), sample, keyCount: keys.length }; } function TracedObject(value) { return NativeObject(value); } NativeObject.setPrototypeOf(TracedObject, NativeObject); TracedObject.prototype = NativeObject.prototype; for (const name of NativeObject.getOwnPropertyNames(NativeObject)) { if (name === "length" || name === "name" || name === "prototype") continue; NativeObject.defineProperty( TracedObject, name, NativeObject.getOwnPropertyDescriptor(NativeObject, name), ); } TracedObject.assign = (target, ...sources) => { const result = NativeObject.assign(target, ...sources); trace("Object.assign", { sourceCount: sources.length, sources: sources.map((item) => previewObject(item)), result: previewObject(result), }); return result; }; const nativeArrayJoin = Array.prototype.join; Array.prototype.join = function tracedJoin(separator) { const result = nativeArrayJoin.call(this, separator); if (separator === "|" || (this.length === 14 && result.includes("|"))) { trace("Array.join", { separator: separator === undefined ? "," : String(separator), length: this.length, result: preview(result), }); } return result; }; const mockCache = new Map(); function preview(value) { if (value === null) return "null"; if (value === undefined) return "undefined"; if (typeof value === "string") return value.length > 120 ? `${value.slice(0, 120)}...(len=${value.length})` : value; if (typeof value === "number" || typeof value === "boolean") return value; if (typeof value === "function") { const text = NativeString(value); return text.length > 160 ? `${text.slice(0, 160)}...(len=${text.length})` : text; } if (Array.isArray(value)) return `[array len=${value.length}]`; try { return `[object ${Object.keys(value).slice(0, 8).join(",")}]`; } catch { return "[unpreviewable]"; } } function mock(path = "mock") { if (mockCache.has(path)) return mockCache.get(path); const fn = function () { return mock(`${path}()`); }; const proxy = new Proxy(fn, { get(_target, prop) { if (prop === Symbol.toPrimitive) return () => ""; if (prop === "toString") return () => `[mock ${path}]`; if (prop === "valueOf") return () => 0; if (prop === "then") return undefined; return mock(`${path}.${String(prop)}`); }, set() { return true; }, apply() { return mock(`${path}()`); }, construct() { return mock(`new ${path}`); }, has() { return true; }, }); mockCache.set(path, proxy); return proxy; } const storage = new Map(); const localStorage = { getItem(key) { const value = storage.get(String(key)) || null; trace("localStorage.getItem", { key: String(key), value }); return value; }, setItem(key, value) { trace("localStorage.setItem", { key: String(key), value: String(value) }); storage.set(String(key), String(value)); }, removeItem(key) { trace("localStorage.removeItem", { key: String(key) }); storage.delete(String(key)); }, }; class FakeXMLHttpRequest { open(method, url) { this.method = method; this.url = url; } setRequestHeader() {} send() { this.readyState = 4; this.status = 200; this.responseText = "{}"; if (typeof this.onreadystatechange === "function") this.onreadystatechange(); if (typeof this.onload === "function") this.onload(); } addEventListener() {} } const rawWindow = {}; const windowProxy = new Proxy(rawWindow, { get(target, prop) { if (prop in target) return target[prop]; const value = mock(`window.${String(prop)}`); target[prop] = value; return value; }, set(target, prop, value) { target[prop] = value; return true; }, has() { return true; }, }); Object.assign(rawWindow, { window: windowProxy, self: windowProxy, top: windowProxy, parent: windowProxy, Object: TracedObject, Array, String: TracedString, Function, Number, Boolean, Date: FakeDate, Math: fakeMath, RegExp, Error, TypeError, Promise, JSON, Infinity, undefined, Uint8Array, parseInt, parseFloat, isNaN, escape, encodeURI, encodeURIComponent, decodeURI, decodeURIComponent, Window: function Window() {}, Navigator: function Navigator() {}, Location: function Location() {}, location: { href: "https://nebula.kuaishou.com/nebula/task/earning?layoutType=4&source=bottom_guide_first", origin: "https://nebula.kuaishou.com", protocol: "https:", host: "nebula.kuaishou.com", hostname: "nebula.kuaishou.com", pathname: "/nebula/task/earning", search: "?layoutType=4&source=bottom_guide_first", }, document: { cookie: "", referrer: "", createElement(tag) { return mock(`element.${tag}`); }, getElementsByTagName(tag) { return [mock(`tag.${tag}`)]; }, addEventListener() {}, body: mock("document.body"), head: mock("document.head"), documentElement: mock("document.documentElement"), }, navigator: { userAgent: "Mozilla/5.0 (Linux; Android 16; PJZ110 Build/BP2A.250605.015; wv) AppleWebKit/537.36", language: "zh-CN", languages: ["zh-CN", "zh"], platform: "Linux armv8l", webdriver: false, }, screen: { width: 1080, height: 2376, colorDepth: 24, pixelDepth: 24 }, localStorage, sessionStorage: localStorage, performance: { now: () => currentNow() % 100000, timing: {}, getEntriesByType: () => [] }, crypto: { getRandomValues(arr) { for (let i = 0; i < arr.length; i += 1) arr[i] = Math.floor(seededRandom() * 256); return arr; }, }, XMLHttpRequest: FakeXMLHttpRequest, fetch() { return Promise.resolve({ ok: true, status: 200, text: () => Promise.resolve("{}"), json: () => Promise.resolve({}) }); }, setTimeout(cb) { if (typeof cb === "function") cb(); return 1; }, clearTimeout() {}, setInterval() { return 1; }, clearInterval() {}, console: { log() {}, warn() {}, error() {}, }, }); rawWindow.globalThis = windowProxy; const context = vm.createContext(windowProxy, { name: "h5-kww", codeGeneration: { strings: true, wasm: false }, }); function loadKwf() { const code = fs.readFileSync(KWF_PATH, "utf8"); vm.runInContext(code, context, { filename: KWF_PATH.pathname, timeout: 5000 }); if (!rawWindow.kwpsec || typeof rawWindow.kwpsec.getData !== "function") { throw new Error(`kwpsec.getData not installed, kwpsec=${preview(rawWindow.kwpsec)}`); } } function setCookie(cookie) { rawWindow.document.cookie = String(cookie || ""); } function buildArg(req) { if (req && req.arg !== undefined) return req.arg; if (req && req.url) { return { url: String(req.url), method: String(req.method || "GET"), headers: req.headers || {}, body: req.body || "", data: req.body || "", }; } return undefined; } function makeKww(req) { traceEvents = req.trace ? [] : null; traceCounts = req.trace ? {} : null; setCookie(req.cookie || ""); const arg = buildArg(req); const getData = rawWindow.kwpsec.getData; const value = arg === undefined ? getData.call(rawWindow.kwpsec) : getData.call(rawWindow.kwpsec, arg); const kww = String(value || ""); if (!kww) { throw new Error("kwpsec.getData returned empty value"); } return { kww, kwfcv1: storage.get("kwfcv1") || "", kwfv1: storage.get("kwfv1") || "", trace: traceEvents || undefined, traceSummary: traceCounts || undefined, }; } loadKwf(); const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity }); rl.on("line", (line) => { let id = null; try { const req = JSON.parse(line); id = req.id ?? null; const result = makeKww(req); process.stdout.write(JSON.stringify({ id, ok: true, ...result }) + "\n"); } catch (err) { process.stdout.write(JSON.stringify({ id, ok: false, error: String(err && err.stack ? err.stack : err) }) + "\n"); } });