@@ -22,18 +22,22 @@ module Codec.Archive.Tar.Check.Internal (
22
22
23
23
-- * Security
24
24
checkSecurity ,
25
+ checkEntrySecurity ,
25
26
FileNameError (.. ),
26
27
27
28
-- * Tarbombs
28
29
checkTarbomb ,
30
+ checkEntryTarbomb ,
29
31
TarBombError (.. ),
30
32
31
33
-- * Portability
32
34
checkPortability ,
35
+ checkEntryPortability ,
33
36
PortabilityError (.. ),
34
37
PortabilityPlatform ,
35
38
) where
36
39
40
+ import Codec.Archive.Tar.LongNames
37
41
import Codec.Archive.Tar.Types
38
42
import Control.Applicative ((<|>) )
39
43
import Control.Monad.Catch (MonadThrow (throwM ))
@@ -67,35 +71,42 @@ import qualified System.FilePath.Posix as FilePath.Posix
67
71
-- link target. A failure in any entry terminates the sequence of entries with
68
72
-- an error.
69
73
--
70
- checkSecurity :: CheckSecurityCallback
71
- checkSecurity e = do
72
- check (entryTarPath e)
74
+ checkSecurity
75
+ :: Entries e
76
+ -> GenEntries FilePath FilePath (Either (Either e DecodeLongNamesError ) FileNameError )
77
+ checkSecurity = checkEntries checkEntrySecurity . decodeLongNames
78
+
79
+ -- |
80
+ -- @since 0.6.0.0
81
+ checkEntrySecurity :: GenEntry FilePath FilePath -> Maybe FileNameError
82
+ checkEntrySecurity e =
83
+ check (entryTarPath e) <|>
73
84
case entryContent e of
74
85
HardLink link ->
75
86
check link
76
87
SymbolicLink link ->
77
88
check (FilePath.Posix. takeDirectory (entryTarPath e) FilePath.Posix. </> link)
78
- _ -> pure ()
89
+ _ -> Nothing
79
90
where
80
91
checkPosix name
81
92
| FilePath.Posix. isAbsolute name
82
- = throwM $ AbsoluteFileName name
93
+ = Just $ AbsoluteFileName name
83
94
| not (FilePath.Posix. isValid name)
84
- = throwM $ InvalidFileName name
95
+ = Just $ InvalidFileName name
85
96
| not (isInsideBaseDir (FilePath.Posix. splitDirectories name))
86
- = throwM $ UnsafeLinkTarget name
87
- | otherwise = pure ()
97
+ = Just $ UnsafeLinkTarget name
98
+ | otherwise = Nothing
88
99
89
100
checkNative (fromFilePathToNative -> name)
90
101
| FilePath.Native. isAbsolute name || FilePath.Native. hasDrive name
91
- = throwM $ AbsoluteFileName name
102
+ = Just $ AbsoluteFileName name
92
103
| not (FilePath.Native. isValid name)
93
- = throwM $ InvalidFileName name
104
+ = Just $ InvalidFileName name
94
105
| not (isInsideBaseDir (FilePath.Native. splitDirectories name))
95
- = throwM $ UnsafeLinkTarget name
96
- | otherwise = pure ()
106
+ = Just $ UnsafeLinkTarget name
107
+ | otherwise = Nothing
97
108
98
- check name = checkPosix name >>= \ _ - > checkNative (fromFilePathToNative name)
109
+ check name = checkPosix name <| > checkNative (fromFilePathToNative name)
99
110
100
111
isInsideBaseDir :: [FilePath ] -> Bool
101
112
isInsideBaseDir = go 0
@@ -143,18 +154,28 @@ showFileNameError mb_plat err = case err of
143
154
-- Note: This check must be used in conjunction with 'checkSecurity'
144
155
-- (or 'checkPortability').
145
156
--
146
- checkTarbomb :: FilePath -> CheckSecurityCallback
147
- checkTarbomb expectedTopDir entry = do
157
+ checkTarbomb
158
+ :: FilePath
159
+ -> Entries e
160
+ -> GenEntries FilePath FilePath (Either (Either e DecodeLongNamesError ) TarBombError )
161
+ checkTarbomb expectedTopDir
162
+ = checkEntries (checkEntryTarbomb expectedTopDir)
163
+ . decodeLongNames
164
+
165
+ -- |
166
+ -- @since 0.6.0.0
167
+ checkEntryTarbomb :: FilePath -> GenEntry FilePath linkTarget -> Maybe TarBombError
168
+ checkEntryTarbomb expectedTopDir entry = do
148
169
case entryContent entry of
149
170
-- Global extended header aka XGLTYPE aka pax_global_header
150
171
-- https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_02
151
- OtherEntryType ' g' _ _ -> pure ()
172
+ OtherEntryType ' g' _ _ -> Nothing
152
173
-- Extended header referring to the next file in the archive aka XHDTYPE
153
- OtherEntryType ' x' _ _ -> pure ()
174
+ OtherEntryType ' x' _ _ -> Nothing
154
175
_ ->
155
176
case FilePath.Posix. splitDirectories (entryTarPath entry) of
156
- (topDir: _) | topDir == expectedTopDir -> pure ()
157
- _ -> throwM $ TarBombError expectedTopDir (entryTarPath entry)
177
+ (topDir: _) | topDir == expectedTopDir -> Nothing
178
+ _ -> Just $ TarBombError expectedTopDir (entryTarPath entry)
158
179
159
180
-- | An error that occurs if a tar file is a \"tar bomb\" that would extract
160
181
-- files outside of the intended directory.
@@ -195,33 +216,40 @@ instance Show TarBombError where
195
216
-- includes characters that are valid in both systems and the \'/\' vs \'\\\'
196
217
-- directory separator conventions.
197
218
--
198
- checkPortability :: CheckSecurityCallback
199
- checkPortability entry
219
+ checkPortability
220
+ :: Entries e
221
+ -> GenEntries FilePath FilePath (Either (Either e DecodeLongNamesError ) PortabilityError )
222
+ checkPortability = checkEntries checkEntryPortability . decodeLongNames
223
+
224
+ -- |
225
+ -- @since 0.6.0.0
226
+ checkEntryPortability :: GenEntry FilePath linkTarget -> Maybe PortabilityError
227
+ checkEntryPortability entry
200
228
| entryFormat entry `elem` [V7Format , GnuFormat ]
201
- = throwM $ NonPortableFormat (entryFormat entry)
229
+ = Just $ NonPortableFormat (entryFormat entry)
202
230
203
231
| not (portableFileType (entryContent entry))
204
- = throwM NonPortableFileType
232
+ = Just NonPortableFileType
205
233
206
234
| not (all portableChar posixPath)
207
- = throwM $ NonPortableEntryNameChar posixPath
235
+ = Just $ NonPortableEntryNameChar posixPath
208
236
209
237
| not (FilePath.Posix. isValid posixPath)
210
- = throwM $ NonPortableFileName " unix" (InvalidFileName posixPath)
238
+ = Just $ NonPortableFileName " unix" (InvalidFileName posixPath)
211
239
| not (FilePath.Windows. isValid windowsPath)
212
- = throwM $ NonPortableFileName " windows" (InvalidFileName windowsPath)
240
+ = Just $ NonPortableFileName " windows" (InvalidFileName windowsPath)
213
241
214
242
| FilePath.Posix. isAbsolute posixPath
215
- = throwM $ NonPortableFileName " unix" (AbsoluteFileName posixPath)
243
+ = Just $ NonPortableFileName " unix" (AbsoluteFileName posixPath)
216
244
| FilePath.Windows. isAbsolute windowsPath
217
- = throwM $ NonPortableFileName " windows" (AbsoluteFileName windowsPath)
245
+ = Just $ NonPortableFileName " windows" (AbsoluteFileName windowsPath)
218
246
219
247
| any (== " .." ) (FilePath.Posix. splitDirectories posixPath)
220
- = throwM $ NonPortableFileName " unix" (InvalidFileName posixPath)
248
+ = Just $ NonPortableFileName " unix" (InvalidFileName posixPath)
221
249
| any (== " .." ) (FilePath.Windows. splitDirectories windowsPath)
222
- = throwM $ NonPortableFileName " windows" (InvalidFileName windowsPath)
250
+ = Just $ NonPortableFileName " windows" (InvalidFileName windowsPath)
223
251
224
- | otherwise = pure ()
252
+ | otherwise = Nothing
225
253
226
254
where
227
255
posixPath = entryTarPath entry
@@ -259,3 +287,10 @@ instance Show PortabilityError where
259
287
= " Non-portable character in archive entry name: " ++ show posixPath
260
288
show (NonPortableFileName platform err)
261
289
= showFileNameError (Just platform) err
290
+
291
+ --------------------------
292
+ -- Utils
293
+
294
+ checkEntries :: (GenEntry a b -> Maybe e' ) -> GenEntries a b e -> GenEntries a b (Either e e' )
295
+ checkEntries checkEntry =
296
+ mapEntries (\ entry -> maybe (Right entry) Left (checkEntry entry))
0 commit comments