@omnitronix/bonnys-fortune-game-engine 1.2.3 → 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.
- package/README.md +0 -0
- package/dist/__tests__/rng-gli19-compliance.test.js +222 -0
- package/dist/__tests__/rng-gli19-compliance.test.js.map +1 -0
- package/dist/__tests__/rng-seed-security.test.js +263 -0
- package/dist/__tests__/rng-seed-security.test.js.map +1 -0
- package/dist/__tests__/rng-seed-type.test.js +282 -0
- package/dist/__tests__/rng-seed-type.test.js.map +1 -0
- package/dist/__tests__/rng-stress-boundary.test.js +386 -0
- package/dist/__tests__/rng-stress-boundary.test.js.map +1 -0
- package/dist/config/reel-strips-config/reels-BASE.csv +0 -0
- package/dist/config/reel-strips-config/reels-BONUS.csv +0 -0
- package/dist/logic/handlers/base-game.handler.js +19 -1
- package/dist/logic/handlers/base-game.handler.js.map +1 -1
- package/dist/logic/handlers/collect-feature-bonus.handler.js +2 -2
- package/dist/logic/handlers/collect-feature-bonus.handler.js.map +1 -1
- package/dist/logic/handlers/free-spins-bonus.handler.js +4 -2
- package/dist/logic/handlers/free-spins-bonus.handler.js.map +1 -1
- package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.js +4 -2
- package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.js.map +1 -1
- package/dist/logic/handlers/treasure-hunt-bonus.handler.js +4 -2
- package/dist/logic/handlers/treasure-hunt-bonus.handler.js.map +1 -1
- package/dist/rng/rng-client.factory.js +45 -1
- package/dist/rng/rng-client.factory.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
File without changes
|
|
@@ -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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"}
|