Shrine::ZipGuard
A Shrine plugin for safe ZIP derivative processing.
It catches common ZIP errors during derivative creation — password-protected
archives and missing required files — and converts them into friendly, I18n-ready
error messages attached to attacher.errors.
Installation
Add this line to your application's Gemfile:
gem "shrine-zip-guard"
And then execute:
bundle install
Usage
Automatic validation
If you only need to validate that the uploaded ZIP contains certain files, enable
the plugin with required_files:
class XmlUploader < Shrine
plugin :derivatives
plugin :zip_guard, required_files: ["*.xml"]
end
This checks that the ZIP is not password-protected and that it contains at least one file matching each glob pattern. Patterns are matched against the root of the archive only. If you want to allow nested files, use a recursive pattern:
plugin :zip_guard, required_files: ["**/*.xml"]
For a specific subdirectory, include the path in the pattern:
plugin :zip_guard, required_files: ["data/*.xml"]
Custom processing with with_zip
If you need to run your own logic (for example extracting files or checking
specific paths), use the with_zip helper inside your Attacher.derivatives block:
class XmlUploader < Shrine
plugin :derivatives
plugin :zip_guard
Attacher.derivatives do |original|
Shrine::Plugins::ZipGuard.with_zip(original, required_files: ["*.xml"]) do |zip_file|
xmls = zip_file.glob("*.xml")
if xmls.size == 1
extracted = Tempfile.new(["extracted", ".xml"], binmode: true)
extracted.write(xmls.first.get_input_stream.read)
extracted.rewind
{ extracted_xml: extracted }
else
{}
end
end
end
end
Note that with_zip performs its own required_files check, so you should not also
pass required_files to the plugin when you provide a custom derivatives block.
Error messages
All user-facing errors are translated via I18n. The gem ships with English
defaults under the shrine.zip_guard key:
en:
shrine:
zip_guard:
password_protected: "ZIP file is password protected. Please upload an unprotected file."
missing_file: "Archive does not contain required file matching '%{pattern}'."
processing_failed: "File could not be processed. Please try again or contact support."
Per-uploader custom messages
You can override any message for a specific uploader by adding a key under
shrine.zip_guard.errors.<uploader_name>:
en:
shrine:
zip_guard:
missing_file: "Archive does not contain required file matching '%{pattern}'."
errors:
digital_box_rd_uploader:
missing_file: "XML file must be at the root level of the ZIP archive."
password_protected: "This ZIP is password protected."
processing_failed: "Could not process this XML ZIP."
If the per-uploader key exists, it is used; otherwise the default key is used.
The class name is converted with underscore, so MyUploaders::XmlUploader
would become my_uploaders/xml_uploader.
Custom fallback for unexpected errors
By default, unexpected ZipGuard::Error subclasses are reported to the Rails error
reporter (if available) and a generic processing_failed message is added to
attacher.errors. You can override this with your own handler:
plugin :zip_guard,
required_files: ["*.xml"],
fallback: lambda { |error, attacher|
Sentry.capture_exception(error)
attacher.errors << "Custom processing error: #{error.}"
}
Supported versions
- Ruby >= 3.1
- Shrine >= 3.0, < 4.0
License
The gem is available as open source under the terms of the MIT License.