css_parser 3.2.0 → 3.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed961d9c30e5f86976f527c41b777f29bdd558b5e149985e536ba74876fbb6c8
4
- data.tar.gz: d12338993a30625469c3cd7ff81932744c877f945a4b361a524744593589a820
3
+ metadata.gz: b1b890ee0d537d338ae0a218c68424271242859ba814a52c27fcd05ffefe1acd
4
+ data.tar.gz: 147e41dae4da802d96124fc07b488b122967a44c0ff79bfe623291c1795e0d9b
5
5
  SHA512:
6
- metadata.gz: c1c189987171a935f347f7fe7f1f528ae2b2ce2d6880eaf6f124ea05c45b4af02685bf765b128188fdc33add0bde3b76b401a99d8268f1534feba6e962a35632
7
- data.tar.gz: 5cc19fa3c0047c20331240acd7c3064afdae0d43227de44a7a347eb7239119b09b287225cfaf8840535aa59ce9511f7b467c059b18298424c55231276cb2db3f
6
+ metadata.gz: 76728b4b816f4b620f28198b6fd5f92f005733bec3afba0df5707d8daf99ab650fd0a0143a6222fc53c5453c32c3a13a76eb4877f3c06f5dc903a559a18d200f
7
+ data.tar.gz: a5b5a901312932dd9d3c50fa9a6cd4ea549180f57a7e4d85d700d5c3ec58f458927be66c2eb7169165333e153d58061dbdfa98c75c0e6ffb6a81181bc78fdd6f
@@ -173,6 +173,21 @@ module CssParser
173
173
  load_uri!(import_uri, import_options)
174
174
  elsif options[:base_dir]
175
175
  import_options[:base_dir] = options[:base_dir]
176
+ # The import path comes from (potentially untrusted) CSS
177
+ # content, not from the caller. Flag this load so load_file!
178
+ # keeps it contained within the import root and refuses
179
+ # absolute paths / `..` traversal that escape it -- otherwise
180
+ # @import turns into arbitrary local file disclosure. This is
181
+ # the base_dir sibling of the file://-via-base_uri hole closed
182
+ # in GHSA-9pmc-p236-855h, which left this arm untouched.
183
+ #
184
+ # import_root is the top-level base_dir, threaded through
185
+ # nested imports so containment is always checked against it.
186
+ # That still allows legitimate `../` imports that stay inside
187
+ # the tree (e.g. subdir/import2.css -> ../simple.css) while
188
+ # rejecting anything that leaves it.
189
+ import_options[:from_import] = true
190
+ import_options[:import_root] = options[:import_root] || options[:base_dir]
176
191
  load_file!(import_path, import_options)
177
192
  end
178
193
  end
@@ -558,8 +573,18 @@ module CssParser
558
573
  end
559
574
 
560
575
  # Load a local CSS file.
576
+ #
577
+ # +from_import+ (internal) marks a load whose +file_name+ originates
578
+ # from an @import rule in parsed CSS rather than from the caller. Such
579
+ # a path is attacker-influenced, so it is contained within the import
580
+ # root (+import_root+, the top-level +base_dir+): absolute paths and
581
+ # `..` traversal that escape the root are refused (raising
582
+ # RemoteFileError when io_exceptions are on, otherwise a silent no-op).
583
+ # Direct callers (the default, +from_import: false+) are unaffected --
584
+ # load_file! remains the explicit, trusted local-file API and may load
585
+ # any readable path.
561
586
  def load_file!(file_name, options = {}, deprecated = nil)
562
- opts = {base_dir: nil, media_types: :all}
587
+ opts = {base_dir: nil, media_types: :all, from_import: false, import_root: nil}
563
588
 
564
589
  if options.is_a? Hash
565
590
  opts.merge!(options)
@@ -570,7 +595,29 @@ module CssParser
570
595
  opts[:media_types] = deprecated if deprecated
571
596
  end
572
597
 
598
+ # Pin the containment root to the first base_dir seen in the chain.
599
+ # load_file! resets base_dir to each file's own directory as it
600
+ # recurses, so without this a file loaded from a subdirectory would
601
+ # shrink the sandbox and reject a legitimate `..` back toward the
602
+ # caller's original base_dir.
603
+ opts[:import_root] ||= opts[:base_dir]
604
+
573
605
  file_name = File.expand_path(file_name, opts[:base_dir])
606
+
607
+ # Containment check for @import-sourced paths: the resolved file must
608
+ # stay inside the import root. Rejects both `@import "/etc/passwd"`
609
+ # (absolute) and `@import "../../secret"` (traversal escaping the
610
+ # tree) from untrusted CSS, while leaving direct load_file! callers
611
+ # untouched and still permitting `..` that stays within the root.
612
+ if opts[:from_import] && (opts[:import_root] || opts[:base_dir])
613
+ root = File.expand_path((opts[:import_root] || opts[:base_dir]).to_s)
614
+ unless file_name == root || file_name.start_with?(root + File::SEPARATOR)
615
+ raise RemoteFileError, file_name if @options[:io_exceptions]
616
+
617
+ return
618
+ end
619
+ end
620
+
574
621
  return unless File.readable?(file_name)
575
622
  return unless circular_reference_check(file_name)
576
623
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CssParser
4
- VERSION = '3.2.0'.freeze
4
+ VERSION = '3.3.0'.freeze
5
5
  end
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.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Dunae