Fix IP tracking for bans

This commit is contained in:
Rory&
2025-12-07 23:18:57 +01:00
parent 23d0a8de55
commit bdbf6d06b2
8 changed files with 96 additions and 3 deletions

View File

@@ -20,7 +20,7 @@ import { route } from "@spacebar/api";
import { Ban, DiscordApiErrors, GuildBanAddEvent, GuildBanRemoveEvent, Member, User, emitEvent } from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { APIBansArray, BanRegistrySchema, GuildBansResponse } from "@spacebar/schemas";
import { APIBansArray, BanCreateSchema, BanRegistrySchema, GuildBansResponse } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });
@@ -198,6 +198,15 @@ router.put(
async (req: Request, res: Response) => {
const { guild_id } = req.params;
const banned_user_id = req.params.user_id;
const opts = req.body as BanCreateSchema;
let deleteMessagesMs = opts.delete_message_days
? (opts.delete_message_days as number) * 86400000
: opts.delete_message_seconds
? (opts.delete_message_seconds as number) * 1000
: 0;
if (deleteMessagesMs < 0) deleteMessagesMs = 0;
if (req.user_id === banned_user_id && banned_user_id === req.permission?.cache.guild?.owner_id)
throw new HTTPError("You are the guild owner, hence can't ban yourself", 403);
@@ -207,6 +216,7 @@ router.put(
const existingBan = await Ban.findOne({
where: { guild_id: guild_id, user_id: banned_user_id },
});
// Bans on already banned users are silently ignored
if (existingBan) return res.status(204).send();
@@ -227,6 +237,7 @@ router.put(
data: {
guild_id: guild_id,
user: banned_user.toPublicUser(),
delete_message_secs: Math.floor(deleteMessagesMs / 1000),
},
guild_id: guild_id,
} as GuildBanAddEvent),

View File

@@ -28,6 +28,7 @@ import ws from "ws";
import { Connection } from "./events/Connection";
import http from "http";
import { cleanupOnStartup } from "./util/Utils";
import { randomString } from "@spacebar/api";
export class Server {
public ws: ws.Server;
@@ -50,6 +51,10 @@ export class Server {
if (server) this.server = server;
else {
this.server = http.createServer(function (req, res) {
if(!req.headers.cookie?.split("; ").find(x => x.startsWith("__sb_sessid="))) {
res.setHeader("Set-Cookie", `__sb_sessid=${randomString(32)}; Secure; HttpOnly; SameSite=None`);
}
res.writeHead(200).end("Online");
});
}

View File

@@ -72,6 +72,13 @@ export async function Connection(
);
}
if (request.headers.cookie?.split("; ").find(x => x.startsWith("__sb_sessid="))) {
socket.fingerprint = request.headers.cookie
.split("; ")
.find((x) => x.startsWith("__sb_sessid="))
?.split("=")[1];
}
//Create session ID when the connection is opened. This allows gateway dump to group the initial websocket messages with the rest of the conversation.
const session_id = genSessionId();
socket.session_id = session_id; //Set the session of the WebSocket object

View File

@@ -29,6 +29,7 @@ import {
Message,
NewUrlUserSignatureData,
GuildMemberAddEvent,
Ban,
} from "@spacebar/util";
import { OPCODES } from "../util/Constants";
import { Send } from "../util/Send";
@@ -174,6 +175,16 @@ async function consume(this: WebSocket, opts: EventOpts) {
case "GUILD_DELETE":
this.events[id]?.();
delete this.events[id];
if (event === "GUILD_DELETE" && this.ipAddress) {
const ban = await Ban.findOne({
where: { guild_id: id, user_id: this.user_id },
});
if (ban) {
ban.ip = this.ipAddress || undefined;
await ban.save();
}
}
break;
case "CHANNEL_CREATE":
if (!permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) {

View File

@@ -33,6 +33,7 @@ export interface WebSocket extends WS {
compress?: "zlib-stream" | "zstd-stream";
ipAddress?: string;
userAgent?: string; // for cdn request signing
fingerprint?: string;
shard_count?: bigint;
shard_id?: bigint;
deflate?: Deflate;

View File

@@ -17,7 +17,7 @@
*/
export interface BanCreateSchema {
delete_message_seconds?: string;
delete_message_days?: string;
delete_message_seconds?: number;
delete_message_days?: number;
reason?: string;
}

View File

@@ -0,0 +1,57 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, OneToOne, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { Team } from "./Team";
import { User } from "./User";
import { Guild } from "./Guild";
@Entity({
name: "instance_bans",
})
export class InstanceBan extends BaseClass {
@Column({ type: "bigint" })
@CreateDateColumn()
created_at: Date = new Date();
@Column()
reason: string;
@Column({ nullable: true })
user_id: string;
@Column({ nullable: true })
fingerprint: string;
@Column({ nullable: true })
ip_address: string;
// chain of trust type tracking
@Column()
is_from_other_instance_ban: boolean;
@Column({ nullable: true })
@RelationId((instance_ban: InstanceBan) => instance_ban.origin_instance_ban)
origin_instance_ban_id?: string;
@JoinColumn({ name: "origin_instance_ban_id" })
@OneToOne(() => InstanceBan, { nullable: true, onDelete: "SET NULL" })
origin_instance_ban?: InstanceBan;
}

View File

@@ -219,6 +219,7 @@ export interface GuildBanAddEvent extends Event {
data: {
guild_id: string;
user: User;
delete_message_secs?: number
};
}