mirror of
https://github.com/spacebarchat/server.git
synced 2026-10-22 15:22:43 +00:00
Prettier u10
This commit is contained in:
1 parent
5a3c1a3f53
commit
c323e55290
5 files changed
+52
-149
No files matched your search
@@ -42,37 +42,22 @@ try {
|
||||
// TODO: specify rate limit in config
|
||||
// TODO: check msg max size
|
||||
|
||||
export async function Connection(
|
||||
this: WS.Server,
|
||||
socket: WebSocket,
|
||||
request: IncomingMessage,
|
||||
) {
|
||||
export async function Connection(this: WS.Server, socket: WebSocket, request: IncomingMessage) {
|
||||
const forwardedFor = Config.get().security.forwardedFor;
|
||||
const ipAddress = forwardedFor
|
||||
? (request.headers[forwardedFor.toLowerCase()] as string)
|
||||
: request.socket.remoteAddress;
|
||||
const ipAddress = forwardedFor ? (request.headers[forwardedFor.toLowerCase()] as string) : request.socket.remoteAddress;
|
||||
|
||||
socket.ipAddress = ipAddress;
|
||||
socket.userAgent = request.headers["user-agent"] as string;
|
||||
|
||||
if (!ipAddress && Config.get().security.cdnSignatureIncludeIp) {
|
||||
return socket.close(
|
||||
CLOSECODES.Decode_error,
|
||||
"Gateway connection rejected: IP address is required.",
|
||||
);
|
||||
return socket.close(CLOSECODES.Decode_error, "Gateway connection rejected: IP address is required.");
|
||||
}
|
||||
|
||||
if (
|
||||
!socket.userAgent &&
|
||||
Config.get().security.cdnSignatureIncludeUserAgent
|
||||
) {
|
||||
return socket.close(
|
||||
CLOSECODES.Decode_error,
|
||||
"Gateway connection rejected: User-Agent header is required.",
|
||||
);
|
||||
if (!socket.userAgent && Config.get().security.cdnSignatureIncludeUserAgent) {
|
||||
return socket.close(CLOSECODES.Decode_error, "Gateway connection rejected: User-Agent header is required.");
|
||||
}
|
||||
|
||||
if (request.headers.cookie?.split("; ").find(x => x.startsWith("__sb_sessid="))) {
|
||||
if (request.headers.cookie?.split("; ").find((x) => x.startsWith("__sb_sessid="))) {
|
||||
socket.fingerprint = request.headers.cookie
|
||||
.split("; ")
|
||||
.find((x) => x.startsWith("__sb_sessid="))
|
||||
@@ -91,9 +76,7 @@ export async function Connection(
|
||||
|
||||
socket.on("error", (err) => console.error("[Gateway]", err));
|
||||
|
||||
console.log(
|
||||
`[Gateway] New connection from ${ipAddress}, total ${this.clients.size}`,
|
||||
);
|
||||
console.log(`[Gateway] New connection from ${ipAddress}, total ${this.clients.size}`);
|
||||
|
||||
if (process.env.WS_LOGEVENTS)
|
||||
[
|
||||
@@ -112,15 +95,12 @@ export async function Connection(
|
||||
const { searchParams } = new URL(`http://localhost${request.url}`);
|
||||
// @ts-ignore
|
||||
socket.encoding = searchParams.get("encoding") || "json";
|
||||
if (!["json", "etf"].includes(socket.encoding))
|
||||
return socket.close(CLOSECODES.Decode_error);
|
||||
if (!["json", "etf"].includes(socket.encoding)) return socket.close(CLOSECODES.Decode_error);
|
||||
|
||||
if (socket.encoding === "etf" && !erlpack)
|
||||
throw new Error("Erlpack is not installed: 'npm i @yukikaze-bot/erlpack'");
|
||||
if (socket.encoding === "etf" && !erlpack) throw new Error("Erlpack is not installed: 'npm i @yukikaze-bot/erlpack'");
|
||||
|
||||
socket.version = Number(searchParams.get("version")) || 8;
|
||||
if (socket.version != 8)
|
||||
return socket.close(CLOSECODES.Invalid_API_version);
|
||||
if (socket.version != 8) return socket.close(CLOSECODES.Invalid_API_version);
|
||||
|
||||
// @ts-ignore
|
||||
socket.compress = searchParams.get("compress") || "";
|
||||
|
||||
@@ -24,7 +24,7 @@ import path from "path";
|
||||
import WS from "ws";
|
||||
import OPCodeHandlers from "../opcodes";
|
||||
import { check } from "../opcodes/instanceOf";
|
||||
import { PayloadSchema } from "@spacebar/schemas"
|
||||
import { PayloadSchema } from "@spacebar/schemas";
|
||||
|
||||
const bigIntJson = BigIntJson({ storeAsString: true });
|
||||
|
||||
@@ -67,22 +67,15 @@ export async function Message(this: WebSocket, buffer: WS.Data) {
|
||||
}
|
||||
} else return this.close(CLOSECODES.Decode_error);
|
||||
|
||||
if (process.env.WS_VERBOSE)
|
||||
console.log(`[Websocket] Incomming message: ${JSON.stringify(data)}`);
|
||||
if (process.env.WS_VERBOSE) console.log(`[Websocket] Incomming message: ${JSON.stringify(data)}`);
|
||||
|
||||
if (process.env.WS_DUMP) {
|
||||
const id = this.session_id || "unknown";
|
||||
|
||||
await fs.mkdir(path.join("dump", id), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join("dump", id, `${Date.now()}.in.json`),
|
||||
JSON.stringify(data, null, 2),
|
||||
);
|
||||
await fs.writeFile(path.join("dump", id, `${Date.now()}.in.json`), JSON.stringify(data, null, 2));
|
||||
|
||||
if (!this.session_id)
|
||||
console.log(
|
||||
"[Gateway] Unknown session id, dumping to unknown folder",
|
||||
);
|
||||
if (!this.session_id) console.log("[Gateway] Unknown session id, dumping to unknown folder");
|
||||
}
|
||||
|
||||
check.call(this, PayloadSchema, data);
|
||||
|
||||
@@ -37,15 +37,13 @@ const recurseJsonReplace = (json: any) => {
|
||||
|
||||
json[key] = JSONReplacer.call(json, key, json[key]);
|
||||
|
||||
if (typeof json[key] == "object" && json[key] !== null)
|
||||
json[key] = recurseJsonReplace(json[key]);
|
||||
if (typeof json[key] == "object" && json[key] !== null) json[key] = recurseJsonReplace(json[key]);
|
||||
}
|
||||
return json;
|
||||
};
|
||||
|
||||
export async function Send(socket: WebSocket, data: Payload) {
|
||||
if (process.env.WS_VERBOSE)
|
||||
console.log(`[Websocket] Outgoing message: ${JSON.stringify(data)}`);
|
||||
if (process.env.WS_VERBOSE) console.log(`[Websocket] Outgoing message: ${JSON.stringify(data)}`);
|
||||
|
||||
if (process.env.WS_DUMP) {
|
||||
const id = socket.session_id || "unknown";
|
||||
@@ -53,10 +51,7 @@ export async function Send(socket: WebSocket, data: Payload) {
|
||||
await fs.mkdir(path.join("dump", id), {
|
||||
recursive: true,
|
||||
});
|
||||
await fs.writeFile(
|
||||
path.join("dump", id, `${Date.now()}.out.json`),
|
||||
JSON.stringify(data, null, 2),
|
||||
);
|
||||
await fs.writeFile(path.join("dump", id, `${Date.now()}.out.json`), JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
let buffer: Buffer | string;
|
||||
@@ -66,18 +61,16 @@ export async function Send(socket: WebSocket, data: Payload) {
|
||||
buffer = erlpack.pack(data);
|
||||
}
|
||||
// TODO: encode circular object
|
||||
else if (socket.encoding === "json")
|
||||
buffer = JSON.stringify(data, JSONReplacer);
|
||||
else if (socket.encoding === "json") buffer = JSON.stringify(data, JSONReplacer);
|
||||
else return;
|
||||
|
||||
// TODO: compression
|
||||
if (socket.compress === "zlib-stream") {
|
||||
buffer = socket.deflate!.process(buffer) as Buffer;
|
||||
} else if (socket.compress === "zstd-stream") {
|
||||
if (typeof(buffer) === "string")
|
||||
buffer = Buffer.from(buffer as string);
|
||||
if (typeof buffer === "string") buffer = Buffer.from(buffer as string);
|
||||
|
||||
buffer = await socket.zstdEncoder!.encode(buffer as Buffer) as Buffer;
|
||||
buffer = (await socket.zstdEncoder!.encode(buffer as Buffer)) as Buffer;
|
||||
}
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
|
||||
@@ -14,11 +14,7 @@ export function parseStreamKey(streamKey: string): {
|
||||
throw new Error(`Invalid stream key type: ${type}`);
|
||||
}
|
||||
|
||||
if (
|
||||
(type === "guild" && streamKeyArray.length < 3) ||
|
||||
(type === "call" && streamKeyArray.length < 2)
|
||||
)
|
||||
throw new Error(`Invalid stream key: ${streamKey}`); // invalid stream key
|
||||
if ((type === "guild" && streamKeyArray.length < 3) || (type === "call" && streamKeyArray.length < 2)) throw new Error(`Invalid stream key: ${streamKey}`); // invalid stream key
|
||||
|
||||
let guildId: string | undefined;
|
||||
if (type === "guild") {
|
||||
@@ -33,12 +29,7 @@ export function parseStreamKey(streamKey: string): {
|
||||
return { type, channelId, guildId, userId };
|
||||
}
|
||||
|
||||
export function generateStreamKey(
|
||||
type: "guild" | "call",
|
||||
guildId: string | undefined,
|
||||
channelId: string,
|
||||
userId: string,
|
||||
): string {
|
||||
export function generateStreamKey(type: "guild" | "call", guildId: string | undefined, channelId: string, userId: string): string {
|
||||
const streamKey = `${type}${type === "guild" ? `:${guildId}` : ""}:${channelId}:${userId}`;
|
||||
|
||||
return streamKey;
|
||||
|
||||
+30
-84
@@ -40,26 +40,17 @@ export class NewUrlSignatureData extends NewUrlUserSignatureData {
|
||||
this.path = data.path;
|
||||
this.url = data.url;
|
||||
if (!this.path && !this.url) {
|
||||
throw new Error(
|
||||
"Either path or url must be provided for URL signing",
|
||||
);
|
||||
throw new Error("Either path or url must be provided for URL signing");
|
||||
}
|
||||
if (this.path && this.url) {
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.warn(
|
||||
"[Signing] Both path and url are provided, using path for signing",
|
||||
this,
|
||||
new Error().stack,
|
||||
);
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.warn("[Signing] Both path and url are provided, using path for signing", this, new Error().stack);
|
||||
}
|
||||
if (this.url) {
|
||||
try {
|
||||
const parsedUrl = new URL(this.url);
|
||||
this.path = parsedUrl.pathname;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
"Invalid URL provided for signing: " + this.url,
|
||||
);
|
||||
throw new Error("Invalid URL provided for signing: " + this.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,15 +92,10 @@ export class UrlSignResult {
|
||||
|
||||
static fromUrl(url: URL | string): UrlSignResult {
|
||||
if (typeof url === "string") {
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.debug("[Signing] Parsing URL from string:", url);
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.debug("[Signing] Parsing URL from string:", url);
|
||||
url = new URL(url);
|
||||
}
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.debug(
|
||||
"[Signing] Parsing URL from URL object:",
|
||||
url.toString(),
|
||||
);
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.debug("[Signing] Parsing URL from URL object:", url.toString());
|
||||
const ex = url.searchParams.get("ex");
|
||||
const is = url.searchParams.get("is");
|
||||
const hm = url.searchParams.get("hm");
|
||||
@@ -133,9 +119,7 @@ export const getUrlSignature = (data: NewUrlSignatureData): UrlSignResult => {
|
||||
// calculate the expiration time
|
||||
const now = Date.now();
|
||||
const issuedAt = now.toString(16);
|
||||
const expiresAt = (now + ms(cdnSignatureDuration as StringValue)).toString(
|
||||
16,
|
||||
);
|
||||
const expiresAt = (now + ms(cdnSignatureDuration as StringValue)).toString(16);
|
||||
|
||||
// hash the url with the cdnSignatureKey
|
||||
return calculateHash(
|
||||
@@ -160,11 +144,7 @@ function calculateHash(request: UrlSignatureData): UrlSignResult {
|
||||
"[Signing] CDN Signature IP is enabled but we couldn't find the IP field in the request. This may cause issues with signature validation. Please report this to the Spacebar team!",
|
||||
);
|
||||
else {
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.log(
|
||||
"[Signing] CDN Signature IP is enabled, adding IP to hash:",
|
||||
request.ip,
|
||||
);
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.log("[Signing] CDN Signature IP is enabled, adding IP to hash:", request.ip);
|
||||
data.update(request.ip!);
|
||||
}
|
||||
}
|
||||
@@ -175,11 +155,7 @@ function calculateHash(request: UrlSignatureData): UrlSignResult {
|
||||
"[Signing] CDN Signature User-Agent is enabled but we couldn't find the user-agent header in the request. This may cause issues with signature validation. Please report this to the Spacebar team!",
|
||||
);
|
||||
else {
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.log(
|
||||
"[Signing] CDN Signature User-Agent is enabled, adding User-Agent to hash:",
|
||||
request.userAgent,
|
||||
);
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.log("[Signing] CDN Signature User-Agent is enabled, adding User-Agent to hash:", request.userAgent);
|
||||
data.update(request.userAgent!);
|
||||
}
|
||||
}
|
||||
@@ -197,12 +173,8 @@ function calculateHash(request: UrlSignatureData): UrlSignResult {
|
||||
{
|
||||
path: request.path,
|
||||
validity: request.issuedAt + " .. " + request.expiresAt,
|
||||
ua: Config.get().security.cdnSignatureIncludeUserAgent
|
||||
? request.userAgent
|
||||
: "[disabled]",
|
||||
ip: Config.get().security.cdnSignatureIncludeIp
|
||||
? request.ip
|
||||
: "[disabled]",
|
||||
ua: Config.get().security.cdnSignatureIncludeUserAgent ? request.userAgent : "[disabled]",
|
||||
ip: Config.get().security.cdnSignatureIncludeIp ? request.ip : "[disabled]",
|
||||
},
|
||||
"->",
|
||||
result,
|
||||
@@ -216,8 +188,7 @@ export const isExpired = (data: UrlSignResult | UrlSignatureData) => {
|
||||
const expiresAt = parseInt(data.expiresAt, 16);
|
||||
|
||||
if (Number.isNaN(issuedAt) || Number.isNaN(expiresAt)) {
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.debug("[Signing] Invalid timestamps in query");
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.debug("[Signing] Invalid timestamps in query");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -225,38 +196,29 @@ export const isExpired = (data: UrlSignResult | UrlSignatureData) => {
|
||||
|
||||
const isExpired = expiresAt < currentTime;
|
||||
if (isExpired) {
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.debug("[Signing] Signature expired");
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.debug("[Signing] Signature expired");
|
||||
return true;
|
||||
}
|
||||
|
||||
const isValidIssuedAt = issuedAt < currentTime;
|
||||
if (!isValidIssuedAt) {
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.debug("[Signing] Signature issued in the future");
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.debug("[Signing] Signature issued in the future");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const hasValidSignature = (
|
||||
req: NewUrlUserSignatureData,
|
||||
sig: UrlSignResult,
|
||||
) => {
|
||||
export const hasValidSignature = (req: NewUrlUserSignatureData, sig: UrlSignResult) => {
|
||||
// if the required query parameters are not present, return false
|
||||
if (!sig.expiresAt || !sig.issuedAt || !sig.hash) {
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.warn(
|
||||
"[Signing] Missing required query parameters for signature validation",
|
||||
);
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.warn("[Signing] Missing required query parameters for signature validation");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the signature is expired
|
||||
if (isExpired(sig)) {
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.warn("[Signing] Signature is expired");
|
||||
if (process.env["LOG_CDN_SIGNATURES"]) console.warn("[Signing] Signature is expired");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -273,42 +235,26 @@ export const hasValidSignature = (
|
||||
const calculated = Buffer.from(calcd);
|
||||
const received = Buffer.from(sig.hash as string);
|
||||
|
||||
console.assert(
|
||||
sig.issuedAt == calcResult.issuedAt,
|
||||
"[Signing] Mismatched issuedAt",
|
||||
{
|
||||
is: sig.issuedAt,
|
||||
calculated: calcResult.issuedAt,
|
||||
},
|
||||
);
|
||||
console.assert(sig.issuedAt == calcResult.issuedAt, "[Signing] Mismatched issuedAt", {
|
||||
is: sig.issuedAt,
|
||||
calculated: calcResult.issuedAt,
|
||||
});
|
||||
|
||||
console.assert(
|
||||
sig.expiresAt == calcResult.expiresAt,
|
||||
"[Signing] Mismatched expiresAt",
|
||||
{
|
||||
ex: sig.expiresAt,
|
||||
calculated: calcResult.expiresAt,
|
||||
},
|
||||
);
|
||||
console.assert(sig.expiresAt == calcResult.expiresAt, "[Signing] Mismatched expiresAt", {
|
||||
ex: sig.expiresAt,
|
||||
calculated: calcResult.expiresAt,
|
||||
});
|
||||
|
||||
console.assert(
|
||||
calculated.length === received.length,
|
||||
"[Signing] Mismatched hash length",
|
||||
{
|
||||
calculated: calculated.length,
|
||||
received: received.length,
|
||||
},
|
||||
);
|
||||
console.assert(calculated.length === received.length, "[Signing] Mismatched hash length", {
|
||||
calculated: calculated.length,
|
||||
received: received.length,
|
||||
});
|
||||
|
||||
const isHashValid =
|
||||
calculated.length === received.length &&
|
||||
timingSafeEqual(calculated, received);
|
||||
const isHashValid = calculated.length === received.length && timingSafeEqual(calculated, received);
|
||||
|
||||
if (!isHashValid)
|
||||
if (process.env["LOG_CDN_SIGNATURES"])
|
||||
console.warn(
|
||||
`Signature validation for ${sig.path} (is=${sig.issuedAt}, ex=${sig.expiresAt}) failed: calculated: ${calcd}, received: ${sig.hash}`,
|
||||
);
|
||||
console.warn(`Signature validation for ${sig.path} (is=${sig.issuedAt}, ex=${sig.expiresAt}) failed: calculated: ${calcd}, received: ${sig.hash}`);
|
||||
|
||||
return isHashValid;
|
||||
};
|
||||
Reference in new issue
Block a user