html-proofer 3.19.4 → 4.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/bin/htmlproofer +30 -57
  3. data/lib/html-proofer.rb +1 -54
  4. data/lib/html_proofer/attribute/url.rb +231 -0
  5. data/lib/html_proofer/attribute.rb +15 -0
  6. data/lib/html_proofer/cache.rb +234 -0
  7. data/lib/html_proofer/check/favicon.rb +35 -0
  8. data/lib/html_proofer/check/images.rb +62 -0
  9. data/lib/html_proofer/check/links.rb +118 -0
  10. data/lib/html_proofer/check/open_graph.rb +34 -0
  11. data/lib/html_proofer/check/scripts.rb +38 -0
  12. data/lib/html_proofer/check.rb +91 -0
  13. data/lib/{html-proofer → html_proofer}/configuration.rb +30 -31
  14. data/lib/html_proofer/element.rb +122 -0
  15. data/lib/html_proofer/failure.rb +17 -0
  16. data/lib/{html-proofer → html_proofer}/log.rb +0 -0
  17. data/lib/html_proofer/reporter/cli.rb +29 -0
  18. data/lib/html_proofer/reporter.rb +23 -0
  19. data/lib/html_proofer/runner.rb +245 -0
  20. data/lib/html_proofer/url_validator/external.rb +189 -0
  21. data/lib/html_proofer/url_validator/internal.rb +86 -0
  22. data/lib/html_proofer/url_validator.rb +16 -0
  23. data/lib/{html-proofer → html_proofer}/utils.rb +5 -8
  24. data/lib/{html-proofer → html_proofer}/version.rb +1 -1
  25. data/lib/html_proofer/xpath_functions.rb +10 -0
  26. data/lib/html_proofer.rb +56 -0
  27. metadata +46 -27
  28. data/lib/html-proofer/cache.rb +0 -194
  29. data/lib/html-proofer/check/favicon.rb +0 -29
  30. data/lib/html-proofer/check/html.rb +0 -37
  31. data/lib/html-proofer/check/images.rb +0 -48
  32. data/lib/html-proofer/check/links.rb +0 -182
  33. data/lib/html-proofer/check/opengraph.rb +0 -46
  34. data/lib/html-proofer/check/scripts.rb +0 -42
  35. data/lib/html-proofer/check.rb +0 -75
  36. data/lib/html-proofer/element.rb +0 -265
  37. data/lib/html-proofer/issue.rb +0 -65
  38. data/lib/html-proofer/middleware.rb +0 -82
  39. data/lib/html-proofer/runner.rb +0 -249
  40. data/lib/html-proofer/url_validator.rb +0 -237
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html-proofer
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.19.4
4
+ version: 4.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-19 00:00:00.000000000 Z
11
+ date: 2022-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.13'
47
+ version: '1.12'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.13'
54
+ version: '1.12'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: parallel
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '2.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: zeitwerk
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.5'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.5'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: awesome_print
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -123,7 +137,7 @@ dependencies:
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
- name: pry-byebug
140
+ name: debug
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
143
  - - ">="
@@ -193,7 +207,7 @@ dependencies:
193
207
  - !ruby/object:Gem::Version
194
208
  version: '0'
195
209
  - !ruby/object:Gem::Dependency
196
- name: rubocop-performance
210
+ name: rubocop-rspec
197
211
  requirement: !ruby/object:Gem::Requirement
198
212
  requirements:
199
213
  - - ">="
@@ -258,28 +272,33 @@ extra_rdoc_files: []
258
272
  files:
259
273
  - bin/htmlproofer
260
274
  - lib/html-proofer.rb
