11/**
2- * @name Arbitrary file write during a tarball extraction from user controlled source
2+ * @name Arbitrary file write during a tarball extraction from a user controlled source
33 * @description Extracting files from a potentially malicious tarball using `shutil.unpack_archive()` without validating
44 * that the destination file path is within the destination directory can cause files outside
5- * the destination directory to be overwritten. More precisely, if the tarball comes from a user controlled
5+ * the destination directory to be overwritten. More precisely, if the tarball comes from a user controlled
66 * location either a remote one or cli argument.
77 * @kind path-problem
88 * @id py/unsafe-unpacking
@@ -28,11 +28,11 @@ class UnsafeUnpackingConfig extends TaintTracking::Configuration {
2828 // A source coming from a remote location
2929 exists ( Http:: Client:: Request request | source = request )
3030 or
31- // A source coming from a CLI argparse module
31+ //A source coming from a CLI argparse module
3232 exists ( Node o , API:: Node ap , MethodCallNode args |
3333 ap = API:: moduleImport ( "argparse" ) .getMember ( "ArgumentParser" ) .getACall ( ) .getReturn ( ) and
3434 args = ap .getMember ( "parse_args" ) .getACall ( ) and
35- args .flowsTo ( o ) and
35+ args .flowsTo ( o ) and
3636 source .( AttrRead ) .accesses ( o , any ( string s ) )
3737 )
3838 }
@@ -57,7 +57,7 @@ class UnsafeUnpackingConfig extends TaintTracking::Configuration {
5757 exists ( Stdlib:: FileLikeObject:: InstanceSource is , Node f , MethodCallNode mc |
5858 is .flowsTo ( f ) and
5959 mc = API:: moduleImport ( "shutil" ) .getMember ( "copyfileobj" ) .getACall ( ) and
60- f = mc .getArg ( 1 ) and
60+ f = mc .getArg ( 1 ) and
6161 nodeFrom = mc .getArg ( 0 ) and
6262 nodeTo = is .( CallCfgNode ) .getArg ( 0 )
6363 )
@@ -70,13 +70,25 @@ class UnsafeUnpackingConfig extends TaintTracking::Configuration {
7070 )
7171 or
7272 // Accessing the name or raw content
73- exists ( AttrRead ar | ar .accesses ( nodeFrom , [ "name" , "raw" ] ) and nodeTo = ar )
73+ exists ( AttrRead ar | ar .accesses ( nodeFrom , [ "name" , "raw" ] ) and ar .flowsTo ( nodeTo ) )
74+ or
75+ //Use of join of filename
76+ exists ( API:: CallNode mcn |
77+ mcn = API:: moduleImport ( "os" ) .getMember ( "path" ) .getMember ( "join" ) .getACall ( ) and
78+ nodeFrom = mcn .getArg ( 1 ) and
79+ mcn .flowsTo ( nodeTo )
80+ )
81+ or
82+ // Read by chunks
83+ exists ( MethodCallNode mc |
84+ nodeFrom = mc .getObject ( ) and mc .getMethodName ( ) = "chunks" and mc .flowsTo ( nodeTo )
85+ )
7486 or
7587 // Considering the use of closing()
76- exists ( API:: Node closing |
77- closing = API:: moduleImport ( "contextlib" ) .getMember ( "closing" ) and
78- closing .getACall ( ) . flowsTo ( nodeTo ) and
79- nodeFrom = closing .getACall ( ) . getArg ( 0 )
88+ exists ( API:: CallNode closing |
89+ closing = API:: moduleImport ( "contextlib" ) .getMember ( "closing" ) . getACall ( ) and
90+ closing .flowsTo ( nodeTo ) and
91+ nodeFrom = closing .getArg ( 0 )
8092 )
8193 )
8294 }
0 commit comments