Skip to content

Commit 2622f67

Browse files
feat(security): two-factor authentication and secret encryption
Signed-off-by: GitHub <[email protected]>
1 parent fd4cfe3 commit 2622f67

22 files changed

+175
-55
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ yarn-error.log
1010
.env
1111
/.env*
1212
/.env.docker
13+
/.env.encrypted
1314

1415
# IDEs and editors
1516
.idea/

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"glob": "^11.0.0",
9090
"json5": "^2.2.3",
9191
"jsonwebtoken": "^9.0.2",
92+
"mlkem": "^2.3.0",
9293
"module-alias": "^2.2.3",
9394
"pg": "^8.13.1",
9495
"pm2": "^5.4.3",

src/main/typescript/api/controllers/AuthController.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import type Request from "@framework/api/http/Request";
2424
import Response from "@framework/api/http/Response";
2525
import { Inject } from "@framework/container/Inject";
2626
import { fetchUser } from "@framework/utils/entities";
27-
import { env } from "@main/env/env";
27+
import { getEnvData } from "@main/env/env";
2828
import { users } from "@main/models/User";
2929
import AuthService from "@main/services/AuthService";
3030
import { APIErrorCode } from "@main/types/APIErrorCode";
@@ -137,11 +137,11 @@ class AuthController extends Controller {
137137

138138
try {
139139
const body = new URLSearchParams({
140-
client_id: env.CLIENT_ID,
141-
client_secret: env.CLIENT_SECRET,
140+
client_id: getEnvData().CLIENT_ID,
141+
client_secret: getEnvData().CLIENT_SECRET,
142142
code,
143143
grant_type: "authorization_code",
144-
redirect_uri: `${env.FRONTEND_URL}/challenge/auth/discord`,
144+
redirect_uri: `${getEnvData().FRONTEND_URL}/challenge/auth/discord`,
145145
scope: "identify guilds"
146146
}).toString();
147147

src/main/typescript/api/controllers/VerificationController.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import Controller from "@framework/api/http/Controller";
2323
import type Request from "@framework/api/http/Request";
2424
import { Inject } from "@framework/container/Inject";
2525
import type VerificationService from "@main/automod/VerificationService";
26-
import { env } from "@main/env/env";
26+
import { getEnvData } from "@main/env/env";
2727
import { verificationEntries } from "@main/models/VerificationEntry";
2828
import type ConfigurationManager from "@main/services/ConfigurationManager";
2929
import { getAxiosClient } from "@main/utils/axios";
@@ -42,7 +42,7 @@ class VerificationController extends Controller {
4242
const response = await getAxiosClient().post(
4343
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
4444
{
45-
secret: env.CF_TURNSTILE_SECRET,
45+
secret: getEnvData().CF_TURNSTILE_SECRET,
4646
response: token,
4747
remoteip: ip
4848
}

src/main/typescript/automod/AIAutoModeration.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import { Inject } from "@framework/container/Inject";
2121
import { Name } from "@framework/services/Name";
2222
import { Service } from "@framework/services/Service";
23-
import { env } from "@main/env/env";
23+
import { getEnvData } from "@main/env/env";
2424
import type { GuildConfig } from "@main/schemas/GuildConfigSchema";
2525
import type PermissionManagerService from "@main/services/PermissionManagerService";
2626
import { safeMemberFetch } from "@main/utils/fetch";
@@ -47,6 +47,8 @@ class AIAutoModeration extends Service {
4747
private counter = 0;
4848

4949
private async analyzeComment(comment: string) {
50+
const env = getEnvData();
51+
5052
if (!env.PERSPECTIVE_API_TOKEN) {
5153
return null;
5254
}
@@ -143,7 +145,7 @@ class AIAutoModeration extends Service {
143145
}
144146

145147
private async moderate(message: Message<true>, event: "create" | "update") {
146-
if (!env.PERSPECTIVE_API_TOKEN) {
148+
if (!getEnvData().PERSPECTIVE_API_TOKEN) {
147149
return null;
148150
}
149151

src/main/typescript/automod/VerificationService.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { Events } from "@framework/types/ClientEvents";
2525
import { BUG } from "@framework/utils/devflow";
2626
import { fetchChannel, fetchMember, fetchUser } from "@framework/utils/entities";
2727
import { Colors } from "@main/constants/Colors";
28-
import { env } from "@main/env/env";
28+
import { getEnvData } from "@main/env/env";
2929
import { verificationEntries, VerificationStatus } from "@main/models/VerificationEntry";
3030
import { VerificationMethod, verificationRecords } from "@main/models/VerificationRecord";
3131
import VerificationExpiredQueue from "@main/queues/VerificationExpiredQueue";
@@ -193,12 +193,12 @@ class VerificationService extends Service {
193193
}
194194

195195
private getVerificationDomain() {
196-
return env.FRONTEND_GUILD_MEMBER_VERIFICATION_URL ?? env.FRONTEND_URL;
196+
return getEnvData().FRONTEND_GUILD_MEMBER_VERIFICATION_URL ?? getEnvData().FRONTEND_URL;
197197
}
198198

199199
private getVerificationURL(guildId: string, memberId: string, token: string) {
200200
const domain = this.getVerificationDomain();
201-
return `${domain}${domain === env.FRONTEND_URL ? "/verify" : ""}/guilds/${encodeURIComponent(guildId)}/challenge/onboarding?t=${encodeURIComponent(token)}&u=${encodeURIComponent(memberId)}`;
201+
return `${domain}${domain === getEnvData().FRONTEND_URL ? "/verify" : ""}/guilds/${encodeURIComponent(guildId)}/challenge/onboarding?t=${encodeURIComponent(token)}&u=${encodeURIComponent(memberId)}`;
202202
}
203203

204204
public async startVerification(member: GuildMember, reason: string, silent = false) {
@@ -208,6 +208,8 @@ class VerificationService extends Service {
208208
return;
209209
}
210210

211+
const env = getEnvData();
212+
211213
await member.roles.add(config.unverified_roles, reason).catch(this.logger.error);
212214
await member.roles.remove(config.verified_roles, reason).catch(this.logger.error);
213215

@@ -409,6 +411,7 @@ class VerificationService extends Service {
409411
}
410412

411413
public async isProxy(ip: string) {
414+
const env = getEnvData();
412415
const response = await getAxiosClient().get(
413416
`https://proxycheck.io/v2/${encodeURIComponent(ip)}?vpn=1&asn=1` +
414417
(env.PROXYCHECKIO_API_KEY
@@ -439,11 +442,11 @@ class VerificationService extends Service {
439442
) {
440443
try {
441444
const body = new URLSearchParams({
442-
client_id: env.CLIENT_ID,
443-
client_secret: env.CLIENT_SECRET,
445+
client_id: getEnvData().CLIENT_ID,
446+
client_secret: getEnvData().CLIENT_SECRET,
444447
code: discordCode,
445448
grant_type: "authorization_code",
446-
redirect_uri: `${env.FRONTEND_GUILD_MEMBER_VERIFICATION_URL}/next/discord`,
449+
redirect_uri: `${getEnvData().FRONTEND_GUILD_MEMBER_VERIFICATION_URL}/next/discord`,
447450
scope: "identify guilds",
448451
state: `${guildId}|${memberId}|${token}`
449452
}).toString();

src/main/typescript/commands/fun/CatCommand.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import { Command } from "@framework/commands/Command";
2121
import type Context from "@framework/commands/Context";
22-
import { env } from "@main/env/env";
22+
import { getEnvData } from "@main/env/env";
2323
import { getAxiosClient } from "@main/utils/axios";
2424
import type { AxiosError } from "axios";
2525

@@ -30,6 +30,7 @@ class CatCommand extends Command {
3030
public override readonly systemPermissions = [];
3131

3232
public override async execute(context: Context): Promise<void> {
33+
const env = getEnvData();
3334
const token = env.CAT_API_TOKEN;
3435

3536
if (!token) {

src/main/typescript/commands/fun/DogCommand.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import { Command } from "@framework/commands/Command";
2121
import type Context from "@framework/commands/Context";
22-
import { env } from "@main/env/env";
22+
import { getEnvData } from "@main/env/env";
2323
import { getAxiosClient } from "@main/utils/axios";
2424
import type { AxiosError } from "axios";
2525

@@ -30,6 +30,7 @@ class DogCommand extends Command {
3030
public override readonly systemPermissions = [];
3131

3232
public override async execute(context: Context): Promise<void> {
33+
const env = getEnvData();
3334
const token = env.DOG_API_TOKEN;
3435

3536
if (!token) {

src/main/typescript/commands/fun/JokeCommand.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import type { Buildable } from "@framework/commands/Command";
2121
import { Command } from "@framework/commands/Command";
2222
import type Context from "@framework/commands/Context";
23-
import { env } from "@main/env/env";
23+
import { getEnvData } from "@main/env/env";
2424
import { getAxiosClient } from "@main/utils/axios";
2525
import { EmbedBuilder } from "discord.js";
2626

@@ -52,6 +52,7 @@ class JokeCommand extends Command {
5252
}
5353

5454
public override async execute(context: Context): Promise<void> {
55+
const env = getEnvData();
5556
const isDadJoke =
5657
context.commandName === "dadjoke" ||
5758
(context.isChatInput() && context.options.getString("type") === "dad_joke");

src/main/typescript/commands/fun/PixabayCommand.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import StringArgument from "@framework/arguments/StringArgument";
2424
import type { Buildable } from "@framework/commands/Command";
2525
import { Command } from "@framework/commands/Command";
2626
import type Context from "@framework/commands/Context";
27-
import { env } from "@main/env/env";
27+
import { getEnvData } from "@main/env/env";
2828
import { getAxiosClient } from "@main/utils/axios";
2929
import { AxiosError } from "axios";
3030

@@ -115,6 +115,7 @@ class PixabayCommand extends Command {
115115
context: Context,
116116
{ query, type }: PixabayCommandArgs
117117
): Promise<void> {
118+
const env = getEnvData();
118119
type = context.isChatInput() ? (context.options.getSubcommand(true) as typeof type) : type;
119120

120121
if (!["image", "photo", "vector", "illustration"].includes(type)) {

src/main/typescript/commands/settings/AboutCommand.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import type { ChatContext } from "@framework/commands/Command";
2121
import { Command } from "@framework/commands/Command";
22-
import { env } from "@main/env/env";
22+
import { getEnvData } from "@main/env/env";
2323
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
2424
import type { MetadataType } from "../../core/DiscordKernel";
2525

@@ -29,6 +29,7 @@ class AboutCommand extends Command {
2929
public override readonly aliases = ["botinfo"];
3030

3131
public override async execute(context: ChatContext) {
32+
const env = getEnvData();
3233
const metadata = this.application.metadata as MetadataType;
3334
const avatar = this.application.getClient().user?.displayAvatarURL();
3435
const emoji = context.emoji("sudobot") || null;

src/main/typescript/commands/settings/RestartCommand.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Command } from "@framework/commands/Command";
2222
import type Context from "@framework/commands/Context";
2323
import { Inject } from "@framework/container/Inject";
2424
import { GatewayEventListener } from "@framework/events/GatewayEventListener";
25+
import { getEnvData } from "@main/env/env";
2526
import StartupManager from "@main/services/StartupManager";
2627
import { emoji } from "@main/utils/emoji";
2728
import {
@@ -135,10 +136,14 @@ class RestartCommand extends Command {
135136

136137
public override async execute(context: Context): Promise<void> {
137138
if (
138-
process.env.CREDENTIAL_SERVER &&
139+
getEnvData().TWO_FACTOR_AUTH_URL &&
139140
(!context.isChatInput() || context.options.getString("credential_key"))
140141
) {
141-
await context.error("Please enter the credential server 2FA code to restart the bot!");
142+
await context.error(
143+
"Please enter the credential server 2FA code to restart the bot" +
144+
(context.isLegacy() ? " using the slash command" : "") +
145+
"!"
146+
);
142147
return;
143148
}
144149

0 commit comments

Comments
 (0)