{"record":{"id":"5867b8646d5d101b","repo":"payloadcms/payload","slug":"you-are-not-allowed-to-perform-this-action-5867b8","errorCode":null,"errorMessage":"You are not allowed to perform this action.","messagePattern":"You are not allowed to perform this action\\.","errorType":"http","errorClass":"Forbidden","httpStatus":403,"severity":"critical","filePath":"packages/payload/src/uploads/generateFileData.ts","lineNumber":122,"sourceCode":"    imageSizes,\n    resizeOptions,\n    staticDir,\n    trimOptions,\n    withMetadata,\n  } = collectionConfig.upload\n\n  const staticPath = staticDir\n\n  const incomingFileData: Document = isDuplicating ? originalDoc : data\n  let isLocalFile = false\n\n  if (\n    !file &&\n    (isDuplicating || shouldReupload(uploadEdits, incomingFileData as Record<string, unknown>))\n  ) {\n    const { filename, url } = incomingFileData as unknown as FileData\n    if (filename && (filename.includes('../') || filename.includes('..\\\\'))) {\n      throw new Forbidden(req.t)\n    }\n\n    if ((serverURL && url?.startsWith(serverURL)) || url?.startsWith('/')) {\n      isLocalFile = true\n    }\n\n    try {\n      if (!disableLocalStorage && isLocalFile) {\n        // File is stored locally\n        const filePath = `${staticPath}/${filename}`\n        const response = await getFileByPath(filePath)\n        file = response\n        overwriteExistingFiles = true\n      } else if (filename && url) {\n        // File is remote\n        file = await getExternalFile({\n          data: incomingFileData as unknown as FileData,\n          req,","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/payload/src/uploads/generateFileData.ts#L104-L140","documentation":"During duplication or re-upload (`shouldReupload`), Payload reconstructs the file from the existing document's `filename`/`url`. If that `filename` contains a path-traversal sequence — either `../` (Unix) or `..\\\\` (Windows backslash) — Payload throws `Forbidden` (HTTP 403). This blocks an attacker from crafting a document whose stored filename escapes `staticDir` on the next write (`getFileByPath(`${staticPath}/${filename}`)` would otherwise resolve outside the upload folder).","triggerScenarios":"A create/update/duplicate operation on an upload collection where `req.file` is absent, `isDuplicating` is true OR `shouldReupload(uploadEdits, incomingFileData)` is true, and `incomingFileData.filename` (the persisted filename on the document) contains `../` or `..\\\\`. This typically means a bad/stored filename in the database.","commonSituations":"A migration imported documents with un-sanitized filenames. A custom hook or external writer stored a relative path in the `filename` field. A plugin-cloud-storage prefix accidentally landed in `filename`. Manual DB edits left traversal sequences in place.","solutions":["Audit and sanitize stored `filename` values in the database (remove `..` segments); the normal write path uses `sanitize-filename` so any non-conforming value was written out-of-band.","Trace where the offending `filename` originated (import script, custom hook, direct DB write) and fix the writer to use `sanitize-filename` / `getSafeFileName`.","If the document is meant to reference an external file, use the `url` path (validated by `isURLAllowed`) rather than embedding traversal in `filename`.","Add a migration to rewrite existing bad rows before re-uploading.","Reject such filenames at ingest with a `beforeChange` hook that validates against `/\\.\\.[\\\\/]/`.","For local file refetch, prefer resolving via the collection's storage metadata instead of trusting the stored filename string."],"exampleFix":"// before — migration wrote a bad filename\n{ filename: '../../etc/passwd', url: '/media/../../etc/passwd' }\n\n// after — sanitize on write\nimport sanitize from 'sanitize-filename'\nawait payload.update({\n  collection: 'media',\n  id,\n  data: { filename: sanitize(badFilename) },\n})","handlingStrategy":"validation","validationCode":"function isSafeFilename(name: string): boolean {\n  return !name.includes('../') && !name.includes('..\\\\')\n}\n\nfor (const doc of await payload.db.find({ collection: 'media', where: {} })) {\n  if (doc.filename && !isSafeFilename(doc.filename)) {\n    // flag for repair — sanitize and rewrite\n  }\n}","typeGuard":"function hasNoTraversal(name: unknown): name is string {\n  return typeof name === 'string' && !name.includes('../') && !name.includes('..\\\\')\n}\n\nif (!hasNoTraversal(doc.filename)) {\n  throw new Error(`Stored filename contains a traversal sequence: ${doc.filename}`)\n}","tryCatchPattern":"// Guard at ingest with a beforeChange hook\nbeforeChange: [({ data }) => {\n  if (typeof data.filename === 'string' && (data.filename.includes('../') || data.filename.includes('..\\\\'))) {\n    throw new Error('Refusing to store a filename with a path-traversal sequence')\n  }\n}]","preventionTips":["Always write filenames through `sanitize-filename` / `getSafeFileName` (Payload's default path).","Audit imported/migrated `filename` values for `../` and `..\\\\`.","Never store user-supplied relative paths in the `filename` field."],"tags":["security","path-traversal","upload","duplicating"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}