summaryrefslogtreecommitdiff
path: root/lib/gitlab/hotlinking_detector.rb
blob: dd58f6aca269e74ee10273d03317ae578ef3cb0e (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
# frozen_string_literal: true

module Gitlab
  class HotlinkingDetector
    IMAGE_FORMATS = %w(image/jpeg image/apng image/png image/webp image/svg+xml image/*).freeze
    MEDIA_FORMATS = %w(video/webm video/ogg video/* application/ogg audio/webm audio/ogg audio/wav audio/*).freeze
    CSS_FORMATS = %w(text/css).freeze
    INVALID_FORMATS = (IMAGE_FORMATS + MEDIA_FORMATS + CSS_FORMATS).freeze
    INVALID_FETCH_MODES = %w(cors no-cors websocket).freeze

    class << self
      def intercept_hotlinking?(request)
        request_accepts = parse_request_accepts(request)

        return false unless Feature.enabled?(:repository_archive_hotlinking_interception)

        # Block attempts to embed as JS
        return true if sec_fetch_invalid?(request)

        # If no Accept header was set, skip the rest
        return false if request_accepts.empty?

        # Workaround for IE8 weirdness
        return false if IMAGE_FORMATS.include?(request_accepts.first) && request_accepts.include?("application/x-ms-application")

        # Block all other media requests if the first format is a media type
        return true if INVALID_FORMATS.include?(request_accepts.first)

        false
      end

      private

      def sec_fetch_invalid?(request)
        fetch_mode = request.headers["Sec-Fetch-Mode"]

        return if fetch_mode.blank?
        return true if INVALID_FETCH_MODES.include?(fetch_mode)
      end

      def parse_request_accepts(request)
        # Rails will already have parsed the Accept header
        return request.accepts if request.respond_to?(:accepts)

        # Grape doesn't parse it, so we can use the Rails system for this
        return Mime::Type.parse(request.headers["Accept"]) if request.respond_to?(:headers) && request.headers["Accept"].present?

        []
      end
    end
  end
end