Skip to content

Commit cb6edc2

Browse files
committed
Validate checksums when fetching cached files for app packaging
* This also deletes any resources with bad/mismatched checksums
1 parent d84cd86 commit cb6edc2

File tree

4 files changed

+39
-3
lines changed

4 files changed

+39
-3
lines changed

errors/v2.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,11 @@
613613
http_code: 502
614614
message: "Deletion of app %s failed because one or more associated resources could not be deleted.\n\n%s"
615615

616+
150010:
617+
name: ResourceChecksumMismatch
618+
http_code: 500
619+
message: "One or more cached resources did not match the given checksum. They have been deleted; please retry package upload."
620+
616621
160001:
617622
name: AppBitsUploadInvalid
618623
http_code: 400

lib/cloud_controller/packager/shared_bits_packer.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,19 @@ def append_matched_resources(app_packager, cached_files_fingerprints, root_path)
4242
cached_resources_dir = File.join(root_path, 'cached_resources_dir')
4343

4444
FileUtils.mkdir(cached_resources_dir)
45+
checksum_mismatch = false
4546
matched_resources.each do |local_destination, file_sha, mode|
47+
file_path = File.join(cached_resources_dir, local_destination)
4648
global_app_bits_cache.download_from_blobstore(file_sha, File.join(cached_resources_dir, local_destination), mode:)
49+
50+
if Digester.new.digest_path(file_path) != file_sha
51+
checksum_mismatch = true
52+
global_app_bits_cache.delete(file_sha)
53+
end
4754
end
55+
56+
raise CloudController::Errors::ApiError.new_from_details('ResourceChecksumMismatch') if checksum_mismatch
57+
4858
app_packager.append_dir_contents(cached_resources_dir)
4959
end
5060

spec/unit/controllers/runtime/app_bits_upload_controller_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def make_request
117117
end
118118

119119
context 'with at least one resource and no application' do
120-
let(:req_body) { { resources: Oj.dump([{ 'fn' => 'lol', 'sha1' => 'abc', 'size' => 2048 }]) } }
120+
let(:req_body) { { resources: Oj.dump([{ 'fn' => 'lol', 'sha1' => 'da39a3ee5e6b4b0d3255bfef95601890afd80709', 'size' => 2048 }]) } }
121121

122122
before do
123123
# rack_test overrides 'CONTENT_TYPE' header with 'boundary' which causes errors when the request does not contain an application
@@ -132,7 +132,7 @@ def make_request
132132
end
133133

134134
context 'with at least one resource and an application' do
135-
let(:req_body) { { resources: Oj.dump([{ 'fn' => 'lol', 'sha1' => 'abc', 'size' => 2048 }]), application: valid_zip } }
135+
let(:req_body) { { resources: Oj.dump([{ 'fn' => 'lol', 'sha1' => 'da39a3ee5e6b4b0d3255bfef95601890afd80709', 'size' => 2048 }]), application: valid_zip } }
136136

137137
it 'succeeds to upload' do
138138
make_request

spec/unit/lib/cloud_controller/packager/local_bits_packer_spec.rb

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module CloudController::Packager
2626
)
2727
end
2828

29-
let(:fingerprints) do
29+
let(:corrupted_fingerprints) do
3030
path = File.join(local_tmp_dir, 'content')
3131
sha = 'some_fake_sha'
3232
File.write(path, 'content')
@@ -35,6 +35,15 @@ module CloudController::Packager
3535
[{ 'fn' => 'path/to/content.txt', 'size' => 123, 'sha1' => sha }]
3636
end
3737

38+
let(:fingerprints) do
39+
path = File.join(local_tmp_dir, 'content')
40+
File.write(path, 'content')
41+
sha = Digester.new.digest_path(path)
42+
global_app_bits_cache.cp_to_blobstore(path, sha)
43+
44+
[{ 'fn' => 'path/to/content.txt', 'size' => 123, 'sha1' => sha }]
45+
end
46+
3847
before do
3948
TestConfig.override(directories: { tmpdir: local_tmp_dir })
4049

@@ -136,6 +145,18 @@ module CloudController::Packager
136145
expect(package_blobstore.exists?(blobstore_key)).to be true
137146
end
138147
end
148+
149+
context 'and there are corrupted cached files' do
150+
let(:cached_files_fingerprints) { corrupted_fingerprints }
151+
152+
it 'deletes the offending files from the blobstore and errors with a ChecksumMismatch error' do
153+
expect(global_app_bits_cache).to receive(:delete).with('some_fake_sha')
154+
expect do
155+
packer.send_package_to_blobstore(blobstore_key, uploaded_files_path, cached_files_fingerprints)
156+
end.
157+
to raise_error(CloudController::Errors::ApiError, /One or more cached resources/)
158+
end
159+
end
139160
end
140161

141162
context 'when the zip file is invalid' do

0 commit comments

Comments
 (0)