261
- - lib/html-proofer/cache.rb
262
- - lib/html-proofer/check.rb
263
- - lib/html-proofer/check/favicon.rb
264
- - lib/html-proofer/check/html.rb
265
- - lib/html-proofer/check/images.rb
266
- - lib/html-proofer/check/links.rb
267
- - lib/html-proofer/check/opengraph.rb
268
- - lib/html-proofer/check/scripts.rb
269
- - lib/html-proofer/configuration.rb
270
- - lib/html-proofer/element.rb
271
- - lib/html-proofer/issue.rb
272
- - lib/html-proofer/log.rb
273
- - lib/html-proofer/middleware.rb
274
- - lib/html-proofer/runner.rb
275
- - lib/html-proofer/url_validator.rb
276
- - lib/html-proofer/utils.rb
277
- - lib/html-proofer/version.rb
275
+ - lib/html_proofer.rb
276
+ - lib/html_proofer/attribute.rb
277
+ - lib/html_proofer/attribute/url.rb
278
+ - lib/html_proofer/cache.rb
279
+ - lib/html_proofer/check.rb
280
+ - lib/html_proofer/check/favicon.rb
281
+ - lib/html_proofer/check/images.rb
282
+ - lib/html_proofer/check/links.rb
283
+ - lib/html_proofer/check/open_graph.rb
284
+ - lib/html_proofer/check/scripts.rb
285
+ - lib/html_proofer/configuration.rb
286
+ - lib/html_proofer/element.rb
287
+ - lib/html_proofer/failure.rb
288
+ - lib/html_proofer/log.rb
289
+ - lib/html_proofer/reporter.rb
290
+ - lib/html_proofer/reporter/cli.rb
291
+ - lib/html_proofer/runner.rb
292
+ - lib/html_proofer/url_validator.rb
293
+ - lib/html_proofer/url_validator/external.rb
294
+ - lib/html_proofer/url_validator/internal.rb
295
+ - lib/html_proofer/utils.rb
296
+ - lib/html_proofer/version.rb
297
+ - lib/html_proofer/xpath_functions.rb
278
298
  homepage: https://github.com/gjtorikian/html-proofer
279
299
  licenses:
280
300
  - MIT
281
301
  metadata:
282
- funding_uri: https://github.com/sponsors/gjtorikian/
283
302
  rubygems_mfa_required: 'true'
284
303
  post_install_message:
285
304
  rdoc_options: []
@@ -295,11 +314,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
295
314
  version: '4.0'
296
315
  required_rubygems_version: !ruby/object:Gem::Requirement
297
316
  requirements:
298
- - - ">="
317
+ - - ">"
299
318
  - !ruby/object:Gem::Version
300
- version: '0'
319
+ version: 1.3.1
301
320
  requirements: []
302
- rubygems_version: 3.3.13
321
+ rubygems_version: 3.3.3
303
322
  signing_key:
304
323
  specification_version: 4
305
324
  summary: A set of tests to validate your HTML output. These tests check if your image
