From 9d8b4585232d54c96a5cdbaaad0ae5851054f49c Mon Sep 17 00:00:00 2001 From: s0urce-c0de <122696391+s0urce-c0de@users.noreply.github.com> Date: Sun, 10 Aug 2025 04:57:24 +0000 Subject: [PATCH 1/6] EzCloudflare --- package.json | 5 ++-- src/index.ts | 4 +-- src/util/ip.ts | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f6efe51..2924045 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "lint:fix": "eslint ./src/ --fix" }, "devDependencies": { - "@types/node": "^24.0.3", "@types/bun": "^1.2.17", + "@types/node": "^24.0.3", "@typescript-eslint/eslint-plugin": "^8.34.1", "@typescript-eslint/parser": "^8.34.1", "eslint": "^9.29.0", @@ -22,7 +22,8 @@ "dependencies": { "@discordjs/core": "^2.1.1", "discord.js": "^14.20.0", - "hono": "^4.8.2" + "hono": "^4.8.2", + "ipaddr.js": "^2.2.0" }, "nodemonConfig": { "execMap": { diff --git a/src/index.ts b/src/index.ts index a13738b..23261dd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,7 @@ import { Hono } from "hono"; import { serveStatic } from "hono/bun"; import * as db from "./db/db.ts"; import * as oauth from "./util/oauth.ts"; -import { getIpData } from "./util/ip.ts"; +import { getIpData , getIp} from "./util/ip.ts"; import { checkRole, grantRole, logWebhook } from "./util/discordManager.ts"; // Initialize the Discord client const client = new Client({ @@ -63,7 +63,7 @@ app.get("/callback", async (c) => { if (!code) { return c.redirect("/error.html"); } - const ip = c.req.header("X-Forwarded-For"); + const ip = getIp(c); if (!ip) { return c.redirect("/error.html"); } diff --git a/src/util/ip.ts b/src/util/ip.ts index 89b55a9..2d7b2de 100644 --- a/src/util/ip.ts +++ b/src/util/ip.ts @@ -1,3 +1,74 @@ +// dont copy the patented and copyrighted EzCloudflare Solution for hono. +// (joke) +import ipaddr from 'ipaddr.js'; +import type { Context } from 'hono'; + +const cfCidrs: [ipaddr.IPv4 | ipaddr.IPv6, number][] = []; +const cfCidrsSet = new Set(); + +async function loadCfCidrs() { + if (cfCidrs.length) return; + + const [v4, v6] = await Promise.all([ + fetch('https://www.cloudflare.com/ips-v4') + .then(r => r.text()) + .then(t => t.trim().split('\n')), + fetch('https://www.cloudflare.com/ips-v6') + .then(r => r.text()) + .then(t => t.trim().split('\n')), + ]); + + [...v4, ...v6].forEach(cidr => { + try { + const parsed = ipaddr.parseCIDR(cidr); + const str = `${parsed[0].toNormalizedString()}/${parsed[1]}`; + if (!cfCidrsSet.has(str)) { + cfCidrsSet.add(str); + cfCidrs.push(parsed); + } + } catch { + // ignore invalid CIDRs + } + }); +} + +async function isCloudflare(ip: string): Promise { + await loadCfCidrs(); + try { + const addr = ipaddr.parse(ip); + return cfCidrs.some(([range, bits]) => addr.match(range, bits)); + } catch { + return false; + } +} + +function isLoopback(ip: string): boolean { + try { + return ipaddr.parse(ip).range() === 'loopback'; + } catch { + return false; + } +} + +export async function getIp(c: Context): Promise { + let ip = (c.req.raw as any).socket?.remoteAddress; + + if (ip && isLoopback(ip)) { + const xff = c.req.header('x-forwarded-for'); + if (xff) { + const parts = xff.split(',').map(ip => ip.trim()); + if (parts.length > 0) ip = parts[parts.length - 1]; + } + } + + if (ip && await isCloudflare(ip)) { + ip = c.req.header('cf-connecting-ip') ?? ip; + } + + return ip; +} + + export async function getIpData(ip: string) { const query = await fetch(`http://ip-api.com/json/${ip}?fields=66842623`); const data = await query.json(); From 7074eede670c1be1d6c139ed558084a0f080f91f Mon Sep 17 00:00:00 2001 From: s0urce-c0de <122696391+s0urce-c0de@users.noreply.github.com> Date: Sun, 10 Aug 2025 04:59:32 +0000 Subject: [PATCH 2/6] i frogot await --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 23261dd..0730e0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -63,7 +63,7 @@ app.get("/callback", async (c) => { if (!code) { return c.redirect("/error.html"); } - const ip = getIp(c); + const ip = await getIp(c); if (!ip) { return c.redirect("/error.html"); } From 8cc96e86d3253454f00ceed4510ab8effc90ca5d Mon Sep 17 00:00:00 2001 From: s0urce-c0de <122696391+s0urce-c0de@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:36:06 +0000 Subject: [PATCH 3/6] remove set and array duplication since when you tell ai to add types (cuz typescript) to your code it apparently writes a new variable for it. --- src/util/ip.ts | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/util/ip.ts b/src/util/ip.ts index 2d7b2de..045f496 100644 --- a/src/util/ip.ts +++ b/src/util/ip.ts @@ -3,12 +3,24 @@ import ipaddr from 'ipaddr.js'; import type { Context } from 'hono'; -const cfCidrs: [ipaddr.IPv4 | ipaddr.IPv6, number][] = []; -const cfCidrsSet = new Set(); +function setSome( + set: Set, + predicate: (value: T, index: number, set: Set) => boolean, + thisArg?: any +): boolean { + let index = 0; + for (const value of set) { + if (predicate.call(thisArg, value, index, set)) { + return true; + } + index++; + } + return false; +} -async function loadCfCidrs() { - if (cfCidrs.length) return; +const cfCiders = new Set<[ipaddr.IPv4 | ipaddr.IPv6, number]>(); +async function loadCfCidrs() { const [v4, v6] = await Promise.all([ fetch('https://www.cloudflare.com/ips-v4') .then(r => r.text()) @@ -20,23 +32,16 @@ async function loadCfCidrs() { [...v4, ...v6].forEach(cidr => { try { - const parsed = ipaddr.parseCIDR(cidr); - const str = `${parsed[0].toNormalizedString()}/${parsed[1]}`; - if (!cfCidrsSet.has(str)) { - cfCidrsSet.add(str); - cfCidrs.push(parsed); - } - } catch { - // ignore invalid CIDRs - } + cfCiders.add(ipaddr.parseCIDR(cidr)) + } catch {} }); } async function isCloudflare(ip: string): Promise { - await loadCfCidrs(); + if (!cfCiders.size)await loadCfCidrs(); try { const addr = ipaddr.parse(ip); - return cfCidrs.some(([range, bits]) => addr.match(range, bits)); + return setSome(cfCiders, (([range, bits]) => addr.match(range, bits))); } catch { return false; } From 178a68c039ff60da84ff15190708a2a8f6055de1 Mon Sep 17 00:00:00 2001 From: s0urce-c0de <122696391+s0urce-c0de@users.noreply.github.com> Date: Mon, 11 Aug 2025 17:42:31 +0000 Subject: [PATCH 4/6] i ran `npm run format && npm run lint:fix` so ignore the other formats. i wrote it in house because of CRAZYcrystals --- package.json | 3 +- src/commands/manverify.ts | 5 +- src/commands/verify.ts | 7 +- src/index.ts | 38 +++- src/public/altflagged.html | 7 +- src/util/discordManager.ts | 8 +- src/util/ip.ts | 107 ++++----- src/util/ipaddr_polyfill.ts | 425 ++++++++++++++++++++++++++++++++++++ 8 files changed, 528 insertions(+), 72 deletions(-) create mode 100644 src/util/ipaddr_polyfill.ts diff --git a/package.json b/package.json index 2924045..d529ab1 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,7 @@ "dependencies": { "@discordjs/core": "^2.1.1", "discord.js": "^14.20.0", - "hono": "^4.8.2", - "ipaddr.js": "^2.2.0" + "hono": "^4.8.2" }, "nodemonConfig": { "execMap": { diff --git a/src/commands/manverify.ts b/src/commands/manverify.ts index 0653a26..cdf39cb 100644 --- a/src/commands/manverify.ts +++ b/src/commands/manverify.ts @@ -62,7 +62,10 @@ export default { await db.setData(member.id, ip); await grantRole(interaction.guild, member.id, memberRoles); - await logWebhook(interaction.client, `<@!${user.id}> was manually verified by <@!${interaction.user.id}>.`); + await logWebhook( + interaction.client, + `<@!${user.id}> was manually verified by <@!${interaction.user.id}>.` + ); const embed = new EmbedBuilder() .setTitle("Manually verified.") .setDescription( diff --git a/src/commands/verify.ts b/src/commands/verify.ts index 8c20d9f..63b4260 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -85,7 +85,7 @@ export default { embeds: [], components: [], }); - }; + } const mainId = await db.checkIp(data.ip); @@ -135,7 +135,10 @@ export default { if (i.customId === "confirm") { await db.setData(user.id, data.ip); await grantRole(interaction.guild!, user.id, memberRoles); - await logWebhook(interaction.client, `<@!${user.id}> was manually verified by <@!${interaction.user.id}>.`); + await logWebhook( + interaction.client, + `<@!${user.id}> was manually verified by <@!${interaction.user.id}>.` + ); //@ts-expect-error const formatter = new Intl.ListFormat("en", { diff --git a/src/index.ts b/src/index.ts index 0730e0e..1384f05 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,8 +7,10 @@ import { Hono } from "hono"; import { serveStatic } from "hono/bun"; import * as db from "./db/db.ts"; import * as oauth from "./util/oauth.ts"; -import { getIpData , getIp} from "./util/ip.ts"; +import { getIpData, getIp } from "./util/ip.ts"; import { checkRole, grantRole, logWebhook } from "./util/discordManager.ts"; +import type { Server, BunRequest } from "bun"; + // Initialize the Discord client const client = new Client({ intents: [ @@ -64,6 +66,7 @@ app.get("/callback", async (c) => { return c.redirect("/error.html"); } const ip = await getIp(c); + console.log(ip); if (!ip) { return c.redirect("/error.html"); } @@ -86,20 +89,29 @@ app.get("/callback", async (c) => { if (await checkRole(guild, id, mutedRole)) { console.log(`${id} tried verifying with muted role`); - await logWebhook(client, `<@!${id}> tried to verify while having the muted role.`); + await logWebhook( + client, + `<@!${id}> tried to verify while having the muted role.` + ); return c.redirect("/altflagged.html"); } if (await checkRole(guild, id, altRole)) { console.log(`${id} tried verifying with alternate role`); - await logWebhook(client, `<@!${id}> tried to verify while having the alternate role.`); + await logWebhook( + client, + `<@!${id}> tried to verify while having the alternate role.` + ); return c.redirect("/altflagged.html"); } const mainId = await db.checkIp(ip); if (mainId && id !== mainId) { - await logWebhook(client, `<@!${id}> was flagged as an alt account. Their main is <@!${mainId}>.`); + await logWebhook( + client, + `<@!${id}> was flagged as an alt account. Their main is <@!${mainId}>.` + ); grantRole(guild, id, altRole); return c.redirect("/altflagged.html"); } @@ -107,12 +119,18 @@ app.get("/callback", async (c) => { const ipData = await getIpData(ip); if (ipData.mobile) { - await logWebhook(client, `<@!${id}> Is trying to verify over a potential mobile data connection.`); + await logWebhook( + client, + `<@!${id}> Is trying to verify over a potential mobile data connection.` + ); return c.redirect("/mobile.html"); } if (ipData.proxy || ipData.hosting) { - await logWebhook(client, `<@!${id}> attempted to verify over a proxy or VPN.`); + await logWebhook( + client, + `<@!${id}> attempted to verify over a proxy or VPN.` + ); return c.redirect("/flagged.html"); } @@ -124,6 +142,12 @@ app.get("/callback", async (c) => { export default { port, - fetch: app.fetch, + fetch: (req: BunRequest, server: Server) => { + // percs is as crazy as crytals for using hono first, and second also using bun. + const ip = server?.requestIP(req)?.address ?? undefined; + console.log(server?.requestIP(req)?.address); + + return app.fetch(req, { ip }); + }, development: process.env.NODE_ENV === "development", }; diff --git a/src/public/altflagged.html b/src/public/altflagged.html index 9875c64..f23373e 100644 --- a/src/public/altflagged.html +++ b/src/public/altflagged.html @@ -40,8 +40,11 @@

Flagged

Verification failed. We have detected you have already verified - yourself on this IP Address. If you believe this was a mistake, create - a ticket and explain your situation. + yourself on this IP Address. + If you believe this was a mistake, create a ticket and explain your + situation.