VYPR
Critical severityNVD Advisory· Published May 26, 2022· Updated Aug 3, 2024

CVE-2022-21831

CVE-2022-21831

Description

A code injection vulnerability exists in the Active Storage >= v5.2.0 that could allow an attacker to execute code via image_processing arguments.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Code injection vulnerability in Rails Active Storage via untrusted image_processing arguments allows RCE (versions >=5.2.0).

Vulnerability

Active Storage in Ruby on Rails versions >=5.2.0 contains a code injection vulnerability through the image_processing feature, specifically when using the mini_magick backend. The vulnerability arises when user-supplied input is passed as transformation method or argument to blob.variant() calls, allowing an attacker to inject arbitrary ImageMagick commands [4].

Exploitation

An attacker needs to supply untrusted input that reaches the transformation method or its arguments in a variant call, for example: <%= image_tag blob.variant(params[:t] => params[:v]) %>. This allows the attacker to control the transformation options, leading to arbitrary code execution [4].

Impact

Successful exploitation allows remote code execution within the context of the web application, potentially compromising the entire application and its data [4].

Mitigation

Fixed in versions 7.0.2.3, 6.1.4.7, 6.0.4.7, and 5.2.6.3. Workarounds include implementing a strict allow-list on accepted transformation methods and arguments, and applying a strict ImageMagick security policy. Patches are available for unsupported releases [4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
activestorageRubyGems
>= 5.2.0, < 5.2.6.35.2.6.3
activestorageRubyGems
>= 6.0.0, < 6.0.4.76.0.4.7
activestorageRubyGems
>= 6.1.0, < 6.1.4.76.1.4.7
activestorageRubyGems
>= 7.0.0, < 7.0.2.37.0.2.3

Affected products

4

Patches

1
0a72f7d670e9

Added image trasnformation validation via configurable allow-list

https://github.com/rails/railsZackFeb 15, 2022via ghsa
5 files changed · +463 0
  • activestorage/lib/active_storage/engine.rb+17 0 modified
    @@ -80,6 +80,20 @@ class Engine < Rails::Engine # :nodoc:
           application/pdf
         )
     
    +    default_unsupported_image_processing_arguments = %w(
    +      -debug
    +      -display
    +      -distribute-cache
    +      -help
    +      -path
    +      -print
    +      -set
    +      -verbose
    +      -version
    +      -write
    +      -write-mask
    +    )
    +
         config.eager_load_namespaces << ActiveStorage
     
         initializer "active_storage.configs" do
    @@ -93,6 +107,9 @@ class Engine < Rails::Engine # :nodoc:
             ActiveStorage.draw_routes       = app.config.active_storage.draw_routes != false
             ActiveStorage.resolve_model_to_route = app.config.active_storage.resolve_model_to_route || :rails_storage_redirect
     
    +        ActiveStorage.supported_image_processing_methods = app.config.active_storage.supported_image_processing_methods || []
    +        ActiveStorage.unsupported_image_processing_arguments = app.config.active_storage.unsupported_image_processing_arguments || default_unsupported_image_processing_arguments
    +
             ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || []
             ActiveStorage.web_image_content_types = app.config.active_storage.web_image_content_types || []
             ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
    
  • activestorage/lib/active_storage.rb+3 0 modified
    @@ -59,6 +59,9 @@ module ActiveStorage
       mattr_accessor :content_types_to_serve_as_binary, default: []
       mattr_accessor :content_types_allowed_inline,     default: []
     
    +  mattr_accessor :supported_image_processing_methods, default: []
    +  mattr_accessor :unsupported_image_processing_arguments
    +
       mattr_accessor :service_urls_expire_in, default: 5.minutes
       mattr_accessor :urls_expire_in
     
    
  • activestorage/lib/active_storage/transformers/image_processing_transformer.rb+352 0 modified
    @@ -13,6 +13,300 @@ module ActiveStorage
       module Transformers
         class ImageProcessingTransformer < Transformer
           private
    +        class UnsupportedImageProcessingMethod < StandardError; end
    +        class UnsupportedImageProcessingArgument < StandardError; end
    +        SUPPORTED_IMAGE_PROCESSING_METHODS = [
    +          "adaptive_blur",
    +          "adaptive_resize",
    +          "adaptive_sharpen",
    +          "adjoin",
    +          "affine",
    +          "alpha",
    +          "annotate",
    +          "antialias",
    +          "append",
    +          "apply",
    +          "attenuate",
    +          "authenticate",
    +          "auto_gamma",
    +          "auto_level",
    +          "auto_orient",
    +          "auto_threshold",
    +          "backdrop",
    +          "background",
    +          "bench",
    +          "bias",
    +          "bilateral_blur",
    +          "black_point_compensation",
    +          "black_threshold",
    +          "blend",
    +          "blue_primary",
    +          "blue_shift",
    +          "blur",
    +          "border",
    +          "bordercolor",
    +          "borderwidth",
    +          "brightness_contrast",
    +          "cache",
    +          "canny",
    +          "caption",
    +          "channel",
    +          "channel_fx",
    +          "charcoal",
    +          "chop",
    +          "clahe",
    +          "clamp",
    +          "clip",
    +          "clip_path",
    +          "clone",
    +          "clut",
    +          "coalesce",
    +          "colorize",
    +          "colormap",
    +          "color_matrix",
    +          "colors",
    +          "colorspace",
    +          "colourspace",
    +          "color_threshold",
    +          "combine",
    +          "combine_options",
    +          "comment",
    +          "compare",
    +          "complex",
    +          "compose",
    +          "composite",
    +          "compress",
    +          "connected_components",
    +          "contrast",
    +          "contrast_stretch",
    +          "convert",
    +          "convolve",
    +          "copy",
    +          "crop",
    +          "cycle",
    +          "deconstruct",
    +          "define",
    +          "delay",
    +          "delete",
    +          "density",
    +          "depth",
    +          "descend",
    +          "deskew",
    +          "despeckle",
    +          "direction",
    +          "displace",
    +          "dispose",
    +          "dissimilarity_threshold",
    +          "dissolve",
    +          "distort",
    +          "dither",
    +          "draw",
    +          "duplicate",
    +          "edge",
    +          "emboss",
    +          "encoding",
    +          "endian",
    +          "enhance",
    +          "equalize",
    +          "evaluate",
    +          "evaluate_sequence",
    +          "extent",
    +          "extract",
    +          "family",
    +          "features",
    +          "fft",
    +          "fill",
    +          "filter",
    +          "flatten",
    +          "flip",
    +          "floodfill",
    +          "flop",
    +          "font",
    +          "foreground",
    +          "format",
    +          "frame",
    +          "function",
    +          "fuzz",
    +          "fx",
    +          "gamma",
    +          "gaussian_blur",
    +          "geometry",
    +          "gravity",
    +          "grayscale",
    +          "green_primary",
    +          "hald_clut",
    +          "highlight_color",
    +          "hough_lines",
    +          "iconGeometry",
    +          "iconic",
    +          "identify",
    +          "ift",
    +          "illuminant",
    +          "immutable",
    +          "implode",
    +          "insert",
    +          "intensity",
    +          "intent",
    +          "interlace",
    +          "interline_spacing",
    +          "interpolate",
    +          "interpolative_resize",
    +          "interword_spacing",
    +          "kerning",
    +          "kmeans",
    +          "kuwahara",
    +          "label",
    +          "lat",
    +          "layers",
    +          "level",
    +          "level_colors",
    +          "limit",
    +          "limits",
    +          "linear_stretch",
    +          "linewidth",
    +          "liquid_rescale",
    +          "list",
    +          "loader",
    +          "log",
    +          "loop",
    +          "lowlight_color",
    +          "magnify",
    +          "map",
    +          "mattecolor",
    +          "median",
    +          "mean_shift",
    +          "metric",
    +          "mode",
    +          "modulate",
    +          "moments",
    +          "monitor",
    +          "monochrome",
    +          "morph",
    +          "morphology",
    +          "mosaic",
    +          "motion_blur",
    +          "name",
    +          "negate",
    +          "noise",
    +          "normalize",
    +          "opaque",
    +          "ordered_dither",
    +          "orient",
    +          "page",
    +          "paint",
    +          "pause",
    +          "perceptible",
    +          "ping",
    +          "pointsize",
    +          "polaroid",
    +          "poly",
    +          "posterize",
    +          "precision",
    +          "preview",
    +          "process",
    +          "quality",
    +          "quantize",
    +          "quiet",
    +          "radial_blur",
    +          "raise",
    +          "random_threshold",
    +          "range_threshold",
    +          "red_primary",
    +          "regard_warnings",
    +          "region",
    +          "remote",
    +          "render",
    +          "repage",
    +          "resample",
    +          "resize",
    +          "resize_to_fill",
    +          "resize_to_fit",
    +          "resize_to_limit",
    +          "resize_and_pad",
    +          "respect_parentheses",
    +          "reverse",
    +          "roll",
    +          "rotate",
    +          "sample",
    +          "sampling_factor",
    +          "saver",
    +          "scale",
    +          "scene",
    +          "screen",
    +          "seed",
    +          "segment",
    +          "selective_blur",
    +          "separate",
    +          "sepia_tone",
    +          "shade",
    +          "shadow",
    +          "shared_memory",
    +          "sharpen",
    +          "shave",
    +          "shear",
    +          "sigmoidal_contrast",
    +          "silent",
    +          "similarity_threshold",
    +          "size",
    +          "sketch",
    +          "smush",
    +          "snaps",
    +          "solarize",
    +          "sort_pixels",
    +          "sparse_color",
    +          "splice",
    +          "spread",
    +          "statistic",
    +          "stegano",
    +          "stereo",
    +          "storage_type",
    +          "stretch",
    +          "strip",
    +          "stroke",
    +          "strokewidth",
    +          "style",
    +          "subimage_search",
    +          "swap",
    +          "swirl",
    +          "synchronize",
    +          "taint",
    +          "text_font",
    +          "threshold",
    +          "thumbnail",
    +          "tile_offset",
    +          "tint",
    +          "title",
    +          "transform",
    +          "transparent",
    +          "transparent_color",
    +          "transpose",
    +          "transverse",
    +          "treedepth",
    +          "trim",
    +          "type",
    +          "undercolor",
    +          "unique_colors",
    +          "units",
    +          "unsharp",
    +          "update",
    +          "valid_image",
    +          "view",
    +          "vignette",
    +          "virtual_pixel",
    +          "visual",
    +          "watermark",
    +          "wave",
    +          "wavelet_denoise",
    +          "weight",
    +          "white_balance",
    +          "white_point",
    +          "white_threshold",
    +          "window",
    +          "window_group"
    +        ].concat(ActiveStorage.supported_image_processing_methods)
    +
    +        UNSUPPORTED_IMAGE_PROCESSING_ARGUMENTS = ActiveStorage.unsupported_image_processing_arguments
    +
             def process(file, format:)
               processor.
                 source(file).
    @@ -28,6 +322,10 @@ def processor
     
             def operations
               transformations.each_with_object([]) do |(name, argument), list|
    +            if ActiveStorage.variant_processor == :mini_magick
    +              validate_transformation(name, argument)
    +            end
    +
                 if name.to_s == "combine_options"
                   raise ArgumentError, <<~ERROR.squish
                     Active Storage's ImageProcessing transformer doesn't support :combine_options,
    @@ -40,6 +338,60 @@ def operations
                 end
               end
             end
    +
    +        def validate_transformation(name, argument)
    +          method_name = name.to_s.gsub("-","_")
    +
    +          unless SUPPORTED_IMAGE_PROCESSING_METHODS.any? { |method| method_name == method }
    +            raise UnsupportedImageProcessingMethod, <<~ERROR.squish
    +              One or more of the provided transformation methods is not supported.
    +            ERROR
    +          end
    +
    +          if argument.present?
    +            if argument.is_a?(String) || argument.is_a?(Symbol)
    +              validate_arg_string(argument)
    +            elsif argument.is_a?(Array)
    +              validate_arg_array(argument)
    +            elsif argument.is_a?(Hash)
    +              validate_arg_hash(argument)
    +            end
    +          end
    +        end
    +
    +        def validate_arg_string(argument)
    +          if UNSUPPORTED_IMAGE_PROCESSING_ARGUMENTS.any? { |bad_arg| argument.to_s.downcase.include?(bad_arg) }; raise UnsupportedImageProcessingArgument end
    +        end
    +
    +        def validate_arg_array(argument)
    +          argument.each do |arg|
    +            if arg.is_a?(Integer) || arg.is_a?(Float)
    +              next
    +            elsif arg.is_a?(String) || arg.is_a?(Symbol)
    +              validate_arg_string(arg)
    +            elsif arg.is_a?(Array)
    +              validate_arg_array(arg)
    +            elsif arg.is_a?(Hash)
    +              validate_arg_hash(arg)
    +            end
    +          end
    +        end
    +
    +        def validate_arg_hash(argument)
    +          argument.each do |key, value|
    +            validate_arg_string(key)
    +
    +            if value.is_a?(Integer) || value.is_a?(Float)
    +              next
    +            elsif value.is_a?(String) || value.is_a?(Symbol)
    +              validate_arg_string(value)
    +            elsif value.is_a?(Array)
    +              validate_arg_array(value)
    +            elsif value.is_a?(Hash)
    +              validate_arg_hash(value)
    +            end
    +          end
    +        end
         end
       end
     end
    
  • activestorage/test/models/variant_test.rb+62 0 modified
    @@ -211,6 +211,68 @@ class ActiveStorage::VariantTest < ActiveSupport::TestCase
         assert_equal :png, blob.send(:default_variant_format)
       end
     
    +  test "variations with dangerous argument string raise UnsupportedImageProcessingArgument" do
    +    process_variants_with :mini_magick do
    +      blob = create_file_blob(filename: "racecar.jpg")
    +      assert_raise(ActiveStorage::Transformers::ImageProcessingTransformer::UnsupportedImageProcessingArgument) do
    +        blob.variant(resize: "-PaTh /tmp/file.erb").processed
    +      end
    +    end
    +  end
    +
    +  test "variations with dangerous argument array raise UnsupportedImageProcessingArgument" do
    +    process_variants_with :mini_magick do
    +      blob = create_file_blob(filename: "racecar.jpg")
    +      assert_raise(ActiveStorage::Transformers::ImageProcessingTransformer::UnsupportedImageProcessingArgument) do
    +        blob.variant(resize: [123, "-write", "/tmp/file.erb"]).processed
    +      end
    +    end
    +  end
    +
    +  test "variations with dangerous argument in a nested array raise UnsupportedImageProcessingArgument" do
    +    process_variants_with :mini_magick do
    +      blob = create_file_blob(filename: "racecar.jpg")
    +      assert_raise(ActiveStorage::Transformers::ImageProcessingTransformer::UnsupportedImageProcessingArgument) do
    +        blob.variant(resize: [123, ["-write", "/tmp/file.erb"], "/tmp/file.erb"]).processed
    +      end
    +
    +      assert_raise(ActiveStorage::Transformers::ImageProcessingTransformer::UnsupportedImageProcessingArgument) do
    +        blob.variant(resize: [123, {"-write /tmp/file.erb": "something"}, "/tmp/file.erb"]).processed
    +      end
    +    end
    +  end
    +
    +  test "variations with dangerous argument hash raise UnsupportedImageProcessingArgument" do
    +    process_variants_with :mini_magick do
    +      blob = create_file_blob(filename: "racecar.jpg")
    +      assert_raise(ActiveStorage::Transformers::ImageProcessingTransformer::UnsupportedImageProcessingArgument) do
    +        blob.variant(saver: {"-write": "/tmp/file.erb"}).processed
    +      end
    +    end
    +  end
    +
    +  test "variations with dangerous argument in a nested hash raise UnsupportedImageProcessingArgument" do
    +    process_variants_with :mini_magick do
    +      blob = create_file_blob(filename: "racecar.jpg")
    +      assert_raise(ActiveStorage::Transformers::ImageProcessingTransformer::UnsupportedImageProcessingArgument) do
    +        blob.variant(saver: {"something": {"-write": "/tmp/file.erb"}}).processed
    +      end
    +
    +      assert_raise(ActiveStorage::Transformers::ImageProcessingTransformer::UnsupportedImageProcessingArgument) do
    +        blob.variant(saver: {"something": ["-write", "/tmp/file.erb"]}).processed
    +      end
    +    end
    +  end
    +
    +  test "variations with unsupported methods raise UnsupportedImageProcessingMethod" do
    +    process_variants_with :mini_magick do
    +      blob = create_file_blob(filename: "racecar.jpg")
    +      assert_raise(ActiveStorage::Transformers::ImageProcessingTransformer::UnsupportedImageProcessingMethod) do
    +        blob.variant(system: "touch /tmp/dangerous").processed
    +      end
    +    end
    +  end
    +
       private
         def process_variants_with(processor)
           previous_processor, ActiveStorage.variant_processor = ActiveStorage.variant_processor, processor
    
  • railties/test/application/configuration_test.rb+29 0 modified
    @@ -3098,6 +3098,35 @@ class MyLogger < ::Logger
           assert_equal :vips, ActiveStorage.variant_processor
         end
     
    +    test "ActiveStorage.supported_image_processing_methods can be configured via config.active_storage.supported_image_processing_methods" do
    +      remove_from_config '.*config\.load_defaults.*\n'
    +
    +      app_file "config/initializers/add_image_processing_methods.rb", <<-RUBY
    +        Rails.application.config.active_storage.supported_image_processing_methods = ["write", "set"]
    +      RUBY
    +
    +      app "development"
    +
    +      assert ActiveStorage.supported_image_processing_methods.include?("write")
    +      assert ActiveStorage.supported_image_processing_methods.include?("set")
    +    end
    +
    +    test "ActiveStorage.unsupported_image_processing_arguments can be configured via config.active_storage.unsupported_image_processing_arguments" do
    +      remove_from_config '.*config\.load_defaults.*\n'
    +
    +      app_file "config/initializers/add_image_processing_arguments.rb", <<-RUBY
    +      Rails.application.config.active_storage.unsupported_image_processing_arguments = %w(
    +        -write
    +        -danger
    +      )
    +      RUBY
    +
    +      app "development"
    +
    +      assert ActiveStorage.unsupported_image_processing_arguments.include?("-danger")
    +      refute ActiveStorage.unsupported_image_processing_arguments.include?("-set")
    +    end
    +
         test "hosts include .localhost in development" do
           app "development"
           assert_includes Rails.application.config.hosts, ".localhost"
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.