-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathpath-traversal.ql
More file actions
130 lines (120 loc) · 3.57 KB
/
path-traversal.ql
File metadata and controls
130 lines (120 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/**
* @name Path Traversal Vulnerability
* @description User-controlled inputs used in file operations may allow for path traversal attacks.
* @kind path-problem
* @problem.severity error
* @precision high
* @id js/path-traversal
* @tags security
* external/cwe/cwe-22
*/
import javascript
import DataFlow::PathGraph
/**
* Identifies sources of user-controlled input
*/
class UserInput extends DataFlow::Node {
UserInput() {
// HTTP request parameters
exists(DataFlow::ParameterNode param |
param.getName().regexpMatch("(?i).*(req|request|param|query|body|user|input).*") and
this = param
)
or
// Access to common request properties
exists(DataFlow::PropRead prop |
(
prop.getPropertyName() = "query" or
prop.getPropertyName() = "body" or
prop.getPropertyName() = "params" or
prop.getPropertyName() = "files"
) and
this = prop
)
}
}
/**
* Identifies fs module imports
*/
class FileSystemAccess extends DataFlow::CallNode {
FileSystemAccess() {
// Node.js fs module functions
exists(string name |
name = this.getCalleeName() and
(
name = "readFile" or
name = "readFileSync" or
name = "writeFile" or
name = "writeFileSync" or
name = "appendFile" or
name = "appendFileSync" or
name = "createReadStream" or
name = "createWriteStream" or
name = "openSync" or
name = "open"
)
)
or
// File system operations via require('fs')
exists(DataFlow::SourceNode fsModule, string methodName |
(fsModule.getAPropertyRead("promises") or fsModule).flowsTo(this.getReceiver()) and
methodName = this.getMethodName() and
(
methodName = "readFile" or
methodName = "writeFile" or
methodName = "appendFile" or
methodName = "readdir" or
methodName = "stat"
)
)
}
DataFlow::Node getPathArgument() {
result = this.getArgument(0)
}
}
/**
* Identifies sanitization of file paths
*/
predicate isPathSanitized(DataFlow::Node node) {
// Check for path normalization or validation
exists(DataFlow::CallNode call |
(
call.getCalleeName() = "resolve" or
call.getCalleeName() = "normalize" or
call.getCalleeName() = "isAbsolute" or
call.getCalleeName() = "relative" or
call.getCalleeName().regexpMatch("(?i).*(sanitize|validate|check).*path.*")
) and
call.flowsTo(node)
)
or
// Check for path traversal mitigation patterns
exists(DataFlow::CallNode call |
call.getCalleeName() = "replace" and
exists(StringLiteral regex |
regex = call.getArgument(0).(DataFlow::RegExpCreationNode).getSource().getAChildExpr() and
regex.getValue().regexpMatch("(\\.\\./|\\.\\.\\\\)")
) and
call.flowsTo(node)
)
}
/**
* Configuration for tracking flow from user input to file system operations
*/
class PathTraversalConfig extends TaintTracking::Configuration {
PathTraversalConfig() { this = "PathTraversalConfig" }
override predicate isSource(DataFlow::Node source) {
source instanceof UserInput
}
override predicate isSink(DataFlow::Node sink) {
exists(FileSystemAccess fileAccess |
sink = fileAccess.getPathArgument()
)
}
override predicate isSanitizer(DataFlow::Node node) {
isPathSanitized(node)
}
}
from PathTraversalConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "File system operation depends on a user-controlled value $@.", source.getNode(), "user input"