Try AJV for all gateway payloads

This commit is contained in:
Rory& committed 2026-10-17 09:06:49 +02:00
1 parent 977aea9375
commit 94bf5ebdfd
10 files changed
+129 -17

No files matched your search

+1
View File
@@ -77,6 +77,7 @@ export async function Message(this: WebSocket, buffer: WS.Data) {
if (!this.session_id) console.log(`[Gateway/${this.user_id ?? this.ipAddress}] Unknown session id, dumping to unknown folder`);
}
// TODO: somehow make this schema work with AJV
check.call(this, PayloadSchema, data);
const OPCodeHandler = OPCodeHandlers[data.op];
+16 -2
View File
@@ -1,11 +1,25 @@
import { WebSocket, Payload } from "@spacebar/gateway";
import { onLazyRequest } from "./LazyRequest";
import { GuildSubscriptionsBulkSchema } from "@spacebar/schemas";
import { getInvalidSchemaValues, GuildSubscriptionsBulkSchema, IdentifySchema, validateSchema } from "@spacebar/schemas";
import { check } from "./instanceOf";
export async function onGuildSubscriptionsBulk(this: WebSocket, payload: Payload) {
const startTime = Date.now();
check.call(this, GuildSubscriptionsBulkSchema, payload.d);
// Check payload matches schema
try {
payload.d = validateSchema<GuildSubscriptionsBulkSchema>("GuildSubscriptionsBulkSchema", payload.d);
} catch (e) {
console.error("AJV validation of GuildSubscriptionsBulk schema failed:", e);
console.error("Provided values:");
if (Array.isArray(e))
for (const invalidSchemaValue of getInvalidSchemaValues(payload.d, e)) {
console.error(`${invalidSchemaValue.instancePath}:`, invalidSchemaValue.value);
}
check.call(this, GuildSubscriptionsBulkSchema, payload.d);
}
const body = payload.d as GuildSubscriptionsBulkSchema;
let guildId: keyof GuildSubscriptionsBulkSchema["subscriptions"];
+17 -3
View File
@@ -20,7 +20,7 @@ import murmur from "murmurhash-js/murmurhash3_gc";
import { getDatabase, Member, Role, Session, User, Channel } from "@spacebar/database";
import { arrayPartition, Stopwatch } from "@spacebar/extensions";
import { WebSocket, Payload, handlePresenceUpdate, OPCODES, Send, handleOffloadedGatewayRequest } from "@spacebar/gateway";
import { LazyRequestSchema } from "@spacebar/schemas";
import { getInvalidSchemaValues, IdentifySchema, LazyRequestSchema, validateSchema } from "@spacebar/schemas";
import { getPermission, listenEvent, Presence, Permissions, getMostRelevantSession, Config } from "@spacebar/util";
import { check } from "./instanceOf";
import { start } from "node:repl";
@@ -153,8 +153,22 @@ async function subscribeToMemberEvents(this: WebSocket, user_id: string) {
export async function onLazyRequest(this: WebSocket, { d }: Payload) {
const sw = Stopwatch.startNew();
// TODO: check data
check.call(this, LazyRequestSchema, d);
// TODO: check data - 2026/10/16 is this still relevant?
// Check payload matches schema
try {
d = validateSchema<LazyRequestSchema>("LazyRequestSchema", d);
} catch (e) {
console.error("AJV validation of LazyRequest schema failed:", e);
console.error("Provided values:");
if (Array.isArray(e))
for (const invalidSchemaValue of getInvalidSchemaValues(d, e)) {
console.error(`${invalidSchemaValue.instancePath}:`, invalidSchemaValue.value);
}
check.call(this, LazyRequestSchema, d);
}
// noinspection JSUnusedLocalSymbols - TODO: implement typing/activities subscriptions
const { guild_id, typing, channels, activities, members } = d as LazyRequestSchema;
+16 -2
View File
@@ -19,12 +19,26 @@
import { Session, User } from "@spacebar/database";
import { WebSocket, Payload } from "@spacebar/gateway";
import { emitEvent, PresenceUpdateEvent } from "@spacebar/util";
import { ActivitySchema, InternalStatusOrder, PrivateStatus } from "@spacebar/schemas";
import { ActivitySchema, getInvalidSchemaValues, IdentifySchema, InternalStatusOrder, PrivateStatus, validateSchema } from "@spacebar/schemas";
import { check } from "./instanceOf";
export async function onPresenceUpdate(this: WebSocket, { d }: Payload) {
const startTime = Date.now();
check.call(this, ActivitySchema, d);
// Check payload matches schema
try {
d = validateSchema<ActivitySchema>("ActivitySchema", d);
} catch (e) {
console.error("AJV validation of Activity schema failed:", e);
console.error("Provided values:");
if (Array.isArray(e))
for (const invalidSchemaValue of getInvalidSchemaValues(d, e)) {
console.error(`${invalidSchemaValue.instancePath}:`, invalidSchemaValue.value);
}
check.call(this, ActivitySchema, d);
}
const presence = d as ActivitySchema;
if (d.status === "unknown") {
+14 -2
View File
@@ -20,7 +20,7 @@ import { FindManyOptions, ILike, In, MoreThan } from "typeorm";
import { getDatabase, Member, Session } from "@spacebar/database";
import { DateBuilder } from "@spacebar/extensions";
import { WebSocket, Payload, OPCODES, Send, handleOffloadedGatewayRequest } from "@spacebar/gateway";
import { RequestGuildMembersSchema } from "@spacebar/schemas";
import { getInvalidSchemaValues, IdentifySchema, RequestGuildMembersSchema, validateSchema } from "@spacebar/schemas";
import { Config, getPermission, GuildMembersChunkEvent, Presence } from "@spacebar/util";
import { check } from "./instanceOf";
@@ -37,7 +37,19 @@ export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) {
if (await handleOffloadedGatewayRequest(this, Config.get().offload.gateway.guildMembersUrl!, guildIds)) return;
}
check.call(this, RequestGuildMembersSchema, d);
// Check payload matches schema
try {
d = validateSchema<RequestGuildMembersSchema>("RequestGuildMembersSchema", d);
} catch (e) {
console.error("AJV validation of RequestGuildMembers schema failed:", e);
console.error("Provided values:");
if (Array.isArray(e))
for (const invalidSchemaValue of getInvalidSchemaValues(d, e)) {
console.error(`${invalidSchemaValue.instancePath}:`, invalidSchemaValue.value);
}
check.call(this, RequestGuildMembersSchema, d);
}
const { presences, nonce, query: requestQuery } = d as RequestGuildMembersSchema;
let { limit, user_ids, guild_id } = d as RequestGuildMembersSchema;
+16 -2
View File
@@ -20,11 +20,25 @@ import { Channel, Member, Stream, StreamSession, VoiceState } from "@spacebar/da
import { genVoiceToken, Payload, WebSocket, generateStreamKey } from "@spacebar/gateway";
import { Config, emitEvent, Snowflake, StreamCreateEvent, StreamServerUpdateEvent, VoiceStateUpdateEvent } from "@spacebar/util";
import { check } from "./instanceOf";
import { StreamCreateSchema } from "@spacebar/schemas";
import { getInvalidSchemaValues, IdentifySchema, StreamCreateSchema, validateSchema } from "@spacebar/schemas";
export async function onStreamCreate(this: WebSocket, data: Payload) {
const startTime = Date.now();
check.call(this, StreamCreateSchema, data.d);
// Check payload matches schema
try {
data.d = validateSchema<StreamCreateSchema>("StreamCreateSchema", data.d);
} catch (e) {
console.error("AJV validation of StreamCreate schema failed:", e);
console.error("Provided values:");
if (Array.isArray(e))
for (const invalidSchemaValue of getInvalidSchemaValues(data.d, e)) {
console.error(`${invalidSchemaValue.instancePath}:`, invalidSchemaValue.value);
}
check.call(this, StreamCreateSchema, data.d);
}
const body = data.d as StreamCreateSchema;
if (body.channel_id.trim().length === 0) return;
+16 -2
View File
@@ -20,11 +20,25 @@ import { Member, Stream, VoiceState } from "@spacebar/database";
import { parseStreamKey, Payload, WebSocket } from "@spacebar/gateway";
import { emitEvent, StreamDeleteEvent, VoiceStateUpdateEvent } from "@spacebar/util";
import { check } from "./instanceOf";
import { StreamDeleteSchema } from "@spacebar/schemas";
import { getInvalidSchemaValues, IdentifySchema, StreamDeleteSchema, validateSchema } from "@spacebar/schemas";
export async function onStreamDelete(this: WebSocket, data: Payload) {
const startTime = Date.now();
check.call(this, StreamDeleteSchema, data.d);
// Check payload matches schema
try {
data.d = validateSchema<StreamDeleteSchema>("StreamDeleteSchema", data.d);
} catch (e) {
console.error("AJV validation of StreamDelete schema failed:", e);
console.error("Provided values:");
if (Array.isArray(e))
for (const invalidSchemaValue of getInvalidSchemaValues(data.d, e)) {
console.error(`${invalidSchemaValue.instancePath}:`, invalidSchemaValue.value);
}
check.call(this, StreamDeleteSchema, data.d);
}
const body = data.d as StreamDeleteSchema;
let parsedKey: {
+16 -2
View File
@@ -20,12 +20,26 @@ import { Not } from "typeorm";
import { Stream, StreamSession } from "@spacebar/database";
import { genVoiceToken, parseStreamKey, Payload, WebSocket } from "@spacebar/gateway";
import { Config, emitEvent, StreamCreateEvent, StreamServerUpdateEvent } from "@spacebar/util";
import { StreamWatchSchema } from "@spacebar/schemas";
import { getInvalidSchemaValues, IdentifySchema, StreamWatchSchema, validateSchema } from "@spacebar/schemas";
import { check } from "./instanceOf";
export async function onStreamWatch(this: WebSocket, data: Payload) {
const startTime = Date.now();
check.call(this, StreamWatchSchema, data.d);
// Check payload matches schema
try {
data.d = validateSchema<StreamWatchSchema>("StreamWatchSchema", data.d);
} catch (e) {
console.error("AJV validation of StreamWatch schema failed:", e);
console.error("Provided values:");
if (Array.isArray(e))
for (const invalidSchemaValue of getInvalidSchemaValues(data.d, e)) {
console.error(`${invalidSchemaValue.instancePath}:`, invalidSchemaValue.value);
}
check.call(this, StreamWatchSchema, data.d);
}
const body = data.d as StreamWatchSchema;
// TODO: apply perms: check if user is allowed to watch
+16 -2
View File
@@ -19,7 +19,7 @@
import { Guild, Member, VoiceState } from "@spacebar/database";
import { Payload, WebSocket, genVoiceToken } from "@spacebar/gateway";
import { Config, emitEvent, VoiceServerUpdateEvent, VoiceStateUpdateEvent } from "@spacebar/util";
import { ConfigVoiceRegion, VoiceStateUpdateSchema } from "@spacebar/schemas";
import { ConfigVoiceRegion, getInvalidSchemaValues, validateSchema, VoiceStateUpdateSchema } from "@spacebar/schemas";
import { check } from "./instanceOf";
// TODO: check if a voice server is setup
@@ -29,7 +29,21 @@ import { check } from "./instanceOf";
export async function onVoiceStateUpdate(this: WebSocket, data: Payload) {
const startTime = Date.now();
check.call(this, VoiceStateUpdateSchema, data.d);
// Check payload matches schema
try {
data.d = validateSchema<VoiceStateUpdateSchema>("VoiceStateUpdateSchema", data.d);
} catch (e) {
console.error("AJV validation of VoiceStateUpdate schema failed:", e);
console.error("Provided values:");
if (Array.isArray(e))
for (const invalidSchemaValue of getInvalidSchemaValues(data.d, e)) {
console.error(`${invalidSchemaValue.instancePath}:`, invalidSchemaValue.value);
}
check.call(this, VoiceStateUpdateSchema, data.d);
}
const body = data.d as VoiceStateUpdateSchema;
const isNew = body.channel_id === null && body.guild_id === null;
let isChanged = false;
+1
View File
@@ -20,6 +20,7 @@ import { instanceOf } from "lambert-server/check";
import { WebSocket } from "@spacebar/gateway";
import { CLOSECODES } from "../util/Constants";
//@deprecated
export function check(this: WebSocket, schema: unknown, data: unknown) {
try {
const error = instanceOf(schema, data, { path: "body" });