From fe2dff97a2b773754394339d63fb1d294c548c84 Mon Sep 17 00:00:00 2001 From: Thom May Date: Tue, 5 Jul 2016 13:40:05 +0100 Subject: [PATCH] Make extraction safer * Ignore file permissions in the archive * Ignore certain paths in the archive Signed-off-by: Thom May --- lib/mixlib/archive.rb | 4 ++-- lib/mixlib/archive/tar.rb | 21 +++++++++++++++++---- lib/mixlib/archive/version.rb | 2 +- spec/mixlib/archive_spec.rb | 7 ++++++- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/mixlib/archive.rb b/lib/mixlib/archive.rb index 507f50a..50f8c83 100644 --- a/lib/mixlib/archive.rb +++ b/lib/mixlib/archive.rb @@ -13,10 +13,10 @@ def initialize(archive, empty: false) @extractor = Mixlib::Archive::Tar.new(archive) end - def extract(destination) + def extract(destination, perms: true, ignore: []) create_and_empty(destination) - extractor.extract(destination) + extractor.extract(destination, perms: perms, ignore: ignore) end private diff --git a/lib/mixlib/archive/tar.rb b/lib/mixlib/archive/tar.rb index e029b24..d2f1958 100644 --- a/lib/mixlib/archive/tar.rb +++ b/lib/mixlib/archive/tar.rb @@ -14,8 +14,14 @@ def initialize(archive, options = {}) @options = options end - def extract(destination) + # Extracts the archive to the given +destination+ + # + # === Parameters + # perms:: should the extracter use permissions from the archive. + # ignore[Array]:: an array of matches of file paths to ignore + def extract(destination, perms: true, ignore: []) # (http://stackoverflow.com/a/31310593/506908) + ignore_re = Regexp.union(ignore) reader do |tar| dest = nil tar.each do |entry| @@ -23,19 +29,26 @@ def extract(destination) dest = File.join(destination, entry.read.strip) next end + next if entry.full_name =~ ignore_re dest ||= File.join(destination, entry.full_name) parent = File.dirname(dest) - FileUtils.mkdir_p(parent, mode: 0755) + FileUtils.mkdir_p(parent) if entry.directory? || (entry.header.typeflag == "" && entry.full_name.end_with?("/")) File.delete(dest) if File.file?(dest) - FileUtils.mkdir_p(dest, mode: entry.header.mode, verbose: false) + + if perms + FileUtils.mkdir_p(dest, mode: entry.header.mode, verbose: false) + else + FileUtils.mkdir_p(dest, verbose: false) + end + elsif entry.file? || (entry.header.typeflag == "" && !entry.full_name.end_with?("/")) FileUtils.rm_rf(dest) if File.directory?(dest) File.open(dest, "wb") do |f| f.print(entry.read) end - FileUtils.chmod(entry.header.mode, dest, verbose: false) + FileUtils.chmod(entry.header.mode, dest, verbose: false) if perms elsif entry.header.typeflag == "2" # handle symlink File.symlink(entry.header.linkname, dest) diff --git a/lib/mixlib/archive/version.rb b/lib/mixlib/archive/version.rb index e5a15d7..181b941 100644 --- a/lib/mixlib/archive/version.rb +++ b/lib/mixlib/archive/version.rb @@ -1,5 +1,5 @@ module Mixlib class Archive - VERSION = "0.1.0" + VERSION = "0.2.0" end end diff --git a/spec/mixlib/archive_spec.rb b/spec/mixlib/archive_spec.rb index 5119b86..2d9b7a7 100644 --- a/spec/mixlib/archive_spec.rb +++ b/spec/mixlib/archive_spec.rb @@ -44,9 +44,14 @@ end it "runs the extractor" do - expect(extractor).to receive(:extract).with(destination) + expect(extractor).to receive(:extract).with(destination, { perms: true, ignore: [] }) archive.extract(destination) end + + it "passes options to the extractor" do + expect(extractor).to receive(:extract).with(destination, { perms: false, ignore: [] }) + archive.extract(destination, perms: false) + end end describe "#create_and_empty" do