{"record":{"id":"ea9c4bcbb4dd948a","repo":"docusealco/docuseal","slug":"file-type-detected-extensions-first-is-not-a","errorCode":null,"errorMessage":"File type '.#{detected_extensions.first}' is not allowed.","messagePattern":"File type '\\.#(.+?)' is not allowed\\.","errorType":"validation","errorClass":"Submitters::NormalizeValues::InvalidDefaultValue","httpStatus":422,"severity":"error","filePath":"lib/submitters/normalize_values.rb","lineNumber":251,"sourceCode":"    end\n\n    def find_or_create_blob_from_html(_account, value, _field)\n      raise InvalidDefaultValue, \"HTML content is not allowed: #{value.first(200)}...\"\n    end\n\n    def find_or_create_blob_from_base64(account, data, type, mime_type: nil)\n      checksum = Digest::MD5.base64digest(data)\n\n      blob = find_blob_by_checksum(checksum, account)\n\n      return blob if blob\n\n      mime_type ||= Marcel::MimeType.for(data)\n\n      detected_extensions = Marcel::TYPE_EXTS[mime_type].to_a.map(&:downcase)\n\n      if detected_extensions.any? { |e| Submitters::DANGEROUS_EXTENSIONS.include?(e) }\n        raise InvalidDefaultValue, \"File type '.#{detected_extensions.first}' is not allowed.\"\n      end\n\n      extension = detected_extensions.first\n      extension = 'png' if extension.blank? && type.in?(%w[signature initials stamp image])\n\n      filename = extension.present? ? \"#{type}.#{extension}\" : type\n\n      ActiveStorage::Blob.create_and_upload!(io: StringIO.new(data), filename:)\n    end\n\n    def find_or_create_blob_from_text(account, text, type)\n      data, width, height = Submitters::GenerateFontImage.call(text, font: type)\n\n      checksum = Digest::MD5.base64digest(data)\n\n      blob = find_blob_by_checksum(checksum, account)\n\n      blob || ActiveStorage::Blob.create_and_upload!(","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/docusealco/docuseal/blob/004a22c1c88109c7ba0b567df011a8cb13894001/lib/submitters/normalize_values.rb#L233-L269","documentation":"Submitters::NormalizeValues#find_or_create_blob_from_base64 (lib/submitters/normalize_values.rb:251) raises InvalidDefaultValue when any extension Marcel maps for the decoded payload's MIME type is in Submitters::DANGEROUS_EXTENSIONS. The content is sniffed from the decoded bytes (Marcel::MimeType.for), so this is a content-based blocklist: base64-encoded executables/scripts/installers are rejected even when the field name suggests an image. The message names the first detected extension.","triggerScenarios":"A base64 default_value for an attachment field whose decoded bytes identify as exe/bat/sh/jar/dll/apk etc.; a polyglot file whose sniffed type maps to a dangerous extension; re-encoding an existing dangerous file to base64 to smuggle it past the filename check.","commonSituations":"Users base64-embedding installers or scripts as 'attachments' via the API; testers probing whether content sniffing exists (it does); MIME mis-detection of unusual binary formats landing on a blocklisted extension.","solutions":["Upload only document/image formats (pdf, png, jpeg, docx...) as base64 attachment values.","If you believe the file is safe and misdetected, re-encode it in a canonical format (e.g. zip it or convert to pdf) so the sniffed type is concrete and allowed.","Rescue Submitters::NormalizeValues::InvalidDefaultValue and return the message verbatim in a 422 so the caller sees which type was detected."],"exampleFix":"# before\nattachment_value = Base64.strict_encode64(File.read('tool.exe'))\n\n# after (ship the binary out-of-band; attach a document instead)\nattachment_value = Base64.strict_encode64(File.read('guide.pdf'))","handlingStrategy":"validation","validationCode":"# Sniff the decoded bytes locally and reject dangerous types before upload\nEXTS = Marcel::TYPE_EXTS[Marcel::MimeType.for(decoded_bytes)].to_a.map(&:downcase)\nraise ArgumentError, 'dangerous content type' if EXTS.any? { |e| Submitters::DANGEROUS_EXTENSIONS.include?(e) }","typeGuard":null,"tryCatchPattern":"begin\n  Submitters::NormalizeValues.normalize_attachment_value(base64_value, field, account, attachments, purpose: :api)\nrescue Submitters::NormalizeValues::InvalidDefaultValue => e\n  render json: { error: e.message }, status: :unprocessable_entity\nend","preventionTips":["Only embed document/image formats as base64 defaults.","Remember this check sniffs CONTENT (Marcel), not the filename — renaming does not help.","Re-package unusual binaries as zip/pdf if they must be delivered alongside the submission."],"tags":["docuseal","default-value","base64","security","extension-blocklist"],"backgroundTag":"blocked-file-extension","analyzedSha":"004a22c1c88109c7ba0b567df011a8cb13894001","analyzedAt":"2026-08-21T13:38:23.343Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}