fix: replace uuid package with standard module (#10318)

This commit is contained in:
Alex
2025-10-11 00:06:51 +03:00
committed by GitHub
parent ae8c2aae15
commit 76701e35ec
30 changed files with 112 additions and 120 deletions

View File

@@ -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, "id">): 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",

View File

@@ -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,
},

View File

@@ -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: <EmailIcon />,
visible: !!mentionType,
attrs: {
id: v4(),
id: crypto.randomUUID(),
type: mentionType,
label: pastedText,
href: pastedText,
modelId: v4(),
modelId: crypto.randomUUID(),
actorId: user?.id,
},
appendSpace: true,

View File

@@ -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(),
})
)
);

View File

@@ -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

View File

@@ -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(),
});

View File

@@ -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,

View File

@@ -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",

View File

@@ -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"],
},

View File

@@ -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,
});

View File

@@ -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({

View File

@@ -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"],

View File

@@ -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,

View File

@@ -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;

View File

@@ -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,

View File

@@ -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];
}

View File

@@ -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
);

View File

@@ -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,

View File

@@ -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,

View File

@@ -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<Props> {
const res = await FileStorage.storeFromUrl(
props.avatarUrl,
`${Buckets.avatars}/${team.id}/${uuidv4()}`,
`${Buckets.avatars}/${team.id}/${randomUUID()}`,
"public-read"
);

View File

@@ -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<Props> {
const res = await FileStorage.storeFromUrl(
props.avatarUrl,
`${Buckets.avatars}/${user.id}/${uuidv4()}`,
`${Buckets.avatars}/${user.id}/${randomUUID()}`,
"public-read"
);

View File

@@ -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,

View File

@@ -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(),
},
],
});

View File

@@ -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: {

View File

@@ -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,
});

View File

@@ -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",
})}`
);

View File

@@ -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<Integration> = {}) {
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({

View File

@@ -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"],
};

View File

@@ -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(
`<img src="/api/attachments.redirect?id=${uuid}" />`
);
@@ -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(
`<img src="${env.URL}/api/attachments.redirect?id=${uuid}" />`
);

View File

@@ -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`
);