Skip to content

Commit a96b510

Browse files
committed
Generate the integrity hash from compiled asset content
1 parent b1445c2 commit a96b510

File tree

7 files changed

+41
-13
lines changed

7 files changed

+41
-13
lines changed

lib/propshaft/asset.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ def initialize(path, logical_path:, load_path:)
1818
@path, @logical_path, @load_path = path, Pathname.new(logical_path), load_path
1919
end
2020

21+
def compiled_content
22+
@compiled_content ||= load_path.compilers.compile(self)
23+
end
24+
2125
def content(encoding: "ASCII-8BIT")
2226
File.read(path, encoding: encoding, mode: "rb")
2327
end
@@ -49,7 +53,7 @@ def integrity(hash_algorithm:)
4953
raise(StandardError.new("Subresource Integrity hash algorithm must be one of SHA2 family (sha256, sha384, sha512)"))
5054
end
5155

52-
[hash_algorithm, Digest::SHA2.new(bitlen).base64digest(content)].join("-")
56+
[hash_algorithm, Digest::SHA2.new(bitlen).base64digest(compiled_content)].join("-")
5357
end
5458

5559
def digested_path

lib/propshaft/processor.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ def output_asset(asset)
5454
def compile_asset(asset)
5555
File.open(output_path.join(asset.digested_path), "w+") do |file|
5656
begin
57-
file.write compilers.compile(asset)
57+
file.write asset.compiled_content
5858
rescue Encoding::UndefinedConversionError
5959
# FIXME: Not sure if there's a better way here?
60-
file.write compilers.compile(asset).force_encoding("UTF-8")
60+
file.write asset.compiled_content.force_encoding("UTF-8")
6161
end
6262
end if compilers.compilable?(asset)
6363
end

lib/propshaft/resolver/dynamic.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def integrity(logical_path)
1616
hash_algorithm = load_path.integrity_hash_algorithm
1717

1818
if hash_algorithm && (asset = find_asset(logical_path))
19-
asset.integrity(hash_algorithm:)
19+
asset.integrity(hash_algorithm: hash_algorithm)
2020
end
2121
end
2222

lib/propshaft/server.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def call(env)
1111
path, digest = extract_path_and_digest(env)
1212

1313
if (asset = @assembly.load_path.find(path)) && asset.fresh?(digest)
14-
compiled_content = @assembly.compilers.compile(asset)
14+
compiled_content = asset.compiled_content
1515

1616
[
1717
200,

test/propshaft/asset_test.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,21 @@ class Propshaft::AssetTest < ActiveSupport::TestCase
7171
assert_equal find_asset("one.txt"), find_asset("one.txt")
7272
end
7373

74+
test "compiled content for non-compilable asset" do
75+
asset = find_asset("one.txt")
76+
assert_equal "One from first path", asset.compiled_content
77+
assert_equal asset.content, asset.compiled_content
78+
end
79+
80+
test "compiled content for css asset with url transformation" do
81+
asset = find_asset("another.css")
82+
compiled = asset.compiled_content
83+
84+
assert_match(/url\("\/archive-[a-f0-9]+\.svg"\)/, compiled)
85+
assert_not_equal asset.content, asset.compiled_content
86+
end
87+
88+
7489
test "costly methods are memoized" do
7590
asset = find_asset("one.txt")
7691
assert_equal asset.digest.object_id, asset.digest.object_id

test/propshaft/manifest_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Propshaft::ManifestTest < ActiveSupport::TestCase
1212

1313
manifest_entry = parsed_manifest["another.css"]
1414
assert_equal "another-c464b1ee.css", manifest_entry["digested_path"]
15-
assert_equal "sha384-RZLbo+FZ8rnE9ct6dNqDcgIYo7DBk/GaB4nCMnNsj6HWp0ePV8q8qky9Qemdpuwl", manifest_entry["integrity"]
15+
assert_equal "sha384-jUiHGq2aPNACr4g68crM1I28TitXJKYhEgokcX6W5VYGwufEKQxfLpe4GakM84ex", manifest_entry["integrity"]
1616
end
1717

1818
test "serializes to the extensible manifest format without integrity hash algorithm" do

test/propshaft/resolver/dynamic_test.rb

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ class Propshaft::Resolver::DynamicTest < ActiveSupport::TestCase
3030
assert_equal "sha384-LdS8l2QTAF8bD8WPb8QSQv0skTWHhmcnS2XU5LBkVQneGzqIqnDRskQtJvi7ADMe", resolver.integrity("one.txt")
3131
end
3232

33+
test "integrity for asset return the value for the compiled content instead of the source" do
34+
compilers = [["text/css", Propshaft::Compiler::CssAssetUrls ]]
35+
resolver = create_resolver(integrity_hash_algorithm: "sha384", compilers: compilers)
36+
assert_equal "sha384-jUiHGq2aPNACr4g68crM1I28TitXJKYhEgokcX6W5VYGwufEKQxfLpe4GakM84ex", resolver.integrity("another.css")
37+
end
38+
3339
test "integrity for asset returns nil for no configured hash format" do
3440
assert_nil @resolver.integrity("one.txt")
3541
end
@@ -40,12 +46,15 @@ class Propshaft::Resolver::DynamicTest < ActiveSupport::TestCase
4046
end
4147

4248
private
43-
def create_resolver(integrity_hash_algorithm: nil)
44-
load_path = Propshaft::LoadPath.new(
45-
Pathname.new("#{__dir__}/../../fixtures/assets/first_path"),
46-
compilers: Propshaft::Compilers.new(nil),
47-
integrity_hash_algorithm: integrity_hash_algorithm
48-
)
49-
Propshaft::Resolver::Dynamic.new(load_path: load_path, prefix: "/assets")
49+
def create_resolver(integrity_hash_algorithm: nil, compilers: [])
50+
assembly = Propshaft::Assembly.new(ActiveSupport::OrderedOptions.new.tap { |config|
51+
config.paths = [
52+
Pathname.new("#{__dir__}/../../fixtures/assets/first_path"),
53+
]
54+
config.compilers = compilers
55+
config.integrity_hash_algorithm = integrity_hash_algorithm
56+
})
57+
58+
Propshaft::Resolver::Dynamic.new(load_path: assembly.load_path, prefix: "/assets")
5059
end
5160
end

0 commit comments

Comments
 (0)