summaryrefslogtreecommitdiff
path: root/lib/gitlab/file_type_detection.rb
blob: 475d50e37bffdbb2d76b06a9bc3ecbfa031a3525 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# frozen_string_literal: true

# The method `filename` must be defined in classes that mix in this module.
#
# This module is intended to be used as a helper and not a security gate
# to validate that a file is safe, as it identifies files only by the
# file extension and not its actual contents.
#
# An example useage of this module is in `FileMarkdownLinkBuilder` that
# renders markdown depending on a file name.
#
# We use Workhorse to detect the real extension when we serve files with
# the `SendsBlob` helper methods, and ask Workhorse to set the content
# type when it serves the file:
# https://gitlab.com/gitlab-org/gitlab/blob/33e5955/app/helpers/workhorse_helper.rb#L48.
#
# Because Workhorse has access to the content when it is downloaded, if
# the type/extension doesn't match the real type, we adjust the
# `Content-Type` and `Content-Disposition` to the one we get from the detection.
module Gitlab
  module FileTypeDetection
    SAFE_IMAGE_EXT = %w[png jpg jpeg gif bmp tiff ico].freeze
    PDF_EXT = 'pdf'
    # We recommend using the .mp4 format over .mov. Videos in .mov format can
    # still be used but you really need to make sure they are served with the
    # proper MIME type video/mp4 and not video/quicktime or your videos won't play
    # on IE >= 9.
    # http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html
    SAFE_VIDEO_EXT = %w[mp4 m4v mov webm ogv].freeze
    SAFE_AUDIO_EXT = %w[mp3 oga ogg spx wav].freeze

    # These extension types can contain dangerous code and should only be embedded inline with
    # proper filtering. They should always be tagged as "Content-Disposition: attachment", not "inline".
    DANGEROUS_IMAGE_EXT = %w[svg].freeze
    DANGEROUS_VIDEO_EXT = [].freeze # None, yet
    DANGEROUS_AUDIO_EXT = [].freeze # None, yet

    def self.extension_match?(filename, extensions)
      return false unless filename.present?

      extension = File.extname(filename).delete('.')
      extensions.include?(extension.downcase)
    end

    def image?
      extension_match?(SAFE_IMAGE_EXT)
    end

    def video?
      extension_match?(SAFE_VIDEO_EXT)
    end

    def audio?
      extension_match?(SAFE_AUDIO_EXT)
    end

    def pdf?
      extension_match?([PDF_EXT])
    end

    def embeddable?
      image? || video? || audio?
    end

    def dangerous_image?
      extension_match?(DANGEROUS_IMAGE_EXT)
    end

    def dangerous_video?
      extension_match?(DANGEROUS_VIDEO_EXT)
    end

    def dangerous_audio?
      extension_match?(DANGEROUS_AUDIO_EXT)
    end

    def dangerous_embeddable?
      dangerous_image? || dangerous_video? || dangerous_audio?
    end

    private

    def extension_match?(extensions)
      ::Gitlab::FileTypeDetection.extension_match?(filename, extensions)
    end
  end
end