* chore: Add system wide rate limiting * Handle and convert HttpError to 429 * Make algo 32-bit to prevent <ES2020 error + fix sms manager unit test * Change core to common to go from 10 requests per minute to 200 * Remove redundant function * Fix integration tests * Make sure we allow all legal POST routes * Allow tRPC post calls * Add matcher tests on middleware * Add matcher tests on middleware * Fix matcher to not use regex * Fix missing POST allow rule for /api/auth/callback/credentials * Missed the api/book/event endpoints * Add missing pages/api routes * Remove POST middleware for now, very risky * Remove tests for POST protection --------- Co-authored-by: Volnei Munhoz <volnei.munhoz@gmail.com>
38 lines
1.5 KiB
TypeScript
38 lines
1.5 KiB
TypeScript
// Note: avoid Node's crypto to support runtimes where it's unavailable (e.g., edge/browsers)
|
|
|
|
export interface PiiHasher {
|
|
hash(input: string): string;
|
|
}
|
|
|
|
export class Md5PiiHasher implements PiiHasher {
|
|
constructor(private readonly salt: string) {}
|
|
hash(input: string) {
|
|
// FNV-1a 32-bit using Math.imul, repeated 4 times with variant salt to get 128-bit hex
|
|
const fnv1a32 = (str: string) => {
|
|
// Convert to UTF-8 bytes without relying on TextEncoder/Buffer
|
|
const data = unescape(encodeURIComponent(str));
|
|
let hash = 0x811c9dc5; // offset basis (2166136261)
|
|
for (let i = 0; i < data.length; i++) {
|
|
hash = Math.imul(hash ^ data.charCodeAt(i), 0x01000193); // 16777619
|
|
}
|
|
return hash >>> 0; // unsigned 32-bit
|
|
};
|
|
|
|
// Four independent 32-bit hashes to produce a stable 128-bit hex string
|
|
const h1 = fnv1a32(`${this.salt}::1::${input}`);
|
|
const h2 = fnv1a32(`${this.salt}::2::${input}`);
|
|
const h3 = fnv1a32(`${this.salt}::3::${input}`);
|
|
const h4 = fnv1a32(`${this.salt}::4::${input}`);
|
|
const toHex8 = (n: number) => n.toString(16).padStart(8, "0");
|
|
return `${toHex8(h1)}${toHex8(h2)}${toHex8(h3)}${toHex8(h4)}`;
|
|
}
|
|
}
|
|
|
|
export const piiHasher: PiiHasher = new Md5PiiHasher(process.env.CALENDSO_ENCRYPTION_KEY!);
|
|
|
|
export const hashEmail = (email: string, hasher: PiiHasher = piiHasher): string => {
|
|
const [localPart, domain] = email.split("@");
|
|
// Simple hash function for email, can be replaced with a more complex one if needed
|
|
return `${hasher.hash(localPart)}@${domain}`;
|
|
};
|