html-proofer 5.0.0 → 5.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2c984bfe049fba10c746d289d8d0256f219d6ae8f5af152f6f8af68e639301b
4
- data.tar.gz: c407f3995e873bbbc42d90de1f082827a40caa90cb12e5cdba7fd514500d1124
3
+ metadata.gz: 1081048f56ee5d0b41f5ba9278d0757d1a1eef6fdb8c50148eb2b47a183b5572
4
+ data.tar.gz: 9a60b977a473c77c12085062b0438eb175751a0a013206ec4dbd1baf03deffb6
5
5
  SHA512:
6
- metadata.gz: 732365ae4207754f70b27843a52d4f8186673fe1282ddaae03244ebff07517816371e970b75e465539bac51e931d51d4b1856866fcd6a3a575ae6e980d7c6465
7
- data.tar.gz: ab01a8425d206863584e6c84f395ab85a22eff9d2e1df0daa4e0f72771fc9bde95a8992da17d2ef8195439ad1de2412135758989bb9ba29766b1978d95e0fad4
6
+ metadata.gz: 0402cc78e35cd4440bf95e9a9d0372f68677d28ad0e901867457c4583caebb4cbd4eefdc50e27e270d85e756ee402c0e2ba6898c3bf1b074fef6f69f8b30ae7a
7
+ data.tar.gz: f5754b9c79336f792cb2064baab958b7c27e62d13a6ae225a9fe5cde286d773e9427251ccd7b44afa7d258e517a5838af5ad6fdff3c63a0b6bf21b8cba08c75f
@@ -3,13 +3,16 @@
3
3
  module HTMLProofer
4
4
  class Attribute
5
5
  class Url < HTMLProofer::Attribute
6
- attr_reader :url, :size
6
+ attr_reader :url, :size, :source, :filename
7
7
 
8
8
  REMOTE_SCHEMES = ["http", "https"].freeze
9
9
 
10
- def initialize(runner, link_attribute, base_url: nil, extract_size: false)
10
+ def initialize(runner, link_attribute, base_url: nil, source: nil, filename: nil, extract_size: false)
11
11
  super
12
12
 
13
+ @source = source
14
+ @filename = filename
15
+
13
16
  if @raw_attribute.nil?
14
17
  @url = nil
15
18
  else
@@ -49,7 +52,8 @@ module HTMLProofer
49
52
 
50
53
  def ignore?
51
54
  return true if /^javascript:/.match?(@url)
52
- return true if ignores_pattern?(@runner.options[:ignore_urls])
55
+
56
+ true if ignores_pattern?(@runner.options[:ignore_urls])
53
57
  end
54
58
 
55
59
  def valid?
@@ -115,9 +119,30 @@ module HTMLProofer
115
119
  def exists?
116
120
  return true if base64?
117
121
 
118
- return @runner.checked_paths[absolute_path] if @runner.checked_paths.key?(absolute_path)
122
+ !resolved_path.nil?
123
+ end
124
+
125
+ def resolved_path
126
+ path_to_resolve = absolute_path
119
127
 
120
- @runner.checked_paths[absolute_path] = File.exist?(absolute_path)
128
+ return @runner.resolved_paths[path_to_resolve] if @runner.resolved_paths.key?(path_to_resolve)
129
+
130
+ # extensionless URLs
131
+ path_with_extension = "#{path_to_resolve}#{@runner.options[:assume_extension]}"
132
+ resolved = if @runner.options[:assume_extension] && File.file?(path_with_extension)
133
+ path_with_extension # existence checked implicitly by File.file?
134
+ # implicit index support
135
+ elsif File.directory?(path_to_resolve) && !unslashed_directory?(path_to_resolve)
136
+ @runner.options[:directory_index_files]
137
+ .map { |dif| File.join(path_to_resolve, dif) }
138
+ .find { |path_with_index| File.file?(path_with_index) }
139
+ # explicit file or directory
140
+ elsif File.exist?(path_to_resolve)
141
+ path_to_resolve
142
+ end
143
+ @runner.resolved_paths[path_to_resolve] = resolved
144
+
145
+ resolved
121
146
  end
122
147
 
123
148
  def base64?
@@ -125,49 +150,31 @@ module HTMLProofer
125
150
  end
