Class: HTMLProofer::Check::Links

Inherits:
HTMLProofer::Check show all
Defined in:
lib/html_proofer/check/links.rb

Constant Summary collapse

SRI_REL_TYPES =

Allowed elements from Subresource Integrity specification w3c.github.io/webappsec-subresource-integrity/#link-element-for-stylesheets

%(stylesheet)

Instance Attribute Summary

Attributes inherited from HTMLProofer::Check

#external_urls, #failures, #internal_urls, #options

Instance Method Summary collapse

Methods inherited from HTMLProofer::Check

#add_failure, #add_to_external_urls, #add_to_internal_urls, #create_element, #initialize, #short_name, short_name, subchecks

Methods included from Utils

#blank?, #create_nokogiri, #pluralize

Constructor Details

This class inherits a constructor from HTMLProofer::Check

Instance Method Details

#allow_hash_href?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/html_proofer/check/links.rb', line 76

def allow_hash_href?
  @runner.options[:allow_hash_href]
end

#allow_missing_href?Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/html_proofer/check/links.rb', line 72

def allow_missing_href?
  @runner.options[:allow_missing_href]
end

#check_schemesObject



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/html_proofer/check/links.rb', line 80

def check_schemes
  case @link.url.scheme
  when "mailto"
    handle_mailto
  when "tel"
    handle_tel
  when "http"
    return unless @runner.options[:enforce_https]

    add_failure("#{@link.url.raw_attribute} is not an HTTPS link", element: @link)
  end
end

#check_sriObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/html_proofer/check/links.rb', line 123

def check_sri
  return unless SRI_REL_TYPES.include?(@link.node["rel"])

  if blank?(@link.node["integrity"]) && blank?(@link.node["crossorigin"])
    add_failure(
      "SRI and CORS not provided in: #{@link.url.raw_attribute}",
      element: @link,
    )
  elsif blank?(@link.node["integrity"])
    add_failure("Integrity is missing in: #{@link.url.raw_attribute}", element: @link)
  elsif blank?(@link.node["crossorigin"])
    add_failure(
      "CORS not provided for external resource in: #{@link.link.url.raw_attribute}",
      element: @link,
    )
  end
end

#handle_mailtoObject



93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/html_proofer/check/links.rb', line 93

def handle_mailto
  if @link.url.path.empty?
    add_failure(
      "#{@link.url.raw_attribute} contains no email address",
      element: @link,
    ) unless ignore_empty_mailto?
  # eg., if any do not match a valid URL
  elsif @link.url.path.split(",").any? { |email| !/#{URI::MailTo::EMAIL_REGEXP}/o.match?(email) }
    add_failure(
      "#{@link.url.raw_attribute} contains an invalid email address",
      element: @link,
    )
  end
end

#handle_telObject



108
109
110
111
112
113
# File 'lib/html_proofer/check/links.rb', line 108

def handle_tel
  add_failure(
    "#{@link.url.raw_attribute} contains no phone number",
    element: @link,
  ) if @link.url.path.empty?
end

#ignore_empty_mailto?Boolean

Returns:

  • (Boolean)


115
116
117
# File 'lib/html_proofer/check/links.rb', line 115

def ignore_empty_mailto?
  @runner.options[:ignore_empty_mailto]
end

#runObject



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
# File 'lib/html_proofer/check/links.rb', line 6

def run
  @html.css("a, link").each do |node|
    @link = create_element(node)

    next if @link.ignore?

    if !allow_hash_href? && @link.node["href"] == "#"
      add_failure("linking to internal hash #, which points to nowhere", element: @link)
      next
    end

    # is there even an href?
    if blank?(@link.url.raw_attribute)
      next if allow_missing_href?

      add_failure("'#{@link.node.name}' tag is missing a reference", element: @link)
      next
    end

    # is it even a valid URL?
    unless @link.url.valid?
      add_failure("#{@link.href} is an invalid URL", element: @link)
      next
    end

    if @link.url.protocol_relative?
      add_failure(
        "#{@link.url} is a protocol-relative URL, use explicit https:// instead",
        element: @link,
      )
      next
    end

    check_schemes

    # intentionally down here because we still want valid? & missing_href? to execute
    next if @link.url.non_http_remote?

    if !@link.url.internal? && @link.url.remote?
      check_sri if @runner.check_sri? && @link.link_tag?

      # we need to skip these for now; although the domain main be valid,
      # curl/Typheous inaccurately return 404s for some links. cc https://git.io/vyCFx
      next if @link.node["rel"] == "dns-prefetch"

      unless @link.url.path?
        add_failure("#{@link.url.raw_attribute} is an invalid URL", element: @link)
        next
      end

      add_to_external_urls(@link.url, @link.line)
    elsif @link.url.internal?
      # does the local directory have a trailing slash?
      if @link.url.unslashed_directory?(@link.url.absolute_path)
        add_failure(
          "internally linking to a directory #{@link.url.raw_attribute} without trailing slash",
          element: @link,
        )
        next
      end

      add_to_internal_urls(@link.url, @link.line)
    end
  end
end