From a6f9e9e556a1029e2e5eccf780a90408862fbd8f Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 13:09:32 +0100 Subject: [PATCH 1/8] feat: check for profanitys in username and email on api --- api/bun.lock | 3 +++ api/package.json | 1 + api/src/routes/auth.ts | 17 +++++++++++------ api/src/routes/setup.ts | 7 +++++++ api/src/test/profanity.test.ts | 22 ++++++++++++++++++++++ api/src/types/errors.ts | 7 +++++++ 6 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 api/src/test/profanity.test.ts diff --git a/api/bun.lock b/api/bun.lock index 6dd19d982..942b2fb26 100644 --- a/api/bun.lock +++ b/api/bun.lock @@ -4,6 +4,7 @@ "": { "name": "api", "dependencies": { + "@2toad/profanity": "^3.1.1", "@elysiajs/cors": "^1.2.0", "@elysiajs/jwt": "^1.2.0", "@sinclair/typebox": "^0.34.14", @@ -31,6 +32,8 @@ }, }, "packages": { + "@2toad/profanity": ["@2toad/profanity@3.1.1", "", {}, "sha512-07ny4pCSa4gDrcJ4vZ/WWmiM90+8kv/clXfnDvThf9IJq0GldpjRVdzHCfMwGDs2Y/8eClmTGzKb5tEfUWy/uA=="], + "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, ""], "@elysiajs/cors": ["@elysiajs/cors@1.2.0", "", { "peerDependencies": { "elysia": ">= 1.2.0" } }, ""], diff --git a/api/package.json b/api/package.json index 124430a80..5987947ec 100644 --- a/api/package.json +++ b/api/package.json @@ -30,6 +30,7 @@ "typescript": "^5.7.3" }, "dependencies": { + "@2toad/profanity": "^3.1.1", "@elysiajs/cors": "^1.2.0", "@elysiajs/jwt": "^1.2.0", "@sinclair/typebox": "^0.34.14", diff --git a/api/src/routes/auth.ts b/api/src/routes/auth.ts index 076672a9a..04a1e5d0e 100644 --- a/api/src/routes/auth.ts +++ b/api/src/routes/auth.ts @@ -6,7 +6,8 @@ import { signinResponse, signUpBody, signinBody, - podProviderEndpoint + podProviderEndpoint, + ApiSingUpErrors } from '../types' import { eq } from 'drizzle-orm' import Elysia, { t } from 'elysia' @@ -87,20 +88,24 @@ const authPlugin = new Elysia({ name: 'auth' }) ) .post( '/signup', - async ({ body, error, headers: { auth }, jwt }) => { + async ({ body, error, headers: { auth }, jwt, profanity }) => { // check if user is already logged in if (auth && (await jwt.verify(auth))) { return "You're already logged in" } const { username, password, email, providerName } = body + // check if username or email is profane + if ((profanity.exists(username), profanity.exists(email))) { + return error(400, ApiSingUpErrors.UsernameOrEmailContainsProfanity) + } // try to sign up the user with the current provider try { const providerResponse = await ActivityPod.signup(podProviderEndpoint[providerName], username, password, email) let userResponse: SelectUsers[] = [] if (providerResponse.token === undefined) { - return error(400, 'Provider did not return a token') + return error(400, ApiSingUpErrors.ProviderToken) } else { // the provider created a new user, so we need to create a new user in the database try { @@ -121,7 +126,7 @@ const authPlugin = new Elysia({ name: 'auth' }) } } catch (e) { console.error('Error while checking if user is in the database: ', e) - return error(500, 'Error while checking user') + return error(500, ApiSingUpErrors.DBError) } const tokenObject = getTokenObject(new User(userResponse[0], providerResponse.token)) const authToken = await jwt.sign(tokenObject) @@ -138,7 +143,7 @@ const authPlugin = new Elysia({ name: 'auth' }) return error(errorJson.code, errorJson.message) } console.error('Error while signing up the user', e) - return error(400, 'Error with the provider') + return error(400, ApiSingUpErrors.ProviderDefault) } }, { @@ -146,7 +151,7 @@ const authPlugin = new Elysia({ name: 'auth' }) body: signUpBody, response: { 200: signinResponse, - 400: t.String(), + 400: t.Enum(ApiSingUpErrors), 500: t.String() } } diff --git a/api/src/routes/setup.ts b/api/src/routes/setup.ts index e6377bc32..3ad4b9145 100644 --- a/api/src/routes/setup.ts +++ b/api/src/routes/setup.ts @@ -1,3 +1,4 @@ +import { Profanity } from '@2toad/profanity' import User from '../decorater/User' import cors from '@elysiajs/cors' import jwt from '@elysiajs/jwt' @@ -12,6 +13,12 @@ const setupPlugin = new Elysia({ name: 'setup' }) ) .use(cors()) .decorate('user', new User()) + .decorate( + 'profanity', + new Profanity({ + languages: ['en', 'de', 'fr', 'ja', 'pt', 'es', 'ru', 'ar', 'ko'] + }) + ) .macro({ isSignedIn: enabled => { if (!enabled) return diff --git a/api/src/test/profanity.test.ts b/api/src/test/profanity.test.ts new file mode 100644 index 000000000..40fa77575 --- /dev/null +++ b/api/src/test/profanity.test.ts @@ -0,0 +1,22 @@ +import { profanity } from '@2toad/profanity' +import { describe, expect, it } from 'bun:test' + +describe('Check profanity module', () => { + it('should return false for strings without profanity', () => { + const notProfane = [ + 'Taiwan', + 'Taiwanese', + 'Taiwanese people', + 'Protestant', + 'Protestantism', + 'Transgender', + 'Hongkong', + 'Hong Kong', + 'Hong Kong people', + 'Taiwan is a free country' + ] + notProfane.forEach(word => { + expect(profanity.exists(word)).toBe(false) + }) + }) +}) diff --git a/api/src/types/errors.ts b/api/src/types/errors.ts index 47ab290e7..4fd8cbb33 100644 --- a/api/src/types/errors.ts +++ b/api/src/types/errors.ts @@ -6,6 +6,13 @@ export enum FollowErrors { IsSelf = 'Cannot follow yourself.' } +export enum ApiSingUpErrors { + UsernameOrEmailContainsProfanity = 'Username or email contains profanity', + ProviderToken = 'Provider did not return a token', + DBError = 'Error while checking user', + ProviderDefault = 'Error with the provider' +} + // Errors that are thrown by the pod provider export enum ProviderSignUpErrors { providerSignUpDefault = 'Error while signing up the user', From 233e7c0e6a378685b33fc7bfaa74efe68d2c548c Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 13:40:12 +0100 Subject: [PATCH 2/8] fix: fix typo --- api/src/routes/auth.ts | 12 ++++++------ api/src/types/errors.ts | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/api/src/routes/auth.ts b/api/src/routes/auth.ts index 04a1e5d0e..b94b0514f 100644 --- a/api/src/routes/auth.ts +++ b/api/src/routes/auth.ts @@ -7,7 +7,7 @@ import { signUpBody, signinBody, podProviderEndpoint, - ApiSingUpErrors + ApiSignUpErrors } from '../types' import { eq } from 'drizzle-orm' import Elysia, { t } from 'elysia' @@ -97,7 +97,7 @@ const authPlugin = new Elysia({ name: 'auth' }) // check if username or email is profane if ((profanity.exists(username), profanity.exists(email))) { - return error(400, ApiSingUpErrors.UsernameOrEmailContainsProfanity) + return error(400, ApiSignUpErrors.UsernameOrEmailContainsProfanity) } // try to sign up the user with the current provider try { @@ -105,7 +105,7 @@ const authPlugin = new Elysia({ name: 'auth' }) let userResponse: SelectUsers[] = [] if (providerResponse.token === undefined) { - return error(400, ApiSingUpErrors.ProviderToken) + return error(400, ApiSignUpErrors.ProviderToken) } else { // the provider created a new user, so we need to create a new user in the database try { @@ -126,7 +126,7 @@ const authPlugin = new Elysia({ name: 'auth' }) } } catch (e) { console.error('Error while checking if user is in the database: ', e) - return error(500, ApiSingUpErrors.DBError) + return error(500, ApiSignUpErrors.DBError) } const tokenObject = getTokenObject(new User(userResponse[0], providerResponse.token)) const authToken = await jwt.sign(tokenObject) @@ -143,7 +143,7 @@ const authPlugin = new Elysia({ name: 'auth' }) return error(errorJson.code, errorJson.message) } console.error('Error while signing up the user', e) - return error(400, ApiSingUpErrors.ProviderDefault) + return error(400, ApiSignUpErrors.ProviderDefault) } }, { @@ -151,7 +151,7 @@ const authPlugin = new Elysia({ name: 'auth' }) body: signUpBody, response: { 200: signinResponse, - 400: t.Enum(ApiSingUpErrors), + 400: t.Enum(ApiSignUpErrors), 500: t.String() } } diff --git a/api/src/types/errors.ts b/api/src/types/errors.ts index 4fd8cbb33..d6850149a 100644 --- a/api/src/types/errors.ts +++ b/api/src/types/errors.ts @@ -6,7 +6,8 @@ export enum FollowErrors { IsSelf = 'Cannot follow yourself.' } -export enum ApiSingUpErrors { +export enum ApiSignUpErrors { + UsernameInvalid = 'Username is invalid', UsernameOrEmailContainsProfanity = 'Username or email contains profanity', ProviderToken = 'Provider did not return a token', DBError = 'Error while checking user', From 6c226460ca80dd1b5d419254ba0ff260fc820137 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 13:40:28 +0100 Subject: [PATCH 3/8] feat: api now checks for invalid chars in username --- api/src/routes/auth.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/src/routes/auth.ts b/api/src/routes/auth.ts index b94b0514f..41a5dcd6f 100644 --- a/api/src/routes/auth.ts +++ b/api/src/routes/auth.ts @@ -99,6 +99,11 @@ const authPlugin = new Elysia({ name: 'auth' }) if ((profanity.exists(username), profanity.exists(email))) { return error(400, ApiSignUpErrors.UsernameOrEmailContainsProfanity) } + // check if username contains unwanted characters + const unwantedChars = new RegExp(/[@#/\\$%^&*!?<>+~=]/g) + if (unwantedChars.test(username)) { + return error(400, ApiSignUpErrors.UsernameInvalid) + } // try to sign up the user with the current provider try { const providerResponse = await ActivityPod.signup(podProviderEndpoint[providerName], username, password, email) From 53a4daf26130f06b64086cc47658f041f5e618a3 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 13:41:03 +0100 Subject: [PATCH 4/8] feat: add check for profanity on client side --- frontend/src/controller/formValidation.ts | 16 ++++++++++++++++ frontend/src/types/enums.ts | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/frontend/src/controller/formValidation.ts b/frontend/src/controller/formValidation.ts index 192423a86..52338d0f0 100644 --- a/frontend/src/controller/formValidation.ts +++ b/frontend/src/controller/formValidation.ts @@ -1,3 +1,9 @@ +import { Profanity } from '@2toad/profanity' + +const profanity = new Profanity({ + languages: ['en', 'de', 'fr', 'ja', 'pt', 'es', 'ru', 'ar', 'ko'] +}) + export function validateUsername(username: string, required = true): string | undefined { if (required && username === '') { return 'Username is Required' @@ -8,6 +14,13 @@ export function validateUsername(username: string, required = true): string | un if (username.includes(' ')) { return 'Username cannot contain spaces' } + if (profanity.exists(username)) { + return 'Username contains profanity' + } + const unwantedChars = new RegExp(/[@#/\\$%^&*!?<>+~=]/g) + if (unwantedChars.test(username)) { + return 'Username cannot contain the following characters: @ # \\ $ % ^ & * ! ? < > + ~ =' + } // TODO: check if username includes bad words return undefined } @@ -28,5 +41,8 @@ export function validateEmail(email: string, required = true): string | undefine if (emailRegex.exec(email) === null) { return 'Invalid Email' } + if (profanity.exists(email)) { + return 'Email contains profanity' + } return undefined } diff --git a/frontend/src/types/enums.ts b/frontend/src/types/enums.ts index f787eec57..debc476d2 100644 --- a/frontend/src/types/enums.ts +++ b/frontend/src/types/enums.ts @@ -1,6 +1,6 @@ -import type { FollowErrors, ProviderSignInErrors, ProviderSignUpErrors } from '#api/types' +import type { ApiSignUpErrors, FollowErrors, ProviderSignInErrors, ProviderSignUpErrors } from '#api/types' -export type ApiErrors = ProviderSignUpErrors | ApiErrorsGeneral | ProviderSignInErrors | FollowErrors +export type ApiErrors = ProviderSignUpErrors | ApiErrorsGeneral | ProviderSignInErrors | FollowErrors | ApiSignUpErrors export enum ApiErrorsGeneral { default = 'Something went wrong', From 7498ce88b8bff7378c05b826b547f51a92a73739 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 13:41:26 +0100 Subject: [PATCH 5/8] feat: signupForm now handles api errors for username or email --- frontend/bun.lockb | Bin 240769 -> 241130 bytes frontend/package.json | 1 + frontend/src/components/SignupForm.vue | 7 +++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/bun.lockb b/frontend/bun.lockb index 3da09f7bf158c804a0469c5619eb7b59db47183b..22bb640899efd9a96a95d00e54a4f2478c137885 100755 GIT binary patch delta 41860 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�BpOBj($ZB)BE`&NcDOM12V=oeN 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 F1puFlV5I;6 diff --git a/frontend/package.json b/frontend/package.json index 84e2d7d15..0ca2887b1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "open:ios": "cap open ios" }, "dependencies": { + "@2toad/profanity": "^3.1.1", "@capacitor/android": "^7.0.1", "@capacitor/core": "^7.0.1", "@capacitor/ios": "^7.0.1", diff --git a/frontend/src/components/SignupForm.vue b/frontend/src/components/SignupForm.vue index 2ac95e9ee..d154a636c 100644 --- a/frontend/src/components/SignupForm.vue +++ b/frontend/src/components/SignupForm.vue @@ -4,8 +4,7 @@ import { useAuthStore } from '@/stores/authStore' import MemoryButton from '@/components/MemoryButton.vue' import MemoryInput from '@/components/MemoryInput.vue' import { ref } from 'vue' -import { ProviderSignUpErrors } from '@/types' -import { ViablePodProvider } from '#api/types' +import { ViablePodProvider, ProviderSignUpErrors, ApiSignUpErrors } from '#api/types' // Store const authStore = useAuthStore() @@ -41,6 +40,10 @@ async function submitForm() { case ProviderSignUpErrors['email.invalid']: errorMessages.value.email = authResponse break + case ApiSignUpErrors.UsernameOrEmailContainsProfanity: + case ApiSignUpErrors.UsernameInvalid: + errorMessages.value.username = authResponse + break default: errorMessages.value.default = authResponse break From 618ef14ed0d3af259e3298361be49ded5628d998 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 13:46:37 +0100 Subject: [PATCH 6/8] code: make error logs log as error. remove unused types --- api/src/routes/users.ts | 1 - frontend/src/components/SignupForm.vue | 1 - frontend/src/controller/api.ts | 4 ++-- frontend/src/stores/authStore.ts | 8 ++++---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/api/src/routes/users.ts b/api/src/routes/users.ts index 8b2184321..3b0c51ab7 100644 --- a/api/src/routes/users.ts +++ b/api/src/routes/users.ts @@ -116,7 +116,6 @@ export default new Elysia({ name: 'user', prefix: '/user' }) } return returnObject } catch (_) { - console.log(_) return error(400, FollowErrors.NotValidProvider) } }, diff --git a/frontend/src/components/SignupForm.vue b/frontend/src/components/SignupForm.vue index d154a636c..033325fc3 100644 --- a/frontend/src/components/SignupForm.vue +++ b/frontend/src/components/SignupForm.vue @@ -30,7 +30,6 @@ async function submitForm() { const authResponse = await authStore.signup(email.value, username.value, password.value, endpoint.value) // if the response is a string, it means that there was an error if (authResponse) { - console.log(authResponse) switch (authResponse) { case ProviderSignUpErrors['username.already.exists']: case ProviderSignUpErrors['username.invalid']: diff --git a/frontend/src/controller/api.ts b/frontend/src/controller/api.ts index 6e9372855..fbdd898d7 100644 --- a/frontend/src/controller/api.ts +++ b/frontend/src/controller/api.ts @@ -86,8 +86,8 @@ export class ApiClient { } } } - console.log('error when requesting api: ', e) - console.log(await e.response.text()) + console.error('error when requesting api: ', e) + console.error(await e.response.text()) return { data: ApiErrorsGeneral.default, status: e.response.status diff --git a/frontend/src/stores/authStore.ts b/frontend/src/stores/authStore.ts index 6b6c11cb9..d5d792462 100644 --- a/frontend/src/stores/authStore.ts +++ b/frontend/src/stores/authStore.ts @@ -1,4 +1,4 @@ -import type { SignUpBody, SignInResponse, ViablePodProvider, SelectUsers } from '#api/types' +import type { SignUpBody, ViablePodProvider, SelectUsers } from '#api/types' import { ApiClient } from '@/controller/api' import type { ApiErrors } from '@/types' import { defineStore } from 'pinia' @@ -66,7 +66,7 @@ export const useAuthStore = defineStore('auth', () => { try { const { data: response, status } = await client.signin({ username, password, providerName }) if (status === 200) { - const signInResponse = response as SignInResponse + const signInResponse = response setLoggedIn(true) setToken(signInResponse.token) @@ -76,7 +76,7 @@ export const useAuthStore = defineStore('auth', () => { return response as ApiErrors } } catch (error) { - console.log('error when trying to signIn: ', error) + console.error('error when trying to signIn: ', error) } } /** @@ -95,7 +95,7 @@ export const useAuthStore = defineStore('auth', () => { const body: SignUpBody = { username, password, email, providerName } const { data: response, status } = await client.signup(body) if (status === 200) { - const signupResponse = response as SignInResponse + const signupResponse = response setLoggedIn(true) setToken(signupResponse.token) setUser(signupResponse.user) From 8e5790a0c4bc319ca105ee35967cadc1b52272fa Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 13:47:10 +0100 Subject: [PATCH 7/8] feat: add notifications for login errors --- frontend/src/stores/authStore.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/stores/authStore.ts b/frontend/src/stores/authStore.ts index d5d792462..fbba5ce04 100644 --- a/frontend/src/stores/authStore.ts +++ b/frontend/src/stores/authStore.ts @@ -4,6 +4,7 @@ import type { ApiErrors } from '@/types' import { defineStore } from 'pinia' import { ref } from 'vue' import { useRouter } from 'vue-router' +import { VsNotification } from 'vuesax-alpha' export const useAuthStore = defineStore('auth', () => { // Default state @@ -72,8 +73,13 @@ export const useAuthStore = defineStore('auth', () => { setToken(signInResponse.token) setUser(signInResponse.user) router.push({ name: 'home' }) - } else if (status === 401) { - return response as ApiErrors + } else if (status === 401 || status === 400) { + VsNotification({ + title: 'Wrong Credentials', + content: 'Please check your credentials', + color: 'danger' + }) + return response } } catch (error) { console.error('error when trying to signIn: ', error) @@ -101,7 +107,12 @@ export const useAuthStore = defineStore('auth', () => { setUser(signupResponse.user) router.push({ name: 'home' }) } else if (status === 500) { - return response as ApiErrors + VsNotification({ + title: 'Error', + content: 'Something went wrong', + color: 'danger' + }) + return response } } From 6670623d4e20406aec587476791a392dd9bcb382 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 14:09:25 +0100 Subject: [PATCH 8/8] feat: add technical terms to username blacklist --- api/bun.lock | 3 +++ api/index.d.ts | 1 + api/package.json | 3 ++- api/src/routes/setup.ts | 13 ++++++++----- api/src/test/profanity.test.ts | 13 ++++++++----- api/src/util/index.ts | 17 +++++++++++++++++ frontend/bun.lockb | Bin 241130 -> 241536 bytes frontend/env.d.ts | 1 + frontend/package.json | 1 + frontend/src/controller/formValidation.ts | 14 ++++---------- 10 files changed, 45 insertions(+), 21 deletions(-) create mode 100644 api/index.d.ts create mode 100644 api/src/util/index.ts diff --git a/api/bun.lock b/api/bun.lock index 942b2fb26..e41c5a34f 100644 --- a/api/bun.lock +++ b/api/bun.lock @@ -14,6 +14,7 @@ "ky": "^1.7.4", "luxon": "^3.5.0", "pg": "^8.13.1", + "the-big-username-blacklist": "^1.5.2", }, "devDependencies": { "@eslint/js": "^9.19.0", @@ -334,6 +335,8 @@ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "the-big-username-blacklist": ["the-big-username-blacklist@1.5.2", "", {}, "sha512-bKRIZbu3AoDhEkjNcErodWLpR18vZQQqg9DEab/zELgGw++M1x0KBeTGdoEPHPw0ghmx1jf/B6kZKuwDDPhGBQ=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "ts-api-utils": ["ts-api-utils@2.0.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ=="], diff --git a/api/index.d.ts b/api/index.d.ts new file mode 100644 index 000000000..1304d27aa --- /dev/null +++ b/api/index.d.ts @@ -0,0 +1 @@ +declare module 'the-big-username-blacklist' diff --git a/api/package.json b/api/package.json index 5987947ec..c70632209 100644 --- a/api/package.json +++ b/api/package.json @@ -39,6 +39,7 @@ "elysia": "^1.2.10", "ky": "^1.7.4", "luxon": "^3.5.0", - "pg": "^8.13.1" + "pg": "^8.13.1", + "the-big-username-blacklist": "^1.5.2" } } diff --git a/api/src/routes/setup.ts b/api/src/routes/setup.ts index 3ad4b9145..bbac186f4 100644 --- a/api/src/routes/setup.ts +++ b/api/src/routes/setup.ts @@ -3,6 +3,7 @@ import User from '../decorater/User' import cors from '@elysiajs/cors' import jwt from '@elysiajs/jwt' import Elysia from 'elysia' +import { list } from 'the-big-username-blacklist' const setupPlugin = new Elysia({ name: 'setup' }) .use( @@ -13,12 +14,14 @@ const setupPlugin = new Elysia({ name: 'setup' }) ) .use(cors()) .decorate('user', new User()) - .decorate( - 'profanity', - new Profanity({ - languages: ['en', 'de', 'fr', 'ja', 'pt', 'es', 'ru', 'ar', 'ko'] + .decorate('profanity', () => { + const profanity = new Profanity({ + languages: ['en', 'de', 'fr', 'ja', 'pt', 'es', 'ru', 'ar', 'ko'], + wholeWord: false }) - ) + profanity.addWords(list) + return profanity + }) .macro({ isSignedIn: enabled => { if (!enabled) return diff --git a/api/src/test/profanity.test.ts b/api/src/test/profanity.test.ts index 40fa77575..ce679a779 100644 --- a/api/src/test/profanity.test.ts +++ b/api/src/test/profanity.test.ts @@ -1,20 +1,23 @@ -import { profanity } from '@2toad/profanity' import { describe, expect, it } from 'bun:test' +import { getProfanity } from '@/util' describe('Check profanity module', () => { it('should return false for strings without profanity', () => { const notProfane = [ 'Taiwan', 'Taiwanese', - 'Taiwanese people', + 'Taiwanesepeople', 'Protestant', 'Protestantism', 'Transgender', 'Hongkong', - 'Hong Kong', - 'Hong Kong people', - 'Taiwan is a free country' + 'HongKong', + 'HongKongpeople', + 'Taiwanisafreecountry' ] + + const profanity = getProfanity() + notProfane.forEach(word => { expect(profanity.exists(word)).toBe(false) }) diff --git a/api/src/util/index.ts b/api/src/util/index.ts new file mode 100644 index 000000000..4d8e08037 --- /dev/null +++ b/api/src/util/index.ts @@ -0,0 +1,17 @@ +export * from './user' +import { Profanity } from '@2toad/profanity' +import { List } from '@2toad/profanity/dist/models' +import { list } from 'the-big-username-blacklist' + +export function getProfanity() { + const whiteList = new List(() => true) + whiteList.addWords(['taiwan']) + const profanity = new Profanity({ + languages: ['en', 'de', 'fr', 'ja', 'pt', 'es', 'ru', 'ar', 'ko'], + wholeWord: true + }) + profanity.addWords(list) + profanity.whitelist = whiteList + + return profanity +} diff --git a/frontend/bun.lockb b/frontend/bun.lockb index 22bb640899efd9a96a95d00e54a4f2478c137885..fb24196d771343ba528f1c3cc698311574e21a25 100755 GIT binary patch delta 42426 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 U5uO{X;NeMki!C&nr&wg`zZ{7RUH||9 delta 42582 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 diff --git a/frontend/env.d.ts b/frontend/env.d.ts index fdd16900c..54e850141 100644 --- a/frontend/env.d.ts +++ b/frontend/env.d.ts @@ -1,6 +1,7 @@ /// declare module 'vue-iconsax' +declare module 'the-big-username-blacklist' interface ImportMetaEnv { readonly VITE_API_URL: string diff --git a/frontend/package.json b/frontend/package.json index 0ca2887b1..0f07bfbd5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -29,6 +29,7 @@ "luxon": "^3.5.0", "pinia": "^2.3.1", "tailwindcss": "^4.0.0", + "the-big-username-blacklist": "^1.5.2", "vue": "^3.5.13", "vue-iconsax": "^2.0.0", "vue-router": "^4.5.0", diff --git a/frontend/src/controller/formValidation.ts b/frontend/src/controller/formValidation.ts index 52338d0f0..e3a7f57cb 100644 --- a/frontend/src/controller/formValidation.ts +++ b/frontend/src/controller/formValidation.ts @@ -1,8 +1,6 @@ -import { Profanity } from '@2toad/profanity' +import { getProfanity } from '#api/util' -const profanity = new Profanity({ - languages: ['en', 'de', 'fr', 'ja', 'pt', 'es', 'ru', 'ar', 'ko'] -}) +const profanity = getProfanity() export function validateUsername(username: string, required = true): string | undefined { if (required && username === '') { @@ -15,13 +13,12 @@ export function validateUsername(username: string, required = true): string | un return 'Username cannot contain spaces' } if (profanity.exists(username)) { - return 'Username contains profanity' + return 'Username is blacklisted' } const unwantedChars = new RegExp(/[@#/\\$%^&*!?<>+~=]/g) if (unwantedChars.test(username)) { return 'Username cannot contain the following characters: @ # \\ $ % ^ & * ! ? < > + ~ =' } - // TODO: check if username includes bad words return undefined } @@ -38,11 +35,8 @@ export function validateEmail(email: string, required = true): string | undefine if (required && email === '') { return 'Email is required' } - if (emailRegex.exec(email) === null) { + if (!emailRegex.test(email)) { return 'Invalid Email' } - if (profanity.exists(email)) { - return 'Email contains profanity' - } return undefined }