From fb5105725ff2a6344cdff7eba62bbd8e78576842 Mon Sep 17 00:00:00 2001
From: Marli Frost <marli@frost.red>
Date: Sat, 12 Sep 2020 10:32:10 +0100
Subject: [PATCH] refactor: reintroduce path sanitization strategy

I've documented the drawbacks of this strategy to make sure users are
aware of the tradeoff being made.
---
 src/read.rs | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/src/read.rs b/src/read.rs
index d19b353f..cf16c626 100644
--- a/src/read.rs
+++ b/src/read.rs
@@ -9,6 +9,7 @@ use crate::zipcrypto::ZipCryptoReaderValid;
 use std::borrow::Cow;
 use std::collections::HashMap;
 use std::io::{self, prelude::*};
+use std::path::Component;
 
 use crate::cp437::FromCp437;
 use crate::types::{DateTime, System, ZipFileData};
@@ -564,11 +565,21 @@ impl<'a> ZipFile<'a> {
     }
 
     /// Get the name of the file
+    ///
+    /// # Warnings
+    ///
+    /// It is dangerous to use this name directly when extracting an archive.
+    /// It may contain an absolute path (`/etc/shadow`), or break out of the
+    /// current directory (`../runtime`). Carelessly writing to these paths
+    /// allows an attacker to craft a ZIP archive that will overwrite critical
+    /// files.
     pub fn name(&self) -> &str {
         &self.data.file_name
     }
 
     /// Get the name of the file, in the raw (internal) byte representation.
+    ///
+    /// The encoding of this data is currently undefined.
     pub fn name_raw(&self) -> &[u8] {
         &self.data.file_name_raw
     }
@@ -578,9 +589,24 @@ impl<'a> ZipFile<'a> {
     #[deprecated(
         since = "0.5.7",
         note = "by stripping `..`s from the path, the meaning of paths can change.
-                You must use a sanitization strategy that's appropriate for your input"
+                `mangled_name` can be used if this behaviour is desirable"
     )]
     pub fn sanitized_name(&self) -> ::std::path::PathBuf {
+        self.mangled_name()
+    }
+
+    /// Rewrite the path, ignoring any path components with special meaning.
+    ///
+    /// - Absolute paths are made relative
+    /// - [`ParentDir`]s are ignored
+    /// - Truncates the filename at a NULL byte
+    ///
+    /// This is appropriate if you need to be able to extract *something* from
+    /// any archive, but will easily misrepresent trivial paths like
+    /// `foo/../bar` as `foo/bar` (instead of `bar`).
+    ///
+    /// [`ParentDir`]: `Component::ParentDir`
+    pub fn mangled_name(&self) -> ::std::path::PathBuf {
         self.data.file_name_sanitized()
     }