@omnitronix/bonnys-fortune-game-engine 1.2.2 → 1.2.6

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.
Files changed (26) hide show
  1. package/README.md +15 -15
  2. package/dist/__tests__/rng-gli19-compliance.test.js +222 -0
  3. package/dist/__tests__/rng-gli19-compliance.test.js.map +1 -0
  4. package/dist/__tests__/rng-seed-security.test.js +263 -0
  5. package/dist/__tests__/rng-seed-security.test.js.map +1 -0
  6. package/dist/__tests__/rng-seed-type.test.js +282 -0
  7. package/dist/__tests__/rng-seed-type.test.js.map +1 -0
  8. package/dist/__tests__/rng-stress-boundary.test.js +386 -0
  9. package/dist/__tests__/rng-stress-boundary.test.js.map +1 -0
  10. package/dist/bonnys-fortune-v1.game-engine.js +3 -1
  11. package/dist/bonnys-fortune-v1.game-engine.js.map +1 -1
  12. package/dist/config/reel-strips-config/reels-BASE.csv +0 -0
  13. package/dist/config/reel-strips-config/reels-BONUS.csv +0 -0
  14. package/dist/logic/handlers/base-game.handler.js +19 -1
  15. package/dist/logic/handlers/base-game.handler.js.map +1 -1
  16. package/dist/logic/handlers/collect-feature-bonus.handler.js +2 -2
  17. package/dist/logic/handlers/collect-feature-bonus.handler.js.map +1 -1
  18. package/dist/logic/handlers/free-spins-bonus.handler.js +4 -2
  19. package/dist/logic/handlers/free-spins-bonus.handler.js.map +1 -1
  20. package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.js +4 -2
  21. package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.js.map +1 -1
  22. package/dist/logic/handlers/treasure-hunt-bonus.handler.js +4 -2
  23. package/dist/logic/handlers/treasure-hunt-bonus.handler.js.map +1 -1
  24. package/dist/rng/rng-client.factory.js +45 -1
  25. package/dist/rng/rng-client.factory.js.map +1 -1
  26. package/package.json +1 -1
package/README.md CHANGED
@@ -241,19 +241,19 @@ The game engine supports 6 different command types:
241
241
 
242
242
  **Returns**: Updated state
243
243
 
244
- #### 5. CHEAT_TRIGGER_BONUS (Testing/Admin)
244
+ #### 5. DEBUG_TRIGGER_BONUS (Testing/Admin)
245
245
 
246
246
  **Purpose**: Force trigger specific bonus game
247
247
 
248
- **Payload**: `CheatTriggerBonusCommand`
248
+ **Payload**: `DebugTriggerBonusCommand`
249
249
 
250
250
  **Returns**: State with pending bonus
251
251
 
252
- #### 6. CHEAT_UPDATE_BONUS_METER_PROGRESS (Testing/Admin)
252
+ #### 6. DEBUG_UPDATE_BONUS_METER_PROGRESS (Testing/Admin)
253
253
 
254
254
  **Purpose**: Manually set bonus meter progress
255
255
 
256
- **Payload**: `CheatUpdateBonusMeterProgressCommand`
256
+ **Payload**: `DebugUpdateBonusMeterProgressCommand`
257
257
 
258
258
  **Returns**: Updated meter state
259
259
 
@@ -552,26 +552,26 @@ RNG_CLIENT_GRPC_URL=grpc://rng-prod.omnitronix.com:50051
552
552
  RNG_CLIENT_POOL_SIZE=5000
553
553
  ```
554
554
 
555
- ### Example 4: Testing with Cheat Commands
555
+ ### Example 4: Testing with Debug Commands
556
556
 
557
557
  ```typescript
