mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 17:46:46 +00:00
Validate incoming API data with zod (#891)
## Description: Validate incoming API data with zod. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors --------- Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -112,7 +112,7 @@ export enum LogSeverity {
|
||||
Fatal = "FATAL",
|
||||
}
|
||||
|
||||
const GameConfigSchema = z.object({
|
||||
export const GameConfigSchema = z.object({
|
||||
gameMap: z.nativeEnum(GameMapType),
|
||||
difficulty: z.nativeEnum(Difficulty),
|
||||
gameType: z.nativeEnum(GameType),
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { z } from "zod";
|
||||
import { GameConfigSchema } from "./Schemas";
|
||||
|
||||
export const CreateGameInputSchema = GameConfigSchema.or(
|
||||
z
|
||||
.object({})
|
||||
.strict()
|
||||
.transform((val) => undefined),
|
||||
);
|
||||
|
||||
export const GameInputSchema = GameConfigSchema.partial();
|
||||
@@ -58,11 +58,10 @@ export class MapPlaylist {
|
||||
infiniteTroops: false,
|
||||
instantBuild: false,
|
||||
disableNPCs: mode === GameMode.Team,
|
||||
disableNukes: false,
|
||||
gameMode: mode,
|
||||
playerTeams: numPlayerTeams,
|
||||
bots: 400,
|
||||
} as GameConfig;
|
||||
} satisfies GameConfig;
|
||||
}
|
||||
|
||||
private getNextMap(): MapWithMode {
|
||||
|
||||
@@ -282,9 +282,7 @@ async function schedulePublicGame(playlist: MapPlaylist) {
|
||||
"Content-Type": "application/json",
|
||||
[config.adminHeader()]: config.adminToken(),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
gameConfig: playlist.gameConfig(),
|
||||
}),
|
||||
body: JSON.stringify(playlist.gameConfig()),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+18
-20
@@ -11,10 +11,10 @@ import { getServerConfigFromServer } from "../core/configuration/ConfigLoader";
|
||||
import { GameType } from "../core/game/Game";
|
||||
import {
|
||||
ClientMessageSchema,
|
||||
GameConfig,
|
||||
GameRecord,
|
||||
GameRecordSchema,
|
||||
} from "../core/Schemas";
|
||||
import { CreateGameInputSchema, GameInputSchema } from "../core/WorkerSchemas";
|
||||
import { archive, readGameRecord } from "./Archive";
|
||||
import { Client } from "./Client";
|
||||
import { GameManager } from "./GameManager";
|
||||
@@ -89,7 +89,13 @@ export function startWorker() {
|
||||
return res.status(400).json({ error: "Game ID is required" });
|
||||
}
|
||||
const clientIP = req.ip || req.socket.remoteAddress || "unknown";
|
||||
const gc = req.body?.gameConfig as GameConfig;
|
||||
const result = CreateGameInputSchema.safeParse(req.body);
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
return res.status(400).json({ error });
|
||||
}
|
||||
|
||||
const gc = result.data;
|
||||
if (
|
||||
gc?.gameType === GameType.Public &&
|
||||
req.headers[config.adminHeader()] !== config.adminToken()
|
||||
@@ -97,9 +103,7 @@ export function startWorker() {
|
||||
log.warn(
|
||||
`cannot create public game ${id}, ip ${ipAnonymize(clientIP)} incorrect admin token`,
|
||||
);
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Invalid admin token for public game creation" });
|
||||
return res.status(401).send("Unauthorized");
|
||||
}
|
||||
|
||||
// Double-check this worker should host this game
|
||||
@@ -144,9 +148,15 @@ export function startWorker() {
|
||||
app.put(
|
||||
"/api/game/:id",
|
||||
gatekeeper.httpHandler(LimiterType.Put, async (req, res) => {
|
||||
const result = GameInputSchema.safeParse(req.body);
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
return res.status(400).json({ error });
|
||||
}
|
||||
const config = result.data;
|
||||
// TODO: only update public game if from local host
|
||||
const lobbyID = req.params.id;
|
||||
if (req.body.gameType === GameType.Public) {
|
||||
if (config.gameType === GameType.Public) {
|
||||
log.info(`cannot update game ${lobbyID} to public`);
|
||||
return res.status(400).json({ error: "Cannot update public game" });
|
||||
}
|
||||
@@ -167,18 +177,7 @@ export function startWorker() {
|
||||
.status(400)
|
||||
.json({ error: "Cannot update game after it has started" });
|
||||
}
|
||||
game.updateGameConfig({
|
||||
gameMap: req.body.gameMap,
|
||||
difficulty: req.body.difficulty,
|
||||
infiniteGold: req.body.infiniteGold,
|
||||
infiniteTroops: req.body.infiniteTroops,
|
||||
instantBuild: req.body.instantBuild,
|
||||
bots: req.body.bots,
|
||||
disableNPCs: req.body.disableNPCs,
|
||||
disabledUnits: req.body.disabledUnits,
|
||||
gameMode: req.body.gameMode,
|
||||
playerTeams: req.body.playerTeams,
|
||||
});
|
||||
game.updateGameConfig(config);
|
||||
res.status(200).json({ success: true });
|
||||
}),
|
||||
);
|
||||
@@ -251,8 +250,7 @@ export function startWorker() {
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
log.info(error);
|
||||
res.status(400).json({ error });
|
||||
return;
|
||||
return res.status(400).json({ error });
|
||||
}
|
||||
|
||||
const gameRecord: GameRecord = result.data;
|
||||
|
||||
Reference in New Issue
Block a user