diff --git a/.github/workflows/validate-app.yml b/.github/workflows/validate-app.yml index ae8b21fad..2f6be746e 100644 --- a/.github/workflows/validate-app.yml +++ b/.github/workflows/validate-app.yml @@ -76,7 +76,6 @@ jobs: - name: Prettier run: npx prettier --check . working-directory: app - continue-on-error: true test: runs-on: ubuntu-24.04 diff --git a/app/.devcontainer/devcontainer.json b/app/.devcontainer/devcontainer.json index 1f65d98ad..376796cdd 100644 --- a/app/.devcontainer/devcontainer.json +++ b/app/.devcontainer/devcontainer.json @@ -23,10 +23,7 @@ ] } }, - "forwardPorts": [ - 3000, - "db:5432" - ], + "forwardPorts": [3000, "db:5432"], "postCreateCommand": "./.devcontainer/postCreateCommand.sh", "remoteUser": "node" } diff --git a/app/eslint.config.mjs b/app/eslint.config.mjs index 8ae5445b6..5cd647f5b 100644 --- a/app/eslint.config.mjs +++ b/app/eslint.config.mjs @@ -20,6 +20,7 @@ const eslintConfig = defineConfig([ ...tanstackQuery.configs["flat/recommended"], reactYouMightNotNeedAnEffect.configs.recommended, prettier, + reactCompiler.configs.recommended, globalIgnores([ ".next/**", @@ -39,7 +40,6 @@ const eslintConfig = defineConfig([ name: "custom-rules", plugins: { "@typescript-eslint": tseslint.plugin, - "react-compiler": reactCompiler, }, languageOptions: { parserOptions: { diff --git a/app/package-lock.json b/app/package-lock.json index 5c20809d9..2915d69fe 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -109,7 +109,7 @@ "csv": "6.4.1", "eslint": "9.39.3", "eslint-config-next": "16.1.6", - "eslint-plugin-react-compiler": "latest", + "eslint-plugin-react-compiler": "19.1.0-rc.2", "eslint-plugin-react-you-might-not-need-an-effect": "0.9.1", "postcss": "8.5.6", "prettier": "3.8.1", @@ -119,7 +119,8 @@ "tailwindcss": "4.2.1", "typescript": "5.9.3", "vercel": "50.22.1", - "vitest": "3.2.4" + "vitest": "3.2.4", + "zod-validation-error": "4.0.2" } }, "node_modules/@algolia/autocomplete-core": { @@ -24492,16 +24493,16 @@ } }, "node_modules/zod-validation-error": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.4.tgz", - "integrity": "sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", "dev": true, "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "zod": "^3.24.4" + "zod": "^3.25.0 || ^4.0.0" } }, "node_modules/zrender": { diff --git a/app/package.json b/app/package.json index a1a980be4..94706c09a 100644 --- a/app/package.json +++ b/app/package.json @@ -113,7 +113,7 @@ "csv": "6.4.1", "eslint": "9.39.3", "eslint-config-next": "16.1.6", - "eslint-plugin-react-compiler": "latest", + "eslint-plugin-react-compiler": "19.1.0-rc.2", "eslint-plugin-react-you-might-not-need-an-effect": "0.9.1", "postcss": "8.5.6", "prettier": "3.8.1", @@ -123,7 +123,8 @@ "tailwindcss": "4.2.1", "typescript": "5.9.3", "vercel": "50.22.1", - "vitest": "3.2.4" + "vitest": "3.2.4", + "zod-validation-error": "4.0.2" }, "prisma": { "schema": "./prisma/schema" @@ -131,6 +132,7 @@ "packageManager": "npm@10.9.4", "overrides": { "@types/react-dom": "19.2.3", - "@types/react": "19.2.14" + "@types/react": "19.2.14", + "zod-validation-error": "4.0.2" } } diff --git a/app/postcss.config.cjs b/app/postcss.config.cjs index ca54d4ad3..19afca407 100644 --- a/app/postcss.config.cjs +++ b/app/postcss.config.cjs @@ -1,6 +1,6 @@ const config = { plugins: { - '@tailwindcss/postcss': {}, + "@tailwindcss/postcss": {}, }, }; diff --git a/app/src/app/api/health/throw/route.ts b/app/src/app/api/health/throw/route.ts deleted file mode 100644 index ed6fbbba5..000000000 --- a/app/src/app/api/health/throw/route.ts +++ /dev/null @@ -1,17 +0,0 @@ -import apiErrorHandler from "@/modules/common/utils/apiErrorHandler"; -import { type NextRequest } from "next/server"; -import { z } from "zod"; - -const bodySchema = z.object({ - message: z.string(), -}); - -export const POST = async (request: NextRequest) => { - try { - const body = (await request.json()) as unknown; - const data = bodySchema.parse(body); - throw new Error(data.message); - } catch (error) { - return apiErrorHandler(error); - } -}; diff --git a/app/src/app/app/help/contributing/page.tsx b/app/src/app/app/help/contributing/page.tsx index 86a14b10a..f61c0945e 100644 --- a/app/src/app/app/help/contributing/page.tsx +++ b/app/src/app/app/help/contributing/page.tsx @@ -94,7 +94,7 @@ export default async function Page() {
@@ -117,7 +117,7 @@ export default async function Page() {
Arten der Mithilfe @@ -156,7 +156,7 @@ export default async function Page() {
Integrierte Apps @@ -180,7 +180,7 @@ export default async function Page() {
Externe Apps @@ -238,7 +238,7 @@ export default async function Page() { @@ -296,7 +296,7 @@ export default async function Page() { @@ -335,7 +335,7 @@ export default async function Page() { @@ -364,7 +364,7 @@ export default async function Page() { @@ -388,14 +388,14 @@ export default async function Page() {
Frequently Asked Questions (FAQ) @@ -411,7 +411,7 @@ export default async function Page() { @@ -434,7 +434,7 @@ export default async function Page() { @@ -452,7 +452,7 @@ export default async function Page() { diff --git a/app/src/app/app/help/support/page.tsx b/app/src/app/app/help/support/page.tsx index d24abbb2a..3d086e2e0 100644 --- a/app/src/app/app/help/support/page.tsx +++ b/app/src/app/app/help/support/page.tsx @@ -35,7 +35,7 @@ export default async function Page() {
diff --git a/app/src/app/dogfight-trainer/opengraph-image.tsx b/app/src/app/dogfight-trainer/opengraph-image.tsx index 96a32a3de..c2a6db90f 100644 --- a/app/src/app/dogfight-trainer/opengraph-image.tsx +++ b/app/src/app/dogfight-trainer/opengraph-image.tsx @@ -14,16 +14,14 @@ export default async function og() { ).then((res) => res.arrayBuffer()); return new ImageResponse( - ( -
-

Dogfight Trainer

+
+

Dogfight Trainer

-
-

Sinister Inc

-

Hoist the Black

-
+
+

Sinister Inc

+

Hoist the Black

- ), +
, { ...size, fonts: [ diff --git a/app/src/app/icon.tsx b/app/src/app/icon.tsx index cc9807962..8bbbd3c7e 100644 --- a/app/src/app/icon.tsx +++ b/app/src/app/icon.tsx @@ -9,25 +9,27 @@ export const contentType = "image/png"; export default function icon() { return new ImageResponse( - ( - + + - - - - - - - - - - - - ), + + + + + + + + + , size, ); } diff --git a/app/src/app/layout.tsx b/app/src/app/layout.tsx index f351dc9c2..dd2abdd8b 100644 --- a/app/src/app/layout.tsx +++ b/app/src/app/layout.tsx @@ -14,7 +14,7 @@ const robotMono = Roboto_Mono({ }); export const metadata: Metadata = { - metadataBase: new URL(env.BASE_URL), + metadataBase: new URL(env.NEXT_PUBLIC_BASE_URL), title: { default: "SAM - Sinister Incorporated", template: "%s | SAM - Sinister Incorporated", diff --git a/app/src/app/manifest.ts b/app/src/app/manifest.ts index e72683625..d688553b7 100644 --- a/app/src/app/manifest.ts +++ b/app/src/app/manifest.ts @@ -1,10 +1,10 @@ import { env } from "@/env"; import { type MetadataRoute } from "next"; import faviconSrc from "../assets/favicon.svg"; -import screenshotDashboardMobileSrc from "../assets/screenshots/screenshot-dashboard-mobile.avif"; -import screenshotDashboardSrc from "../assets/screenshots/screenshot-dashboard.avif"; import screenshotAppsMobileSrc from "../assets/screenshots/screenshot-apps-mobile.avif"; import screenshotAppsSrc from "../assets/screenshots/screenshot-apps.avif"; +import screenshotDashboardMobileSrc from "../assets/screenshots/screenshot-dashboard-mobile.avif"; +import screenshotDashboardSrc from "../assets/screenshots/screenshot-dashboard.avif"; export default function manifest(): MetadataRoute.Manifest { return { @@ -13,8 +13,8 @@ export default function manifest(): MetadataRoute.Manifest { description: "Sinister Administration Module (SAM) for the Star Citizen organization Sinister Incorporated", categories: ["entertainment", "games"], // https://github.com/w3c/manifest/wiki/Categories - scope: env.BASE_URL, // Will open links outside the app in the browser - start_url: `${env.BASE_URL}/app`, + scope: env.NEXT_PUBLIC_BASE_URL, // Will open links outside the app in the browser + start_url: `${env.NEXT_PUBLIC_BASE_URL}/app`, shortcuts: [ // Can't be individualized based on permissions { diff --git a/app/src/app/opengraph-image.tsx b/app/src/app/opengraph-image.tsx index 1c88b7d98..283fecb01 100644 --- a/app/src/app/opengraph-image.tsx +++ b/app/src/app/opengraph-image.tsx @@ -14,12 +14,10 @@ export default async function og() { ).then((res) => res.arrayBuffer()); return new ImageResponse( - ( -
-

SAM

-

Sinister Incorporated

-
- ), +
+

SAM

+

Sinister Incorporated

+
, { ...size, fonts: [ diff --git a/app/src/app/preview-channel/_opengraph-image.tsx b/app/src/app/preview-channel/_opengraph-image.tsx index 48c15638a..35aaf62c0 100644 --- a/app/src/app/preview-channel/_opengraph-image.tsx +++ b/app/src/app/preview-channel/_opengraph-image.tsx @@ -14,16 +14,14 @@ export default async function og() { ).then((res) => res.arrayBuffer()); return new ImageResponse( - ( -
-

Preview Channel

+
+

Preview Channel

-
-

Sinister Inc

-

Hoist the Black

-
+
+

Sinister Inc

+

Hoist the Black

- ), +
, { ...size, fonts: [ diff --git a/app/src/env.ts b/app/src/env.ts index a5b510724..a69fa6f0b 100644 --- a/app/src/env.ts +++ b/app/src/env.ts @@ -39,19 +39,6 @@ export const env = createEnv({ UNLEASH_SERVER_API_URL: z.url().optional(), /** Unleash (or any other Unleash-compatible feature flag provider like GitLab) */ UNLEASH_SERVER_API_TOKEN: z.string().optional(), - BASE_URL: z.preprocess( - // Uses VERCEL_URL if BASE_URL is not set, e.g. on Vercel's preview deployments - (str) => { - if (str) { - return str; - } else if (process.env.VERCEL_URL) { - return `https://${process.env.VERCEL_URL}`; - } - - return "http://localhost:3000"; - }, - z.string().url(), - ), COMMIT_SHA: z.preprocess( // Uses VERCEL_GIT_COMMIT_SHA if COMMIT_SHA is not set (str) => str || process.env.VERCEL_GIT_COMMIT_SHA, @@ -101,8 +88,8 @@ export const env = createEnv({ (str) => { if (str) { return str; - } else if (process.env.BASE_URL) { - return process.env.BASE_URL.replace(/https?:\/\//, ""); + } else if (process.env.NEXT_PUBLIC_BASE_URL) { + return process.env.NEXT_PUBLIC_BASE_URL.replace(/https?:\/\//, ""); } else if (process.env.VERCEL_URL) { return process.env.VERCEL_URL; } @@ -111,6 +98,19 @@ export const env = createEnv({ }, z.string(), ), + NEXT_PUBLIC_BASE_URL: z.preprocess( + // Uses VERCEL_URL if BASE_URL is not set, e.g. on Vercel's preview deployments + (str) => { + if (str) { + return str; + } else if (process.env.VERCEL_URL) { + return `https://${process.env.VERCEL_URL}`; + } + + return "http://localhost:3000"; + }, + z.url(), + ), }, /* @@ -143,7 +143,7 @@ export const env = createEnv({ COMMIT_SHA: process.env.COMMIT_SHA, NEXT_PUBLIC_CARE_BEAR_SHOOTER_BUILD_URL: process.env.NEXT_PUBLIC_CARE_BEAR_SHOOTER_BUILD_URL, - BASE_URL: process.env.BASE_URL, + NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL, AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY, AWS_EVENT_BUS_ARN: process.env.AWS_EVENT_BUS_ARN, diff --git a/app/src/modules/auth/components/AdminEnabler.tsx b/app/src/modules/auth/components/AdminEnabler.tsx index 7664c36cd..8d22c0344 100644 --- a/app/src/modules/auth/components/AdminEnabler.tsx +++ b/app/src/modules/auth/components/AdminEnabler.tsx @@ -13,9 +13,9 @@ export const AdminEnabler = ({ className, enabled = false }: Props) => { const handleClick = () => { if (enabled) { - document.cookie = `enable_admin=; path=/; max-age=0;`; + document.cookie = `enable_admin=; path=/; samesite=lax; max-age=0;`; } else { - document.cookie = `enable_admin=1; path=/; max-age=${60 * 60 * 24 * 7};`; + document.cookie = `enable_admin=1; path=/; samesite=lax; max-age=${60 * 60 * 24 * 7};`; } router.refresh(); diff --git a/app/src/modules/career/nodes/Markdown/client/schema.ts b/app/src/modules/career/nodes/Markdown/client/schema.ts index b58bf4787..2cc16895e 100644 --- a/app/src/modules/career/nodes/Markdown/client/schema.ts +++ b/app/src/modules/career/nodes/Markdown/client/schema.ts @@ -1,11 +1,12 @@ "use client"; + import { FlowNodeMarkdownPosition } from "@prisma/client"; import z from "zod"; export const schema = z.object({ id: z.cuid2(), - markdown: z.string(), - markdownPosition: z.nativeEnum(FlowNodeMarkdownPosition), - backgroundColor: z.string(), + markdown: z.string().max(5000), + markdownPosition: z.enum(FlowNodeMarkdownPosition), + backgroundColor: z.string().optional(), backgroundTransparency: z.coerce.number().min(0).max(1), }); diff --git a/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts b/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts index 27434c749..f657446df 100644 --- a/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts +++ b/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts @@ -11,8 +11,8 @@ export const updateFlowSchema = z.object({ width: z.number(), height: z.number(), data: z.object({ - markdown: z.string(), - markdownPosition: z.nativeEnum(FlowNodeMarkdownPosition), + markdown: z.string().max(5000), + markdownPosition: z.enum(FlowNodeMarkdownPosition), backgroundColor: z.string().optional(), backgroundTransparency: z.number().min(0).max(1).optional(), }), diff --git a/app/src/modules/career/nodes/Role/client/schema.ts b/app/src/modules/career/nodes/Role/client/schema.ts index c42b3bb86..154f2401e 100644 --- a/app/src/modules/career/nodes/Role/client/schema.ts +++ b/app/src/modules/career/nodes/Role/client/schema.ts @@ -6,7 +6,7 @@ import z from "zod"; export const schema = z.object({ id: z.cuid2(), roleId: z.string(), - roleImage: z.nativeEnum(FlowNodeRoleImage), + roleImage: z.enum(FlowNodeRoleImage), backgroundColor: z.string(), backgroundTransparency: z.coerce.number().min(0).max(1), showUnlocked: z.preprocess((value) => value === "true", z.boolean()), diff --git a/app/src/modules/career/nodes/Role/server/updateFlowSchema.ts b/app/src/modules/career/nodes/Role/server/updateFlowSchema.ts index e5f0c91a8..faad04ace 100644 --- a/app/src/modules/career/nodes/Role/server/updateFlowSchema.ts +++ b/app/src/modules/career/nodes/Role/server/updateFlowSchema.ts @@ -14,7 +14,7 @@ export const updateFlowSchema = z.object({ role: z.object({ id: z.cuid(), }), - roleImage: z.nativeEnum(FlowNodeRoleImage), + roleImage: z.enum(FlowNodeRoleImage), backgroundColor: z.string().optional(), backgroundTransparency: z.number().min(0).max(1).optional(), showUnlocked: z.boolean().nullish(), diff --git a/app/src/modules/career/nodes/RoleCitizens/client/schema.ts b/app/src/modules/career/nodes/RoleCitizens/client/schema.ts index e94c03fda..b5e9d51c8 100644 --- a/app/src/modules/career/nodes/RoleCitizens/client/schema.ts +++ b/app/src/modules/career/nodes/RoleCitizens/client/schema.ts @@ -6,7 +6,7 @@ import z from "zod"; export const schema = z.object({ id: z.cuid2(), roleId: z.string(), - roleCitizensAlignment: z.nativeEnum(FlowNodeRoleCitizensAlignment), + roleCitizensAlignment: z.enum(FlowNodeRoleCitizensAlignment), roleCitizensHideRole: z.preprocess((value) => value === "true", z.boolean()), backgroundColor: z.string(), backgroundTransparency: z.coerce.number().min(0).max(1), diff --git a/app/src/modules/career/nodes/RoleCitizens/server/updateFlowSchema.ts b/app/src/modules/career/nodes/RoleCitizens/server/updateFlowSchema.ts index 8bece926c..16fd6a1bc 100644 --- a/app/src/modules/career/nodes/RoleCitizens/server/updateFlowSchema.ts +++ b/app/src/modules/career/nodes/RoleCitizens/server/updateFlowSchema.ts @@ -14,7 +14,7 @@ export const updateFlowSchema = z.object({ role: z.object({ id: z.cuid(), }), - roleCitizensAlignment: z.nativeEnum(FlowNodeRoleCitizensAlignment), + roleCitizensAlignment: z.enum(FlowNodeRoleCitizensAlignment), roleCitizensHideRole: z.boolean(), backgroundColor: z.string().optional(), backgroundTransparency: z.number().min(0).max(1).optional(), diff --git a/app/src/modules/citizen/actions/updateRoleAssignment.ts b/app/src/modules/citizen/actions/updateRoleAssignment.ts index 61b53e6ea..d60a3c754 100644 --- a/app/src/modules/citizen/actions/updateRoleAssignment.ts +++ b/app/src/modules/citizen/actions/updateRoleAssignment.ts @@ -28,6 +28,15 @@ export const updateRoleAssignments = createAuthenticatedAction( requestPayload: formData, }; + /** + * Further validate the request + */ + if (Array.from(formData.keys()).length > 500) + return { + error: t("Common.badRequest"), + requestPayload: formData, + }; + /** * */ diff --git a/app/src/modules/common/components/CreateContext.tsx b/app/src/modules/common/components/CreateContext.tsx index ea4ece630..08f0ae3fe 100644 --- a/app/src/modules/common/components/CreateContext.tsx +++ b/app/src/modules/common/components/CreateContext.tsx @@ -20,9 +20,9 @@ const CreateCitizenForm = dynamic(() => ); const CreateOrganizationForm = dynamic(() => - import( - "@/modules/spynet/components/CreateOrganization/CreateOrganizationForm" - ).then((mod) => mod.CreateOrganizationForm), + import("@/modules/spynet/components/CreateOrganization/CreateOrganizationForm").then( + (mod) => mod.CreateOrganizationForm, + ), ); const CreateRoleForm = dynamic(() => @@ -32,9 +32,9 @@ const CreateRoleForm = dynamic(() => ); const CreatePenaltyEntryForm = dynamic(() => - import( - "@/modules/penalty-points/components/CreatePenaltyEntry/CreatePenaltyEntryForm" - ).then((mod) => mod.CreatePenaltyEntryForm), + import("@/modules/penalty-points/components/CreatePenaltyEntry/CreatePenaltyEntryForm").then( + (mod) => mod.CreatePenaltyEntryForm, + ), ); const CreateTaskForm = dynamic(() => @@ -44,15 +44,15 @@ const CreateTaskForm = dynamic(() => ); const CreateProfitDistributionCycleForm = dynamic(() => - import( - "@/modules/profit-distribution/components/CreateProfitDistributionCycleForm" - ).then((mod) => mod.CreateProfitDistributionCycleForm), + import("@/modules/profit-distribution/components/CreateProfitDistributionCycleForm").then( + (mod) => mod.CreateProfitDistributionCycleForm, + ), ); const CreateSilcTransactionForm = dynamic(() => - import( - "@/modules/silc/components/CreateSilcTransactionForm" - ).then((mod) => mod.CreateSilcTransactionForm), + import("@/modules/silc/components/CreateSilcTransactionForm").then( + (mod) => mod.CreateSilcTransactionForm, + ), ); export const createForms = { diff --git a/app/src/modules/common/utils/api.ts b/app/src/modules/common/utils/api.ts index 43159f51c..f70228f41 100644 --- a/app/src/modules/common/utils/api.ts +++ b/app/src/modules/common/utils/api.ts @@ -4,6 +4,7 @@ * * We also create a few inference helpers for input and output types. */ +import { env } from "@/env"; import type { AppRouter } from "@/server/api/root"; import { httpBatchLink, loggerLink } from "@trpc/client"; import { createTRPCNext } from "@trpc/next"; @@ -12,8 +13,7 @@ import superjson from "superjson"; const getBaseUrl = () => { if (typeof window !== "undefined") return ""; // browser should use relative url - if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url - return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost + return env.NEXT_PUBLIC_BASE_URL; // SSR should use the provided BASE_URL }; /** A set of type-safe react-query hooks for your tRPC API. */ diff --git a/app/src/modules/notifications/actions/updateMyNotificationSettings.ts b/app/src/modules/notifications/actions/updateMyNotificationSettings.ts index 3da7049ac..6f37104af 100644 --- a/app/src/modules/notifications/actions/updateMyNotificationSettings.ts +++ b/app/src/modules/notifications/actions/updateMyNotificationSettings.ts @@ -30,6 +30,15 @@ export const updateMyNotificationSettings = createAuthenticatedAction( requestPayload: formData, }; + /** + * Further validate the request + */ + if (Array.from(formData.keys()).length > 100) + return { + error: t("Common.badRequest"), + requestPayload: formData, + }; + /** * */ diff --git a/app/src/modules/notifications/utils/type-handlers/email_confirmation.ts b/app/src/modules/notifications/utils/type-handlers/email_confirmation.ts index 4227d8bce..d95410b93 100644 --- a/app/src/modules/notifications/utils/type-handlers/email_confirmation.ts +++ b/app/src/modules/notifications/utils/type-handlers/email_confirmation.ts @@ -30,7 +30,7 @@ export const emailConfirmationHandler = async (payload: Payload) => { { to: payload.userEmail, templateProps: { - baseUrl: env.BASE_URL, + baseUrl: env.NEXT_PUBLIC_BASE_URL, host: env.NEXT_PUBLIC_HOST, token: emailConfirmationToken, }, diff --git a/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts b/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts index a188bcc67..f8dd2bcb9 100644 --- a/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts +++ b/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts @@ -33,6 +33,15 @@ export const updateParticipantAttribute = createAuthenticatedAction( requestPayload: formData, }; + /** + * Further validate the request + */ + if (Array.from(formData.keys()).length > 100) + return { + error: t("Common.badRequest"), + requestPayload: formData, + }; + /** * */ diff --git a/app/src/modules/tasks/actions/createTask.ts b/app/src/modules/tasks/actions/createTask.ts index 0dc6766bd..4ff05952f 100644 --- a/app/src/modules/tasks/actions/createTask.ts +++ b/app/src/modules/tasks/actions/createTask.ts @@ -12,13 +12,13 @@ import { serializeError } from "serialize-error"; import { z } from "zod"; const schema = z.object({ - visibility: z.nativeEnum(TaskVisibility), + visibility: z.enum(TaskVisibility), assignmentLimit: z.coerce.number().min(1).nullable(), assignedToIds: z.array(z.cuid()).max(250).optional(), // Arbitrary (untested) limit to prevent DDoS title: z.string().trim().max(64), description: z.string().trim().max(2048).optional(), expiresAt: z.coerce.date().optional(), - rewardType: z.nativeEnum(TaskRewardType), + rewardType: z.enum(TaskRewardType), rewardTypeTextValue: z.string().trim().max(2048).optional(), rewardTypeSilcValue: z.coerce.number().optional(), rewardTypeNewSilcValue: z.coerce.number().optional(), diff --git a/app/src/proxy.ts b/app/src/proxy.ts index 9a0930b31..8edb0fcd4 100644 --- a/app/src/proxy.ts +++ b/app/src/proxy.ts @@ -7,7 +7,11 @@ export function proxy(request: NextRequest) { request.cookies.get("next-auth.session-token") || request.cookies.get("__Secure-next-auth.session-token"); - // Early return. Make sure to use `authenticatePage()` on individual pages in order to fully authenticate the user. + /** + * This is only an early return. Actual verification of the session is done + * on the individual pages. Use `authenticatePage()` in order to fully + * authenticate the user. + */ if (pathname.startsWith("/app") && !sessionCookie) { return NextResponse.redirect(new URL(`/`, request.url)); } diff --git a/app/src/server/api/routers/ai.ts b/app/src/server/api/routers/ai.ts index 4fe7acf88..0ceaa2782 100644 --- a/app/src/server/api/routers/ai.ts +++ b/app/src/server/api/routers/ai.ts @@ -1,10 +1,10 @@ +import { env } from "@/env"; import { isOpenAIEnabled } from "@/modules/common/utils/isOpenAIEnabled"; import { log } from "@/modules/logging"; import { getRoles } from "@/modules/roles/queries"; import { TRPCError } from "@trpc/server"; import OpenAI from "openai"; import { type ChatCompletionMessageParam } from "openai/resources/index.mjs"; -import { env } from "process"; import { serializeError } from "serialize-error"; import { z } from "zod"; import { createTRPCRouter, protectedProcedure } from "../trpc";