From 76701e35ec5083b47971eca4165259badf1bf8c5 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 11 Oct 2025 00:06:51 +0300 Subject: [PATCH] fix: replace uuid package with standard module (#10318) --- app/actions/index.ts | 13 ++++---- app/editor/components/MentionMenu.tsx | 11 ++++--- app/editor/components/PasteMenu.tsx | 5 ++-- app/editor/extensions/PasteHandler.tsx | 5 ++-- .../Document/components/CommentForm.tsx | 3 +- app/scenes/Search/Search.tsx | 3 +- app/stores/DialogsStore.ts | 3 +- package.json | 1 - server/commands/accountProvisioner.test.ts | 18 +++++------ server/commands/attachmentCreator.ts | 4 +-- server/commands/collectionExporter.ts | 4 +-- server/commands/userProvisioner.test.ts | 8 ++--- server/models/Collection.test.ts | 16 +++++----- server/models/base/Model.test.ts | 4 +-- .../models/helpers/SubscriptionHelper.test.ts | 6 ++-- server/queues/processors/ImportsProcessor.ts | 6 ++-- server/queues/tasks/APIImportTask.ts | 4 +-- server/queues/tasks/ImportJSONTask.ts | 8 ++--- server/queues/tasks/ImportMarkdownZipTask.ts | 6 ++-- server/queues/tasks/UploadTeamAvatarTask.ts | 4 +-- server/queues/tasks/UploadUserAvatarTask.ts | 4 +-- server/routes/api/attachments/attachments.ts | 6 ++-- server/routes/api/auth/auth.test.ts | 16 +++++----- .../authenticationProviders.test.ts | 4 +-- server/routes/api/documents/documents.ts | 4 +-- .../api/notifications/notifications.test.ts | 4 +-- server/test/factories.ts | 6 ++-- server/utils/oauth/OAuthInterface.test.ts | 6 ++-- server/utils/parseAttachmentIds.test.ts | 20 ++++++------- server/validation.test.ts | 30 +++++++++---------- 30 files changed, 112 insertions(+), 120 deletions(-) diff --git a/app/actions/index.ts b/app/actions/index.ts index 3f74231f97..7c679561bf 100644 --- a/app/actions/index.ts +++ b/app/actions/index.ts @@ -2,7 +2,6 @@ import { LocationDescriptor } from "history"; import flattenDeep from "lodash/flattenDeep"; import { toast } from "sonner"; import { Optional } from "utility-types"; -import { v4 as uuidv4 } from "uuid"; import { Action, ActionContext, @@ -46,7 +45,7 @@ export function createAction(definition: Optional): Action { return definition.perform?.(context); } : undefined, - id: definition.id ?? uuidv4(), + id: definition.id ?? crypto.randomUUID(), }; } @@ -202,7 +201,7 @@ export function createActionV2( return definition.perform(context); } : () => {}, - id: definition.id ?? uuidv4(), + id: definition.id ?? crypto.randomUUID(), }; } @@ -213,7 +212,7 @@ export function createInternalLinkActionV2( ...definition, type: "action", variant: "internal_link", - id: definition.id ?? uuidv4(), + id: definition.id ?? crypto.randomUUID(), }; } @@ -224,7 +223,7 @@ export function createExternalLinkActionV2( ...definition, type: "action", variant: "external_link", - id: definition.id ?? uuidv4(), + id: definition.id ?? crypto.randomUUID(), }; } @@ -235,7 +234,7 @@ export function createActionV2WithChildren( ...definition, type: "action", variant: "action_with_children", - id: definition.id ?? uuidv4(), + id: definition.id ?? crypto.randomUUID(), }; } @@ -252,7 +251,7 @@ export function createRootMenuAction( actions: (ActionV2Variant | ActionV2Group | TActionV2Separator)[] ): ActionV2WithChildren { return { - id: uuidv4(), + id: crypto.randomUUID(), type: "action", variant: "action_with_children", name: "root_action", diff --git a/app/editor/components/MentionMenu.tsx b/app/editor/components/MentionMenu.tsx index f20a597b1b..c0bd873527 100644 --- a/app/editor/components/MentionMenu.tsx +++ b/app/editor/components/MentionMenu.tsx @@ -5,7 +5,6 @@ import { useState, useCallback, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { useLocation } from "react-router-dom"; import { toast } from "sonner"; -import { v4 } from "uuid"; import Icon from "@shared/components/Icon"; import { MenuItem } from "@shared/editor/types"; import { MentionType } from "@shared/types"; @@ -92,7 +91,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) { section: UserSection, appendSpace: true, attrs: { - id: v4(), + id: crypto.randomUUID(), type: MentionType.User, modelId: user.id, actorId, @@ -124,7 +123,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) { section: DocumentsSection, appendSpace: true, attrs: { - id: v4(), + id: crypto.randomUUID(), type: MentionType.Document, modelId: doc.id, actorId, @@ -152,7 +151,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) { section: CollectionsSection, appendSpace: true, attrs: { - id: v4(), + id: crypto.randomUUID(), type: MentionType.Collection, modelId: collection.id, actorId, @@ -172,9 +171,9 @@ function MentionMenu({ search, isActive, ...rest }: Props) { priority: -1, appendSpace: true, attrs: { - id: v4(), + id: crypto.randomUUID(), type: MentionType.Document, - modelId: v4(), + modelId: crypto.randomUUID(), actorId, label: search, }, diff --git a/app/editor/components/PasteMenu.tsx b/app/editor/components/PasteMenu.tsx index 2e8bf83985..6ce9004ac8 100644 --- a/app/editor/components/PasteMenu.tsx +++ b/app/editor/components/PasteMenu.tsx @@ -2,7 +2,6 @@ import { observer } from "mobx-react"; import { EmailIcon, LinkIcon } from "outline-icons"; import React from "react"; import { useTranslation } from "react-i18next"; -import { v4 } from "uuid"; import { EmbedDescriptor } from "@shared/editor/embeds"; import { MenuItem } from "@shared/editor/types"; import { MentionType } from "@shared/types"; @@ -97,11 +96,11 @@ function useItems({ icon: , visible: !!mentionType, attrs: { - id: v4(), + id: crypto.randomUUID(), type: mentionType, label: pastedText, href: pastedText, - modelId: v4(), + modelId: crypto.randomUUID(), actorId: user?.id, }, appendSpace: true, diff --git a/app/editor/extensions/PasteHandler.tsx b/app/editor/extensions/PasteHandler.tsx index 82f0f2d3d7..f049a8665d 100644 --- a/app/editor/extensions/PasteHandler.tsx +++ b/app/editor/extensions/PasteHandler.tsx @@ -8,7 +8,6 @@ import { TextSelection, } from "prosemirror-state"; import { Decoration, DecorationSet } from "prosemirror-view"; -import { v4 } from "uuid"; import Extension, { WidgetProps } from "@shared/editor/lib/Extension"; import { codeLanguages } from "@shared/editor/lib/code"; import isMarkdown from "@shared/editor/lib/isMarkdown"; @@ -144,7 +143,7 @@ export default class PasteHandler extends Extension { type: MentionType.Document, modelId: document.id, label: document.titleWithDefault, - id: v4(), + id: crypto.randomUUID(), }) ) ); @@ -189,7 +188,7 @@ export default class PasteHandler extends Extension { type: MentionType.Collection, modelId: collection.id, label: collection.name, - id: v4(), + id: crypto.randomUUID(), }) ) ); diff --git a/app/scenes/Document/components/CommentForm.tsx b/app/scenes/Document/components/CommentForm.tsx index 8e053456c4..dd43f765bd 100644 --- a/app/scenes/Document/components/CommentForm.tsx +++ b/app/scenes/Document/components/CommentForm.tsx @@ -7,7 +7,6 @@ import * as React from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { useTheme } from "styled-components"; -import { v4 as uuidv4 } from "uuid"; import { ProsemirrorData } from "@shared/types"; import { getEventFiles } from "@shared/utils/files"; import { AttachmentValidation, CommentValidation } from "@shared/validations"; @@ -156,7 +155,7 @@ function CommentForm({ comments ); - comment.id = uuidv4(); + comment.id = crypto.randomUUID(); comments.add(comment); comment diff --git a/app/scenes/Search/Search.tsx b/app/scenes/Search/Search.tsx index 3d8f2ff32f..6f15941de6 100644 --- a/app/scenes/Search/Search.tsx +++ b/app/scenes/Search/Search.tsx @@ -6,7 +6,6 @@ import { useHistory, useLocation, useRouteMatch } from "react-router-dom"; import { Waypoint } from "react-waypoint"; import styled from "styled-components"; import breakpoint from "styled-components-breakpoint"; -import { v4 as uuidv4 } from "uuid"; import { Pagination } from "@shared/constants"; import { hideScrollbars } from "@shared/styles"; import { @@ -105,7 +104,7 @@ function Search() { // without a flash of loading. if (query) { searches.add({ - id: uuidv4(), + id: crypto.randomUUID(), query, createdAt: new Date().toISOString(), }); diff --git a/app/stores/DialogsStore.ts b/app/stores/DialogsStore.ts index 261c4894ca..6007d1b55e 100644 --- a/app/stores/DialogsStore.ts +++ b/app/stores/DialogsStore.ts @@ -1,6 +1,5 @@ import { observable, action } from "mobx"; import * as React from "react"; -import { v4 as uuidv4 } from "uuid"; type DialogDefinition = { title: string; @@ -66,7 +65,7 @@ export default class DialogsStore { this.modalStack.clear(); } - this.modalStack.set(id ?? replaceId ?? uuidv4(), { + this.modalStack.set(id ?? replaceId ?? crypto.randomUUID(), { title, content, style, diff --git a/package.json b/package.json index 2cbb0005d7..367f725705 100644 --- a/package.json +++ b/package.json @@ -262,7 +262,6 @@ "ukkonen": "^2.2.0", "umzug": "^3.8.2", "utility-types": "^3.11.0", - "uuid": "^8.3.2", "validator": "13.15.15", "vaul": "^1.1.2", "vite": "npm:rolldown-vite@latest", diff --git a/server/commands/accountProvisioner.test.ts b/server/commands/accountProvisioner.test.ts index ede6f84d98..0a11d0d2f6 100644 --- a/server/commands/accountProvisioner.test.ts +++ b/server/commands/accountProvisioner.test.ts @@ -1,5 +1,5 @@ import { faker } from "@faker-js/faker"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import WelcomeEmail from "@server/emails/templates/WelcomeEmail"; import { TeamDomain } from "@server/models"; import Collection from "@server/models/Collection"; @@ -35,7 +35,7 @@ describe("accountProvisioner", () => { providerId: faker.internet.domainName(), }, authentication: { - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, @@ -137,7 +137,7 @@ describe("accountProvisioner", () => { providerId: authenticationProvider.providerId, }, authentication: { - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, @@ -271,7 +271,7 @@ describe("accountProvisioner", () => { providerId: authenticationProvider.providerId, }, authentication: { - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, @@ -313,7 +313,7 @@ describe("accountProvisioner", () => { providerId: authenticationProvider.providerId, }, authentication: { - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, @@ -361,7 +361,7 @@ describe("accountProvisioner", () => { providerId: authenticationProvider.providerId, }, authentication: { - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, @@ -405,7 +405,7 @@ describe("accountProvisioner", () => { providerId: faker.internet.domainName(), }, authentication: { - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, @@ -458,7 +458,7 @@ describe("accountProvisioner", () => { providerId: faker.internet.domainName(), }, authentication: { - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, @@ -491,7 +491,7 @@ describe("accountProvisioner", () => { providerId: domain, }, authentication: { - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, diff --git a/server/commands/attachmentCreator.ts b/server/commands/attachmentCreator.ts index 2cff019a20..866fc6ca9b 100644 --- a/server/commands/attachmentCreator.ts +++ b/server/commands/attachmentCreator.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { AttachmentPreset } from "@shared/types"; import { Attachment, User } from "@server/models"; import AttachmentHelper from "@server/models/helpers/AttachmentHelper"; @@ -47,7 +47,7 @@ export default async function attachmentCreator({ const acl = AttachmentHelper.presetToAcl(preset); const key = AttachmentHelper.getKey({ acl, - id: uuidv4(), + id: randomUUID(), name, userId: user.id, }); diff --git a/server/commands/collectionExporter.ts b/server/commands/collectionExporter.ts index c431c82bb0..2091d1e68b 100644 --- a/server/commands/collectionExporter.ts +++ b/server/commands/collectionExporter.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { FileOperationFormat, FileOperationType, @@ -25,7 +25,7 @@ function getKeyForFileOp( ) { return `${ Buckets.uploads - }/${teamId}/${uuidv4()}/${name}-export.${format.replace(/outline-/, "")}.zip`; + }/${teamId}/${randomUUID()}/${name}-export.${format.replace(/outline-/, "")}.zip`; } async function collectionExporter({ diff --git a/server/commands/userProvisioner.test.ts b/server/commands/userProvisioner.test.ts index b1f91f221b..05c731186e 100644 --- a/server/commands/userProvisioner.test.ts +++ b/server/commands/userProvisioner.test.ts @@ -1,5 +1,5 @@ import { faker } from "@faker-js/faker"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { UserRole } from "@shared/types"; import { TeamDomain } from "@server/models"; import { @@ -60,7 +60,7 @@ describe("userProvisioner", () => { teamId: existing.teamId, authentication: { authenticationProviderId: authenticationProvider.id, - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, @@ -94,7 +94,7 @@ describe("userProvisioner", () => { teamId: existing.teamId, authentication: { authenticationProviderId: authenticationProvider.id, - providerId: uuidv4(), + providerId: randomUUID(), accessToken: "123", scopes: ["read"], }, @@ -148,7 +148,7 @@ describe("userProvisioner", () => { email: "test@example.com", teamId: existing.teamId, authentication: { - authenticationProviderId: uuidv4(), + authenticationProviderId: randomUUID(), providerId: existingAuth.providerId, accessToken: "123", scopes: ["read"], diff --git a/server/models/Collection.test.ts b/server/models/Collection.test.ts index afdb2d8708..eceb65e5e6 100644 --- a/server/models/Collection.test.ts +++ b/server/models/Collection.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { randomString } from "@shared/random"; import slugify from "@shared/utils/slugify"; import { @@ -101,7 +101,7 @@ describe("getDocumentTree", () => { describe("#addDocumentToStructure", () => { it("should add as last element without index", async () => { const collection = await buildCollection(); - const id = uuidv4(); + const id = randomUUID(); const newDocument = await buildDocument({ id, title: "New end node", @@ -119,7 +119,7 @@ describe("#addDocumentToStructure", () => { it("should add with an index", async () => { const collection = await buildCollection(); - const id = uuidv4(); + const id = randomUUID(); const newDocument = await buildDocument({ id, title: "New end node", @@ -136,7 +136,7 @@ describe("#addDocumentToStructure", () => { const document = await buildDocument({ collectionId: collection.id }); await collection.reload(); - const id = uuidv4(); + const id = randomUUID(); const newDocument = await buildDocument({ id, title: "New end node", @@ -156,12 +156,12 @@ describe("#addDocumentToStructure", () => { await collection.reload(); const newDocument = await buildDocument({ - id: uuidv4(), + id: randomUUID(), title: "node", parentDocumentId: document.id, teamId: collection.teamId, }); - const id = uuidv4(); + const id = randomUUID(); const secondDocument = await buildDocument({ id, title: "New start node", @@ -239,9 +239,9 @@ describe("#addDocumentToStructure", () => { describe("options: documentJson", () => { it("should append supplied json over document's own", async () => { const collection = await buildCollection(); - const id = uuidv4(); + const id = randomUUID(); const newDocument = await buildDocument({ - id: uuidv4(), + id: randomUUID(), title: "New end node", parentDocumentId: null, teamId: collection.teamId, diff --git a/server/models/base/Model.test.ts b/server/models/base/Model.test.ts index 884e9f88b8..dae789d483 100644 --- a/server/models/base/Model.test.ts +++ b/server/models/base/Model.test.ts @@ -1,5 +1,5 @@ import { faker } from "@faker-js/faker"; -import { v4 as uuid } from "uuid"; +import { randomUUID } from "crypto"; import { TeamPreference } from "@shared/types"; import { buildDocument, buildTeam } from "@server/test/factories"; import User from "../User"; @@ -40,7 +40,7 @@ describe("Model", () => { }); it("should return full array if value changed", async () => { - const collaboratorId = uuid(); + const collaboratorId = randomUUID(); const document = await buildDocument(); const prev = document.collaboratorIds; diff --git a/server/models/helpers/SubscriptionHelper.test.ts b/server/models/helpers/SubscriptionHelper.test.ts index a91f18f4d4..6f5f283d81 100644 --- a/server/models/helpers/SubscriptionHelper.test.ts +++ b/server/models/helpers/SubscriptionHelper.test.ts @@ -1,12 +1,12 @@ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import env from "@server/env"; import SubscriptionHelper from "./SubscriptionHelper"; describe("SubscriptionHelper", () => { describe("unsubscribeUrl", () => { it("should return a valid unsubscribe URL", () => { - const userId = uuidv4(); - const documentId = uuidv4(); + const userId = randomUUID(); + const documentId = randomUUID(); const unsubscribeUrl = SubscriptionHelper.unsubscribeUrl( userId, diff --git a/server/queues/processors/ImportsProcessor.ts b/server/queues/processors/ImportsProcessor.ts index 404cc99d00..3e72199eeb 100644 --- a/server/queues/processors/ImportsProcessor.ts +++ b/server/queues/processors/ImportsProcessor.ts @@ -9,7 +9,7 @@ import { Transaction, UniqueConstraintError, } from "sequelize"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { randomElement } from "@shared/random"; import { ImportInput, ImportTaskInput } from "@shared/schema"; import { @@ -514,7 +514,7 @@ export default abstract class ImportsProcessor< const json = node.toJSON() as ProsemirrorData; const attrs = json.attrs ?? {}; - attrs.id = uuidv4(); + attrs.id = randomUUID(); attrs.actorId = actorId; const externalId = attrs.modelId as string; @@ -597,7 +597,7 @@ export default abstract class ImportsProcessor< } } - idMap[externalId] = internalId ?? uuidv4(); + idMap[externalId] = internalId ?? randomUUID(); return idMap[externalId]; } diff --git a/server/queues/tasks/APIImportTask.ts b/server/queues/tasks/APIImportTask.ts index fdaabb054a..7294aedec0 100644 --- a/server/queues/tasks/APIImportTask.ts +++ b/server/queues/tasks/APIImportTask.ts @@ -4,7 +4,7 @@ import truncate from "lodash/truncate"; import uniqBy from "lodash/uniqBy"; import { Fragment, Node } from "prosemirror-model"; import { Transaction, WhereOptions } from "sequelize"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { ImportTaskInput, ImportTaskOutput } from "@shared/schema"; import { AttachmentPreset, @@ -290,7 +290,7 @@ export default abstract class APIImportTask< await sequelize.transaction(async (transaction) => { const dbPromises = attachmentsData.map(async (item) => { - const modelId = uuidv4(); + const modelId = randomUUID(); const acl = AttachmentHelper.presetToAcl( AttachmentPreset.DocumentAttachment ); diff --git a/server/queues/tasks/ImportJSONTask.ts b/server/queues/tasks/ImportJSONTask.ts index 878d7e67f4..e67a609687 100644 --- a/server/queues/tasks/ImportJSONTask.ts +++ b/server/queues/tasks/ImportJSONTask.ts @@ -3,7 +3,7 @@ import fs from "fs-extra"; import find from "lodash/find"; import mime from "mime-types"; import { Fragment, Node } from "prosemirror-model"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { ProsemirrorData } from "@shared/types"; import { schema, serializer } from "@server/editor"; import Logger from "@server/logging/Logger"; @@ -72,7 +72,7 @@ export default class ImportJSONTask extends ImportTask { collectionId: string ) { Object.values(documents).forEach((node) => { - const id = uuidv4(); + const id = randomUUID(); output.documents.push({ ...node, path: "", @@ -101,7 +101,7 @@ export default class ImportJSONTask extends ImportTask { [id: string]: AttachmentJSONExport; }) { Object.values(attachments).forEach((node) => { - const id = uuidv4(); + const id = randomUUID(); const mimeType = mime.lookup(node.key) || "application/octet-stream"; output.attachments.push({ @@ -128,7 +128,7 @@ export default class ImportJSONTask extends ImportTask { throw new Error(`Could not parse ${node.path}. ${err.message}`); } - const collectionId = uuidv4(); + const collectionId = randomUUID(); output.collections.push({ ...item.collection, diff --git a/server/queues/tasks/ImportMarkdownZipTask.ts b/server/queues/tasks/ImportMarkdownZipTask.ts index 8155decd6e..677830417a 100644 --- a/server/queues/tasks/ImportMarkdownZipTask.ts +++ b/server/queues/tasks/ImportMarkdownZipTask.ts @@ -2,7 +2,7 @@ import path from "path"; import fs from "fs-extra"; import escapeRegExp from "lodash/escapeRegExp"; import mime from "mime-types"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import documentImporter from "@server/commands/documentImporter"; import { createContext } from "@server/context"; import Logger from "@server/logging/Logger"; @@ -66,7 +66,7 @@ export default class ImportMarkdownZipTask extends ImportTask { return parseNodeChildren(child.children, collectionId); } - const id = uuidv4(); + const id = randomUUID(); // this is an attachment if ( @@ -144,7 +144,7 @@ export default class ImportMarkdownZipTask extends ImportTask { // All nodes in the root level should be collections for (const node of tree) { if (node.children.length > 0) { - const collectionId = uuidv4(); + const collectionId = randomUUID(); output.collections.push({ id: collectionId, name: node.title, diff --git a/server/queues/tasks/UploadTeamAvatarTask.ts b/server/queues/tasks/UploadTeamAvatarTask.ts index 76f67630ea..d8f8e0115c 100644 --- a/server/queues/tasks/UploadTeamAvatarTask.ts +++ b/server/queues/tasks/UploadTeamAvatarTask.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { Team } from "@server/models"; import { Buckets } from "@server/models/helpers/AttachmentHelper"; import FileStorage from "@server/storage/files"; @@ -23,7 +23,7 @@ export default class UploadTeamAvatarTask extends BaseTask { const res = await FileStorage.storeFromUrl( props.avatarUrl, - `${Buckets.avatars}/${team.id}/${uuidv4()}`, + `${Buckets.avatars}/${team.id}/${randomUUID()}`, "public-read" ); diff --git a/server/queues/tasks/UploadUserAvatarTask.ts b/server/queues/tasks/UploadUserAvatarTask.ts index 8731bb9935..64bf91cfc0 100644 --- a/server/queues/tasks/UploadUserAvatarTask.ts +++ b/server/queues/tasks/UploadUserAvatarTask.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { User } from "@server/models"; import { Buckets } from "@server/models/helpers/AttachmentHelper"; import FileStorage from "@server/storage/files"; @@ -23,7 +23,7 @@ export default class UploadUserAvatarTask extends BaseTask { const res = await FileStorage.storeFromUrl( props.avatarUrl, - `${Buckets.avatars}/${user.id}/${uuidv4()}`, + `${Buckets.avatars}/${user.id}/${randomUUID()}`, "public-read" ); diff --git a/server/routes/api/attachments/attachments.ts b/server/routes/api/attachments/attachments.ts index 58da112080..2175b2ecc8 100644 --- a/server/routes/api/attachments/attachments.ts +++ b/server/routes/api/attachments/attachments.ts @@ -1,6 +1,6 @@ import Router from "koa-router"; import { WhereOptions } from "sequelize"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { AttachmentPreset } from "@shared/types"; import { bytesToHumanReadable, getFileNameFromUrl } from "@shared/utils/files"; import { AttachmentValidation } from "@shared/validations"; @@ -113,7 +113,7 @@ router.post( ); } - const modelId = uuidv4(); + const modelId = randomUUID(); const acl = AttachmentHelper.presetToAcl(preset); const key = AttachmentHelper.getKey({ acl, @@ -185,7 +185,7 @@ router.post( authorize(user, "update", document); const name = getFileNameFromUrl(url) ?? "file"; - const modelId = uuidv4(); + const modelId = randomUUID(); const acl = AttachmentHelper.presetToAcl(preset); const key = AttachmentHelper.getKey({ acl, diff --git a/server/routes/api/auth/auth.test.ts b/server/routes/api/auth/auth.test.ts index 4220fc3ba2..e37fcdf482 100644 --- a/server/routes/api/auth/auth.test.ts +++ b/server/routes/api/auth/auth.test.ts @@ -1,9 +1,9 @@ import { faker } from "@faker-js/faker"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { buildUser, buildTeam } from "@server/test/factories"; import { getTestServer, setSelfHosted } from "@server/test/support"; -const mockTeamInSessionId = uuidv4(); +const mockTeamInSessionId = randomUUID(); jest.mock("@server/utils/authentication", () => ({ getSessionsInCookie() { @@ -107,7 +107,7 @@ describe("#auth.config", () => { authenticationProviders: [ { name: "slack", - providerId: uuidv4(), + providerId: randomUUID(), }, ], }); @@ -130,7 +130,7 @@ describe("#auth.config", () => { authenticationProviders: [ { name: "slack", - providerId: uuidv4(), + providerId: randomUUID(), }, ], }); @@ -153,7 +153,7 @@ describe("#auth.config", () => { authenticationProviders: [ { name: "slack", - providerId: uuidv4(), + providerId: randomUUID(), }, ], }); @@ -177,7 +177,7 @@ describe("#auth.config", () => { authenticationProviders: [ { name: "slack", - providerId: uuidv4(), + providerId: randomUUID(), enabled: false, }, ], @@ -201,7 +201,7 @@ describe("#auth.config", () => { authenticationProviders: [ { name: "slack", - providerId: uuidv4(), + providerId: randomUUID(), }, ], }); @@ -220,7 +220,7 @@ describe("#auth.config", () => { authenticationProviders: [ { name: "slack", - providerId: uuidv4(), + providerId: randomUUID(), }, ], }); diff --git a/server/routes/api/authenticationProviders/authenticationProviders.test.ts b/server/routes/api/authenticationProviders/authenticationProviders.test.ts index 8771b1e690..7561c66d18 100644 --- a/server/routes/api/authenticationProviders/authenticationProviders.test.ts +++ b/server/routes/api/authenticationProviders/authenticationProviders.test.ts @@ -1,4 +1,4 @@ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { buildUser, buildAdmin, buildTeam } from "@server/test/factories"; import { getTestServer, setSelfHosted } from "@server/test/support"; @@ -77,7 +77,7 @@ describe("#authenticationProviders.update", () => { }); const googleProvider = await team.$create("authenticationProvider", { name: "google", - providerId: uuidv4(), + providerId: randomUUID(), }); const res = await server.post("/api/authenticationProviders.update", { body: { diff --git a/server/routes/api/documents/documents.ts b/server/routes/api/documents/documents.ts index ca190eeaf0..bf3f7af374 100644 --- a/server/routes/api/documents/documents.ts +++ b/server/routes/api/documents/documents.ts @@ -12,7 +12,7 @@ import remove from "lodash/remove"; import uniq from "lodash/uniq"; import mime from "mime-types"; import { Op, ScopeOptions, Sequelize, WhereOptions } from "sequelize"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { NavigationNode, StatusFilter, UserRole } from "@shared/types"; import { subtractDate } from "@shared/utils/date"; import slugify from "@shared/utils/slugify"; @@ -1607,7 +1607,7 @@ router.post( const key = AttachmentHelper.getKey({ acl, - id: uuidv4(), + id: randomUUID(), name: fileName, userId: user.id, }); diff --git a/server/routes/api/notifications/notifications.test.ts b/server/routes/api/notifications/notifications.test.ts index 1b353e4ce6..c275434a6c 100644 --- a/server/routes/api/notifications/notifications.test.ts +++ b/server/routes/api/notifications/notifications.test.ts @@ -1,5 +1,5 @@ import queryString from "query-string"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { randomElement } from "@shared/random"; import { NotificationEventType } from "@shared/types"; import NotificationSettingsHelper from "@server/models/helpers/NotificationSettingsHelper"; @@ -335,7 +335,7 @@ describe("#notifications.pixel", () => { it("should return 404 for notification that does not exist", async () => { const res = await server.get( `/api/notifications.pixel?${queryString.stringify({ - id: uuidv4(), + id: randomUUID(), token: "invalid-token", })}` ); diff --git a/server/test/factories.ts b/server/test/factories.ts index 102dfc011b..886b0ef690 100644 --- a/server/test/factories.ts +++ b/server/test/factories.ts @@ -4,7 +4,7 @@ import isNull from "lodash/isNull"; import { Node } from "prosemirror-model"; import { InferCreationAttributes } from "sequelize"; import { DeepPartial } from "utility-types"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { randomString } from "@shared/random"; import { CollectionPermission, @@ -282,7 +282,7 @@ export async function buildIntegration(overrides: Partial = {}) { type: IntegrationType.Post, events: ["documents.update", "documents.publish"], settings: { - serviceTeamId: uuidv4(), + serviceTeamId: randomUUID(), }, authenticationId: authentication.id, ...overrides, @@ -559,7 +559,7 @@ export async function buildAttachment( overrides.documentId = document.id; } - const id = uuidv4(); + const id = randomUUID(); const acl = overrides.acl || "public-read"; const name = fileName || faker.system.fileName(); return Attachment.create({ diff --git a/server/utils/oauth/OAuthInterface.test.ts b/server/utils/oauth/OAuthInterface.test.ts index 9d92eaaced..c03a514257 100644 --- a/server/utils/oauth/OAuthInterface.test.ts +++ b/server/utils/oauth/OAuthInterface.test.ts @@ -1,4 +1,4 @@ -import { v4 } from "uuid"; +import { randomUUID } from "crypto"; import { Scope } from "@shared/types"; import { OAuthInterface } from "./OAuthInterface"; import { @@ -9,10 +9,10 @@ import { describe("OAuthInterface", () => { const user = { - id: v4(), + id: randomUUID(), }; const client = { - id: v4(), + id: randomUUID(), grants: ["authorization_code", "refresh_token"], redirectUris: ["https://example.com/callback"], }; diff --git a/server/utils/parseAttachmentIds.test.ts b/server/utils/parseAttachmentIds.test.ts index 041c780901..ab2d38956a 100644 --- a/server/utils/parseAttachmentIds.test.ts +++ b/server/utils/parseAttachmentIds.test.ts @@ -1,5 +1,5 @@ import { expect } from "@jest/globals"; -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import env from "@server/env"; import parseAttachmentIds from "./parseAttachmentIds"; @@ -8,7 +8,7 @@ it("should return an empty array with no matches", () => { }); it("should not return orphaned UUID's", () => { - const uuid = uuidv4(); + const uuid = randomUUID(); expect( parseAttachmentIds(`some random text with a uuid ${uuid} @@ -17,7 +17,7 @@ it("should not return orphaned UUID's", () => { }); it("should parse attachment ID from markdown", () => { - const uuid = uuidv4(); + const uuid = randomUUID(); const results = parseAttachmentIds( `![caption text](/api/attachments.redirect?id=${uuid})` ); @@ -26,7 +26,7 @@ it("should parse attachment ID from markdown", () => { }); it("should parse attachment ID from markdown with additional query params", () => { - const uuid = uuidv4(); + const uuid = randomUUID(); const results = parseAttachmentIds( `![caption text](/api/attachments.redirect?id=${uuid}&size=2)` ); @@ -35,7 +35,7 @@ it("should parse attachment ID from markdown with additional query params", () = }); it("should parse attachment ID from markdown with fully qualified url", () => { - const uuid = uuidv4(); + const uuid = randomUUID(); const results = parseAttachmentIds( `![caption text](${env.URL}/api/attachments.redirect?id=${uuid})` ); @@ -44,7 +44,7 @@ it("should parse attachment ID from markdown with fully qualified url", () => { }); it("should parse attachment ID from markdown with title", () => { - const uuid = uuidv4(); + const uuid = randomUUID(); const results = parseAttachmentIds( `![caption text](/api/attachments.redirect?id=${uuid} "align-left")` ); @@ -53,8 +53,8 @@ it("should parse attachment ID from markdown with title", () => { }); it("should parse multiple attachment IDs from markdown", () => { - const uuid = uuidv4(); - const uuid2 = uuidv4(); + const uuid = randomUUID(); + const uuid2 = randomUUID(); const results = parseAttachmentIds(`![caption text](/api/attachments.redirect?id=${uuid}) @@ -67,7 +67,7 @@ some text }); it("should parse attachment ID from html", () => { - const uuid = uuidv4(); + const uuid = randomUUID(); const results = parseAttachmentIds( `` ); @@ -76,7 +76,7 @@ it("should parse attachment ID from html", () => { }); it("should parse attachment ID from html with fully qualified url", () => { - const uuid = uuidv4(); + const uuid = randomUUID(); const results = parseAttachmentIds( `` ); diff --git a/server/validation.test.ts b/server/validation.test.ts index ff45b4bbd4..aab1d68f79 100644 --- a/server/validation.test.ts +++ b/server/validation.test.ts @@ -1,65 +1,65 @@ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { Buckets } from "./models/helpers/AttachmentHelper"; import { ValidateKey } from "./validation"; describe("#ValidateKey.isValid", () => { it("should return false if number of key components are not equal to 4", () => { expect( - ValidateKey.isValid(`${Buckets.uploads}/${uuidv4()}/${uuidv4()}`) + ValidateKey.isValid(`${Buckets.uploads}/${randomUUID()}/${randomUUID()}`) ).toBe(false); expect( - ValidateKey.isValid(`${Buckets.uploads}/${uuidv4()}/${uuidv4()}/foo/bar`) + ValidateKey.isValid(`${Buckets.uploads}/${randomUUID()}/${randomUUID()}/foo/bar`) ).toBe(false); }); it("should return false if the first key component is not a valid bucket", () => { - expect(ValidateKey.isValid(`foo/${uuidv4()}/${uuidv4()}/bar.png`)).toBe( + expect(ValidateKey.isValid(`foo/${randomUUID()}/${randomUUID()}/bar.png`)).toBe( false ); }); it("should return false if second and third key components are not UUID", () => { expect( - ValidateKey.isValid(`${Buckets.uploads}/foo/${uuidv4()}/bar.png`) + ValidateKey.isValid(`${Buckets.uploads}/foo/${randomUUID()}/bar.png`) ).toBe(false); expect( - ValidateKey.isValid(`${Buckets.uploads}/${uuidv4()}/foo/bar.png`) + ValidateKey.isValid(`${Buckets.uploads}/${randomUUID()}/foo/bar.png`) ).toBe(false); }); it("should return true successfully validating key", () => { expect( - ValidateKey.isValid(`${Buckets.public}/${uuidv4()}/${uuidv4()}/foo.png`) + ValidateKey.isValid(`${Buckets.public}/${randomUUID()}/${randomUUID()}/foo.png`) ).toBe(true); expect( - ValidateKey.isValid(`${Buckets.uploads}/${uuidv4()}/${uuidv4()}/foo.png`) + ValidateKey.isValid(`${Buckets.uploads}/${randomUUID()}/${randomUUID()}/foo.png`) ).toBe(true); expect( - ValidateKey.isValid(`${Buckets.avatars}/${uuidv4()}/${uuidv4()}`) + ValidateKey.isValid(`${Buckets.avatars}/${randomUUID()}/${randomUUID()}`) ).toBe(true); }); }); describe("#ValidateKey.sanitize", () => { it("should sanitize malicious looking keys", () => { - const uuid1 = uuidv4(); - const uuid2 = uuidv4(); + const uuid1 = randomUUID(); + const uuid2 = randomUUID(); expect( ValidateKey.sanitize(`public/${uuid1}/${uuid2}/~\.\u0000\malicious_key`) ).toEqual(`public/${uuid1}/${uuid2}/~.malicious_key`); }); it("should remove potential path traversal", () => { - const uuid1 = uuidv4(); - const uuid2 = uuidv4(); + const uuid1 = randomUUID(); + const uuid2 = randomUUID(); expect( ValidateKey.sanitize(`public/${uuid1}/${uuid2}/../../malicious_key`) ).toEqual(`public/${uuid1}/${uuid2}/malicious_key`); }); it("should remove problematic characters", () => { - const uuid1 = uuidv4(); - const uuid2 = uuidv4(); + const uuid1 = randomUUID(); + const uuid2 = randomUUID(); expect(ValidateKey.sanitize(`public/${uuid1}/${uuid2}/test#:*?`)).toEqual( `public/${uuid1}/${uuid2}/test` );