@@ -1,194 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'utils'
4
- require 'date'
5
- require 'json'
6
- require 'uri'
7
-
8
- module HTMLProofer
9
- class Cache
10
- include HTMLProofer::Utils
11
-
12
- DEFAULT_STORAGE_DIR = File.join('tmp', '.htmlproofer')
13
- DEFAULT_CACHE_FILE_NAME = 'cache.log'
14
-
15
- URI_REGEXP = URI::DEFAULT_PARSER.make_regexp
16
-
17
- attr_reader :exists, :cache_log, :storage_dir, :cache_file
18
-
19
- def initialize(logger, options)
20
- @logger = logger
21
- @cache_log = {}
22
-
23
- @cache_datetime = DateTime.now
24
- @cache_time = @cache_datetime.to_time
25
-
26
- if options.nil? || options.empty?
27
- define_singleton_method('use_cache?') { false }
28
- else
29
- define_singleton_method('use_cache?') { true }
30
- setup_cache!(options)
31
- @parsed_timeframe = parsed_timeframe(options[:timeframe])
32
- end
33
- end
34
-
35
- def within_timeframe?(time)
36
- return false if time.nil?
37
-
38
- (@parsed_timeframe..@cache_time).cover?(Time.parse(time))
39
- end
40
-
41
- def urls
42
- @cache_log['urls'] || []
43
- end
44
-
45
- def size
46
- @cache_log.length
47
- end
48
-
49
- def parsed_timeframe(timeframe)
50
- time, date = timeframe.match(/(\d+)(\D)/).captures
51
- time = time.to_i
52
- case date
53
- when 'M'
54
- time_ago(time, :months)
55
- when 'w'
56
- time_ago(time, :weeks)
57
- when 'd'
58
- time_ago(time, :days)
59
- when 'h'
60
- time_ago(time, :hours)
61
- else
62
- raise ArgumentError, "#{date} is not a valid timeframe!"
63
- end
64
- end
65
-
66
- def add(url, filenames, status, msg = '')
67
- return unless use_cache?
68
-
69
- data = {
70
- time: @cache_time,
71
- filenames: filenames,
72
- status: status,
73
- message: msg
74
- }
75
-
76
- @cache_log[clean_url(url)] = data
77
- end
78
-
79
- def detect_url_changes(found, type)
80
- found_urls = found.keys.map { |url| clean_url(url) }
81
-
82
- # if there were no urls, bail
83
- return {} if found_urls.empty?
84
-
85
- existing_urls = @cache_log.keys.map { |url| clean_url(url) }
86
-
87
- # prepare to add new URLs detected
88
- additions = found.reject do |url, _|
89
- url = clean_url(url)
90
- if existing_urls.include?(url)
91
- true
92
- else
93
- @logger.log :debug, "Adding #{url} to cache check"
94
- false
95
- end
96
- end
97
-
98
- new_link_count = additions.length
99
- new_link_text = pluralize(new_link_count, 'link', 'links')
100
- @logger.log :info, "Adding #{new_link_text} to the cache..."
101
-
102
- # remove from cache URLs that no longer exist
103
- deletions = 0
104
- @cache_log.delete_if do |url, _|
105
- url = clean_url(url)
106
-
107
- if found_urls.include?(url)
108
- false
109
- elsif url_matches_type?(url, type)
110
- @logger.log :debug, "Removing #{url} from cache check"
111
- deletions += 1
112
- true
113
- end
114
- end
115
-
116
- del_link_text = pluralize(deletions, 'link', 'links')
117
- @logger.log :info, "Removing #{del_link_text} from the cache..."
118
-
119
- additions
120
- end
121
-
122
- # TODO: Garbage performance--both the external and internal
123
- # caches need access to this file. Write a proper versioned
124
- # schema in the future
125
- def write
126
- File.write(cache_file, @cache_log.to_json)
127
- end
128
-
129
- def load?
130
- @load.nil?
131
- end
132
-
133
- def retrieve_urls(urls, type)
134
- urls_to_check = detect_url_changes(urls, type)
135
-
136
- @cache_log.each_pair do |url, cache|
137
- next if within_timeframe?(cache['time']) && cache['message'].empty? # these were successes to skip
138
-
139
- if url_matches_type?(url, type)
140
- urls_to_check[url] = cache['filenames'] # recheck expired links
141
- end
142
- end
143
- urls_to_check
144
- end
145
-
146
- # FIXME: it seems that Typhoeus actually acts on escaped URLs,
147
- # but there's no way to get at that information, and the cache
148
- # stores unescaped URLs. Because of this, some links, such as
149
- # github.com/search/issues?q=is:open+is:issue+fig are not matched
150
- # as github.com/search/issues?q=is%3Aopen+is%3Aissue+fig
151
- def unescape_url(url)
152
- Addressable::URI.unescape(url)
153
- end
154
-
155
- def clean_url(url)
156
- unescape_url(url)
157
- end
158
-
159
- def setup_cache!(options)
160
- @storage_dir = options[:storage_dir] || DEFAULT_STORAGE_DIR
161
-
162
- FileUtils.mkdir_p(storage_dir) unless Dir.exist?(storage_dir)
163
-
164
- cache_file_name = options[:cache_file] || DEFAULT_CACHE_FILE_NAME
165
-
166
- @cache_file = File.join(storage_dir, cache_file_name)
167
-
168
- return unless File.exist?(@cache_file)
169
-
170
- contents = File.read(@cache_file)
171
- @cache_log = contents.empty? ? {} : JSON.parse(contents)
172
- end
173
-
174
- private
175
-
176
- def time_ago(measurement, unit)
177
- case unit
178
- when :months
179
- @cache_datetime >> -measurement
180
- when :weeks
181
- @cache_datetime - (measurement * 7)
182
- when :days
183
- @cache_datetime - measurement
184
- when :hours
185
- @cache_datetime - Rational(measurement / 24.0)
186
- end.to_time
187
- end
188
-
189
- def url_matches_type?(url, type)
190
- return true if type == :internal && url !~ URI_REGEXP
191
- return true if type == :external && url =~ URI_REGEXP
192
- end
193
- end
194
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class FaviconCheck < ::HTMLProofer::Check
4
- def run
5
- found = false
6
- @html.xpath('//link[not(ancestor::pre or ancestor::code)]').each do |node|
7
- favicon = create_element(node)
8
- next if favicon.ignore?
9
-
10
- found = true if favicon.rel.split.last.eql? 'icon'
11
- break if found
12
- end
13
-
14
- return if found
15
-
16
- return if immediate_redirect?
17
-
18
- add_issue('no favicon specified')
19
- end
20
-
21
- private
22
-
23
- # allow any instant-redirect meta tag
24
- def immediate_redirect?
25
- @html.xpath("//meta[@http-equiv='refresh']").attribute('content').value.start_with? '0;'
26
- rescue StandardError
27
- false
28
- end
29
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class HtmlCheck < ::HTMLProofer::Check
4
- # tags embedded in scripts are used in templating languages: http://git.io/vOovv
5
- SCRIPT_EMBEDS_MSG = /Element script embeds close tag/.freeze
6
- INVALID_TAG_MSG = /Tag ([\w\-:]+) invalid/.freeze
7
- INVALID_PREFIX = /Namespace prefix/.freeze
8
- PARSE_ENTITY_REF = /htmlParseEntityRef: no name/.freeze
9
- DOCTYPE_MSG = /Expected a doctype token/.freeze
10
- EOF_IN_TAG = /End of input in tag/.freeze
11
- MISMATCHED_TAGS = /That tag isn't allowed here/.freeze
12
-
13
- def run
14
- @html.errors.each do |error|
15
- add_issue(error.message, line: error.line) if report?(error.message)
16
- end
17
- end
18
-
19
- def report?(message)
20
- case message
21
- when SCRIPT_EMBEDS_MSG
22
- options[:validation][:report_script_embeds]
23
- when INVALID_TAG_MSG, INVALID_PREFIX
24
- options[:validation][:report_invalid_tags]
25
- when PARSE_ENTITY_REF
26
- options[:validation][:report_missing_names]
27
- when DOCTYPE_MSG
28
- options[:validation][:report_missing_doctype]
29
- when EOF_IN_TAG
30
- options[:validation][:report_eof_tags]
31
- when MISMATCHED_TAGS
32
- options[:validation][:report_mismatched_tags]
33
- else
34
- true
35
- end
36
- end
37
- end
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ImageCheck < ::HTMLProofer::Check
4
- SCREEN_SHOT_REGEX = /Screen(?: |%20)Shot(?: |%20)\d+-\d+-\d+(?: |%20)at(?: |%20)\d+.\d+.\d+/.freeze
5
-
6
- def empty_alt_tag?
7
- @img.alt.nil? || @img.alt.strip.empty?
8
- end
9
-
10
- def terrible_filename?
11
- @img.url =~ SCREEN_SHOT_REGEX
12
- end
13
-
14
- def missing_src?
15
- blank?(@img.url)
16
- end
17
-
18
- def run
19
- @html.css('img').each do |node|
20
- @img = create_element(node)
21
- line = node.line
22
- content = node.content
23
-
24
- next if @img.ignore?
25
-
26
- # screenshot filenames should return because of terrible names
27
- if terrible_filename?
28
- add_issue("image has a terrible filename (#{@img.url})", line: line, content: content)
29
- next
30
- end
31
-
32
- # does the image exist?
33
- if missing_src?
34
- add_issue('image has no src or srcset attribute', line: line, content: content)
35
- elsif @img.remote?
36
- add_to_external_urls(@img.url)
37
- elsif !@img.exists?
38
- add_issue("internal image #{@img.url} does not exist", line: line, content: content)
39
- end
40
-
41
- add_issue("image #{@img.url} does not have an alt attribute", line: line, content: content) if empty_alt_tag? && !@img.ignore_empty_alt? && !@img.ignore_alt?
42
-
43
- add_issue("image #{@img.url} uses the http scheme", line: line, content: content) if @img.check_img_http? && @img.scheme == 'http'
44
- end
45
-
46
- external_urls
47
- end
48
- end
@@ -1,182 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class LinkCheck < ::HTMLProofer::Check
4
- include HTMLProofer::Utils
5
-
6
- def missing_href?
7
- return blank?(@link.src) if @node.name == 'source'
8
-
9
- blank?(@link.href) && blank?(@link.name) && blank?(@link.id)
10
- end
11
-
12
- def placeholder?
13
- (!blank?(@link.id) || !blank?(@link.name)) && @link.href.nil?
14
- end
15
-
16
- def run
17
- @html.css('a, link, source').each do |node|
18
- @link = create_element(node)
19
- line = node.line
20
- content = node.to_s
21
-
22
- next if @link.ignore?
23
-
24
- next if placeholder?
25
- next if @link.allow_hash_href? && @link.href == '#'
26
-
27
- # is it even a valid URL?
28
- unless @link.valid?
29
- add_issue("#{@link.href} is an invalid URL", line: line, content: content)
30
- next
31
- end
32
-
33
- check_schemes(@link, line, content)
34
-
35
- # is there even an href?
36
- if missing_href?
37
- next if @link.allow_missing_href?
38
- # HTML5 allows dropping the href: http://git.io/vBX0z
39
- next if @html.internal_subset.nil? || (@html.internal_subset.name == 'html' && @html.internal_subset.external_id.nil?)
40
-
41
- add_issue('anchor has no href attribute', line: line, content: content)
42
- next
43
- end
44
-
45
- # intentionally here because we still want valid? & missing_href? to execute
46
- next if @link.non_http_remote?
47
-
48
- if !@link.href&.start_with?('#') && !@link.internal? && @link.remote?
49
- check_sri(line, content) if @link.check_sri? && node.name == 'link'
50
- # we need to skip these for now; although the domain main be valid,
51
- # curl/Typheous inaccurately return 404s for some links. cc https://git.io/vyCFx
52
- next if @link.respond_to?(:rel) && @link.rel == 'dns-prefetch'
53
-
54
- unless @link.path?
55
- add_issue("#{@link.href} is an invalid URL", line: line, content: content)
56
- next
57
- end
58
-
59
- add_to_external_urls(@link.href || @link.src)
60
- next
61
- elsif @link.internal?
62
- add_to_internal_urls(@link.href, InternalLink.new(@link, @path, line, content))
63
- add_issue("internally linking to #{@link.href}, which does not exist", line: line, content: content) if !@link.exists? && !@link.hash
64
- end
65
- end
66
-
67
- external_urls
68
- end
69
-
70
- def check_internal_link(link, path, line, content)
71
- # does the local directory have a trailing slash?
72
- if link.unslashed_directory?(link.absolute_path)
73
- add_issue("internally linking to a directory #{link.absolute_path} without trailing slash", path: path, line: line, content: content)
74
- return false
75
- end
76
-
77
- return true unless link.hash
78
-
79
- # verify the target hash
80
- handle_hash(link, path, line, content)
81
- end
82
-
83
- def check_schemes(link, line, content)
84
- case link.scheme
85
- when 'mailto'
86
- handle_mailto(link, line, content)
87
- when 'tel'
88
- handle_tel(link, line, content)
89
- when 'http'
90
- return unless @options[:enforce_https]
91
-
92
- add_issue("#{link.href} is not an HTTPS link", line: line, content: content)
93
- end
94
- end
95
-
96
- def handle_mailto(link, line, content)
97
- if link.path.empty?
98
- add_issue("#{link.href} contains no email address", line: line, content: content) unless link.ignore_empty_mailto?
99
- elsif !link.path.include?('@')
100
- add_issue("#{link.href} contains an invalid email address", line: line, content: content)
101
- end
102
- end
103
-
104
- def handle_tel(link, line, content)
105
- add_issue("#{link.href} contains no phone number", line: line, content: content) if link.path.empty?
106
- end
107
-
108
- def handle_hash(link, path, line, content)
109
- if link.internal? && !hash_exists?(link.html, link.hash) # rubocop:disable Style/GuardClause
110
- return add_issue("linking to internal hash ##{link.hash} that does not exist", path: path, line: line, content: content)
111
- elsif link.external?
112
- return external_link_check(link, line, content)
113
- end
114
-
115
- true
116
- end
117
-
118
- def external_link_check(link, line, content)
119
- if link.exists? # rubocop:disable Style/GuardClause
120
- target_html = create_nokogiri(link.absolute_path)
121
- return add_issue("linking to #{link.href}, but #{link.hash} does not exist", line: line, content: content) unless hash_exists?(target_html, link.hash)
122
- else
123
- return add_issue("trying to find hash of #{link.href}, but #{link.absolute_path} does not exist", line: line, content: content)
124
- end
125
-
126
- true
127
- end
128
-
129
- def hash_exists?(html, href_hash)
130
- decoded_href_hash = Addressable::URI.unescape(href_hash)
131
- fragment_ids = [href_hash, decoded_href_hash]
132
- # https://www.w3.org/TR/html5/single-page.html#scroll-to-fragid
133
- fragment_ids.include?('top') || !find_fragments(html, fragment_ids).empty?
134
- end
135
-
136
- def find_fragments(html, fragment_ids)
137
- xpaths = fragment_ids.flat_map do |frag_id|
138
- escaped_frag_id = "'#{frag_id.split("'").join("', \"'\", '")}', ''"
139
- [
140
- "//*[case_sensitive_equals(@id, concat(#{escaped_frag_id}))]",
141
- "//*[case_sensitive_equals(@name, concat(#{escaped_frag_id}))]"
142
- ]
143
- end
144
- xpaths << XpathFunctions.new
145
-
146
- html.xpath(*xpaths)
147
- end
148
-
149
- # Whitelist for affected elements from Subresource Integrity specification
150
- # https://w3c.github.io/webappsec-subresource-integrity/#link-element-for-stylesheets
151
- SRI_REL_TYPES = %(stylesheet)
152
-
153
- def check_sri(line, content)
154
- return unless SRI_REL_TYPES.include?(@link.rel)
155
-
156
- if !defined?(@link.integrity) && !defined?(@link.crossorigin)
157
- add_issue("SRI and CORS not provided in: #{@link.src}", line: line, content: content)
158
- elsif !defined?(@link.integrity)
159
- add_issue("Integrity is missing in: #{@link.src}", line: line, content: content)
160
- elsif !defined?(@link.crossorigin)
161
- add_issue("CORS not provided for external resource in: #{@link.src}", line: line, content: content)
162
- end
163
- end
164
-
165
- class XpathFunctions
166
- def case_sensitive_equals(node_set, str_to_match)
167
- node_set.find_all { |node| node.to_s.== str_to_match.to_s }
168
- end
169
- end
170
-
171
- class InternalLink
172
- attr_reader :link, :href, :path, :line, :content
173
-
174
- def initialize(link, path, line, content)
175
- @link = link
176
- @href = @link.href
177
- @path = path
178
- @line = line
179
- @content = content
180
- end
181
- end
182
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class OpenGraphElement < ::HTMLProofer::Element
4
- attr_reader :src
5
-
6
- def initialize(obj, check, logger)
7
- super(obj, check, logger)
8
- # Fake up src from the content attribute
9
- instance_variable_set('@src', @content)
10
-
11
- @src.insert 0, 'http:' if %r{^//}.match?(@src)
12
- end
13
- end
14
-
15
- class OpenGraphCheck < ::HTMLProofer::Check
16
- def missing_src?
17
- !@opengraph.src
18
- end
19
-
20
- def empty_src?
21
- blank?(@opengraph.src)
22
- end
23
-
24
- def run
25
- @html.css('meta[property="og:url"], meta[property="og:image"]').each do |m|
26
- @opengraph = OpenGraphElement.new(m, self, @logger)
27
-
28
- next if @opengraph.ignore?
29
-
30
- # does the opengraph exist?
31
- if missing_src?
32
- add_issue('open graph has no content attribute', line: m.line, content: m.content)
33
- elsif empty_src?
34
- add_issue('open graph content attribute is empty', line: m.line, content: m.content)
35
- elsif !@opengraph.valid?
36
- add_issue("#{@opengraph.src} is an invalid URL", line: m.line)
37
- elsif @opengraph.remote?
38
- add_to_external_urls(@opengraph.url)
39
- else
40
- add_issue("internal open graph #{@opengraph.url} does not exist", line: m.line, content: m.content) unless @opengraph.exists?
41
- end
42
- end
43
-
44
- external_urls
45
- end
46
- end