@@ -9,6 +9,9 @@ private import codeql.ruby.frameworks.core.Gem
9
9
private import codeql.ruby.frameworks.data.ModelsAsData
10
10
private import codeql.ruby.frameworks.data.internal.ApiGraphModelsExtensions
11
11
private import queries.modeling.internal.Util as Util
12
+ private import codeql.util.Unit
13
+ private import codeql.ruby.AST
14
+ private import codeql.ruby.ast.Module
12
15
13
16
/** Holds if the given callable is not worth supporting. */
14
17
private predicate isUninteresting ( DataFlow:: MethodNode c ) {
@@ -26,56 +29,78 @@ private predicate gemFileStep(Gem::GemSpec gem, Folder folder, int n) {
26
29
}
27
30
28
31
/**
29
- * A callable method or accessor from either the Ruby Standard Library, a 3rd party library, or from the source .
32
+ * Gets the namespace of an endpoint in `file` .
30
33
*/
31
- class Endpoint extends DataFlow:: MethodNode {
32
- Endpoint ( ) { this .isPublic ( ) and not isUninteresting ( this ) }
34
+ string getNamespace ( File file ) {
35
+ exists ( Folder folder | folder = file .getParentContainer ( ) |
36
+ // The nearest gemspec to this endpoint, if one exists
37
+ result = min ( Gem:: GemSpec g , int n | gemFileStep ( g , folder , n ) | g order by n ) .getName ( )
38
+ or
39
+ not gemFileStep ( _, folder , _) and
40
+ result = ""
41
+ )
42
+ }
33
43
34
- File getFile ( ) { result = this .getLocation ( ) .getFile ( ) }
44
+ abstract class Endpoint instanceof AstNode {
45
+ string getNamespace ( ) { result = getNamespace ( this .( AstNode ) .getLocation ( ) .getFile ( ) ) }
35
46
36
- string getName ( ) { result = this .getMethodName ( ) }
47
+ string getFileName ( ) { result = this .( AstNode ) . getLocation ( ) . getFile ( ) . getBaseName ( ) }
37
48
38
- /**
39
- * Gets the namespace of this endpoint.
40
- */
41
- bindingset [ this ]
42
- string getNamespace ( ) {
43
- exists ( Folder folder | folder = this .getFile ( ) .getParentContainer ( ) |
44
- // The nearest gemspec to this endpoint, if one exists
45
- result = min ( Gem:: GemSpec g , int n | gemFileStep ( g , folder , n ) | g order by n ) .getName ( )
46
- or
47
- not gemFileStep ( _, folder , _) and
48
- result = ""
49
- )
49
+ string toString ( ) { result = this .( AstNode ) .toString ( ) }
50
+
51
+ abstract string getType ( ) ;
52
+
53
+ abstract string getName ( ) ;
54
+
55
+ abstract string getParameters ( ) ;
56
+
57
+ abstract boolean getSupportedStatus ( ) ;
58
+
59
+ abstract string getSupportedType ( ) ;
60
+ }
61
+
62
+ /**
63
+ * A callable method or accessor from source code.
64
+ */
65
+ class MethodEndpoint extends Endpoint {
66
+ private DataFlow:: MethodNode methodNode ;
67
+
68
+ MethodEndpoint ( ) {
69
+ this = methodNode .asExpr ( ) .getExpr ( ) and
70
+ methodNode .isPublic ( ) and
71
+ not isUninteresting ( methodNode )
50
72
}
51
73
74
+ DataFlow:: MethodNode getNode ( ) { result = methodNode }
75
+
76
+ override string getName ( ) { result = methodNode .getMethodName ( ) }
77
+
52
78
/**
53
79
* Gets the unbound type name of this endpoint.
54
80
*/
55
- bindingset [ this ]
56
- string getTypeName ( ) {
81
+ override string getType ( ) {
57
82
result =
58
- any ( DataFlow:: ModuleNode m | m .getOwnInstanceMethod ( this .getMethodName ( ) ) = this )
83
+ any ( DataFlow:: ModuleNode m | m .getOwnInstanceMethod ( this .getName ( ) ) = methodNode )
59
84
.getQualifiedName ( ) or
60
85
result =
61
- any ( DataFlow:: ModuleNode m | m .getOwnSingletonMethod ( this .getMethodName ( ) ) = this )
86
+ any ( DataFlow:: ModuleNode m | m .getOwnSingletonMethod ( this .getName ( ) ) = methodNode )
62
87
.getQualifiedName ( ) + "!"
63
88
}
64
89
65
90
/**
66
91
* Gets the parameter types of this endpoint.
67
92
*/
68
- bindingset [ this ]
69
- string getParameterTypes ( ) {
93
+ override string getParameters ( ) {
70
94
// For now, return the names of postional and keyword parameters. We don't always have type information, so we can't return type names.
71
95
// We don't yet handle splat params or block params.
72
96
result =
73
97
"(" +
74
98
concat ( string key , string value |
75
- value = any ( int i | i .toString ( ) = key | this .asCallable ( ) .getParameter ( i ) ) .getName ( )
99
+ value =
100
+ any ( int i | i .toString ( ) = key | methodNode .asCallable ( ) .getParameter ( i ) ) .getName ( )
76
101
or
77
102
exists ( DataFlow:: ParameterNode param |
78
- param = this .asCallable ( ) .getKeywordParameter ( key )
103
+ param = methodNode .asCallable ( ) .getKeywordParameter ( key )
79
104
|
80
105
value = key + ":"
81
106
)
@@ -90,11 +115,11 @@ class Endpoint extends DataFlow::MethodNode {
90
115
91
116
/** Holds if this API is a known source. */
92
117
pragma [ nomagic]
93
- abstract predicate isSource ( ) ;
118
+ predicate isSource ( ) { this . getNode ( ) instanceof SourceCallable }
94
119
95
120
/** Holds if this API is a known sink. */
96
121
pragma [ nomagic]
97
- abstract predicate isSink ( ) ;
122
+ predicate isSink ( ) { this . getNode ( ) instanceof SinkCallable }
98
123
99
124
/** Holds if this API is a known neutral. */
100
125
pragma [ nomagic]
@@ -108,9 +133,11 @@ class Endpoint extends DataFlow::MethodNode {
108
133
this .hasSummary ( ) or this .isSource ( ) or this .isSink ( ) or this .isNeutral ( )
109
134
}
110
135
111
- boolean getSupportedStatus ( ) { if this .isSupported ( ) then result = true else result = false }
136
+ override boolean getSupportedStatus ( ) {
137
+ if this .isSupported ( ) then result = true else result = false
138
+ }
112
139
113
- string getSupportedType ( ) {
140
+ override string getSupportedType ( ) {
114
141
this .isSink ( ) and result = "sink"
115
142
or
116
143
this .isSource ( ) and result = "source"
@@ -132,7 +159,7 @@ string methodClassification(Call method) {
132
159
133
160
class TestFile extends File {
134
161
TestFile ( ) {
135
- this .getRelativePath ( ) .regexpMatch ( ".*(test|spec).+" ) and
162
+ this .getRelativePath ( ) .regexpMatch ( ".*(test|spec|examples ).+" ) and
136
163
not this .getAbsolutePath ( ) .matches ( "%/ql/test/%" ) // allows our test cases to work
137
164
}
138
165
}
@@ -164,10 +191,32 @@ class SourceCallable extends DataFlow::CallableNode {
164
191
}
165
192
166
193
/**
167
- * A class of effectively public callables from source code.
194
+ * A module defined in source code
168
195
*/
169
- class PublicEndpointFromSource extends Endpoint {
170
- override predicate isSource ( ) { this instanceof SourceCallable }
196
+ class ModuleEndpoint extends Endpoint {
197
+ private DataFlow:: ModuleNode moduleNode ;
198
+
199
+ ModuleEndpoint ( ) {
200
+ this =
201
+ min ( AstNode n , Location loc |
202
+ n = moduleNode .getADeclaration ( ) and
203
+ loc = n .getLocation ( )
204
+ |
205
+ n order by loc .getFile ( ) .getAbsolutePath ( ) , loc .getStartLine ( ) , loc .getStartColumn ( )
206
+ ) and
207
+ not moduleNode .( Module ) .isBuiltin ( ) and
208
+ not moduleNode .getLocation ( ) .getFile ( ) instanceof TestFile
209
+ }
210
+
211
+ DataFlow:: ModuleNode getNode ( ) { result = moduleNode }
212
+
213
+ override string getType ( ) { result = this .getNode ( ) .getQualifiedName ( ) }
214
+
215
+ override string getName ( ) { result = "" }
216
+
217
+ override string getParameters ( ) { result = "" }
218
+
219
+ override boolean getSupportedStatus ( ) { result = false }
171
220
172
- override predicate isSink ( ) { this instanceof SinkCallable }
221
+ override string getSupportedType ( ) { result = "" }
173
222
}
0 commit comments