Classes for abuseipdb/ipdata

This commit is contained in:
Rory&
2025-12-01 23:24:25 +01:00
parent cd30b0a656
commit 8a8d3bc633
7 changed files with 307 additions and 0 deletions

View File

@@ -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<AbuseIpDbCheckResponse | null> {
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<AbuseIpDbCheckResponse>;
this.ipCheckCache.set(ip, {
data: await resp,
expires: new DateBuilder().addHours(12).buildTimestamp(),
});
return await resp;
}
static async getBlacklist(): Promise<AbuseIpDbBlacklistResponse | null> {
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<AbuseIpDbBlacklistResponse>;
this.blacklistCache = {
data: await resp,
expires: new DateBuilder().addHours(Math.ceil(24 / abuseipdbBlacklistRatelimit)).buildTimestamp(),
};
return await resp;
}
static async isIpBlacklisted(ip: string): Promise<boolean> {
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;
}
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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",
},
],
};

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
export * from "./AbuseIpDbClient";

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
export * from "./abuseipdb";
export * from "./ipdata";

View File

@@ -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<IpDataIpLookupResponse | null> {
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<IpDataIpLookupResponse>;
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;
}
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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,
};

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
export * from "./IpDataClient";