docusealco/docuseal · error · Submitters::NormalizeValues::InvalidDefaultValue

File type '.#{detected_extensions.first}' is not allowed.

Error message

File type '.#{detected_extensions.first}' is not allowed.

What it means

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.

Source

Thrown at lib/submitters/normalize_values.rb:251

    end

    def find_or_create_blob_from_html(_account, value, _field)
      raise InvalidDefaultValue, "HTML content is not allowed: #{value.first(200)}..."
    end

    def find_or_create_blob_from_base64(account, data, type, mime_type: nil)
      checksum = Digest::MD5.base64digest(data)

      blob = find_blob_by_checksum(checksum, account)

      return blob if blob

      mime_type ||= Marcel::MimeType.for(data)

      detected_extensions = Marcel::TYPE_EXTS[mime_type].to_a.map(&:downcase)

      if detected_extensions.any? { |e| Submitters::DANGEROUS_EXTENSIONS.include?(e) }
        raise InvalidDefaultValue, "File type '.#{detected_extensions.first}' is not allowed."
      end

      extension = detected_extensions.first
      extension = 'png' if extension.blank? && type.in?(%w[signature initials stamp image])

      filename = extension.present? ? "#{type}.#{extension}" : type

      ActiveStorage::Blob.create_and_upload!(io: StringIO.new(data), filename:)
    end

    def find_or_create_blob_from_text(account, text, type)
      data, width, height = Submitters::GenerateFontImage.call(text, font: type)

      checksum = Digest::MD5.base64digest(data)

      blob = find_blob_by_checksum(checksum, account)

      blob || ActiveStorage::Blob.create_and_upload!(

View on GitHub (pinned to 004a22c1c8)

Solutions

  1. Upload only document/image formats (pdf, png, jpeg, docx...) as base64 attachment values.
  2. 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.
  3. Rescue Submitters::NormalizeValues::InvalidDefaultValue and return the message verbatim in a 422 so the caller sees which type was detected.

Example fix

# before
attachment_value = Base64.strict_encode64(File.read('tool.exe'))

# after (ship the binary out-of-band; attach a document instead)
attachment_value = Base64.strict_encode64(File.read('guide.pdf'))
Defensive patterns

Strategy: validation

Validate before calling

# Sniff the decoded bytes locally and reject dangerous types before upload
EXTS = Marcel::TYPE_EXTS[Marcel::MimeType.for(decoded_bytes)].to_a.map(&:downcase)
raise ArgumentError, 'dangerous content type' if EXTS.any? { |e| Submitters::DANGEROUS_EXTENSIONS.include?(e) }

Try / catch

begin
  Submitters::NormalizeValues.normalize_attachment_value(base64_value, field, account, attachments, purpose: :api)
rescue Submitters::NormalizeValues::InvalidDefaultValue => e
  render json: { error: e.message }, status: :unprocessable_entity
end

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of docusealco/docuseal@004a22c1c8 (2026-08-21). Data as JSON: /api/errors/ea9c4bcbb4dd948a. Report an issue: GitHub.