126
151
 
127
152
  def absolute_path
128
- path = file_path || @runner.current_filename
153
+ path = full_path || @filename
129
154
 
130
155
  File.expand_path(path, Dir.pwd)
131
156
  end
132
157
 
133
- def file_path
158
+ def full_path
134
159
  return if path.nil? || path.empty?
135
160
 
136
- path_dot_ext = ""
137
-
138
- path_dot_ext = path + @runner.options[:assume_extension] unless blank?(@runner.options[:assume_extension])
139
-
140
161
  base = if absolute_path?(path) # path relative to root
141
- # either overwrite with root_dir; or, if source is directory, use that; or, just get the current file's dirname
142
- @runner.options[:root_dir] || (File.directory?(@runner.current_source) ? @runner.current_source : File.dirname(@runner.current_source))
143
- # relative links, path is a file
144
- elsif File.exist?(File.expand_path(path,
145
- @runner.current_source)) || File.exist?(File.expand_path(path_dot_ext, @runner.current_source))
146
- File.dirname(@runner.current_filename)
147
- # relative links in nested dir, path is a file
148
- elsif File.exist?(File.join(File.dirname(@runner.current_filename),
149
- path)) || File.exist?(File.join(File.dirname(@runner.current_filename), path_dot_ext))
150
- File.dirname(@runner.current_filename)
151
- # relative link, path is a directory
162
+ # either overwrite with root_dir; or, if source is directory, use that; or, just get the source file's dirname
163
+ @runner.options[:root_dir] || (File.directory?(@source) ? @source : File.dirname(@source))
152
164
  else
153
- @runner.current_filename
154
- end
155
-
156
- file = File.join(base, path)
157
-
158
- if @runner.options[:assume_extension] && File.file?("#{file}#{@runner.options[:assume_extension]}")
159
- file = "#{file}#{@runner.options[:assume_extension]}"
160
- elsif File.directory?(file) && !unslashed_directory?(file) # implicit index support
161
- file = File.join(file, @runner.options[:directory_index_file])
165
+ # path relative to the file where the link is defined
166
+ File.dirname(@filename)
162
167
  end
163
168
 
164
- file
169
+ File.join(base, path)
165
170
  end
166
171
 
167
172
  def unslashed_directory?(file)
168
173
  return false unless File.directory?(file)
169
174
 
170
- !file.end_with?(File::SEPARATOR) && !follow_location?
175
+ # the trailing slash must be read off the URL, not off `file`: the latter
176
+ # is produced by File.expand_path, which strips trailing separators
177
+ !path.to_s.end_with?("/") && !follow_location?
171
178
  end
172
179
 
173
180
  def follow_location?
@@ -213,15 +220,28 @@ module HTMLProofer
213
220
  url.start_with?("?")
214
221
  end
215
222
 
