{"record":{"id":"8d4a47ee4fb989f5","repo":"Budibase/budibase","slug":"file-filename-contains-active-content-which-i","errorCode":null,"errorMessage":"File \"${fileName}\" contains active content which is not permitted","messagePattern":"File \"(.+?)\" contains active content which is not permitted","errorType":"http","errorClass":"ActiveContentFileError","httpStatus":400,"severity":"error","filePath":"packages/server/src/api/controllers/static/index.ts","lineNumber":294,"sourceCode":"        )\n      }\n\n      const extensionLower = extension.toLowerCase()\n      const isPublicUser =\n        ctx.roleId === roles.BUILTIN_ROLE_IDS.PUBLIC ||\n        ctx.user?.roleId === roles.BUILTIN_ROLE_IDS.PUBLIC\n      const enforceInvalidExtension = isPublicUser || !env.SELF_HOSTED\n      if (\n        enforceInvalidExtension &&\n        InvalidFileExtensions.includes(extensionLower)\n      ) {\n        throw new BadRequestError(\n          `File \"${fileName}\" has an invalid extension: \"${extension}\"`\n        )\n      }\n\n      if (ACTIVE_CONTENT_EXTENSIONS.has(extensionLower)) {\n        throw new ActiveContentFileError(fileName)\n      }\n\n      const mimeType =\n        typeof rawMimeType === \"string\" ? rawMimeType.toLowerCase() : undefined\n      if (\n        mimeType &&\n        ACTIVE_CONTENT_MIME_TYPES.some(type => mimeType.includes(type))\n      ) {\n        throw new ActiveContentFileError(fileName)\n      }\n\n      if (\n        filePath &&\n        (typeof filePath === \"string\" || Buffer.isBuffer(filePath)) &&\n        (await detectActiveContent(filePath))\n      ) {\n        throw new ActiveContentFileError(fileName)\n      }","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/static/index.ts#L276-L312","documentation":"Thrown as ActiveContentFileError by uploadFile when the file's extension is in ACTIVE_CONTENT_EXTENSIONS (html, htm, js, mjs, svg, wasm, xhtml, etc.). These types can execute scripts when served, enabling stored XSS, so they are rejected regardless of role or environment.","triggerScenarios":"Uploading any file whose extension is html/js/svg/wasm and similar active content types to the attachment upload endpoint.","commonSituations":"Teams trying to attach HTML reports, SVG logos, or JS bundles as app attachments; hosting a static site's assets inside Budibase attachments.","solutions":["Rename/convert to a safe format (SVG logo -> PNG, HTML report -> PDF)","Zip the active-content files and upload the archive instead","Serve the active content from a dedicated static host/CDN outside Budibase"],"exampleFix":"// before\nupload 'logo.svg' as attachment\n// after\nexport and upload 'logo.png'","handlingStrategy":"validation","validationCode":"const ACTIVE = new Set(['html','htm','js','jse','mjs','svg','svgz','wasm','xhtml','mhtml','shtml'])\nfunction isNotActiveContent(name) {\n  return !ACTIVE.has((name.split('.').pop() || '').toLowerCase())\n}","typeGuard":"const isActiveContentFile = (name: string): boolean =>\n  ['html','htm','js','jse','mjs','svg','svgz','wasm','xhtml','mhtml','shtml'].includes((name.split('.').pop() || '').toLowerCase())","tryCatchPattern":"try {\n  await api.uploadFile(fd)\n} catch (err) {\n  if (err instanceof ActiveContentFileError) {\n    // convert to a safe format (PNG/PDF) and retry\n  } else throw err\n}","preventionTips":["Convert SVG to PNG and HTML to PDF before attaching","Never attempt to host executable web assets (js/wasm/html) as attachments","Run the client-side extension check for the ACTIVE_CONTENT_EXTENSIONS list before every upload"],"tags":["security","upload","xss","file-extension"],"backgroundTag":"active-content-upload-blocked","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}