Hello community,
here is the log from the commit of package rubygem-activestorage-5.2 for
openSUSE:Factory checked in at 2018-12-06 12:17:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-activestorage-5.2 (Old)
and /work/SRC/openSUSE:Factory/.rubygem-activestorage-5.2.new.19453 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-activestorage-5.2"
Thu Dec 6 12:17:46 2018 rev:3 rq:655327 version:5.2.1.1
Changes:
--------
---
/work/SRC/openSUSE:Factory/rubygem-activestorage-5.2/rubygem-activestorage-5.2.changes
2018-08-12 20:53:38.761256926 +0200
+++
/work/SRC/openSUSE:Factory/.rubygem-activestorage-5.2.new.19453/rubygem-activestorage-5.2.changes
2018-12-06 12:17:47.925488762 +0100
@@ -1,0 +2,32 @@
+Mon Dec 3 06:26:14 UTC 2018 - [email protected]
+
+- updated to version 5.2.1.1 (boo#1118076)
+
+- addresses a security vulnerability (CVE-2018-16477, boo#1117641)
+
+ Signed download URLs generated by `ActiveStorage` for Google Cloud Storage
+ service and Disk service include `content-disposition` and `content-type`
+ parameters that an attacker can modify. This can be used to upload specially
+ crafted HTML files and have them served and executed inline. Combined with
+ other techniques such as cookie bombing and specially crafted AppCache
+ manifests,
+ an attacker can gain access to private signed URLs within a specific
+ storage path.
+
+ Vulnerable apps are those using either GCS or the Disk service in
+ production.
+ Other storage services such as S3 or Azure aren't affected.
+
+ All users running an affected release should either upgrade or use one of
+ the
+ workarounds immediately. For those using GCS, it's also recommended to run
+ the
+ following to update existing blobs:
+
+ ```
+ ActiveStorage::Blob.find_each do |blob|
+ blob.send :update_service_metadata
+ end
+ ```
+
+-------------------------------------------------------------------
Old:
----
activestorage-5.2.1.gem
New:
----
activestorage-5.2.1.1.gem
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-activestorage-5.2.spec ++++++
--- /var/tmp/diff_new_pack.w6LOr7/_old 2018-12-06 12:17:48.377488277 +0100
+++ /var/tmp/diff_new_pack.w6LOr7/_new 2018-12-06 12:17:48.377488277 +0100
@@ -24,7 +24,7 @@
#
Name: rubygem-activestorage-5.2
-Version: 5.2.1
+Version: 5.2.1.1
Release: 0
%define mod_name activestorage
%define mod_full_name %{mod_name}-%{version}
++++++ activestorage-5.2.1.gem -> activestorage-5.2.1.1.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/CHANGELOG.md new/CHANGELOG.md
--- old/CHANGELOG.md 2018-08-07 23:42:20.000000000 +0200
+++ new/CHANGELOG.md 2018-11-27 21:12:28.000000000 +0100
@@ -1,3 +1,12 @@
+## Rails 5.2.1.1 (November 27, 2018) ##
+
+* Prevent content type and disposition bypass in storage service URLs.
+
+ Fix CVE-2018-16477.
+
+ *Rosa Gutierrez*
+
+
## Rails 5.2.1 (August 07, 2018) ##
* Fix direct upload with zero-byte files.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/app/controllers/active_storage/disk_controller.rb
new/app/controllers/active_storage/disk_controller.rb
--- old/app/controllers/active_storage/disk_controller.rb 2018-08-07
23:42:20.000000000 +0200
+++ new/app/controllers/active_storage/disk_controller.rb 2018-11-27
21:12:28.000000000 +0100
@@ -11,10 +11,10 @@
def show
if key = decode_verified_key
- response.headers["Content-Type"] = params[:content_type] ||
DEFAULT_SEND_FILE_TYPE
- response.headers["Content-Disposition"] = params[:disposition] ||
DEFAULT_SEND_FILE_DISPOSITION
+ response.headers["Content-Type"] = key[:content_type] ||
DEFAULT_SEND_FILE_TYPE
+ response.headers["Content-Disposition"] = key[:disposition] ||
DEFAULT_SEND_FILE_DISPOSITION
- disk_service.download key do |chunk|
+ disk_service.download key[:key] do |chunk|
response.stream.write chunk
end
else
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/app/models/active_storage/blob/identifiable.rb
new/app/models/active_storage/blob/identifiable.rb
--- old/app/models/active_storage/blob/identifiable.rb 2018-08-07
23:42:20.000000000 +0200
+++ new/app/models/active_storage/blob/identifiable.rb 2018-11-27
21:12:28.000000000 +0100
@@ -2,7 +2,10 @@
module ActiveStorage::Blob::Identifiable
def identify
- update! content_type: identify_content_type, identified: true unless
identified?
+ unless identified?
+ update! content_type: identify_content_type, identified: true
+ update_service_metadata
+ end
end
def identified?
@@ -17,4 +20,8 @@
def download_identifiable_chunk
service.download_chunk key, 0...4.kilobytes
end
+
+ def update_service_metadata
+ service.update_metadata key, service_metadata if service_metadata.any?
+ end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/app/models/active_storage/blob.rb
new/app/models/active_storage/blob.rb
--- old/app/models/active_storage/blob.rb 2018-08-07 23:42:20.000000000
+0200
+++ new/app/models/active_storage/blob.rb 2018-11-27 21:12:28.000000000
+0100
@@ -120,8 +120,8 @@
def service_url(expires_in: service.url_expires_in, disposition: :inline,
filename: nil, **options)
filename = ActiveStorage::Filename.wrap(filename || self.filename)
- service.url key, expires_in: expires_in, filename: filename, content_type:
content_type,
- disposition: forcibly_serve_as_binary? ? :attachment : disposition,
**options
+ service.url key, expires_in: expires_in, filename: filename, content_type:
content_type_for_service_url,
+ disposition: forced_disposition_for_service_url || disposition, **options
end
# Returns a URL that can be used to directly upload a file for this blob on
the service. This URL is intended to be
@@ -152,7 +152,7 @@
self.byte_size = io.size
self.identified = true
- service.upload(key, io, checksum: checksum)
+ service.upload key, io, checksum: checksum, **service_metadata
end
# Downloads the file associated with this blob. If no block is given, the
entire file is read into memory and returned.
@@ -203,5 +203,29 @@
ActiveStorage.content_types_to_serve_as_binary.include?(content_type)
end
+ def allowed_inline?
+ ActiveStorage.content_types_allowed_inline.include?(content_type)
+ end
+
+ def content_type_for_service_url
+ forcibly_serve_as_binary? ? ActiveStorage.binary_content_type :
content_type
+ end
+
+ def forced_disposition_for_service_url
+ if forcibly_serve_as_binary? || !allowed_inline?
+ :attachment
+ end
+ end
+
+ def service_metadata
+ if forcibly_serve_as_binary?
+ { content_type: ActiveStorage.binary_content_type, disposition:
:attachment, filename: filename }
+ elsif !allowed_inline?
+ { content_type: content_type, disposition: :attachment, filename:
filename }
+ else
+ { content_type: content_type }
+ end
+ end
+
ActiveSupport.run_load_hooks(:active_storage_blob, self)
end
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_storage/engine.rb
new/lib/active_storage/engine.rb
--- old/lib/active_storage/engine.rb 2018-08-07 23:42:20.000000000 +0200
+++ new/lib/active_storage/engine.rb 2018-11-27 21:12:28.000000000 +0100
@@ -37,6 +37,18 @@
text/xml
application/xml
application/xhtml+xml
+ application/mathml+xml
+ text/cache-manifest
+ )
+
+ config.active_storage.content_types_allowed_inline = %w(
+ image/png
+ image/gif
+ image/jpg
+ image/jpeg
+ image/vnd.adobe.photoshop
+ image/vnd.microsoft.icon
+ application/pdf
)
config.eager_load_namespaces << ActiveStorage
@@ -51,6 +63,8 @@
ActiveStorage.variable_content_types =
app.config.active_storage.variable_content_types || []
ActiveStorage.content_types_to_serve_as_binary =
app.config.active_storage.content_types_to_serve_as_binary || []
+ ActiveStorage.content_types_allowed_inline =
app.config.active_storage.content_types_allowed_inline || []
+ ActiveStorage.binary_content_type =
app.config.active_storage.binary_content_type || "application/octet-stream"
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_storage/gem_version.rb
new/lib/active_storage/gem_version.rb
--- old/lib/active_storage/gem_version.rb 2018-08-07 23:42:20.000000000
+0200
+++ new/lib/active_storage/gem_version.rb 2018-11-27 21:12:28.000000000
+0100
@@ -10,7 +10,7 @@
MAJOR = 5
MINOR = 2
TINY = 1
- PRE = nil
+ PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_storage/service/azure_storage_service.rb
new/lib/active_storage/service/azure_storage_service.rb
--- old/lib/active_storage/service/azure_storage_service.rb 2018-08-07
23:42:20.000000000 +0200
+++ new/lib/active_storage/service/azure_storage_service.rb 2018-11-27
21:12:28.000000000 +0100
@@ -17,7 +17,7 @@
@container = container
end
- def upload(key, io, checksum: nil)
+ def upload(key, io, checksum: nil, **)
instrument :upload, key: key, checksum: checksum do
begin
blobs.create_block_blob(container, key, IO.try_convert(io) || io,
content_md5: checksum)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_storage/service/disk_service.rb
new/lib/active_storage/service/disk_service.rb
--- old/lib/active_storage/service/disk_service.rb 2018-08-07
23:42:20.000000000 +0200
+++ new/lib/active_storage/service/disk_service.rb 2018-11-27
21:12:28.000000000 +0100
@@ -15,7 +15,7 @@
@root = root
end
- def upload(key, io, checksum: nil)
+ def upload(key, io, checksum: nil, **)
instrument :upload, key: key, checksum: checksum do
IO.copy_stream(io, make_path_for(key))
ensure_integrity_of(key, checksum) if checksum
@@ -75,17 +75,23 @@
def url(key, expires_in:, filename:, disposition:, content_type:)
instrument :url, key: key do |payload|
- verified_key_with_expiration = ActiveStorage.verifier.generate(key,
expires_in: expires_in, purpose: :blob_key)
-
- generated_url =
- url_helpers.rails_disk_service_url(
- verified_key_with_expiration,
- host: current_host,
- filename: filename,
- disposition: content_disposition_with(type: disposition, filename:
filename),
+ content_disposition = content_disposition_with(type: disposition,
filename: filename)
+ verified_key_with_expiration = ActiveStorage.verifier.generate(
+ {
+ key: key,
+ disposition: content_disposition,
content_type: content_type
- )
-
+ },
+ { expires_in: expires_in,
+ purpose: :blob_key }
+ )
+
+ generated_url =
url_helpers.rails_disk_service_url(verified_key_with_expiration,
+ host: current_host,
+ disposition: content_disposition,
+ content_type: content_type,
+ filename: filename
+ )
payload[:url] = generated_url
generated_url
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_storage/service/gcs_service.rb
new/lib/active_storage/service/gcs_service.rb
--- old/lib/active_storage/service/gcs_service.rb 2018-08-07
23:42:20.000000000 +0200
+++ new/lib/active_storage/service/gcs_service.rb 2018-11-27
21:12:28.000000000 +0100
@@ -15,21 +15,29 @@
@config = config
end
- def upload(key, io, checksum: nil)
+ def upload(key, io, checksum: nil, content_type: nil, disposition: nil,
filename: nil)
instrument :upload, key: key, checksum: checksum do
begin
- # The official GCS client library doesn't allow us to create a file
with no Content-Type metadata.
- # We need the file we create to have no Content-Type so we can
control it via the response-content-type
- # param in signed URLs. Workaround: let the GCS client create the
file with an inferred
- # Content-Type (usually "application/octet-stream") then clear it.
- bucket.create_file(io, key, md5: checksum).update do |file|
- file.content_type = nil
- end
+ # GCS's signed URLs don't include params such as
response-content-type response-content_disposition
+ # in the signature, which means an attacker can modify them and
bypass our effort to force these to
+ # binary and attachment when the file's content type requires it.
The only way to force them is to
+ # store them as object's metadata.
+ content_disposition = content_disposition_with(type: disposition,
filename: filename) if disposition && filename
+ bucket.create_file(io, key, md5: checksum, content_type:
content_type, content_disposition: content_disposition)
rescue Google::Cloud::InvalidArgumentError
raise ActiveStorage::IntegrityError
end
end
end
+
+ def update_metadata(key, content_type:, disposition: nil, filename: nil)
+ instrument :update_metadata, key: key, content_type: content_type,
disposition: disposition do
+ file_for(key).update do |file|
+ file.content_type = content_type
+ file.content_disposition = content_disposition_with(type:
disposition, filename: filename) if disposition && filename
+ end
+ end
+ end
# FIXME: Download in chunks when given a block.
def download(key)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_storage/service/mirror_service.rb
new/lib/active_storage/service/mirror_service.rb
--- old/lib/active_storage/service/mirror_service.rb 2018-08-07
23:42:20.000000000 +0200
+++ new/lib/active_storage/service/mirror_service.rb 2018-11-27
21:12:28.000000000 +0100
@@ -24,9 +24,9 @@
# Upload the +io+ to the +key+ specified to all services. If a +checksum+
is provided, all services will
# ensure a match when the upload has completed or raise an
ActiveStorage::IntegrityError.
- def upload(key, io, checksum: nil)
+ def upload(key, io, checksum: nil, **options)
each_service.collect do |service|
- service.upload key, io.tap(&:rewind), checksum: checksum
+ service.upload key, io.tap(&:rewind), checksum: checksum, **options
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_storage/service.rb
new/lib/active_storage/service.rb
--- old/lib/active_storage/service.rb 2018-08-07 23:42:20.000000000 +0200
+++ new/lib/active_storage/service.rb 2018-11-27 21:12:28.000000000 +0100
@@ -64,10 +64,16 @@
# Upload the +io+ to the +key+ specified. If a +checksum+ is provided, the
service will
# ensure a match when the upload has completed or raise an
ActiveStorage::IntegrityError.
- def upload(key, io, checksum: nil)
+ def upload(key, io, checksum: nil, **options)
raise NotImplementedError
end
+ # Update metadata for the file identified by +key+ in the service.
+ # Override in subclasses only if the service needs to store specific
+ # metadata that has to be updated upon identification.
+ def update_metadata(key, **metadata)
+ end
+
# Return the content of the file at the +key+.
def download(key)
raise NotImplementedError
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/active_storage.rb new/lib/active_storage.rb
--- old/lib/active_storage.rb 2018-08-07 23:42:20.000000000 +0200
+++ new/lib/active_storage.rb 2018-11-27 21:12:28.000000000 +0100
@@ -48,4 +48,6 @@
mattr_accessor :paths, default: {}
mattr_accessor :variable_content_types, default: []
mattr_accessor :content_types_to_serve_as_binary, default: []
+ mattr_accessor :content_types_allowed_inline, default: []
+ mattr_accessor :binary_content_type, default: "application/octet-stream"
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 2018-08-07 23:42:20.000000000 +0200
+++ new/metadata 2018-11-27 21:12:28.000000000 +0100
@@ -1,14 +1,14 @@
--- !ruby/object:Gem::Specification
name: activestorage
version: !ruby/object:Gem::Version
- version: 5.2.1
+ version: 5.2.1.1
platform: ruby
authors:
- David Heinemeier Hansson
autorequire:
bindir: bin
cert_chain: []
-date: 2018-08-07 00:00:00.000000000 Z
+date: 2018-11-27 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: actionpack
@@ -16,28 +16,28 @@
requirements:
- - '='
- !ruby/object:Gem::Version
- version: 5.2.1
+ version: 5.2.1.1
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '='
- !ruby/object:Gem::Version
- version: 5.2.1
+ version: 5.2.1.1
- !ruby/object:Gem::Dependency
name: activerecord
requirement: !ruby/object:Gem::Requirement
requirements:
- - '='
- !ruby/object:Gem::Version
- version: 5.2.1
+ version: 5.2.1.1
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '='
- !ruby/object:Gem::Version
- version: 5.2.1
+ version: 5.2.1.1
- !ruby/object:Gem::Dependency
name: marcel
requirement: !ruby/object:Gem::Requirement
@@ -124,8 +124,8 @@
licenses:
- MIT
metadata:
- source_code_uri: https://github.com/rails/rails/tree/v5.2.1/activestorage
- changelog_uri:
https://github.com/rails/rails/blob/v5.2.1/activestorage/CHANGELOG.md
+ source_code_uri: https://github.com/rails/rails/tree/v5.2.1.1/activestorage
+ changelog_uri:
https://github.com/rails/rails/blob/v5.2.1.1/activestorage/CHANGELOG.md
post_install_message:
rdoc_options: []
require_paths: