mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-30 05:09:42 +00:00
fix: reject non-integer tile refs in GameMap.isValidRef (#4720)
`GameMap.isValidRef` only checked that a `TileRef` was in range — not that it was an integer. Since the simulation runs on every client and intents carry raw tile refs, a single ~60-byte malformed intent with a fractional ref (e.g. `1.5`) passed validation in executions (`ConstructionExecution`, `TransportShipExecution`, `MoveWarshipExecution`) and could corrupt typed-array indexing / freeze the game for the whole lobby. Require `Number.isInteger(ref)` in `isValidRef`, matching what `isValidCoord` already does for x/y. This also rejects `NaN` and `±Infinity` explicitly. Overhead is ~3 ns per call, and `isValidRef` is boundary validation (per intent / per path request), not inner-loop code. New `tests/core/game/GameMap.isValidRef.test.ts`: - in-range integer refs accepted - out-of-range refs rejected - in-range fractional refs rejected (the attack vector) - `NaN` / `±Infinity` rejected All existing tests for consumers (pathfinding, transport ship, construction) pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
evanpelle
co-authored by
Claude Fable 5
parent
dd1277e245
commit
decf8ce355
@@ -166,7 +166,7 @@ export class GameMapImpl implements GameMap {
|
||||
}
|
||||
|
||||
isValidRef(ref: TileRef): boolean {
|
||||
return ref >= 0 && ref < this.refToX.length;
|
||||
return Number.isInteger(ref) && ref >= 0 && ref < this.refToX.length;
|
||||
}
|
||||
|
||||
x(ref: TileRef): number {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { GameMapImpl } from "../../../src/core/game/GameMap";
|
||||
|
||||
describe("GameMap.isValidRef", () => {
|
||||
const map = new GameMapImpl(10, 8, new Uint8Array(10 * 8), 0);
|
||||
|
||||
it("accepts integer refs within range", () => {
|
||||
expect(map.isValidRef(0)).toBe(true);
|
||||
expect(map.isValidRef(1)).toBe(true);
|
||||
expect(map.isValidRef(10 * 8 - 1)).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects refs outside the map", () => {
|
||||
expect(map.isValidRef(-1)).toBe(false);
|
||||
expect(map.isValidRef(10 * 8)).toBe(false);
|
||||
expect(map.isValidRef(Number.MAX_SAFE_INTEGER)).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects non-integer refs even when in range", () => {
|
||||
// A fractional ref passes a pure range check but corrupts anything that
|
||||
// indexes typed arrays or iterates from it — a single malicious intent
|
||||
// could freeze the simulation on every client.
|
||||
expect(map.isValidRef(1.5)).toBe(false);
|
||||
expect(map.isValidRef(0.0000001)).toBe(false);
|
||||
expect(map.isValidRef(79.999)).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects NaN and infinities", () => {
|
||||
expect(map.isValidRef(NaN)).toBe(false);
|
||||
expect(map.isValidRef(Infinity)).toBe(false);
|
||||
expect(map.isValidRef(-Infinity)).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user