From 323893a5aedcdf7d69ef4e62f36c74b487b0e5af Mon Sep 17 00:00:00 2001 From: Daniel Cousens <413395+dcousens@users.noreply.github.com> Date: Wed, 20 Sep 2023 17:23:14 +1000 Subject: [PATCH 1/2] use 128-bit random base64url identifiers for images & files, not uuid --- .../core/src/lib/assets/createFilesContext.ts | 16 ++++----- .../src/lib/assets/createImagesContext.ts | 10 ++++-- pnpm-lock.yaml | 35 ++----------------- tests/api-tests/package.json | 2 +- 4 files changed, 18 insertions(+), 45 deletions(-) diff --git a/packages/core/src/lib/assets/createFilesContext.ts b/packages/core/src/lib/assets/createFilesContext.ts index eff8d887799..224d6a21ac9 100644 --- a/packages/core/src/lib/assets/createFilesContext.ts +++ b/packages/core/src/lib/assets/createFilesContext.ts @@ -1,20 +1,18 @@ -import crypto from 'crypto' +import { randomBytes } from 'node:crypto' import type { KeystoneConfig, FilesContext } from '../../types' import { localFileAssetsAPI } from './local' import { s3FileAssetsAPI } from './s3' import type { FileAdapter } from './types' -const defaultTransformName = (path: string) => { - // Appends a UUID to the filename so that people can't brute-force guess stored filenames - // - // This regex lazily matches for any characters that aren't a new line +// appends a 128-bit random identifier to the filename to prevent guessing +function defaultTransformName (path: string) { + // this regex lazily matches for any characters that aren't a new line // it then optionally matches the last instance of a "." symbol // followed by any alphanumerical character before the end of the string const [, name, ext] = path.match(/^([^:\n].*?)(\.[A-Za-z0-9]{0,10})?$/) as RegExpMatchArray - const id = crypto.randomBytes(12).toString('base64url').slice(0, 12) - + const id = randomBytes(16).toString('base64url') const urlSafeName = name.replace(/[^A-Za-z0-9]/g, '-') if (ext) return `${urlSafeName}-${id}${ext}` return `${urlSafeName}-${id}` @@ -46,11 +44,11 @@ export function createFilesContext (config: KeystoneConfig): FilesContext { }, getDataFromStream: async (stream, originalFilename) => { const storageConfig = config.storage![storageString] - const { transformName = defaultTransformName } = storageConfig as typeof storageConfig & { + const { transformName = defaultTransformName } = storageConfig as (typeof storageConfig) & { type: 'file' } - const filename = await transformName(originalFilename) + const filename = await transformName(originalFilename) const { filesize } = await adapter.upload(stream, filename) return { filename, filesize } }, diff --git a/packages/core/src/lib/assets/createImagesContext.ts b/packages/core/src/lib/assets/createImagesContext.ts index b8da9e8e7b0..86768d01f0b 100644 --- a/packages/core/src/lib/assets/createImagesContext.ts +++ b/packages/core/src/lib/assets/createImagesContext.ts @@ -1,11 +1,16 @@ -import { v4 as uuid } from 'uuid' +import { randomBytes } from 'node:crypto' import imageSize from 'image-size' + import type { KeystoneConfig, ImagesContext } from '../../types' import type { ImageAdapter } from './types' import { localImageAssetsAPI } from './local' import { s3ImageAssetsAPI } from './s3' import { streamToBuffer } from './utils' +function defaultTransformName (path: string) { + return randomBytes(16).toString('base64url') +} + async function getImageMetadataFromBuffer (buffer: Buffer) { const fileType = await (await import('file-type')).fileTypeFromBuffer(buffer) if (!fileType) { @@ -50,13 +55,12 @@ export function createImagesContext (config: KeystoneConfig): ImagesContext { }, getDataFromStream: async (stream, originalFilename) => { const storageConfig = config.storage![storageString] - const { transformName = () => uuid() } = storageConfig + const { transformName = defaultTransformName } = storageConfig const buffer = await streamToBuffer(stream) const { extension, ...rest } = await getImageMetadataFromBuffer(buffer) const id = await transformName(originalFilename, extension) - await adapter.upload(buffer, id, extension) return { id, extension, ...rest } }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8222cbf49a5..305e3c40b67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2301,8 +2301,8 @@ importers: specifier: ^2.0.11 version: 2.0.16 '@types/uuid': - specifier: ^8.3.1 - version: 8.3.4 + specifier: ^9.0.0 + version: 9.0.8 express: specifier: ^4.17.1 version: 4.19.2 @@ -5638,7 +5638,6 @@ packages: cpu: [ppc64] os: [aix] requiresBuild: true - dev: false optional: true /@esbuild/aix-ppc64@0.20.2: @@ -5681,7 +5680,6 @@ packages: cpu: [arm64] os: [android] requiresBuild: true - dev: false optional: true /@esbuild/android-arm64@0.20.2: @@ -5724,7 +5722,6 @@ packages: cpu: [arm] os: [android] requiresBuild: true - dev: false optional: true /@esbuild/android-arm@0.20.2: @@ -5767,7 +5764,6 @@ packages: cpu: [x64] os: [android] requiresBuild: true - dev: false optional: true /@esbuild/android-x64@0.20.2: @@ -5810,7 +5806,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: false optional: true /@esbuild/darwin-arm64@0.20.2: @@ -5853,7 +5848,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: false optional: true /@esbuild/darwin-x64@0.20.2: @@ -5896,7 +5890,6 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true - dev: false optional: true /@esbuild/freebsd-arm64@0.20.2: @@ -5939,7 +5932,6 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true - dev: false optional: true /@esbuild/freebsd-x64@0.20.2: @@ -5982,7 +5974,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: false optional: true /@esbuild/linux-arm64@0.20.2: @@ -6025,7 +6016,6 @@ packages: cpu: [arm] os: [linux] requiresBuild: true - dev: false optional: true /@esbuild/linux-arm@0.20.2: @@ -6068,7 +6058,6 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true - dev: false optional: true /@esbuild/linux-ia32@0.20.2: @@ -6111,7 +6100,6 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true - dev: false optional: true /@esbuild/linux-loong64@0.20.2: @@ -6154,7 +6142,6 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true - dev: false optional: true /@esbuild/linux-mips64el@0.20.2: @@ -6197,7 +6184,6 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true - dev: false optional: true /@esbuild/linux-ppc64@0.20.2: @@ -6240,7 +6226,6 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true - dev: false optional: true /@esbuild/linux-riscv64@0.20.2: @@ -6283,7 +6268,6 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true - dev: false optional: true /@esbuild/linux-s390x@0.20.2: @@ -6326,7 +6310,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: false optional: true /@esbuild/linux-x64@0.20.2: @@ -6369,7 +6352,6 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true - dev: false optional: true /@esbuild/netbsd-x64@0.20.2: @@ -6412,7 +6394,6 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true - dev: false optional: true /@esbuild/openbsd-x64@0.20.2: @@ -6455,7 +6436,6 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true - dev: false optional: true /@esbuild/sunos-x64@0.20.2: @@ -6498,7 +6478,6 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true - dev: false optional: true /@esbuild/win32-arm64@0.20.2: @@ -6541,7 +6520,6 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true - dev: false optional: true /@esbuild/win32-ia32@0.20.2: @@ -6584,7 +6562,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: false optional: true /@esbuild/win32-x64@0.20.2: @@ -9518,13 +9495,8 @@ packages: resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} dev: false - /@types/uuid@8.3.4: - resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - dev: false - /@types/uuid@9.0.8: resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - dev: true /@types/webidl-conversions@7.0.3: resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -9655,7 +9627,7 @@ packages: '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.3) '@vanilla-extract/babel-plugin-debug-ids': 1.0.5 '@vanilla-extract/css': 1.14.2 - esbuild: 0.17.6 + esbuild: 0.19.12 eval: 0.1.8 find-up: 5.0.0 javascript-stringify: 2.1.0 @@ -12345,7 +12317,6 @@ packages: '@esbuild/win32-arm64': 0.19.12 '@esbuild/win32-ia32': 0.19.12 '@esbuild/win32-x64': 0.19.12 - dev: false /esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} diff --git a/tests/api-tests/package.json b/tests/api-tests/package.json index 6caff12d6f5..45a1c156f90 100644 --- a/tests/api-tests/package.json +++ b/tests/api-tests/package.json @@ -35,7 +35,7 @@ "@types/mime": "^2.0.3", "@types/superagent": "^4.1.15", "@types/supertest": "^2.0.11", - "@types/uuid": "^8.3.1", + "@types/uuid": "^9.0.0", "express": "^4.17.1" } } From f6ba6649864be395a4fc19d96ba278392dee58c1 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <413395+dcousens@users.noreply.github.com> Date: Tue, 2 Apr 2024 14:06:59 +1100 Subject: [PATCH 2/2] add changeset --- .changeset/use-128-bit-images-files.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/use-128-bit-images-files.md diff --git a/.changeset/use-128-bit-images-files.md b/.changeset/use-128-bit-images-files.md new file mode 100644 index 00000000000..c9134e77975 --- /dev/null +++ b/.changeset/use-128-bit-images-files.md @@ -0,0 +1,5 @@ +--- +'@keystone-6/core': major +--- + +Changes `file` and `image` to use random 128-bit `base64url` identifiers by default, not `uuid`