mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 13:50:43 +00:00
Fix emoji exploit (#640)
## Description: This should fix the exploit that allows players to send custom text as an "emoji". It does this by introducing a emoji ID (index into the emoji table) instead of sending the raw emoji as a string. ## Please complete the following: - [ ] I have added screenshots for all UI updates - [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 ## Please put your Discord username so you can be contacted if a bug or regression is found: PilkeySEK
This commit is contained in:
@@ -88,7 +88,7 @@ export class SendTargetPlayerIntentEvent implements GameEvent {
|
||||
export class SendEmojiIntentEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly recipient: PlayerView | typeof AllPlayers,
|
||||
public readonly emoji: string,
|
||||
public readonly emoji: number,
|
||||
) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,24 +4,11 @@ import { EventBus } from "../../../core/EventBus";
|
||||
import { AllPlayers } from "../../../core/game/Game";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { TerraNulliusImpl } from "../../../core/game/TerraNulliusImpl";
|
||||
import { emojiTable, flattenedEmojiTable } from "../../../core/Util";
|
||||
import { ShowEmojiMenuEvent } from "../../InputHandler";
|
||||
import { SendEmojiIntentEvent } from "../../Transport";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
|
||||
const emojiTable: string[][] = [
|
||||
["😀", "😊", "🥰", "😇", "😎"],
|
||||
["😞", "🥺", "😭", "😱", "😡"],
|
||||
["😈", "🤡", "🖕", "🥱", "🤦♂️"],
|
||||
["👋", "👏", "🤌", "💪", "🫡"],
|
||||
["👍", "👎", "❓", "🐔", "🐀"],
|
||||
["🤝", "🆘", "🕊️", "🏳️", "⏳"],
|
||||
["🔥", "💥", "💀", "☢️", "⚠️"],
|
||||
["↖️", "⬆️", "↗️", "👑", "🥇"],
|
||||
["⬅️", "🎯", "➡️", "🥈", "🥉"],
|
||||
["↙️", "⬇️", "↘️", "❤️", "💔"],
|
||||
["💰", "⚓", "⛵", "🏡", "🛡️"],
|
||||
];
|
||||
|
||||
@customElement("emoji-table")
|
||||
export class EmojiTable extends LitElement {
|
||||
public eventBus: EventBus;
|
||||
@@ -130,7 +117,12 @@ export class EmojiTable extends LitElement {
|
||||
targetPlayer == this.game.myPlayer()
|
||||
? AllPlayers
|
||||
: (targetPlayer as PlayerView);
|
||||
this.eventBus.emit(new SendEmojiIntentEvent(recipient, emoji));
|
||||
this.eventBus.emit(
|
||||
new SendEmojiIntentEvent(
|
||||
recipient,
|
||||
flattenedEmojiTable.indexOf(emoji),
|
||||
),
|
||||
);
|
||||
this.hideTable();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { flattenedEmojiTable } from "../../../core/Util";
|
||||
import { MouseUpEvent } from "../../InputHandler";
|
||||
import {
|
||||
SendAllianceRequestIntentEvent,
|
||||
@@ -122,9 +123,16 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
e.stopPropagation();
|
||||
this.emojiTable.showTable((emoji: string) => {
|
||||
if (myPlayer == other) {
|
||||
this.eventBus.emit(new SendEmojiIntentEvent(AllPlayers, emoji));
|
||||
this.eventBus.emit(
|
||||
new SendEmojiIntentEvent(
|
||||
AllPlayers,
|
||||
flattenedEmojiTable.indexOf(emoji),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
this.eventBus.emit(new SendEmojiIntentEvent(other, emoji));
|
||||
this.eventBus.emit(
|
||||
new SendEmojiIntentEvent(other, flattenedEmojiTable.indexOf(emoji)),
|
||||
);
|
||||
}
|
||||
this.emojiTable.hideTable();
|
||||
this.hide();
|
||||
|
||||
+5
-8
@@ -9,6 +9,7 @@ import {
|
||||
PlayerType,
|
||||
UnitType,
|
||||
} from "./game/Game";
|
||||
import { flattenedEmojiTable } from "./Util";
|
||||
|
||||
export type GameID = string;
|
||||
export type ClientID = string;
|
||||
@@ -133,14 +134,10 @@ const SafeString = z
|
||||
)
|
||||
.max(1000);
|
||||
|
||||
const EmojiSchema = z.string().refine(
|
||||
(val) => {
|
||||
return /\p{Emoji}/u.test(val);
|
||||
},
|
||||
{
|
||||
message: "Must contain at least one emoji character",
|
||||
},
|
||||
);
|
||||
const EmojiSchema = z
|
||||
.number()
|
||||
.nonnegative()
|
||||
.max(flattenedEmojiTable.length - 1);
|
||||
const ID = z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9]+$/)
|
||||
|
||||
@@ -307,3 +307,19 @@ export function createRandomName(
|
||||
}
|
||||
return randomName;
|
||||
}
|
||||
|
||||
export const emojiTable: string[][] = [
|
||||
["😀", "😊", "🥰", "😇", "😎"],
|
||||
["😞", "🥺", "😭", "😱", "😡"],
|
||||
["😈", "🤡", "🖕", "🥱", "🤦♂️"],
|
||||
["👋", "👏", "🤌", "💪", "🫡"],
|
||||
["👍", "👎", "❓", "🐔", "🐀"],
|
||||
["🤝", "🆘", "🕊️", "🏳️", "⏳"],
|
||||
["🔥", "💥", "💀", "☢️", "⚠️"],
|
||||
["↖️", "⬆️", "↗️", "👑", "🥇"],
|
||||
["⬅️", "🎯", "➡️", "🥈", "🥉"],
|
||||
["↙️", "⬇️", "↘️", "❤️", "💔"],
|
||||
["💰", "⚓", "⛵", "🏡", "🛡️"],
|
||||
];
|
||||
// 2d to 1d array
|
||||
export const flattenedEmojiTable: string[] = [].concat(...emojiTable);
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
PlayerID,
|
||||
PlayerType,
|
||||
} from "../game/Game";
|
||||
import { flattenedEmojiTable } from "../Util";
|
||||
|
||||
export class EmojiExecution implements Execution {
|
||||
private requestor: Player;
|
||||
@@ -17,7 +18,7 @@ export class EmojiExecution implements Execution {
|
||||
constructor(
|
||||
private senderID: PlayerID,
|
||||
private recipientID: PlayerID | typeof AllPlayers,
|
||||
private emoji: string,
|
||||
private emoji: number,
|
||||
) {}
|
||||
|
||||
init(mg: Game, ticks: number): void {
|
||||
@@ -38,10 +39,12 @@ export class EmojiExecution implements Execution {
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
const emojiString = flattenedEmojiTable.at(this.emoji);
|
||||
|
||||
if (this.requestor.canSendEmoji(this.recipient)) {
|
||||
this.requestor.sendEmoji(this.recipient, this.emoji);
|
||||
this.requestor.sendEmoji(this.recipient, emojiString);
|
||||
if (
|
||||
this.emoji == "🖕" &&
|
||||
emojiString == "🖕" &&
|
||||
this.recipient != AllPlayers &&
|
||||
this.recipient.type() == PlayerType.FakeHuman
|
||||
) {
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import { euclDistFN, manhattanDistFN, TileRef } from "../game/GameMap";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { GameID } from "../Schemas";
|
||||
import { calculateBoundingBox, simpleHash } from "../Util";
|
||||
import { calculateBoundingBox, flattenedEmojiTable, simpleHash } from "../Util";
|
||||
import { ConstructionExecution } from "./ConstructionExecution";
|
||||
import { EmojiExecution } from "./EmojiExecution";
|
||||
import { NukeExecution } from "./NukeExecution";
|
||||
@@ -43,6 +43,7 @@ export class FakeHumanExecution implements Execution {
|
||||
private lastEmojiSent = new Map<Player, Tick>();
|
||||
private lastNukeSent: [Tick, TileRef][] = [];
|
||||
private embargoMalusApplied = new Set<PlayerID>();
|
||||
private heckleEmoji: number[];
|
||||
|
||||
constructor(
|
||||
gameID: GameID,
|
||||
@@ -55,6 +56,7 @@ export class FakeHumanExecution implements Execution {
|
||||
this.attackTick = this.random.nextInt(0, this.attackRate);
|
||||
this.triggerRatio = this.random.nextInt(60, 90) / 100;
|
||||
this.reserveRatio = this.random.nextInt(30, 60) / 100;
|
||||
this.heckleEmoji = ["🤡", "😡"].map((e) => flattenedEmojiTable.indexOf(e));
|
||||
}
|
||||
|
||||
init(mg: Game) {
|
||||
@@ -267,7 +269,7 @@ export class FakeHumanExecution implements Execution {
|
||||
new EmojiExecution(
|
||||
this.player.id(),
|
||||
enemy.id(),
|
||||
this.random.randElement(["🤡", "😡"]),
|
||||
this.random.randElement(this.heckleEmoji),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Tick,
|
||||
} from "../../game/Game";
|
||||
import { PseudoRandom } from "../../PseudoRandom";
|
||||
import { flattenedEmojiTable } from "../../Util";
|
||||
import { AttackExecution } from "../AttackExecution";
|
||||
import { EmojiExecution } from "../EmojiExecution";
|
||||
|
||||
@@ -15,13 +16,17 @@ export class BotBehavior {
|
||||
private enemy: Player | null = null;
|
||||
private enemyUpdated: Tick;
|
||||
|
||||
private assistAcceptEmoji: number;
|
||||
|
||||
constructor(
|
||||
private random: PseudoRandom,
|
||||
private game: Game,
|
||||
private player: Player,
|
||||
private triggerRatio: number,
|
||||
private reserveRatio: number,
|
||||
) {}
|
||||
) {
|
||||
this.assistAcceptEmoji = flattenedEmojiTable.indexOf("👍");
|
||||
}
|
||||
|
||||
handleAllianceRequests() {
|
||||
for (const req of this.player.incomingAllianceRequests()) {
|
||||
@@ -33,7 +38,7 @@ export class BotBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
private emoji(player: Player, emoji: string) {
|
||||
private emoji(player: Player, emoji: number) {
|
||||
if (player.type() !== PlayerType.Human) return;
|
||||
this.game.addExecution(
|
||||
new EmojiExecution(this.player.id(), player.id(), emoji),
|
||||
@@ -78,7 +83,7 @@ export class BotBehavior {
|
||||
this.player.updateRelation(ally, -20);
|
||||
this.enemy = target;
|
||||
this.enemyUpdated = this.game.ticks();
|
||||
this.emoji(ally, "👍");
|
||||
this.emoji(ally, this.assistAcceptEmoji);
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user