216
- def sans_hash
223
+ def without_hash
217
224
  @url.to_s.sub(/##{hash}/, "")
218
225
  end
219
226
 
220
- # catch any obvious issues, like strings in port numbers
227
+ # catch any obvious issues
221
228
  private def clean_url!
222
- return if @url =~ /^([!#{Regexp.last_match(0)}-;=?-\[\]_a-z~]|%[0-9a-fA-F]{2})+$/
223
-
224
- @url = Addressable::URI.parse(@url).normalize.to_s
229
+ parsed_url = Addressable::URI.parse(@url)
230
+ url = if parsed_url.scheme.nil?
231
+ parsed_url
232
+ else
233
+ parsed_url.normalize
234
+ end.to_s
235
+
236
+ # normalize strips this off, which causes issues with cache
237
+ @url = if @url.end_with?("/") && !url.end_with?("/")
238
+ "#{url}/"
239
+ elsif !@url.end_with?("/") && url.end_with?("/")
240
+ url.chop
241
+ else
242
+ url
243
+ end
244
+ rescue Addressable::URI::InvalidURIError # -- error will be reported at check time
225
245
  end
226
246
 
227
247
  private def swap_urls!
@@ -34,14 +34,14 @@ module HTMLProofer
34
34
  setup_cache!(options)
35
35
 
36
36
  @external_timeframe = parsed_timeframe(options[:timeframe][:external])
37
- define_singleton_method(:external_enabled?) { !@external_timeframe.nil? }
37
+ define_singleton_method(:external_enabled?) { !@external_timeframe.nil? } # rubocop:disable ThreadSafety/ClassInstanceVariable
38
38
  @internal_timeframe = parsed_timeframe(options[:timeframe][:internal])
39
- define_singleton_method(:internal_enabled?) { !@internal_timeframe.nil? }
39
+ define_singleton_method(:internal_enabled?) { !@internal_timeframe.nil? } # rubocop:disable ThreadSafety/ClassInstanceVariable
40
40
  end
41
41
  end
42
42
 
43
43
  def parsed_timeframe(timeframe)
44
- return nil if timeframe.nil?
44
+ return if timeframe.nil?
45
45
 
46
46
  time, date = timeframe.match(/(\d+)(\D)/).captures
47
47
  time = time.to_i
@@ -71,8 +71,10 @@ module HTMLProofer
71
71
  return unless external_enabled?
72
72
 
73
73
  clean_url = cleaned_url(url)
74
+ # Filter out non-serializable element from metadata
75
+ cleaned_filenames = filenames.map { |f| f.except(:element) }
74
76
  @cache_log[:external][clean_url] =
75
- { time: @cache_time.to_s, found: found, status_code: status_code, message: msg, metadata: filenames }
77
+ { time: @cache_time.to_s, found: found, status_code: status_code, message: msg, metadata: cleaned_filenames }
76
78
  end
77
79
 
78
80
  def detect_url_changes(urls_detected, type)
@@ -93,8 +95,10 @@ module HTMLProofer
93
95
  # if there are no urls, bail
94
96
  return {} if urls_detected.empty?
95
97
 
96
- urls_detected = urls_detected.transform_keys do |url|
97
- cleaned_url(url)
98
+ if type == :external
99
+ urls_detected = urls_detected.transform_keys do |url|
100
+ cleaned_url(url)
101
+ end
98
102
  end
99
103
 
100
104
  urls_to_check = detect_url_changes(urls_detected, type)
@@ -184,7 +188,9 @@ module HTMLProofer
184
188
  hsh[url] = metadata_additions
185
189
  # remove from the cache the detected metadata additions as they correspond to failures to be rechecked
186
190
  # (this works assuming the detected url metadata have "found" set to false)
187
- @cache_log[:internal][url][:metadata] = cache_metadata.difference(metadata_additions)
191
+ # Compare without the :element key since cached metadata doesn't have it
192
+ metadata_additions_for_diff = metadata_additions.map { |m| m.except(:element) }
193
+ @cache_log[:internal][url][:metadata] = cache_metadata.difference(metadata_additions_for_diff)
188
194
  end
189
195
  end
190
196
 
@@ -250,7 +256,7 @@ module HTMLProofer
250
256
  SECONDS_PER_HOUR = 3600
251
257
  SECONDS_PER_DAY = 86400
252
258
  SECONDS_PER_WEEK = 604800
253
- SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
259
+ SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
254
260
 
255
261
  private def time_ago(measurement, unit)
256
262
  case unit
@@ -267,7 +273,8 @@ module HTMLProofer
267
273
 
268
274
  private def url_matches_type?(url, type)
269
275
  return true if type == :internal && url !~ URI_REGEXP
270
- return true if type == :external && url =~ URI_REGEXP
276
+
277
+ true if type == :external && url =~ URI_REGEXP
271
278
  end
272
279
 
273
280
  private def cleaned_url(url)
@@ -17,13 +17,17 @@ module HTMLProofer
17
17
 
18
18
  if found
19
19
  if @favicon.url.protocol_relative?
20
- add_failure("favicon link #{@favicon.url} is a protocol-relative URL, use explicit https:// instead",
21
- line: @favicon.line, content: @favicon.content)
20
+ add_failure(
21
+ "favicon link #{@favicon.url} is a protocol-relative URL, use explicit https:// instead",
22
+ element: @favicon,
23
+ )
22
24
  elsif @favicon.url.remote?
23
- add_to_external_urls(@favicon.url, @favicon.line)
25
+ add_to_external_urls(@favicon.url, @favicon)
24
26
  elsif !@favicon.url.exists?
25
- add_failure("internal favicon #{@favicon.url.raw_attribute} does not exist", line: @favicon.line,
26
- content: @favicon.content)
27
+ add_failure(
28
+ "internal favicon #{@favicon.url.raw_attribute} does not exist",
29
+ element: @favicon,
30
+ )
27
31
  end
28
32
  else
29
33
  add_failure("no favicon provided")
@@ -12,48 +12,62 @@ module HTMLProofer
12
12
  next if @img.ignore?
13
13
 
14
14
  # screenshot filenames should return because of terrible names
15
- add_failure("image has a terrible filename (#{@img.url.raw_attribute})", line: @img.line,
16
- content: @img.content) if terrible_filename?
15
+ add_failure(
16
+ "image has a terrible filename (#{@img.url.raw_attribute})",
17
+ element: @img,
18
+ ) if terrible_filename?
17
19
 
18
20
  # does the image exist?
19
21
  if missing_src?
20
- add_failure("image has no src or srcset attribute", line: @img.line, content: @img.content)
21
- elsif @img.url.protocol_relative?
22
- add_failure("image link #{@img.url} is a protocol-relative URL, use explicit https:// instead",
23
- line: @img.line, content: @img.content)
24
- elsif @img.url.remote?
25
- add_to_external_urls(@img.url, @img.line)
26
- elsif !@img.url.exists? && !@img.multiple_srcsets? && !@img.multiple_sizes?
27
- add_failure("internal image #{@img.url.raw_attribute} does not exist", line: @img.line,
28
- content: @img.content)
22
+ add_failure("image has no src or srcset attribute", element: @img)
29
23
  elsif @img.multiple_srcsets? || @img.multiple_sizes?
30
24
  @img.srcsets_wo_sizes.each do |srcset|
31
- srcset_url = HTMLProofer::Attribute::Url.new(@runner, srcset, base_url: @img.base_url, extract_size: true)
25
+ srcset_url = HTMLProofer::Attribute::Url.new(@runner, srcset, base_url: @img.base_url, source: @img.url.source, filename: @img.url.filename, extract_size: true)
32
26
 
33
27
  if srcset_url.protocol_relative?
34
- add_failure("image link #{srcset_url.url} is a protocol-relative URL, use explicit https:// instead",
35
- line: @img.line, content: @img.content)
28
+ add_failure(
29
+ "image link #{srcset_url.url} is a protocol-relative URL, use explicit https:// instead",
30
+ element: @img,
31
+ )
36
32
  elsif srcset_url.remote?
37
- add_to_external_urls(srcset_url.url, @img.line)
33
+ add_to_external_urls(srcset_url.url, @img)
38
34
  elsif !srcset_url.exists?
39
- add_failure("internal image #{srcset} does not exist", line: @img.line, content: @img.content)
35
+ add_failure("internal image #{srcset} does not exist", element: @img)
40
36
  end
41
37
  end
38
+ elsif @img.url.protocol_relative?
39
+ add_failure(
40
+ "image link #{@img.url} is a protocol-relative URL, use explicit https:// instead",
41
+ element: @img,
42
+ )
43
+ elsif @img.url.remote?
44
+ add_to_external_urls(@img.url, @img)
45
+ elsif !@img.url.exists? && !@img.multiple_srcsets? && !@img.multiple_sizes?
46
+ add_failure(
47
+ "internal image #{@img.url.raw_attribute} does not exist",
48
+ element: @img,
49
+ )
42
50
  end
43
51
 
44
52
  # if this is an img element, check that the alt attribute is present
45
53
  if @img.img_tag? && !ignore_element?
46
54
  if missing_alt_tag? && !ignore_missing_alt?
47
- add_failure("image #{@img.url.raw_attribute} does not have an alt attribute", line: @img.line,
48
- content: @img.content)
55
+ add_failure(
56
+ "image #{@img.url.raw_attribute} does not have an alt attribute",
57
+ element: @img,
58
+ )
49
59
  elsif (empty_alt_tag? || alt_all_spaces?) && !ignore_empty_alt?
50
- add_failure("image #{@img.url.raw_attribute} has an alt attribute, but no content", line: @img.line,
51
- content: @img.content)
60
+ add_failure(
61
+ "image #{@img.url.raw_attribute} has an alt attribute, but no content",
62
+ element: @img,
63
+ )
52
64
  end
53
65
  end
54
66
 
55
- add_failure("image #{@img.url.raw_attribute} uses the http scheme", line: @img.line,
56
- content: @img.content) if @runner.enforce_https? && @img.url.http?
67
+ add_failure(
68
+ "image #{@img.url.raw_attribute} uses the http scheme",
69
+ element: @img,
70
+ ) if @runner.enforce_https? && @img.url.http?
57
71
  end
58
72
 
59
73
  external_urls
@@ -10,7 +10,7 @@ module HTMLProofer
10
10
  next if @link.ignore?
11
11
 
12
12
  if !allow_hash_href? && @link.node["href"] == "#"
13
- add_failure("linking to internal hash #, which points to nowhere", line: @link.line, content: @link.content)
13
+ add_failure("linking to internal hash #, which points to nowhere", element: @link)
14
14
  next
15
15
  end
16
16
 
@@ -18,19 +18,21 @@ module HTMLProofer
18
18
  if blank?(@link.url.raw_attribute)
19
19
  next if allow_missing_href?
20
20
 
21
- add_failure("'#{@link.node.name}' tag is missing a reference", line: @link.line, content: @link.content)
21
+ add_failure("'#{@link.node.name}' tag is missing a reference", element: @link)
22
22
  next
23
23
  end
24
24
 
25
25
  # is it even a valid URL?
26
26
  unless @link.url.valid?
27
- add_failure("#{@link.href} is an invalid URL", line: @link.line, content: @link.content)
27
+ add_failure("#{@link.href} is an invalid URL", element: @link)
28
28
  next
29
29
  end
30
30
 
31
31
  if @link.url.protocol_relative?
32
- add_failure("#{@link.url} is a protocol-relative URL, use explicit https:// instead",
33
- line: @link.line, content: @link.content)
32
+ add_failure(
33
+ "#{@link.url} is a protocol-relative URL, use explicit https:// instead",
34
+ element: @link,
35
+ )
34
36
  next
35
37
  end
36
38
 
@@ -47,20 +49,22 @@ module HTMLProofer
47
49
  next if @link.node["rel"] == "dns-prefetch"
48
50
 
49
51
  unless @link.url.path?
50
- add_failure("#{@link.url.raw_attribute} is an invalid URL", line: @link.line, content: @link.content)
52
+ add_failure("#{@link.url.raw_attribute} is an invalid URL", element: @link)
51
53
  next
52
54
  end
53
55
 
54
- add_to_external_urls(@link.url, @link.line)
56
+ add_to_external_urls(@link.url, @link)
55
57
  elsif @link.url.internal?
56
58
  # does the local directory have a trailing slash?
57
59
  if @link.url.unslashed_directory?(@link.url.absolute_path)
58
- add_failure("internally linking to a directory #{@link.url.raw_attribute} without trailing slash",
59
- line: @link.line, content: @link.content)
60
+ add_failure(
61
+ "internally linking to a directory #{@link.url.raw_attribute} without trailing slash",
62
+ element: @link,
63
+ )
60
64
  next
61
65
  end
62
66
 
63
- add_to_internal_urls(@link.url, @link.line)
67
+ add_to_internal_urls(@link.url, @link)
64
68
  end
65
69
  end
66
70
  end
@@ -82,23 +86,30 @@ module HTMLProofer
82
86
  when "http"
83
87
  return unless @runner.options[:enforce_https]
84
88
 
85
- add_failure("#{@link.url.raw_attribute} is not an HTTPS link", line: @link.line, content: @link.content)
89
+ add_failure("#{@link.url.raw_attribute} is not an HTTPS link", element: @link)
86
90
  end
87
91
  end
88
92
 
89
93
  def handle_mailto
90
94
  if @link.url.path.empty?
91
- add_failure("#{@link.url.raw_attribute} contains no email address", line: @link.line,
92
- content: @link.content) unless ignore_empty_mailto?
93
- elsif !/#{URI::MailTo::EMAIL_REGEXP}/o.match?(@link.url.path)
94
- add_failure("#{@link.url.raw_attribute} contains an invalid email address", line: @link.line,
95
- content: @link.content)
95
+ add_failure(
96
+ "#{@link.url.raw_attribute} contains no email address",
97
+ element: @link,
98
+ ) unless ignore_empty_mailto?
99
+ # eg., if any do not match a valid URL
100
+ elsif @link.url.path.split(",").any? { |email| !/#{URI::MailTo::EMAIL_REGEXP}/o.match?(email) }
101
+ add_failure(
102
+ "#{@link.url.raw_attribute} contains an invalid email address",
103
+ element: @link,
104
+ )
96
105
  end
97
106
  end
98
107
 
99
108
  def handle_tel
100
- add_failure("#{@link.url.raw_attribute} contains no phone number", line: @link.line,
101
- content: @link.content) if @link.url.path.empty?
109
+ add_failure(
110
+ "#{@link.url.raw_attribute} contains no phone number",
111
+ element: @link,
112
+ ) if @link.url.path.empty?
102
113
  end
103
114
 
104
115
  def ignore_empty_mailto?
@@ -113,13 +124,17 @@ module HTMLProofer
113
124
  return unless SRI_REL_TYPES.include?(@link.node["rel"])
114
125
 
115
126
  if blank?(@link.node["integrity"]) && blank?(@link.node["crossorigin"])
116
- add_failure("SRI and CORS not provided in: #{@link.url.raw_attribute}", line: @link.line,
117
- content: @link.content)
127
+ add_failure(
128
+ "SRI and CORS not provided in: #{@link.url.raw_attribute}",
129
+ element: @link,
130
+ )
118
131
  elsif blank?(@link.node["integrity"])
119
- add_failure("Integrity is missing in: #{@link.url.raw_attribute}", line: @link.line, content: @link.content)
132
+ add_failure("Integrity is missing in: #{@link.url.raw_attribute}", element: @link)
120
133
  elsif blank?(@link.node["crossorigin"])
121
- add_failure("CORS not provided for external resource in: #{@link.link.url.raw_attribute}", line: @link.line,
122
- content: @link.content)
134
+ add_failure(
135
+ "CORS not provided for external resource in: #{@link.link.url.raw_attribute}",
136
+ element: @link,
137
+ )
123
138
  end
124
139
  end
125
140
 
@@ -11,19 +11,23 @@ module HTMLProofer
11
11
 
12
12
  # does the open_graph exist?
13
13
  if missing_content?
14
- add_failure("open graph has no content attribute", line: @open_graph.line, content: @open_graph.content)
14
+ add_failure("open graph has no content attribute", element: @open_graph)
15
15
  elsif empty_content?
16
- add_failure("open graph content attribute is empty", line: @open_graph.line, content: @open_graph.content)
16
+ add_failure("open graph content attribute is empty", element: @open_graph)
17
17
  elsif !@open_graph.url.valid?
18
- add_failure("#{@open_graph.src} is an invalid URL", line: @open_graph.line)
18
+ add_failure("#{@open_graph.src} is an invalid URL", element: @open_graph)
19
19
  elsif @open_graph.url.protocol_relative?
20
- add_failure("open graph link #{@open_graph.url} is a protocol-relative URL, use explicit https:// instead",
21
- line: @open_graph.line, content: @open_graph.content)
20
+ add_failure(
21
+ "open graph link #{@open_graph.url} is a protocol-relative URL, use explicit https:// instead",
22
+ element: @open_graph,
23
+ )
22
24
  elsif @open_graph.url.remote?
23
- add_to_external_urls(@open_graph.url, @open_graph.line)
25
+ add_to_external_urls(@open_graph.url, @open_graph)
24
26
  else
25
- add_failure("internal open graph #{@open_graph.url.raw_attribute} does not exist", line: @open_graph.line,
26
- content: @open_graph.content) unless @open_graph.url.exists?
27
+ add_failure(
28
+ "internal open graph #{@open_graph.url.raw_attribute} does not exist",
29
+ element: @open_graph,
30
+ ) unless @open_graph.url.exists?
27
31
  end
28
32
  end
29
33
 
@@ -12,16 +12,20 @@ module HTMLProofer
12
12
 
13
13
  # does the script exist?
14
14
  if missing_src?
15
- add_failure("script is empty and has no src attribute", line: @script.line, content: @script.content)
15
+ add_failure("script is empty and has no src attribute", element: @script)
16
16
  elsif @script.url.protocol_relative?
17
- add_failure("script link #{@script.url} is a protocol-relative URL, use explicit https:// instead",
18
- line: @script.line, content: @script.content)
17
+ add_failure(
18
+ "script link #{@script.url} is a protocol-relative URL, use explicit https:// instead",
19
+ element: @script,
20
+ )
19
21
  elsif @script.url.remote?
20
- add_to_external_urls(@script.url, @script.line)
22
+ add_to_external_urls(@script.url, @script)
21
23
  check_sri if @runner.check_sri?
22
24
  elsif !@script.url.exists?
23
- add_failure("internal script reference #{@script.src} does not exist", line: @script.line,
24
- content: @script.content)
25
+ add_failure(
26
+ "internal script reference #{@script.src} does not exist",
27
+ element: @script,
28
+ )
25
29
  end
26
30
  end
27
31
 
@@ -34,14 +38,20 @@ module HTMLProofer
34
38
 
35
39
  def check_sri
36
40
  if blank?(@script.node["integrity"]) && blank?(@script.node["crossorigin"])
37
- add_failure("SRI and CORS not provided in: #{@script.url.raw_attribute}", line: @script.line,
38
- content: @script.content)
41
+ add_failure(
42
+ "SRI and CORS not provided in: #{@script.url.raw_attribute}",
43
+ element: @script,
44
+ )
39
45
  elsif blank?(@script.node["integrity"])
40
- add_failure("Integrity is missing in: #{@script.url.raw_attribute}", line: @script.line,
41
- content: @script.content)
46
+ add_failure(
47
+ "Integrity is missing in: #{@script.url.raw_attribute}",
48
+ element: @script,
49
+ )
42
50
  elsif blank?(@script.node["crossorigin"])
43
- add_failure("CORS not provided for external resource in: #{@script.url.raw_attribute}", line: @script.line,
44
- content: @script.content)
51
+ add_failure(
52
+ "CORS not provided for external resource in: #{@script.url.raw_attribute}",
53
+ element: @script,
54
+ )
45
55
  end
46
56
  end
47
57
  end
@@ -24,36 +24,44 @@ module HTMLProofer
24
24
  raise NotImplementedError, "HTMLProofer::Check subclasses must implement #run"
25
25
  end
26
26
 
27
- def add_failure(description, line: nil, status: nil, content: nil)
28
- @failures << Failure.new(@runner.current_filename, short_name, description, line: line, status: status,
29
- content: content)
27
+ def add_failure(description, element: nil, line: nil, status: nil, content: nil)
28
+ @failures << Failure.new(
29
+ @runner.current_filename,
30
+ short_name,
31
+ description,
32
+ line: element.nil? ? line : element.line,
33
+ status: status,
34
+ content: element.nil? ? content : element.content,
35
+ element: element,
36
+ )
30
37
  end
31
38
 
32
39
  def short_name
33
40
  self.class.name.split("::").last
34
41
  end
35
42
 
36
- def add_to_internal_urls(url, line)
43
+ def add_to_internal_urls(url, element)
37
44
  url_string = url.raw_attribute
38
45
 
39
46
  @internal_urls[url_string] = [] if @internal_urls[url_string].nil?
40
47
 
41
48
  metadata = {
42
- source: @runner.current_source,
43
- filename: @runner.current_filename,
44
- line: line,
49
+ source: url.source,
50
+ filename: url.filename,
51
+ line: element.line,
45
52
  base_url: base_url,
46
53
  found: false,
54
+ element: element,
47
55
  }
48
56
  @internal_urls[url_string] << metadata
49
57
  end
50
58
 
51
- def add_to_external_urls(url, line)
59
+ def add_to_external_urls(url, element)
52
60
  url_string = url.to_s
53
61
 
54
62
  @external_urls[url_string] = [] if @external_urls[url_string].nil?
55
63
 
56
- @external_urls[url_string] << { filename: @runner.current_filename, line: line }
64
+ @external_urls[url_string] << { filename: url.filename, line: element.line, element: element }
57
65
  end
58
66
 
59
67
  class << self