css_parser 3.0.0 → 3.1.0
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 +4 -4
- data/lib/css_parser/parser.rb +52 -6
- data/lib/css_parser/version.rb +1 -1
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c675c0d95c9efce004a49e17cf09c880c44c4c11375f754c50586052c03202d
|
|
4
|
+
data.tar.gz: 2d729de7e39e045d1e2594e4c847c495ef7765d34c37b76f53ef9348de0e1f96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ab3178e90c357ad4ede9c90ac70526bbe4b90a475690afa60551d4acfc2bb41a70db125bdda040a1bbcfa8f4e3dfa454401116aec745e639af9244c71c3f7c6a
|
|
7
|
+
data.tar.gz: 15838f208ef82d6a2d5adbddcb5486ad5b13b0d88f32e3923b6f7bd064fbbe08dc4f6dc4af0a299a517ff7c4faec3010a044b59209b809248e58622b08a29c4a
|
data/lib/css_parser/parser.rb
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'strscan'
|
|
4
|
+
require 'digest'
|
|
5
|
+
require 'base64'
|
|
4
6
|
|
|
5
7
|
module CssParser
|
|
6
8
|
# Exception class used for any errors encountered while downloading remote files.
|
|
7
9
|
class RemoteFileError < IOError; end
|
|
8
10
|
|
|
11
|
+
# Exception class used when a fetched remote file fails Subresource Integrity verification.
|
|
12
|
+
class IntegrityError < RemoteFileError; end
|
|
13
|
+
|
|
9
14
|
# Exception class used if a request is made to load a CSS file more than once.
|
|
10
15
|
class CircularReferenceError < StandardError; end
|
|
11
16
|
|
|
@@ -37,6 +42,15 @@ module CssParser
|
|
|
37
42
|
# was GHSA-9pmc-p236-855h.
|
|
38
43
|
REMOTE_ALLOWED_SCHEMES = %w[http https].freeze
|
|
39
44
|
|
|
45
|
+
# Subresource Integrity hash algorithms this library can verify, strongest first.
|
|
46
|
+
# Mirrors the SRI spec's "agility" rule (https://www.w3.org/TR/SRI/#agility):
|
|
47
|
+
# if `integrity` lists multiple algorithms, only the strongest is checked.
|
|
48
|
+
INTEGRITY_ALGORITHMS = {
|
|
49
|
+
'sha512' => Digest::SHA512,
|
|
50
|
+
'sha384' => Digest::SHA384,
|
|
51
|
+
'sha256' => Digest::SHA256
|
|
52
|
+
}.freeze
|
|
53
|
+
|
|
40
54
|
# Array of CSS files that have been loaded.
|
|
41
55
|
attr_reader :loaded_uris
|
|
42
56
|
|
|
@@ -492,9 +506,7 @@ module CssParser
|
|
|
492
506
|
#
|
|
493
507
|
# You can also pass in file://test.css
|
|
494
508
|
#
|
|
495
|
-
# See add_block! for options
|
|
496
|
-
#
|
|
497
|
-
# Deprecated: originally accepted three params: `uri`, `base_uri` and `media_types`
|
|
509
|
+
# See add_block! for options and integrity_matches? for integrity
|
|
498
510
|
def load_uri!(uri, options = {}, deprecated = nil)
|
|
499
511
|
uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme
|
|
500
512
|
|
|
@@ -502,7 +514,7 @@ module CssParser
|
|
|
502
514
|
|
|
503
515
|
if options.is_a? Hash
|
|
504
516
|
opts.merge!(options)
|
|
505
|
-
else
|
|
517
|
+
else # TODO: start raising
|
|
506
518
|
warn '[DEPRECATION] `load_uri!` with positional arguments is deprecated. ' \
|
|
507
519
|
'Please use keyword arguments instead.', uplevel: 1
|
|
508
520
|
opts[:base_uri] = options if options.is_a? String
|
|
@@ -538,7 +550,7 @@ module CssParser
|
|
|
538
550
|
end
|
|
539
551
|
read_local_file(uri)
|
|
540
552
|
else
|
|
541
|
-
src_and_charset, = read_remote_file(uri) # skip charset
|
|
553
|
+
src_and_charset, = read_remote_file(uri, integrity: opts[:integrity]) # skip charset
|
|
542
554
|
src_and_charset
|
|
543
555
|
end
|
|
544
556
|
|
|
@@ -677,10 +689,14 @@ module CssParser
|
|
|
677
689
|
# is still validated on every redirect hop, so cross-scheme
|
|
678
690
|
# redirect to `file://` (the original GHSA-9pmc-p236-855h sink)
|
|
679
691
|
# remains closed even on this opt-in path.
|
|
692
|
+
#
|
|
693
|
+
# `integrity:`, when given, is verified against the raw response body
|
|
694
|
+
# (before charset decoding, matching Subresource Integrity semantics)
|
|
695
|
+
# -- see `integrity_matches?` and `load_uri!`'s documentation.
|
|
680
696
|
#--
|
|
681
697
|
# TODO: add option to fail silently or throw and exception on a 404
|
|
682
698
|
#++
|
|
683
|
-
def read_remote_file(uri) # :nodoc:
|
|
699
|
+
def read_remote_file(uri, integrity: nil) # :nodoc:
|
|
684
700
|
uri = Addressable::URI.parse(uri.to_s)
|
|
685
701
|
|
|
686
702
|
unless circular_reference_check(uri.to_s)
|
|
@@ -711,11 +727,19 @@ module CssParser
|
|
|
711
727
|
return '', nil
|
|
712
728
|
end
|
|
713
729
|
|
|
730
|
+
if integrity && !integrity_matches?(res.body, integrity)
|
|
731
|
+
raise IntegrityError, uri.to_s if @options[:io_exceptions]
|
|
732
|
+
|
|
733
|
+
return nil, nil
|
|
734
|
+
end
|
|
735
|
+
|
|
714
736
|
charset = res.respond_to?(:charset) ? res.encoding : 'utf-8'
|
|
715
737
|
src = res.body
|
|
716
738
|
src.encode!('UTF-8', charset) if charset
|
|
717
739
|
|
|
718
740
|
[src, charset]
|
|
741
|
+
rescue IntegrityError # already is a RemoteFileError
|
|
742
|
+
raise
|
|
719
743
|
rescue
|
|
720
744
|
raise RemoteFileError, uri.to_s if @options[:io_exceptions]
|
|
721
745
|
|
|
@@ -723,6 +747,28 @@ module CssParser
|
|
|
723
747
|
end
|
|
724
748
|
end
|
|
725
749
|
|
|
750
|
+
# Verifies +body+ (raw response bytes, not yet charset-decoded) against a
|
|
751
|
+
# Subresource Integrity value -- a `<algorithm>-<base64 digest>` token, or
|
|
752
|
+
# several whitespace-separated tokens (https://www.w3.org/TR/SRI/#the-integrity-attribute).
|
|
753
|
+
#
|
|
754
|
+
# - Tokens using an algorithm this library doesn't recognize are ignored
|
|
755
|
+
# - Multiple recognized algorithms only check the strongest one is checked
|
|
756
|
+
# - Value containing no recognized algorithm is treated as unverifiable and matches by default
|
|
757
|
+
def integrity_matches?(body, integrity) # :nodoc:
|
|
758
|
+
candidates = integrity.to_s.split.filter_map do |token|
|
|
759
|
+
algorithm, value = token.split('-', 2)
|
|
760
|
+
[algorithm, value] if algorithm && value && INTEGRITY_ALGORITHMS.key?(algorithm)
|
|
761
|
+
end
|
|
762
|
+
return true if candidates.empty?
|
|
763
|
+
|
|
764
|
+
algorithms_present = candidates.map(&:first)
|
|
765
|
+
algorithm = INTEGRITY_ALGORITHMS.each_key.find { |a| algorithms_present.include?(a) }
|
|
766
|
+
expected_values = candidates.select { |a, _v| a == algorithm }.map { |_a, v| v }
|
|
767
|
+
digest_class = INTEGRITY_ALGORITHMS.fetch(algorithm)
|
|
768
|
+
|
|
769
|
+
expected_values.include?(Base64.strict_encode64(digest_class.digest(body)))
|
|
770
|
+
end
|
|
771
|
+
|
|
726
772
|
# Net::HTTP path used only when `allow_local_network: true`. Validates
|
|
727
773
|
# the URI scheme on every redirect hop so a `Location: file://...`
|
|
728
774
|
# cannot be followed even on this opt-in code path.
|
data/lib/css_parser/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: css_parser
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Dunae
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: base64
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: ssrf_filter
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|