Skip to content

Commit b6c9f84

Browse files
committed
C#: ZipSlip - refactor to use Source, Sink, Sanitizer
This commit refactors the existing predicates to be classes extending Source, Sink or Sanitizer, as appropriate.
1 parent 09b2387 commit b6c9f84

File tree

1 file changed

+77
-52
lines changed
  • csharp/ql/src/semmle/code/csharp/security/dataflow

1 file changed

+77
-52
lines changed

csharp/ql/src/semmle/code/csharp/security/dataflow/ZipSlip.qll

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -27,77 +27,102 @@ module ZipSlip {
2727
}
2828

2929
override predicate isSource(DataFlow::Node source) {
30-
exists(PropertyAccess pa |
31-
source.asExpr() = archiveFullName(pa)
32-
)
30+
source instanceof Source
3331
}
3432

3533
override predicate isSink(DataFlow::Node sink) {
36-
exists(MethodCall mc |
37-
sink.asExpr() = compressionExtractToFileArgument(mc) or
38-
sink.asExpr() = fileOpenArgument(mc)
39-
)
40-
or
41-
exists(ObjectCreation oc |
42-
sink.asExpr() = streamConstructorArgument(oc) or
43-
sink.asExpr() = fileInfoConstructorArgument(oc)
44-
)
34+
sink instanceof Sink
4535
}
4636

4737
override predicate isSanitizer(DataFlow::Node node) {
48-
exists(MethodCall mc |
49-
node.asExpr() = fileNameExtraction(mc) or
50-
node.asExpr() = stringCheck(mc)
51-
)
38+
node instanceof Sanitizer
5239
}
5340
}
5441

55-
// access to full name of the archive item
56-
Expr archiveFullName(PropertyAccess pa) {
57-
pa.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression.ZipArchiveEntry") and
58-
pa.getTarget().getName() = "FullName" and
59-
result = pa
42+
/** An access to the `FullName` property of a `ZipArchiveEntry`. */
43+
class ArchiveFullNameSource extends Source {
44+
ArchiveFullNameSource() {
45+
exists(PropertyAccess pa |
46+
this.asExpr() = pa |
47+
pa.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression.ZipArchiveEntry") and
48+
pa.getTarget().getName() = "FullName"
49+
)
50+
}
6051
}
6152

62-
// argument to extract to file extension method
63-
Expr compressionExtractToFileArgument(MethodCall mc) {
64-
mc.getTarget().hasQualifiedName("System.IO.Compression.ZipFileExtensions", "ExtractToFile") and
65-
result = mc.getArgumentForName("destinationFileName")
53+
/** An argument to the `ExtractToFile` extension method. */
54+
class ExtractToFileArgSink extends Sink {
55+
ExtractToFileArgSink() {
56+
exists(MethodCall mc |
57+
mc.getTarget().hasQualifiedName("System.IO.Compression.ZipFileExtensions", "ExtractToFile") and
58+
this.asExpr() = mc.getArgumentForName("destinationFileName")
59+
)
60+
}
6661
}
6762

68-
// File Stream created from tainted file name through File.Open/File.Create
69-
Expr fileOpenArgument(MethodCall mc) {
70-
(
71-
mc.getTarget().hasQualifiedName("System.IO.File", "Open") or
72-
mc.getTarget().hasQualifiedName("System.IO.File", "OpenWrite") or
73-
mc.getTarget().hasQualifiedName("System.IO.File", "Create")
74-
) and
75-
result = mc.getArgumentForName("path")
63+
/** A path argument to a `File.Open`, `File.OpenWrite` or `File.Create` method call. */
64+
class FileOpenArgSink extends Sink {
65+
FileOpenArgSink() {
66+
exists(MethodCall mc |
67+
mc.getTarget().hasQualifiedName("System.IO.File", "Open") or
68+
mc.getTarget().hasQualifiedName("System.IO.File", "OpenWrite") or
69+
mc.getTarget().hasQualifiedName("System.IO.File", "Create") |
70+
this.asExpr() = mc.getArgumentForName("path")
71+
)
72+
}
7673
}
7774

78-
// File Stream created from tainted file name passed directly to the constructor
79-
Expr streamConstructorArgument(ObjectCreation oc) {
80-
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileStream") and
81-
result = oc.getArgumentForName("path")
75+
/** A path argument to a call to the `FileStream` constructor. */
76+
class FileStreamArgSink extends Sink {
77+
FileStreamArgSink() {
78+
exists(ObjectCreation oc |
79+
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileStream") |
80+
this.asExpr() = oc.getArgumentForName("path")
81+
)
82+
}
8283
}
8384

84-
// constructor to FileInfo can take tainted file name and subsequently be used to open file stream
85-
Expr fileInfoConstructorArgument(ObjectCreation oc) {
86-
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileInfo") and
87-
result = oc.getArgumentForName("fileName")
85+
/**
86+
* A path argument to a call to the `FileStream` constructor.
87+
*
88+
* This constructor can accept a tainted file name and subsequently be used to open a file stream.
89+
*/
90+
class FileInfoArgSink extends Sink {
91+
FileInfoArgSink() {
92+
exists(ObjectCreation oc |
93+
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileInfo") |
94+
this.asExpr() = oc.getArgumentForName("fileName")
95+
)
96+
}
8897
}
89-
// extracting just file name, not the full path
90-
Expr fileNameExtraction(MethodCall mc) {
91-
mc.getTarget().hasQualifiedName("System.IO.Path", "GetFileName") and
92-
result = mc.getAnArgument()
98+
99+
/**
100+
* An argument to GetFileName.
101+
*
102+
* This is considered a sanitizer because it extracts the just the file name, not the full path.
103+
*/
104+
class GetFileNameSanitizer extends Sanitizer {
105+
GetFileNameSanitizer() {
106+
exists(MethodCall mc |
107+
mc.getTarget().hasQualifiedName("System.IO.Path", "GetFileName") |
108+
this.asExpr() = mc.getAnArgument()
109+
)
110+
}
93111
}
94112

95-
// Checks the string for relative path, or checks the destination folder for whitelisted/target path, etc.
96-
Expr stringCheck(MethodCall mc) {
97-
(
98-
mc.getTarget().hasQualifiedName("System.String", "StartsWith") or
99-
mc.getTarget().hasQualifiedName("System.String", "Substring")
100-
) and
101-
result = mc.getQualifier()
113+
/**
114+
* A qualifier in a call to `StartsWith` or `Substring` string method.
115+
*
116+
* A call to a String method such as `StartsWith` or `Substring` can indicate a check for a
117+
* relative path, or a check against the destination folder for whitelisted/target path, etc.
118+
*/
119+
class StringCheckSanitizer extends Sanitizer {
120+
StringCheckSanitizer() {
121+
exists(MethodCall mc |
122+
mc.getTarget().hasQualifiedName("System.String", "StartsWith") or
123+
mc.getTarget().hasQualifiedName("System.String", "Substring") |
124+
this.asExpr() = mc.getQualifier()
125+
)
126+
}
102127
}
103128
}

0 commit comments

Comments
 (0)