558
558
  async function testBonusGame() {
559
559
  const gameEngine = new BonnysFortuneV1GameEngine();
560
-
560
+
561
561
  // Initialize session
562
562
  const initResult = await gameEngine.processCommand({}, {}, {
563
563
  id: 'test-init',
564
564
  type: 'INIT_SESSION_STATE',
565
565
  payload: { betAmountThresholds: [10], defaultBetAmount: 10 }
566
566
  });
567
-
567
+
568
568
  // Force trigger treasure hunt bonus
569
- const cheatResult = await gameEngine.processCommand(
569
+ const debugResult = await gameEngine.processCommand(
570
570
  initResult.publicState,
571
571
  initResult.privateState,
572
572
  {
573
- id: 'test-cheat',
574
- type: 'CHEAT_TRIGGER_BONUS',
573
+ id: 'test-debug',
574
+ type: 'DEBUG_TRIGGER_BONUS',
575
575
  payload: {
576
576
  sessionId: 'test-session',
577
577
  bonusType: 'bonusGame1',
@@ -579,11 +579,11 @@ async function testBonusGame() {
579
579
  }
580
580
  }
581
581
  );
582
-
582
+
583
583
  // Now start the bonus
584
584
  const bonusResult = await gameEngine.processCommand(
585
- cheatResult.publicState,
586
- cheatResult.privateState,
585
+ debugResult.publicState,
586
+ debugResult.privateState,
587
587
  {
588
588
  id: 'test-bonus-start',
589
589
  type: 'START_BONUS_ROUND',
@@ -593,11 +593,11 @@ async function testBonusGame() {
593
593
  betAmount: 10,
594
594
  gameCode: 'bonnys-fortune',
595
595
  gameVersion: '1.0.0',
596
- bonusId: cheatResult.privateState.pendingBonuses[0].bonusId
596
+ bonusId: debugResult.privateState.pendingBonuses[0].bonusId
597
597
  }
598
598
  }
599
599
  );
600
-
600
+
601
601
  console.log('Bonus results:', bonusResult.outcome);
602
602
  }
603
603
  ```
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ /**
3
+ * GLI-19 Compliance Tests - Bonny's Fortune
4
+ * Audit Trail & Tamper Detection Verification
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const bonnys_fortune_v1_game_engine_1 = require("../bonnys-fortune-v1.game-engine");
41
+ const crypto = __importStar(require("crypto"));
42
+ describe('GLI-19 Compliance - Audit Trail & Tamper Detection', () => {
43
+ let engine;
44
+ beforeEach(() => {
45
+ engine = new bonnys_fortune_v1_game_engine_1.BonnysFortuneV1GameEngine();
46
+ });
47
+ async function initSession() {
48
+ return engine.processCommand(null, null, {
49
+ id: 'init-gli19',
50
+ type: 'INIT_SESSION_STATE',
51
+ payload: { betAmountThresholds: [1, 2, 5, 10], defaultBetAmount: 1 },
52
+ });
53
+ }
54
+ async function executeSpin(publicState, privateState) {
55
+ return engine.processCommand(publicState, privateState, {
56
+ id: `spin-${Math.random().toString(36).slice(2)}`,
57
+ type: 'SPIN',
58
+ payload: { sessionId: 'test', betAmount: 1, gameCode: 'bonnys-fortune', gameVersion: '1.0.0', lines: 10, creditsPerLine: 1 },
59
+ });
60
+ }
61
+ function canonicalizeForHash(outcome) {
62
+ const entries = Object.entries(outcome)
63
+ .map(([actionId, record]) => ({
64
+ actionId, result: Number(record.result), seed: String(record.seed),
65
+ min: Number(record.min), max: Number(record.max),
66
+ }))
67
+ .sort((a, b) => a.actionId.localeCompare(b.actionId));
68
+ return JSON.stringify(entries);
69
+ }
70
+ function generateHash(data) {
71
+ return crypto.createHash('sha256').update(data).digest('hex');
72
+ }
73
+ // ============================================================
74
+ // AUDIT TRAIL COMPLETENESS (GLI-19 3.4)
75
+ // ============================================================
76
+ describe('Section 3.4: Audit Trail Completeness', () => {
77
+ it('should include all required fields in RNG records', async () => {
78
+ const init = await initSession();
79
+ const spin = await executeSpin(init.publicState, init.privateState);
80
+ expect(spin.rngOutcome).toBeDefined();
81
+ for (const [actionId, record] of Object.entries(spin.rngOutcome)) {
82
+ expect(record).toHaveProperty('seed');
83
+ expect(record).toHaveProperty('result');
84
+ expect(record).toHaveProperty('min');
85
+ expect(record).toHaveProperty('max');
86
+ expect(typeof actionId).toBe('string');
87
+ expect(actionId.length).toBeGreaterThan(0);
88
+ }
89
+ });
90
+ it('should track at least one RNG call per spin', async () => {
91
+ const init = await initSession();
92
+ const spin = await executeSpin(init.publicState, init.privateState);
93
+ expect(Object.keys(spin.rngOutcome || {}).length).toBeGreaterThan(0);
94
+ });
95
+ it('should have unique actionIds within command', async () => {
96
+ const init = await initSession();
97
+ const spin = await executeSpin(init.publicState, init.privateState);
98
+ const actionIds = Object.keys(spin.rngOutcome || {});
99
+ expect(actionIds.length).toBe(new Set(actionIds).size);
100
+ });
101
+ });
102
+ // ============================================================
103
+ // TAMPER DETECTION (GLI-19 3.4.2)
104
+ // ============================================================
105
+ describe('Section 3.4.2: Tamper Detection', () => {
106
+ it('should detect modified seed', async () => {
107
+ const init = await initSession();
108
+ const spin = await executeSpin(init.publicState, init.privateState);
109
+ const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
110
+ const tampered = JSON.parse(JSON.stringify(spin.rngOutcome));
111
+ const key = Object.keys(tampered)[0];
112
+ tampered[key].seed = tampered[key].seed + 1;
113
+ expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
114
+ });
115
+ it('should detect modified result', async () => {
116
+ const init = await initSession();
117
+ const spin = await executeSpin(init.publicState, init.privateState);
118
+ const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
119
+ const tampered = JSON.parse(JSON.stringify(spin.rngOutcome));
120
+ const key = Object.keys(tampered)[0];
121
+ tampered[key].result = tampered[key].result + 1;
122
+ expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
123
+ });
124
+ it('should detect inserted record', async () => {
125
+ const init = await initSession();
126
+ const spin = await executeSpin(init.publicState, init.privateState);
127
+ const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
128
+ const tampered = { ...spin.rngOutcome, injected: { result: 5, seed: 99999, min: 0, max: 100 } };
129
+ expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
130
+ });
131
+ it('should detect removed record', async () => {
132
+ const init = await initSession();
133
+ const spin = await executeSpin(init.publicState, init.privateState);
134
+ const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
135
+ const tampered = { ...spin.rngOutcome };
136
+ delete tampered[Object.keys(tampered)[0]];
137
+ expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
138
+ });
139
+ it('should detect modified actionId', async () => {
140
+ const init = await initSession();
141
+ const spin = await executeSpin(init.publicState, init.privateState);
142
+ const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
143
+ const entries = Object.entries(spin.rngOutcome);
144
+ const tampered = {};
145
+ entries.forEach(([key, value], i) => {
146
+ tampered[i === 0 ? 'tampered_' + key : key] = value;
147
+ });
148
+ expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
149
+ });
150
+ it('should handle record reordering (canonicalization sorts)', () => {
151
+ const outcome1 = {
152
+ action_z: { result: 5, seed: 100, min: 0, max: 10 },
153
+ action_a: { result: 6, seed: 200, min: 0, max: 10 },
154
+ };
155
+ const outcome2 = {
156
+ action_a: { result: 6, seed: 200, min: 0, max: 10 },
157
+ action_z: { result: 5, seed: 100, min: 0, max: 10 },
158
+ };
159
+ expect(generateHash(canonicalizeForHash(outcome1))).toBe(generateHash(canonicalizeForHash(outcome2)));
160
+ });
161
+ });
162
+ // ============================================================
163
+ // DETERMINISTIC REPLAY (GLI-19 4.2.1)
164
+ // ============================================================
165
+ describe('Section 4.2.1: Deterministic Replay', () => {
166
+ it('should produce identical hash after serialization', async () => {
167
+ const init = await initSession();
168
+ const spin = await executeSpin(init.publicState, init.privateState);
169
+ const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
170
+ const serialized = JSON.stringify(spin.rngOutcome);
171
+ const restored = JSON.parse(serialized);
172
+ expect(generateHash(canonicalizeForHash(restored))).toBe(originalHash);
173
+ });
174
+ it('should produce identical hash for number and string seeds', async () => {
175
+ const init = await initSession();
176
+ const spin = await executeSpin(init.publicState, init.privateState);
177
+ const hashWithNumbers = generateHash(canonicalizeForHash(spin.rngOutcome));
178
+ const withStrings = {};
179
+ for (const [k, v] of Object.entries(spin.rngOutcome)) {
180
+ withStrings[k] = { ...v, seed: String(v.seed) };
181
+ }
182
+ expect(generateHash(canonicalizeForHash(withStrings))).toBe(hashWithNumbers);
183
+ });
184
+ it('should be reproducible across multiple calls', async () => {
185
+ const init = await initSession();
186
+ const spin = await executeSpin(init.publicState, init.privateState);
187
+ const hashes = Array(100).fill(0).map(() => generateHash(canonicalizeForHash(spin.rngOutcome)));
188
+ expect(new Set(hashes).size).toBe(1);
189
+ });
190
+ });
191
+ // ============================================================
192
+ // DATA INTEGRITY
193
+ // ============================================================
194
+ describe('Data Integrity', () => {
195
+ it('should verify result within bounds', async () => {
196
+ const init = await initSession();
197
+ const spin = await executeSpin(init.publicState, init.privateState);
198
+ for (const [, record] of Object.entries(spin.rngOutcome)) {
199
+ expect(record.result).toBeGreaterThanOrEqual(record.min);
200
+ expect(record.result).toBeLessThanOrEqual(record.max);
201
+ }
202
+ });
203
+ it('should verify all values are finite', async () => {
204
+ const init = await initSession();
205
+ const spin = await executeSpin(init.publicState, init.privateState);
206
+ for (const [, record] of Object.entries(spin.rngOutcome)) {
207
+ expect(Number.isFinite(record.seed)).toBe(true);
208
+ expect(Number.isFinite(record.result)).toBe(true);
209
+ expect(Number.isFinite(record.min)).toBe(true);
210
+ expect(Number.isFinite(record.max)).toBe(true);
211
+ }
212
+ });
213
+ it('should produce consistent SHA-256 hash format', async () => {
214
+ const init = await initSession();
215
+ const spin = await executeSpin(init.publicState, init.privateState);
216
+ const hash = generateHash(canonicalizeForHash(spin.rngOutcome));
217
+ expect(hash.length).toBe(64);
218
+ expect(/^[a-f0-9]+$/.test(hash)).toBe(true);
219
+ });
220
+ });
221
+ });
222
+ //# sourceMappingURL=rng-gli19-compliance.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rng-gli19-compliance.test.js","sourceRoot":"","sources":["../../src/__tests__/rng-gli19-compliance.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ /**
3
+ * RNG Seed Security Tests - Bonny's Fortune
4
+ * Comprehensive Attack Vector Coverage for GLI-19 Compliance
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const bonnys_fortune_v1_game_engine_1 = require("../bonnys-fortune-v1.game-engine");
8
+ const bonnys_fortune_types_1 = require("../logic/bonnys-fortune.types");
9
+ describe('RNG Seed Security - Attack Vector Tests', () => {
10
+ let engine;
11
+ beforeEach(() => {
12
+ engine = new bonnys_fortune_v1_game_engine_1.BonnysFortuneV1GameEngine();
13
+ });
14
+ async function initSession() {
15
+ return engine.processCommand(null, null, {
16
+ id: 'init-security',
17
+ type: 'INIT_SESSION_STATE',
18
+ payload: { betAmountThresholds: [1, 2, 5, 10], defaultBetAmount: 1 },
19
+ });
20
+ }
21
+ async function executeSpin(publicState, privateState) {
22
+ return engine.processCommand(publicState, privateState, {
23
+ id: `spin-${Math.random().toString(36).slice(2)}`,
24
+ type: 'SPIN',
25
+ payload: { sessionId: 'test', betAmount: 1, gameCode: 'bonnys-fortune', gameVersion: '1.0.0', lines: 10, creditsPerLine: 1 },
26
+ });
27
+ }
28
+ async function executeBonusRound(publicState, privateState) {
29
+ const pendingBonus = privateState.pendingBonuses?.[0];
30
+ if (!pendingBonus) {
31
+ throw new Error('No pending bonus found');
32
+ }
33
+ return engine.processCommand(publicState, privateState, {
34
+ id: `bonus-${Math.random().toString(36).slice(2)}`,
35
+ type: 'START_BONUS_ROUND',
36
+ payload: {
37
+ sessionId: 'test',
38
+ betAmount: pendingBonus.betAmount || 1,
39
+ bonusId: pendingBonus.bonusId,
40
+ bonusType: pendingBonus.bonusType,
41
+ gameCode: 'bonnys-fortune',
42
+ gameVersion: '1.0.0',
43
+ },
44
+ });
45
+ }
46
+ async function executeSpinWithBonusHandling(publicState, privateState) {
47
+ const results = [];
48
+ let pub = publicState;
49
+ let priv = privateState;
50
+ const spin = await executeSpin(pub, priv);
51
+ results.push(spin);
52
+ pub = spin.publicState;
53
+ priv = spin.privateState;
54
+ // Handle bonus rounds - any non-BASE_GAME_SPIN with pending bonuses
55
+ while (priv.nextSpinType !== bonnys_fortune_types_1.SpinType.BASE_GAME_SPIN && priv.pendingBonuses?.length > 0) {
56
+ const bonus = await executeBonusRound(pub, priv);
57
+ results.push(bonus);
58
+ pub = bonus.publicState;
59
+ priv = bonus.privateState;
60
+ }
61
+ return { results, publicState: pub, privateState: priv };
62
+ }
63
+ function canonicalizeForHash(outcome) {
64
+ const entries = Object.entries(outcome)
65
+ .map(([actionId, record]) => ({
66
+ actionId,
67
+ result: Number(record.result),
68
+ seed: String(record.seed),
69
+ min: Number(record.min),
70
+ max: Number(record.max),
71
+ }))
72
+ .sort((a, b) => a.actionId.localeCompare(b.actionId));
73
+ return JSON.stringify(entries);
74
+ }
75
+ // ============================================================
76
+ // MALFORMED SEED INJECTION
77
+ // ============================================================
78
+ describe('Malformed Seed Injection', () => {
79
+ it('should handle whitespace-padded seeds differently', () => {
80
+ const outcome1 = { test: { result: 5, seed: 123, min: 0, max: 10 } };
81
+ const outcome2 = { test: { result: 5, seed: ' 123', min: 0, max: 10 } };
82
+ expect(canonicalizeForHash(outcome1)).not.toBe(canonicalizeForHash(outcome2));
83
+ });
84
+ it('should escape special JSON characters in actionIds', () => {
85
+ const outcome = { 'action\n"test': { result: 5, seed: 123, min: 0, max: 10 } };
86
+ const canonical = canonicalizeForHash(outcome);
87
+ expect(() => JSON.parse(canonical)).not.toThrow();
88
+ });
89
+ it('should handle zero-width unicode characters', () => {
90
+ const outcome = { test: { result: 5, seed: '123\u200B456', min: 0, max: 10 } };
91
+ const canonical = canonicalizeForHash(outcome);
92
+ expect(canonical.length).toBeGreaterThan(0);
93
+ });
94
+ it('should not interpret hex strings as numbers', () => {
95
+ const outcome = { test: { result: 5, seed: '0xFF', min: 0, max: 10 } };
96
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"0xFF"');
97
+ });
98
+ it('should not interpret octal strings as numbers', () => {
99
+ const outcome = { test: { result: 5, seed: '0777', min: 0, max: 10 } };
100
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"0777"');
101
+ });
102
+ });
103
+ // ============================================================
104
+ // TYPE CONFUSION
105
+ // ============================================================
106
+ describe('Type Confusion', () => {
107
+ it('should handle array seeds', () => {
108
+ const outcome = { test: { result: 5, seed: [123], min: 0, max: 10 } };
109
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"123"');
110
+ });
111
+ it('should handle null seeds', () => {
112
+ const outcome = { test: { result: 5, seed: null, min: 0, max: 10 } };
113
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"null"');
114
+ });
115
+ it('should handle undefined seeds', () => {
116
+ const outcome = { test: { result: 5, seed: undefined, min: 0, max: 10 } };
117
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"undefined"');
118
+ });
119
+ it('should handle boolean seeds', () => {
120
+ const outcome = { test: { result: 5, seed: true, min: 0, max: 10 } };
121
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"true"');
122
+ });
123
+ it('should handle BigInt seeds', () => {
124
+ const outcome = { test: { result: 5, seed: BigInt(123), min: 0, max: 10 } };
125
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"123"');
126
+ });
127
+ it('should handle Symbol seeds', () => {
128
+ const symbol = Symbol('test');
129
+ expect(String(symbol)).toBe('Symbol(test)');
130
+ });
131
+ it('should handle object with custom toString', () => {
132
+ const malicious = { toString: () => '123', valueOf: () => 456 };
133
+ const outcome = { test: { result: 5, seed: malicious, min: 0, max: 10 } };
134
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"123"');
135
+ });
136
+ });
137
+ // ============================================================
138
+ // INTEGER BOUNDARIES
139
+ // ============================================================
140
+ describe('Integer Boundaries', () => {
141
+ it('should handle MAX_SAFE_INTEGER', () => {
142
+ const outcome = { test: { result: 5, seed: Number.MAX_SAFE_INTEGER, min: 0, max: 10 } };
143
+ expect(canonicalizeForHash(outcome)).toContain(`"seed":"${Number.MAX_SAFE_INTEGER}"`);
144
+ });
145
+ it('should handle MIN_SAFE_INTEGER', () => {
146
+ const outcome = { test: { result: 5, seed: Number.MIN_SAFE_INTEGER, min: -100, max: 100 } };
147
+ expect(canonicalizeForHash(outcome)).toContain(`"seed":"${Number.MIN_SAFE_INTEGER}"`);
148
+ });
149
+ it('should handle zero seed', () => {
150
+ const outcome = { test: { result: 5, seed: 0, min: 0, max: 10 } };
151
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"0"');
152
+ });
153
+ it('should handle negative seeds', () => {
154
+ const outcome = { test: { result: 5, seed: -12345, min: -100, max: 100 } };
155
+ expect(canonicalizeForHash(outcome)).toContain('"seed":"-12345"');
156
+ });
157
+ it('should handle NaN in result', () => {
158
+ const outcome = { test: { result: NaN, seed: 123, min: 0, max: 10 } };
159
+ expect(canonicalizeForHash(outcome)).toContain('"result":null');
160
+ });
161
+ it('should handle Infinity', () => {
162
+ const outcome = { test: { result: Infinity, seed: 123, min: 0, max: 10 } };
163
+ expect(canonicalizeForHash(outcome)).toContain('"result":null');
164
+ });
165
+ });
166
+ // ============================================================
167
+ // HASH COLLISION PREVENTION
168
+ // ============================================================
169
+ describe('Hash Collision Prevention', () => {
170
+ it('should produce different hashes for adjacent seeds', () => {
171
+ const outcome1 = { action: { result: 5, seed: 123, min: 0, max: 10 } };
172
+ const outcome2 = { action: { result: 5, seed: 124, min: 0, max: 10 } };
173
+ expect(canonicalizeForHash(outcome1)).not.toBe(canonicalizeForHash(outcome2));
174
+ });
175
+ it('should produce different hashes for different actionIds', () => {
176
+ const outcome1 = { action_a: { result: 5, seed: 123, min: 0, max: 10 } };
177
+ const outcome2 = { action_b: { result: 5, seed: 123, min: 0, max: 10 } };
178
+ expect(canonicalizeForHash(outcome1)).not.toBe(canonicalizeForHash(outcome2));
179
+ });
180
+ it('should produce same hash for number and string seeds after canonicalization', () => {
181
+ const outcome1 = { action: { result: 5, seed: 123, min: 0, max: 10 } };
182
+ const outcome2 = { action: { result: 5, seed: '123', min: 0, max: 10 } };
183
+ expect(canonicalizeForHash(outcome1)).toBe(canonicalizeForHash(outcome2));
184
+ });
185
+ it('should resist JSON injection in actionId', () => {
186
+ const outcome1 = { 'a","seed":"999': { result: 5, seed: 123, min: 0, max: 10 } };
187
+ const outcome2 = { 'a': { result: 5, seed: 999, min: 0, max: 10 } };
188
+ expect(canonicalizeForHash(outcome1)).not.toBe(canonicalizeForHash(outcome2));
189
+ });
190
+ });
191
+ // ============================================================
192
+ // PROTOTYPE POLLUTION
193
+ // ============================================================
194
+ describe('Prototype Pollution Prevention', () => {
195
+ it('should handle constructor as actionId', () => {
196
+ const outcome = { 'constructor': { result: 5, seed: 123, min: 0, max: 10 } };
197
+ const canonical = canonicalizeForHash(outcome);
198
+ expect(() => JSON.parse(canonical)).not.toThrow();
199
+ });
200
+ it('should handle toString as actionId', () => {
201
+ const outcome = { 'toString': { result: 5, seed: 123, min: 0, max: 10 } };
202
+ expect(() => canonicalizeForHash(outcome)).not.toThrow();
203
+ });
204
+ it('should not pollute prototype', () => {
205
+ const outcome = { 'normal': { result: 6, seed: 456, min: 0, max: 10 } };
206
+ canonicalizeForHash(outcome);
207
+ expect({}.__proto__.result).toBeUndefined();
208
+ });
209
+ });
210
+ // ============================================================
211
+ // ENGINE RNG VALIDATION
212
+ // ============================================================
213
+ describe('Engine RNG Validation', () => {
214
+ it('should return numeric seeds from engine', async () => {
215
+ const init = await initSession();
216
+ const spin = await executeSpin(init.publicState, init.privateState);
217
+ expect(spin.success).toBe(true);
218
+ if (spin.rngOutcome) {
219
+ for (const [, record] of Object.entries(spin.rngOutcome)) {
220
+ expect(typeof record.seed).toBe('number');
221
+ expect(Number.isSafeInteger(record.seed)).toBe(true);
222
+ }
223
+ }
224
+ });
225
+ it('should have valid result within range', async () => {
226
+ const init = await initSession();
227
+ const spin = await executeSpin(init.publicState, init.privateState);
228
+ if (spin.rngOutcome) {
229
+ for (const [, record] of Object.entries(spin.rngOutcome)) {
230
+ expect(record.result).toBeGreaterThanOrEqual(record.min);
231
+ expect(record.result).toBeLessThanOrEqual(record.max);
232
+ }
233
+ }
234
+ });
235
+ it('should have unique actionIds', async () => {
236
+ const init = await initSession();
237
+ const spin = await executeSpin(init.publicState, init.privateState);
238
+ if (spin.rngOutcome) {
239
+ const actionIds = Object.keys(spin.rngOutcome);
240
+ expect(actionIds.length).toBe(new Set(actionIds).size);
241
+ }
242
+ });
243
+ it('should maintain type consistency across 50 spins', async () => {
244
+ const init = await initSession();
245
+ let pub = init.publicState;
246
+ let priv = init.privateState;
247
+ for (let i = 0; i < 50; i++) {
248
+ const { results, publicState, privateState } = await executeSpinWithBonusHandling(pub, priv);
249
+ // Verify RNG structure for all results (spin + any bonus rounds)
250
+ for (const result of results) {
251
+ if (result.rngOutcome) {
252
+ for (const [, record] of Object.entries(result.rngOutcome)) {
253
+ expect(typeof record.seed).toBe('number');
254
+ }
255
+ }
256
+ }
257
+ pub = publicState;
258
+ priv = privateState;
259
+ }
260
+ });
261
+ });
262
+ });
263
+ //# sourceMappingURL=rng-seed-security.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rng-seed-security.test.js","sourceRoot":"","sources":["../../src/__tests__/rng-seed-security.test.ts"],"names":[],"mappings":"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"}