valkyrie 3.6.0 → 3.6.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: dd992e314f4262841181b047c1069253b9ae74131fe5e88760ff266ee934d7b4
4
- data.tar.gz: 0b9d0d17a2853fb2eda435a3afc2a6202af17b19a4d5d0ad4ecfbeaea0e34de5
3
+ metadata.gz: 6c19142683e0f4cfdc60b4da2e25a5e0caa37ba49916469e86c15fe89ea147d6
4
+ data.tar.gz: a62e7d67469524073b275e057c69fc3b532ea869779c4a11e80da88142f23a58
5
5
  SHA512:
6
- metadata.gz: a00872e00399a98d2435b846b6742af2095624e1054c80079e98b94b8ca8fe92d0f114d13f67668b348dfd0d3ab972c00abeef33f58cc5caab90fcc7b94ba76a
7
- data.tar.gz: f8c7dcce4c7e2513d272e53a4c187eba830c16d37483dd0678f60921660b6d8b22f3dc051d9e459b6c7d6363b0c4b518c7be6cccec0c8ef854512075e8c2fbea
6
+ metadata.gz: 7f2b508542a8c1b4b54273de73e37dafddeb453cfb26ececd8dc19f0600fecc140839c9cfe417d82f2dac25aa7d1f8a4c3f8907faf782dc5ec874ec8b4c8808d
7
+ data.tar.gz: f81fbbe0cafe17915422106dc8046ecd72225646a0cb747d62caa81c449f72c7f8d7674dbde8ac1d01683aa0b934392b63da2cddc19786bed33b7cfe7775c3dc
data/.circleci/config.yml CHANGED
@@ -26,8 +26,9 @@ jobs:
26
26
  JAVA_OPTIONS: "-Djetty.http.port=8988"
27
27
  - image: fcrepo/fcrepo:6.5-tomcat9
28
28
  environment:
29
- CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
30
- JAVA_OPTS: "-Djetty.http.port=8978 -Dfcrepo.dynamic.jms.port=61619 -Dfcrepo.dynamic.stomp.port=61615 -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
29
+ CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
30
+ JAVA_OPTS: "-Djetty.http.port=8978 -Dfcrepo.dynamic.jms.port=61619 -Dfcrepo.dynamic.stomp.port=61615"
31
+ ENCODED_SOLIDUS_HANDLING: "passthrough"
31
32
  - image: solr:9.9.0
32
33
  command: bin/solr -cloud -noprompt -f -p 8994
33
34
  environment:
data/.lando.yml CHANGED
@@ -45,6 +45,7 @@ services:
45
45
  ports:
46
46
  - 8978:8080
47
47
  environment:
48
- CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
49
- JAVA_OPTS: "-Dfcrepo.dynamic.jms.port=61619 -Dfcrepo.dynamic.stomp.port=61615 -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
48
+ CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
49
+ JAVA_OPTS: "-Dfcrepo.dynamic.jms.port=61619 -Dfcrepo.dynamic.stomp.port=61615"
50
+ ENCODED_SOLIDUS_HANDLING: "passthrough"
50
51
  portforward: true
data/Appraisals CHANGED
@@ -5,8 +5,10 @@ end
5
5
 
6
6
  appraise "activerecord-8-0" do
7
7
  gem "activerecord", "~> 8.0.0"
8
+ gem "json", "< 3.0"
8
9
  end
9
10
 
10
11
  appraise "activerecord-7-2" do
11
12
  gem "activerecord", "~> 7.2.0"
13
+ gem "json", "< 3.0"
12
14
  end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # v3.6.2 2026-10-30
