From 8a8d3bc63369d72e089bad9da1615658462e51ae Mon Sep 17 00:00:00 2001 From: Rory& Date: Mon, 1 Dec 2025 23:24:25 +0100 Subject: [PATCH] Classes for abuseipdb/ipdata --- .../networking/abuseipdb/AbuseIpDbClient.ts | 65 +++++++++++++++ .../abuseipdb/AbuseIpDbSampleResponses.ts | 73 +++++++++++++++++ src/util/util/networking/abuseipdb/index.ts | 18 +++++ src/util/util/networking/index.ts | 19 +++++ .../util/networking/ipdata/IpDataClient.ts | 35 ++++++++ .../ipdata/IpDataSampleResponses.ts | 79 +++++++++++++++++++ src/util/util/networking/ipdata/index.ts | 18 +++++ 7 files changed, 307 insertions(+) create mode 100644 src/util/util/networking/abuseipdb/AbuseIpDbClient.ts create mode 100644 src/util/util/networking/abuseipdb/AbuseIpDbSampleResponses.ts create mode 100644 src/util/util/networking/abuseipdb/index.ts create mode 100644 src/util/util/networking/index.ts create mode 100644 src/util/util/networking/ipdata/IpDataClient.ts create mode 100644 src/util/util/networking/ipdata/IpDataSampleResponses.ts create mode 100644 src/util/util/networking/ipdata/index.ts diff --git a/src/util/util/networking/abuseipdb/AbuseIpDbClient.ts b/src/util/util/networking/abuseipdb/AbuseIpDbClient.ts new file mode 100644 index 000000000..90a1ca292 --- /dev/null +++ b/src/util/util/networking/abuseipdb/AbuseIpDbClient.ts @@ -0,0 +1,65 @@ +import { Config, DateBuilder } from "@spacebar/util"; +import { AbuseIpDbBlacklistResponse, AbuseIpDbCheckResponse } from "./AbuseIpDbSampleResponses"; + +export default class AbuseIpDbClient { + private static ipCheckCache: Map< + string, + { + data: AbuseIpDbCheckResponse; + expires: number; + } + > = new Map(); + + private static blacklistCache: { + data: AbuseIpDbBlacklistResponse; + expires: number; + } | null = null; + + static async checkIpAddress(ip: string): Promise { + const { ipdataApiKey } = Config.get().security; + if (!ipdataApiKey) return null; + if (this.ipCheckCache.get(ip)?.expires ?? 0 > Date.now()) return this.ipCheckCache.get(ip)!.data; + + console.log(`[AbuseIPDB] Checking IP address ${ip}...`); + const resp = (await (await fetch(`https://api.abuseipdb.com/api/v2/check?ipAddress=${ip}`)).json()) as Promise; + this.ipCheckCache.set(ip, { + data: await resp, + expires: new DateBuilder().addHours(12).buildTimestamp(), + }); + return await resp; + } + + static async getBlacklist(): Promise { + const { abuseIpDbApiKey, abuseipdbBlacklistRatelimit } = Config.get().security; + if (!abuseIpDbApiKey) return null; + if (this.blacklistCache?.expires ?? 0 > Date.now()) return this.blacklistCache!.data; + + console.log("[AbuseIPDB] Fetching blacklist..."); + const resp = (await ( + await fetch(`https://api.abuseipdb.com/api/v2/blacklist`, { + headers: { + Key: abuseIpDbApiKey, + Accept: "application/json", + }, + }) + ).json()) as Promise; + + this.blacklistCache = { + data: await resp, + expires: new DateBuilder().addHours(Math.ceil(24 / abuseipdbBlacklistRatelimit)).buildTimestamp(), + }; + + return await resp; + } + + static async isIpBlacklisted(ip: string): Promise { + const { abuseipdbConfidenceScoreTreshold } = Config.get().security; + const blacklist = await this.getBlacklist(); + if (!blacklist) return false; + + const entry = blacklist.data.find((x) => x.ipAddress === ip); + if (!entry) return false; + + return entry.abuseConfidenceScore >= abuseipdbConfidenceScoreTreshold; + } +} diff --git a/src/util/util/networking/abuseipdb/AbuseIpDbSampleResponses.ts b/src/util/util/networking/abuseipdb/AbuseIpDbSampleResponses.ts new file mode 100644 index 000000000..210af2cee --- /dev/null +++ b/src/util/util/networking/abuseipdb/AbuseIpDbSampleResponses.ts @@ -0,0 +1,73 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 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 . +*/ + +export type AbuseIpDbCheckResponse = typeof abuseIpDbCheckResponseSample; +export type AbuseIpDbBlacklistResponse = typeof abuseIpDbBlacklistResponseSample; + +const abuseIpDbCheckResponseSample = { + data: { + ipAddress: "118.25.6.39", + isPublic: true, + ipVersion: 4, + isWhitelisted: false, + abuseConfidenceScore: 100, + countryCode: "CN", + countryName: "China", + usageType: "Data Center/Web Hosting/Transit", + isp: "Tencent Cloud Computing (Beijing) Co. Ltd", + domain: "tencent.com", + hostnames: [], + isTor: false, + totalReports: 1, + numDistinctUsers: 1, + lastReportedAt: "2018-12-20T20:55:14+00:00", + reports: [ + { + reportedAt: "2018-12-20T20:55:14+00:00", + comment: "Dec 20 20:55:14 srv206 sshd[13937]: Invalid user oracle from 118.25.6.39", + categories: [18, 22], + reporterId: 1, + reporterCountryCode: "US", + reporterCountryName: "United States", + }, + ], + }, +}; + +const abuseIpDbBlacklistResponseSample = { + meta: { + generatedAt: "2020-09-24T19:54:11+00:00", + }, + data: [ + { + ipAddress: "5.188.10.179", + abuseConfidenceScore: 100, + lastReportedAt: "2020-09-24T19:17:02+00:00", + }, + { + ipAddress: "185.222.209.14", + abuseConfidenceScore: 100, + lastReportedAt: "2020-09-24T19:17:02+00:00", + }, + { + ipAddress: "191.96.249.183", + abuseConfidenceScore: 100, + lastReportedAt: "2020-09-24T19:17:01+00:00", + }, + ], +}; diff --git a/src/util/util/networking/abuseipdb/index.ts b/src/util/util/networking/abuseipdb/index.ts new file mode 100644 index 000000000..a81780784 --- /dev/null +++ b/src/util/util/networking/abuseipdb/index.ts @@ -0,0 +1,18 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 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 . +*/ +export * from "./AbuseIpDbClient"; diff --git a/src/util/util/networking/index.ts b/src/util/util/networking/index.ts new file mode 100644 index 000000000..717ba2a68 --- /dev/null +++ b/src/util/util/networking/index.ts @@ -0,0 +1,19 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 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 . +*/ +export * from "./abuseipdb"; +export * from "./ipdata"; diff --git a/src/util/util/networking/ipdata/IpDataClient.ts b/src/util/util/networking/ipdata/IpDataClient.ts new file mode 100644 index 000000000..7571770b2 --- /dev/null +++ b/src/util/util/networking/ipdata/IpDataClient.ts @@ -0,0 +1,35 @@ +import { Config, DateBuilder } from "@spacebar/util"; +import { IpDataIpLookupResponse } from "./IpDataSampleResponses"; + +export default class IpDataClient { + private static ipInfoCache: Map< + string, + { + data: IpDataIpLookupResponse; + expires: number; + } + > = new Map(); + + static async getIpInfo(ip: string): Promise { + const { ipdataApiKey } = Config.get().security; + if (!ipdataApiKey) return null; + if (this.ipInfoCache.get(ip)?.expires ?? 0 > Date.now()) return this.ipInfoCache.get(ip)!.data; + + console.log(`[IpData] Fetching info for IP address ${ip}...`); + const resp = (await (await fetch(`https://eu-api.ipdata.co/${ip}?api-key=${ipdataApiKey}`)).json()) as Promise; + this.ipInfoCache.set(ip, { + data: await resp, + expires: new DateBuilder().addHours(12).buildTimestamp(), + }); + return await resp; + } + + //TODO add function that support both ip and domain names + static isProxy(data: IpDataIpLookupResponse) { + if (!data || !data.asn || !data.threat) return false; + if (data.asn.type !== "isp") return true; + if (Object.values(data.threat).some((x) => x)) return true; + + return false; + } +} diff --git a/src/util/util/networking/ipdata/IpDataSampleResponses.ts b/src/util/util/networking/ipdata/IpDataSampleResponses.ts new file mode 100644 index 000000000..fa6d0fa84 --- /dev/null +++ b/src/util/util/networking/ipdata/IpDataSampleResponses.ts @@ -0,0 +1,79 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 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 . +*/ + +export type IpDataIpLookupResponse = typeof ipDataSampleIpLookupResponse; + +const ipDataSampleIpLookupResponse = { + ip: "", + is_eu: true, + city: "", + region: "", + region_code: "", + country_name: "", + country_code: "", + continent_name: "", + continent_code: "", + latitude: 0, + longitude: 0, + postal: "", + calling_code: "", + flag: "", + emoji_flag: "", + emoji_unicode: "", + asn: { + asn: "", + name: "", + domain: "", + route: "", + type: "isp", + }, + languages: [ + { + name: "", + native: "", + }, + ], + currency: { + name: "", + code: "", + symbol: "", + native: "", + plural: "", + }, + time_zone: { + name: "", + abbr: "", + offset: "", + is_dst: true, + current_time: "", + }, + threat: { + is_tor: false, + is_icloud_relay: false, + is_proxy: false, + is_datacenter: false, + is_anonymous: false, + is_known_attacker: false, + is_known_abuser: false, + is_threat: false, + is_bogon: false, + blocklists: [], + }, + count: 0, + status: 200, +}; diff --git a/src/util/util/networking/ipdata/index.ts b/src/util/util/networking/ipdata/index.ts new file mode 100644 index 000000000..a0391cb4b --- /dev/null +++ b/src/util/util/networking/ipdata/index.ts @@ -0,0 +1,18 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 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 . +*/ +export * from "./IpDataClient";