feat: implement banned prefix list, security fixes, validation fixes for some endpoints
This commit is contained in:
parent
b2906afe5d
commit
5504091e4d
60
data/bannedprefix.txt
Normal file
60
data/bannedprefix.txt
Normal file
@ -0,0 +1,60 @@
|
||||
postmaster
|
||||
webmaster
|
||||
admin
|
||||
administrator
|
||||
hostmaster
|
||||
info
|
||||
support
|
||||
contact
|
||||
abuse
|
||||
security
|
||||
root
|
||||
billing
|
||||
sales
|
||||
service
|
||||
helpdesk
|
||||
management
|
||||
moderator
|
||||
owner
|
||||
verify
|
||||
verification
|
||||
access
|
||||
login
|
||||
register
|
||||
registration
|
||||
subscribe
|
||||
subscription
|
||||
unsubscribe
|
||||
unsubscription
|
||||
password
|
||||
reset
|
||||
help
|
||||
data
|
||||
manager
|
||||
recovery
|
||||
restore
|
||||
sysadmin
|
||||
audit
|
||||
domainadmin
|
||||
dns
|
||||
auth
|
||||
2fa
|
||||
mfa
|
||||
otp
|
||||
it
|
||||
finance
|
||||
legal
|
||||
compliance
|
||||
test
|
||||
example
|
||||
sandbox
|
||||
alert
|
||||
alerts
|
||||
notice
|
||||
vaultwarden
|
||||
pass
|
||||
gitea
|
||||
notification
|
||||
feedback
|
||||
no-reply
|
||||
noreply
|
@ -6,18 +6,31 @@ import { eq } from "drizzle-orm";
|
||||
import fs from "fs/promises";
|
||||
import { drizzle } from "drizzle-orm/bun-sqlite";
|
||||
import { updateAccountsCache } from "../../utils/updateAccountsCache";
|
||||
import { isBannedPrefix } from "../../utils/validators";
|
||||
|
||||
const db = drizzle(process.env.DB_FILE_NAME!);
|
||||
|
||||
export const addAccount = async (req: Request, res: Response): Promise<void> => {
|
||||
const { email, password, migrate } = req.body;
|
||||
|
||||
if (!email || !password) {
|
||||
console.log("[!] Error\nTASK| addAccount\nERR | Missing email or password");
|
||||
res.status(400).json({ error: "Missing email or password" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateEmail(email)) {
|
||||
console.log("[!] Error\nTASK| addAccount\nERR | Invalid email format");
|
||||
res.status(400).json({ error: "Invalid email format" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (await isBannedPrefix(email)) {
|
||||
console.log("[!] Error\nTASK| addAccount\nERR | Banned email prefix\nACC |", email);
|
||||
res.status(400).json({ error: "Banned email prefix" });
|
||||
return;
|
||||
}
|
||||
|
||||
let finalPassword = password;
|
||||
|
||||
if (migrate) {
|
||||
@ -27,6 +40,8 @@ export const addAccount = async (req: Request, res: Response): Promise<void> =>
|
||||
const line = data.split("\n").find(l => l.trim() === email);
|
||||
if (!line) {
|
||||
console.log("[!] Error\nTASK| addAccount (subtask: migrate)\nERR | Account not found in migrate.txt\nACC |", email);
|
||||
|
||||
// A backend error is returned so users do not attempt to abuse the migration form
|
||||
res.status(500).json({ error: "Backend error" });
|
||||
return;
|
||||
} else {
|
||||
|
@ -8,7 +8,12 @@ const db = drizzle(process.env.DB_FILE_NAME!);
|
||||
|
||||
export const getUserAccount = async (req: Request, res: Response): Promise<void> => {
|
||||
const { email } = req.body;
|
||||
console.log("[*] Task started\nTASK| getUserAccount\nACC |", email);
|
||||
|
||||
if (!email) {
|
||||
console.log("[!] Error\nTASK| getUserAccount\nERR | Missing email\nACC |", email);
|
||||
res.status(400).json({error: "Missing email"});
|
||||
return
|
||||
}
|
||||
|
||||
if (!validateEmail(email)) {
|
||||
console.log("[!] Error\nTASK| getUserAccount\nERR | Invalid email format\nACC |", email);
|
||||
@ -16,6 +21,8 @@ export const getUserAccount = async (req: Request, res: Response): Promise<void>
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[*] Task started\nTASK| getUserAccount\nACC |", email);
|
||||
|
||||
try {
|
||||
const account = await db
|
||||
.select()
|
||||
|
@ -33,12 +33,12 @@ app.listen(PORT, () => {
|
||||
figlet('mail-connect', (err, data) => {
|
||||
if (err) {
|
||||
console.log('mail-connect');
|
||||
console.log('Version: 0.1.0');
|
||||
console.log('Version: 0.1.1');
|
||||
console.log(`API listening on port ${PORT}\n`);
|
||||
console.dir("[!] " + err);
|
||||
} else {
|
||||
console.log(data);
|
||||
console.log('Version: 0.1.0');
|
||||
console.log('Version: 0.1.1');
|
||||
console.log(`API listening on port ${PORT}\n`);
|
||||
}
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import rateLimit from "express-rate-limit";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
|
||||
interface RateLimitOptions {
|
||||
windowMs: number;
|
||||
@ -13,8 +14,9 @@ interface RateLimitConfig {
|
||||
|
||||
export const loadRateLimitConfig = async (): Promise<RateLimitConfig> => {
|
||||
try {
|
||||
await fs.access(`${process.env.MAILCONNECT_ROOT_DIR}/ratelimit.json`);
|
||||
const data = await fs.readFile(`${process.env.MAILCONNECT_ROOT_DIR}/ratelimit.json`, "utf8");
|
||||
const rateLimitPath = path.join(process.cwd(), "ratelimit.json");
|
||||
await fs.access(rateLimitPath);
|
||||
const data = await fs.readFile(rateLimitPath, "utf8");
|
||||
return JSON.parse(data);
|
||||
} catch (err) {
|
||||
console.error("[!] Error loading ratelimit config:\n", err);
|
||||
|
@ -1,8 +1,27 @@
|
||||
import validator from "validator";
|
||||
import PasswordValidator from "password-validator";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
|
||||
let bannedPrefixesCache: string[] | null = null;
|
||||
|
||||
export const validateEmail = (email: string): boolean => validator.isEmail(email);
|
||||
|
||||
export const isBannedPrefix = async (email: string): Promise<boolean> => {
|
||||
try {
|
||||
if (!bannedPrefixesCache) {
|
||||
const filePath = path.join(process.cwd(), "data", "bannedprefix.txt");
|
||||
const data = await fs.readFile(filePath, "utf8");
|
||||
bannedPrefixesCache = data.split("\n").filter(prefix => prefix.trim());
|
||||
}
|
||||
const prefix = email.split("@")[0];
|
||||
return bannedPrefixesCache.includes(prefix);
|
||||
} catch (error) {
|
||||
console.error("[!] Error checking for banned prefix:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const passwordSchema = new PasswordValidator();
|
||||
passwordSchema.is().min(8).is().max(64).has().letters().has().digits().has().not().spaces();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user