active_storage_validations 3.0.5 → 3.0.6

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: 3d55df23fe62535fca12b811b9f1ff5b7ae1c1636899e9674a88b2077ce688dc
4
- data.tar.gz: a0cafde4518c1ffbd84fcd69a88f1e56239b7845c81b6294cdf96ba394f88883
3
+ metadata.gz: fb09e2b76b2325f8dab17bd4774b91bb1f041766e3705e563f338951d04e191a
4
+ data.tar.gz: 56ed87a99def7941a8d17e4433980c947c8771c3dd42141d0f1f13702c8556c2
5
5
  SHA512:
6
- metadata.gz: 70333f40a64146d92428921d0e6adb4ce1c5a3cdb547c3378def688d735f1ecdad0665143c1e3bc5f364b5f5a924dd36403dce618e6937a05594eb09799f8a2c
7
- data.tar.gz: 966a85f0f4a99283ba24e42a657d9dc8b9cc384161c9a034e4568d90ede2b4710d57f79aea1cee4a2b995350d7fd9a0b431afba0aab8993956284019c1ca7506
6
+ metadata.gz: 84fb802a4958ef5b3e00e088383768540849a883b3f2bd757b19ed24c0516d1fd8a498aaaac93ef0e02d6105dca0c6058e5a85ac3a4d1a1b0d79cd1e682dfb45
7
+ data.tar.gz: de96e6b525ca4ff0e9e3342b9bd1719c199e956b807467c62d6f063e649f80348e9b323525b1c99f203b025e08a995bc8534b1fd01c434579eca5526ee2ced61
@@ -19,6 +19,28 @@ module ActiveStorageValidations
19
19
  ].freeze
20
20
  METADATA_KEYS = %i[].freeze
21
21
 
22
+ # Content types whose libvips loaders are marked untrusted / unfuzzed
23
+ # (VIPS_OPERATION_UNTRUSTED). After Rails calls `Vips.block_untrusted(true)`,
24
+ # analysis of these types returns no width/height (empty metadata here),
25
+ # which is not the same as a corrupt / unprocessable file.
26
+ #
27
+ # Matches the formats called out in the Active Storage security release
28
+ # changelogs (7.2.3.2 / 8.0.5.1 / 8.1.3.1):
29
+ # https://github.com/rails/rails/blob/v8.1.3.1/activestorage/CHANGELOG.md
30
+ # https://github.com/rails/rails/security/advisories/GHSA-xr9x-r78c-5hrm
31
+ VIPS_UNTRUSTED_CONTENT_TYPES = %w[
32
+ image/bmp
33
+ image/vnd.microsoft.icon
34
+ image/vnd.adobe.photoshop
35
+ image/svg+xml
36
+ image/jxl
37
+ image/jp2
38
+ image/x-portable-anymap
39
+ image/x-portable-bitmap
40
+ image/x-portable-graymap
41
+ image/x-portable-pixmap
42
+ ].freeze
43
+
22
44
  def validate_each(record, attribute, _value)
23
45
  return if no_attachments?(record, attribute)
24
46
 
@@ -28,10 +50,47 @@ module ActiveStorageValidations
28
50
  private
29
51
 
30
52
  def is_valid?(record, attribute, attachable, metadata)
31
- return if !metadata.empty?
53
+ return if metadata.present?
54
+ return if vips_unanalyzable_by_policy?(attachable)
32
55
 
33
56
  errors_options = initialize_error_options(options, attachable)
34
57
  add_error(record, attribute, ERROR_TYPES.first, **errors_options)
35
58
  end
59
+
60
+ def vips_unanalyzable_by_policy?(attachable)
61
+ image_processor == :vips &&
62
+ self.class.vips_untrusted_operations_blocked? &&
63
+ VIPS_UNTRUSTED_CONTENT_TYPES.include?(attachable_content_type(attachable))
64
+ end
65
+
66
+ class << self
67
+ # Detect whether libvips is currently blocking untrusted operations.
68
+ # There is no public getter for `Vips.block_untrusted`, so we probe whether
69
+ # an untrusted loader (svgload) is available via `vips_foreign_find_load`.
70
+ def vips_untrusted_operations_blocked?
71
+ return @vips_untrusted_operations_blocked if defined?(@vips_untrusted_operations_blocked)
72
+
73
+ @vips_untrusted_operations_blocked = detect_vips_untrusted_operations_blocked?
74
+ end
75
+
76
+ def reset_vips_untrusted_operations_blocked_cache!
77
+ remove_instance_variable(:@vips_untrusted_operations_blocked) if defined?(@vips_untrusted_operations_blocked)
78
+ end
79
+
80
+ private
81
+
82
+ def detect_vips_untrusted_operations_blocked?
83
+ require "vips"
84
+ require "tempfile"
85
+
86
+ Tempfile.create([ "asv_vips_untrusted_probe", ".svg" ]) do |file|
87
+ file.write('<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>')
88
+ file.flush
89
+ ::Vips.vips_foreign_find_load(file.path).nil?
90
+ end
91
+ rescue LoadError, StandardError
92
+ false
93
+ end
94
+ end
36
95
  end
37
96
  end
@@ -44,7 +44,7 @@ module ActiveStorageValidations
44
44
  return to_enum(:attachables_and_blobs, record, attribute) if changes.blank? || !block_given?
45
45
 
46
46
  if changes.is_a?(ActiveStorage::Attached::Changes::CreateMany)
47
- changes.attachables.zip(changes.blobs).uniq { |_, blob| blob&.id }.each do |attachable, blob|
47
+ changes.attachables.zip(changes.blobs).uniq { |attachable, blob| attachable_blob_key(attachable, blob) }.each do |attachable, blob|
48
48
  yield attachable, blob
49
49
  end
50
50
  else
@@ -52,6 +52,12 @@ module ActiveStorageValidations
52
52
  end
53
53
  end
54
54
 
55
+ def attachable_blob_key(attachable, blob)
56
+ return [ :blob_id, blob.id ] if blob&.id
57
+
58
+ [ :attachable, attachable.object_id ]
59
+ end
60
+
55
61
  def changes_for(record, attribute)
56
62
  if record.public_send(attribute).is_a?(ActiveStorage::Attached::One)
57
63
  record.attachment_changes[attribute.to_s].presence || record.public_send(attribute)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorageValidations
4
- VERSION = "3.0.5"
4
+ VERSION = "3.0.6"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_storage_validations
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.5
4
+ version: 3.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Kasyanchuk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-17 00:00:00.000000000 Z
11
+ date: 2026-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: 4.9.5
111
+ - !ruby/object:Gem::Dependency
112
+ name: minitest
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '5.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '5.0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: minitest-focus
113
127
  requirement: !ruby/object:Gem::Requirement