@@ -9,6 +9,9 @@ private import codeql.ruby.frameworks.core.Gem
99private import codeql.ruby.frameworks.data.ModelsAsData
1010private import codeql.ruby.frameworks.data.internal.ApiGraphModelsExtensions
1111private 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
1215
1316/** Holds if the given callable is not worth supporting. */
1417private predicate isUninteresting ( DataFlow:: MethodNode c ) {
@@ -26,56 +29,78 @@ private predicate gemFileStep(Gem::GemSpec gem, Folder folder, int n) {
2629}
2730
2831/**
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` .
3033 */
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+ }
3343
34- File getFile ( ) { result = this .getLocation ( ) .getFile ( ) }
44+ abstract class Endpoint instanceof AstNode {
45+ string getNamespace ( ) { result = getNamespace ( this .( AstNode ) .getLocation ( ) .getFile ( ) ) }
3546
36- string getName ( ) { result = this .getMethodName ( ) }
47+ string getFileName ( ) { result = this .( AstNode ) . getLocation ( ) . getFile ( ) . getBaseName ( ) }
3748
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 )
5072 }
5173
74+ DataFlow:: MethodNode getNode ( ) { result = methodNode }
75+
76+ override string getName ( ) { result = methodNode .getMethodName ( ) }
77+
5278 /**
5379 * Gets the unbound type name of this endpoint.
5480 */
55- bindingset [ this ]
56- string getTypeName ( ) {
81+ override string getType ( ) {
5782 result =
58- any ( DataFlow:: ModuleNode m | m .getOwnInstanceMethod ( this .getMethodName ( ) ) = this )
83+ any ( DataFlow:: ModuleNode m | m .getOwnInstanceMethod ( this .getName ( ) ) = methodNode )
5984 .getQualifiedName ( ) or
6085 result =
61- any ( DataFlow:: ModuleNode m | m .getOwnSingletonMethod ( this .getMethodName ( ) ) = this )
86+ any ( DataFlow:: ModuleNode m | m .getOwnSingletonMethod ( this .getName ( ) ) = methodNode )
6287 .getQualifiedName ( ) + "!"
6388 }
6489
6590 /**
6691 * Gets the parameter types of this endpoint.
6792 */
68- bindingset [ this ]
69- string getParameterTypes ( ) {
93+ override string getParameters ( ) {
7094 // For now, return the names of postional and keyword parameters. We don't always have type information, so we can't return type names.
7195 // We don't yet handle splat params or block params.
7296 result =
7397 "(" +
7498 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 ( )
76101 or
77102 exists ( DataFlow:: ParameterNode param |
78- param = this .asCallable ( ) .getKeywordParameter ( key )
103+ param = methodNode .asCallable ( ) .getKeywordParameter ( key )
79104 |
80105 value = key + ":"
81106 )
@@ -90,11 +115,11 @@ class Endpoint extends DataFlow::MethodNode {
90115
91116 /** Holds if this API is a known source. */
92117 pragma [ nomagic]
93- abstract predicate isSource ( ) ;
118+ predicate isSource ( ) { this . getNode ( ) instanceof SourceCallable }
94119
95120 /** Holds if this API is a known sink. */
96121 pragma [ nomagic]
97- abstract predicate isSink ( ) ;
122+ predicate isSink ( ) { this . getNode ( ) instanceof SinkCallable }
98123
99124 /** Holds if this API is a known neutral. */
100125 pragma [ nomagic]
@@ -108,9 +133,11 @@ class Endpoint extends DataFlow::MethodNode {
108133 this .hasSummary ( ) or this .isSource ( ) or this .isSink ( ) or this .isNeutral ( )
109134 }
110135
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+ }
112139
113- string getSupportedType ( ) {
140+ override string getSupportedType ( ) {
114141 this .isSink ( ) and result = "sink"
115142 or
116143 this .isSource ( ) and result = "source"
@@ -132,7 +159,7 @@ string methodClassification(Call method) {
132159
133160class TestFile extends File {
134161 TestFile ( ) {
135- this .getRelativePath ( ) .regexpMatch ( ".*(test|spec).+" ) and
162+ this .getRelativePath ( ) .regexpMatch ( ".*(test|spec|examples ).+" ) and
136163 not this .getAbsolutePath ( ) .matches ( "%/ql/test/%" ) // allows our test cases to work
137164 }
138165}
@@ -164,10 +191,32 @@ class SourceCallable extends DataFlow::CallableNode {
164191}
165192
166193/**
167- * A class of effectively public callables from source code.
194+ * A module defined in source code
168195 */
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 }
171220
172- override predicate isSink ( ) { this instanceof SinkCallable }
221+ override string getSupportedType ( ) { result = "" }
173222}
0 commit comments