提交 f1b2732a 编写于 作者: R Rafael Mendonça França

Merge branch '5-2-sec' into 5-2-stable

......@@ -26,63 +26,63 @@ GIT
PATH
remote: .
specs:
actioncable (5.2.1)
actionpack (= 5.2.1)
actioncable (5.2.1.1)
actionpack (= 5.2.1.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailer (5.2.1)
actionpack (= 5.2.1)
actionview (= 5.2.1)
activejob (= 5.2.1)
actionmailer (5.2.1.1)
actionpack (= 5.2.1.1)
actionview (= 5.2.1.1)
activejob (= 5.2.1.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.2.1)
actionview (= 5.2.1)
activesupport (= 5.2.1)
actionpack (5.2.1.1)
actionview (= 5.2.1.1)
activesupport (= 5.2.1.1)
rack (~> 2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.2.1)
activesupport (= 5.2.1)
actionview (5.2.1.1)
activesupport (= 5.2.1.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (5.2.1)
activesupport (= 5.2.1)
activejob (5.2.1.1)
activesupport (= 5.2.1.1)
globalid (>= 0.3.6)
activemodel (5.2.1)
activesupport (= 5.2.1)
activerecord (5.2.1)
activemodel (= 5.2.1)
activesupport (= 5.2.1)
activemodel (5.2.1.1)
activesupport (= 5.2.1.1)
activerecord (5.2.1.1)
activemodel (= 5.2.1.1)
activesupport (= 5.2.1.1)
arel (>= 9.0)
activestorage (5.2.1)
actionpack (= 5.2.1)
activerecord (= 5.2.1)
activestorage (5.2.1.1)
actionpack (= 5.2.1.1)
activerecord (= 5.2.1.1)
marcel (~> 0.3.1)
activesupport (5.2.1)
activesupport (5.2.1.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
rails (5.2.1)
actioncable (= 5.2.1)
actionmailer (= 5.2.1)
actionpack (= 5.2.1)
actionview (= 5.2.1)
activejob (= 5.2.1)
activemodel (= 5.2.1)
activerecord (= 5.2.1)
activestorage (= 5.2.1)
activesupport (= 5.2.1)
rails (5.2.1.1)
actioncable (= 5.2.1.1)
actionmailer (= 5.2.1.1)
actionpack (= 5.2.1.1)
actionview (= 5.2.1.1)
activejob (= 5.2.1.1)
activemodel (= 5.2.1.1)
activerecord (= 5.2.1.1)
activestorage (= 5.2.1.1)
activesupport (= 5.2.1.1)
bundler (>= 1.3.0)
railties (= 5.2.1)
railties (= 5.2.1.1)
sprockets-rails (>= 2.0.0)
railties (5.2.1)
actionpack (= 5.2.1)
activesupport (= 5.2.1)
railties (5.2.1.1)
actionpack (= 5.2.1.1)
activesupport (= 5.2.1.1)
method_source
rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0)
......@@ -269,7 +269,7 @@ GEM
hiredis (0.6.1-java)
http_parser.rb (0.6.0)
httpclient (2.8.3)
i18n (1.1.0)
i18n (1.1.1)
concurrent-ruby (~> 1.0)
io-like (0.3.0)
jdbc-mysql (5.1.46)
......@@ -291,16 +291,16 @@ GEM
logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
loofah (2.2.2)
loofah (2.2.3)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (0.3.2)
marcel (0.3.3)
mimemagic (~> 0.3.2)
memoist (0.16.0)
metaclass (0.0.4)
method_source (0.9.0)
method_source (0.9.2)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
......@@ -564,4 +564,4 @@ DEPENDENCIES
websocket-client-simple!
BUNDLED WITH
1.16.3
1.17.1
## Rails 5.2.1.1 (November 27, 2018) ##
* No changes.
## Rails 5.2.1 (August 07, 2018) ##
* No changes.
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
{
"name": "actioncable",
"version": "5.2.1",
"version": "5.2.1-1",
"description": "WebSocket framework for Ruby on Rails.",
"main": "lib/assets/compiled/action_cable.js",
"files": [
......
## Rails 5.2.1.1 (November 27, 2018) ##
* No changes.
## Rails 5.2.1 (August 07, 2018) ##
* Ensure mail gem is eager autoloaded when eager load is true to prevent thread deadlocks.
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -33,6 +33,11 @@
*Andrew White*
## Rails 5.2.1.1 (November 27, 2018) ##
* No changes.
## Rails 5.2.1 (August 07, 2018) ##
* Prevent `?null=` being passed on JSON encoded test requests.
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
## Rails 5.2.1.1 (November 27, 2018) ##
* No changes.
## Rails 5.2.1 (August 07, 2018) ##
* Fix leak of `skip_default_ids` and `allow_method_names_outside_object` options
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
{
"name": "rails-ujs",
"version": "5.2.1",
"version": "5.2.1-1",
"description": "Ruby on Rails unobtrusive scripting adapter",
"main": "lib/assets/compiled/rails-ujs.js",
"files": [
......
......@@ -12,6 +12,18 @@
*Alan Wu*
## Rails 5.2.1.1 (November 27, 2018) ##
* Do not deserialize GlobalID objects that were not generated by Active Job.
Trusting any GlobaID object when deserializing jobs can allow attackers to access
information that should not be accessible to them.
Fix CVE-2018-16476.
*Rafael Mendonça França*
## Rails 5.2.1 (August 07, 2018) ##
* Pass the error instance as the second parameter of block executed by `discard_on`.
......
......@@ -77,7 +77,7 @@ def serialize_argument(argument)
def deserialize_argument(argument)
case argument
when String
GlobalID::Locator.locate(argument) || argument
argument
when *TYPE_WHITELIST
argument
when Array
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -38,6 +38,10 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
assert_arguments_roundtrip [@person]
end
test "should keep Global IDs strings as they are" do
assert_arguments_roundtrip [@person.to_gid.to_s]
end
test "should dive deep into arrays and hashes" do
assert_arguments_roundtrip [3, [@person]]
assert_arguments_roundtrip [{ "a" => @person }]
......
......@@ -5,6 +5,11 @@
*Ryuta Kamizono*
## Rails 5.2.1.1 (November 27, 2018) ##
* No changes.
## Rails 5.2.1 (August 07, 2018) ##
* No changes.
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -47,6 +47,11 @@
*Darwin Wu*
## Rails 5.2.1.1 (November 27, 2018) ##
* No changes.
## Rails 5.2.1 (August 07, 2018) ##
* PostgreSQL: Support new relkind for partitioned tables.
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
## 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.
......
......@@ -9,7 +9,7 @@ class ActiveStorage::DiskController < ActiveStorage::BaseController
def show
if key = decode_verified_key
serve_file disk_service.path_for(key), content_type: params[:content_type], disposition: params[:disposition]
serve_file disk_service.path_for(key[:key]), content_type: key[:content_type], disposition: key[:disposition]
else
head :not_found
end
......
......@@ -124,8 +124,8 @@ def text?
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
......@@ -156,7 +156,7 @@ def upload(io)
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.
......@@ -208,5 +208,29 @@ def forcibly_serve_as_binary?
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
......@@ -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?
......@@ -21,4 +24,8 @@ def download_identifiable_chunk
""
end
end
def update_service_metadata
service.update_metadata key, service_metadata if service_metadata.any?
end
end
......@@ -48,4 +48,6 @@ module ActiveStorage
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
......@@ -37,6 +37,18 @@ class Engine < Rails::Engine # :nodoc:
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 @@ class Engine < Rails::Engine # :nodoc:
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
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -64,10 +64,16 @@ def build(configurator:, service: nil, **service_config) #:nodoc:
# 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
......
......@@ -17,7 +17,7 @@ def initialize(storage_account_name:, storage_access_key:, container:)
@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)
......
......@@ -15,7 +15,7 @@ def initialize(root:)
@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 exist?(key)
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
......
......@@ -15,22 +15,30 @@ def initialize(**config)
@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)
instrument :download, key: key do
......
......@@ -24,9 +24,9 @@ def initialize(primary:, mirrors:)
# 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
......
{
"name": "activestorage",
"version": "5.2.1",
"version": "5.2.1-1",
"description": "Attach cloud and local files in Rails applications",
"main": "app/assets/javascripts/activestorage.js",
"files": [
......
......@@ -5,11 +5,12 @@
class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
test "showing blob inline" do
blob = create_blob
blob = create_blob(filename: "hello.jpg", content_type: "image/jpg")
get blob.service_url
assert_response :ok
assert_equal "inline; filename=\"hello.txt\"; filename*=UTF-8''hello.txt", response.headers["Content-Disposition"]
assert_equal "text/plain", response.headers["Content-Type"]
assert_equal "inline; filename=\"hello.jpg\"; filename*=UTF-8''hello.jpg", response.headers["Content-Disposition"]
assert_equal "image/jpg", response.headers["Content-Type"]
assert_equal "Hello world!", response.body
end
......@@ -31,6 +32,10 @@ class ActiveStorage::DiskControllerTest < ActionDispatch::IntegrationTest
assert_equal " worl", response.body
end
test "showing blob with invalid key" do
get rails_disk_service_url(encoded_key: "Invalid key", filename: "hello.txt")
assert_response :not_found
end
test "directly uploading blob with integrity" do
data = "Something else entirely!"
......
......@@ -77,12 +77,21 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
end
end
test "urls force attachment as content disposition for content types served as binary" do
test "urls force content_type to binary and attachment as content disposition for content types served as binary" do
blob = create_blob(content_type: "text/html")
freeze_time do
assert_equal expected_url_for(blob, disposition: :attachment), blob.service_url
assert_equal expected_url_for(blob, disposition: :attachment), blob.service_url(disposition: :inline)
assert_equal expected_url_for(blob, disposition: :attachment, content_type: "application/octet-stream"), blob.service_url
assert_equal expected_url_for(blob, disposition: :attachment, content_type: "application/octet-stream"), blob.service_url(disposition: :inline)
end
end
test "urls force attachment as content disposition when the content type is not allowed inline" do
blob = create_blob(content_type: "application/zip")
freeze_time do
assert_equal expected_url_for(blob, disposition: :attachment, content_type: "application/zip"), blob.service_url
assert_equal expected_url_for(blob, disposition: :attachment, content_type: "application/zip"), blob.service_url(disposition: :inline)
end
end
......@@ -104,7 +113,7 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
options = [
blob.key,
expires_in: blob.service.url_expires_in,
disposition: :inline,
disposition: :attachment,
content_type: blob.content_type,
filename: blob.filename,
thumb_size: "300x300",
......@@ -139,9 +148,13 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
end
private
def expected_url_for(blob, disposition: :inline, filename: nil)
def expected_url_for(blob, disposition: :attachment, filename: nil, content_type: nil)
filename ||= blob.filename
query_string = { content_type: blob.content_type, disposition: "#{disposition}; #{filename.parameters}" }.to_param
"https://example.com/rails/active_storage/disk/#{ActiveStorage.verifier.generate(blob.key, expires_in: 5.minutes, purpose: :blob_key)}/#{filename}?#{query_string}"
content_type ||= blob.content_type
query = { disposition: disposition.to_s + "; #{filename.parameters}", content_type: content_type }
key_params = { key: blob.key }.merge(query)
"https://example.com/rails/active_storage/disk/#{ActiveStorage.verifier.generate(key_params, expires_in: 5.minutes, purpose: :blob_key)}/#{filename}?#{query.to_param}"
end
end
......@@ -78,6 +78,6 @@ class ActiveStorage::VariantTest < ActiveSupport::TestCase
test "service_url doesn't grow in length despite long variant options" do
blob = create_file_blob(filename: "racecar.jpg")
variant = blob.variant(font: "a" * 10_000).processed
assert_operator variant.service_url.length, :<, 525
assert_operator variant.service_url.length, :<, 726
end
end
......@@ -31,6 +31,55 @@ class ActiveStorage::Service::GCSServiceTest < ActiveSupport::TestCase
end
end
test "upload with content_type and content_disposition" do
begin
key = SecureRandom.base58(24)
data = "Something else entirely!"
@service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data), disposition: :attachment, filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain")
url = @service.url(key, expires_in: 2.minutes, disposition: :inline, content_type: "text/html", filename: ActiveStorage::Filename.new("test.html"))
response = Net::HTTP.get_response(URI(url))
assert_equal "text/plain", response.content_type
assert_match /attachment;.*test.txt/, response["Content-Disposition"]
ensure
@service.delete key
end
end
test "upload with content_type" do
begin
key = SecureRandom.base58(24)
data = "Something else entirely!"
@service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data), content_type: "text/plain")
url = @service.url(key, expires_in: 2.minutes, disposition: :inline, content_type: "text/html", filename: ActiveStorage::Filename.new("test.html"))
response = Net::HTTP.get_response(URI(url))
assert_equal "text/plain", response.content_type
assert_match /inline;.*test.html/, response["Content-Disposition"]
ensure
@service.delete key
end
end
test "update metadata" do
begin
key = SecureRandom.base58(24)
data = "Something else entirely!"
@service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data), disposition: :attachment, filename: ActiveStorage::Filename.new("test.html"), content_type: "text/html")
@service.update_metadata(key, disposition: :inline, filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain")
url = @service.url(key, expires_in: 2.minutes, disposition: :attachment, content_type: "text/html", filename: ActiveStorage::Filename.new("test.html"))
response = Net::HTTP.get_response(URI(url))
assert_equal "text/plain", response.content_type
assert_match /inline;.*test.txt/, response["Content-Disposition"]
ensure
@service.delete key
end
end
test "signed URL generation" do
assert_match(/storage\.googleapis\.com\/.*response-content-disposition=inline.*test\.txt.*response-content-type=text%2Fplain/,
@service.url(@key, expires_in: 2.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain"))
......
......@@ -16,6 +16,11 @@
*Ashe Connor*, *Aaron Patterson*
## Rails 5.2.1.1 (November 27, 2018) ##
* No changes.
## Rails 5.2.1 (August 07, 2018) ##
* Redis cache store: `delete_matched` no longer blocks the Redis server.
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
## Rails 5.2.1.1 (November 27, 2018) ##
* No changes.
## Rails 5.2.1 (August 07, 2018) ##
* No changes.
......
## Rails 5.2.1.1 (November 27, 2018) ##
* No changes.
## Rails 5.2.1 (August 07, 2018) ##
* Respect `NODE_ENV` when running `rails yarn:install`.
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
......@@ -10,7 +10,7 @@ module VERSION
MAJOR = 5
MINOR = 2
TINY = 1
PRE = nil
PRE = "1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册