2
+
3
+ * Prevent disk adapter from reading/writing outside base path. ([tpendragon](https://github.com/tpendragon))
4
+
5
+ # v3.6.1 2026-07-30
6
+
7
+ * Set binary encoding for fedora storage adapter responses. ([dlpierce](https://github.com/dlpierce))
8
+ * Use new `ENCODED_SOLIDUS_HANDLING` option for OCFL Fedora containers. ([dlpierce](https://github.com/dlpierce))
9
+ * Upgrade to simplecov 1.0. ([dlpierce](https://github.com/dlpierce))
10
+
1
11
  # v3.6.0 2026-07-23
2
12
 
3
13
  * Update dependencies. ([tpendragon](https://github.com/tpendragon))
@@ -7,5 +7,6 @@ gem "activerecord", "~> 7.2.0"
7
7
  gem "ldp"
8
8
  gem "pg"
9
9
  gem "rsolr"
10
+ gem "json", "< 3.0"
10
11
 
11
12
  gemspec path: "../"
@@ -7,5 +7,6 @@ gem "activerecord", "~> 8.0.0"
7
7
  gem "ldp"
8
8
  gem "pg"
9
9
  gem "rsolr"
10
+ gem "json", "< 3.0"
10
11
 
11
12
  gemspec path: "../"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # :nocov:
2
+ # simplecov:disable
3
3
  begin
4
4
  gem 'ldp'
5
5
  rescue Gem::LoadError => e
@@ -7,7 +7,7 @@ rescue Gem::LoadError => e
7
7
  "You are using the Fedora adapter without installing the #{e.name} gem. "\
8
8
  "Add `gem '#{e.name}'` to your Gemfile."
9
9
  end
10
- # :nocov:
10
+ # simplecov:enable
11
11
  module Valkyrie::Persistence
12
12
  # Implements the DataMapper Pattern to store metadata into Fedora
13
13
  module Fedora
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # :nocov:
2
+ # simplecov:disable
3
3
  begin
4
4
  gem 'pg'
5
5
  gem 'activerecord'
@@ -8,7 +8,7 @@ rescue Gem::LoadError => e
8
8
  "You are using the Postgres adapter without installing the #{e.name} gem. "\
9
9
  "Add `gem '#{e.name}'` to your Gemfile."
10
10
  end
11
- # :nocov:
11
+ # simplecov:enable
12
12
  module Valkyrie::Persistence
13
13
  # Implements the DataMapper Pattern to store metadata into Postgres
14
14
  module Postgres
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # :nocov:
2
+ # simplecov:disable
3
3
  begin
4
4
  gem 'rsolr'
5
5
  rescue Gem::LoadError => e
@@ -7,7 +7,7 @@ rescue Gem::LoadError => e
7
7
  "You are using the Solr adapter without installing the #{e.name} gem. "\
8
8
  "Add `gem '#{e.name}'` to your Gemfile."
9
9
  end
10
- # :nocov:
10
+ # simplecov:enable
11
11
  module Valkyrie::Persistence
12
12
  # Implements the DataMapper Pattern to store metadata into Solr
13
13
  module Solr
@@ -94,6 +94,7 @@ RSpec.shared_examples 'a Valkyrie::StorageAdapter' do
94
94
  expect(file).to respond_to(:read).with(0).arguments
95
95
  expect(file).to respond_to(:rewind).with(0).arguments
96
96
  expect(file.stream).to respond_to(:read)
97
+ expect(file.stream.external_encoding).to eq(Encoding::ASCII_8BIT)
97
98
  new_file = Tempfile.new
98
99
  expect { IO.copy_stream(file, new_file) }.not_to raise_error
99
100
 
@@ -18,6 +18,7 @@ module Valkyrie::Storage
18
18
  # @return [Valkyrie::StorageAdapter::File]
19
19
  def upload(file:, original_filename:, resource: nil, **_extra_arguments)
20
20
  new_path = path_generator.generate(resource: resource, file: file, original_filename: original_filename)
21
+ raise ArgumentError, "path must be within base_path" unless within_base_path?(new_path)
21
22
  FileUtils.mkdir_p(new_path.parent)
22
23
  file_mover.call(file.path, new_path)
23
24
  find_by(id: Valkyrie::ID.new("#{protocol}#{new_path}"))
@@ -26,7 +27,7 @@ module Valkyrie::Storage
26
27
  # @param id [Valkyrie::ID]
27
28
  # @return [Boolean] true if this adapter can handle this type of identifer
28
29
  def handles?(id:)
29
- id.to_s.start_with?("#{protocol}#{base_path}")
30
+ id.to_s.start_with?(protocol) && within_base_path?(id.to_s.delete_prefix(protocol))
30
31
  end
31
32
 
32
33
  # @param feature [Symbol] Feature to test for.
@@ -41,7 +42,9 @@ module Valkyrie::Storage
41
42
  end
42
43
 
43
44
  def file_path(id)
44
- id.to_s.delete_prefix(protocol)
45
+ path = id.to_s.delete_prefix(protocol)
46
+ raise Valkyrie::StorageAdapter::FileNotFound unless within_base_path?(path)
47
+ path
45
48
  end
46
49
 
47
50
  # Return the file associated with the given identifier
@@ -54,6 +57,12 @@ module Valkyrie::Storage
54
57
  raise Valkyrie::StorageAdapter::FileNotFound
55
58
  end
56
59
 
60
+ def within_base_path?(path)
61
+ expanded_base_path = File.expand_path(base_path)
62
+ expanded_path = File.expand_path(path)
63
+ expanded_path == expanded_base_path || expanded_path.start_with?("#{expanded_base_path}/")
64
+ end
65
+
57
66
  ## LazyFile takes File.open parameters but doesn't leave a file handle open on
58
67
  # instantiation. This way StorageAdapter#find_by doesn't open a handle
59
68
  # silently and never clean up after itself.
@@ -196,7 +196,7 @@ module Valkyrie::Storage
196
196
 
197
197
  # @return [StringIO]
198
198
  def response(id:)
199
- io = StringIO.new
199
+ io = StringIO.new.binmode
200
200
  response = connection.http.get(fedora_identifier(id: id)) do |request|
201
201
  request.options.on_data = proc { |chunk, _size| io.write(chunk) }
202
202
  end
@@ -23,6 +23,7 @@ module Valkyrie::Storage
23
23
  def upload(file:, original_filename:, resource: nil, paused: false, **extra_arguments)
24
24
  version_timestamp = current_timestamp
25
25
  new_path = path_generator.generate(resource: resource, file: file, original_filename: "v-#{version_timestamp}-#{original_filename}")
26
+ raise ArgumentError, "path must be within base_path" unless within_base_path?(new_path)
26
27
  # If we've gone faster than milliseconds here, pause a millisecond and
27
28
  # re-call. Probably only an issue for test suites.
28
29
  return sleep(0.001) && upload(file: file, original_filename: original_filename, resource: resource, paused: true, **extra_arguments) if !paused && File.exist?(new_path)
@@ -58,7 +59,7 @@ module Valkyrie::Storage
58
59
  # @param id [Valkyrie::ID]
59
60
  # @return [Boolean] true if this adapter can handle this type of identifer
60
61
  def handles?(id:)
61
- id.to_s.start_with?("#{protocol}#{base_path}")
62
+ id.to_s.start_with?(protocol) && within_base_path?(id.to_s.delete_prefix(protocol))
62
63
  end
63
64
 
64
65
  # @param feature [Symbol] Feature to test for.
@@ -113,13 +114,22 @@ module Valkyrie::Storage
113
114
  end
114
115
 
115
116
  def file_path(version_id)
116
- version_id.to_s.gsub(/^#{Regexp.escape(protocol)}/, '')
117
+ path = version_id.to_s.gsub(/^#{Regexp.escape(protocol)}/, '')
118
+ raise Valkyrie::StorageAdapter::FileNotFound unless within_base_path?(path)
119
+ path
120
+ end
121
+
122
+ def within_base_path?(path)
123
+ expanded_base_path = File.expand_path(base_path)
124
+ expanded_path = File.expand_path(path)
125
+ expanded_path == expanded_base_path || expanded_path.start_with?("#{expanded_base_path}/")
117
126
  end
118
127
 
119
128
  # @return VersionId A VersionId value that's resolved a current reference,
120
129
  # so we can access the `version_id` and current reference.
121
130
  def version_id(id)
122
131
  id = VersionId.new(id)
132
+ raise Valkyrie::StorageAdapter::FileNotFound unless within_base_path?(id.file_path)
123
133
  return id unless id.versioned?
124
134
  id.resolve_current
125
135
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Valkyrie
3
- VERSION = "3.6.0"
3
+ VERSION = "3.6.2"
4
4
  end
data/valkyrie.gemspec CHANGED
@@ -43,7 +43,7 @@ Gem::Specification.new do |spec|
43
43
  spec.add_development_dependency "webmock"
44
44
  spec.add_development_dependency "pry-byebug"
45
45
  spec.add_development_dependency "database_cleaner"
46
- spec.add_development_dependency "simplecov"
46
+ spec.add_development_dependency "simplecov", "~> 1.0"
47
47
  spec.add_development_dependency "bixby"
48
48
  spec.add_development_dependency 'yard'
49
49
  spec.add_development_dependency 'solr_wrapper'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: valkyrie
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 3.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Trey Pendragon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-23 00:00:00.000000000 Z
11
+ date: 2026-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-struct
@@ -312,16 +312,16 @@ dependencies:
312
312
  name: simplecov
313
313
  requirement: !ruby/object:Gem::Requirement
314
314
  requirements:
315
- - - ">="
315
+ - - "~>"
316
316
  - !ruby/object:Gem::Version
317
- version: '0'
317
+ version: '1.0'
318
318
  type: :development
319
319
  prerelease: false
320
320
  version_requirements: !ruby/object:Gem::Requirement
321
321
  requirements:
322
- - - ">="
322
+ - - "~>"
323
323
  - !ruby/object:Gem::Version
324
- version: '0'
324
+ version: '1.0'
325
325
  - !ruby/object:Gem::Dependency
326
326
  name: bixby
327
327
  requirement: !ruby/object:Gem::Requirement