from Constructor c, Annotation ann, AnnotationType anntp where ann = c.getAnAnnotation() and anntp = ann.getType() and anntp.hasQualifiedName("java.lang", "Deprecated") select ann
返回private字段的JavaDoc注释内容:
1 2 3 4 5 6
import java
from Field f, Javadoc jdoc where f.isPrivate() and jdoc = f.getDoc().getJavadoc() select jdoc
/** * Holds if data can flow in one local step from `node1` to `node2`. */ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) { exists(Node n1, Node n2 | node1.asNode() = n1 and node2.asNode() = n2 and simpleLocalFlowStepExt(n1, n2) and not outBarrier(node1, config) and not inBarrier(node2, config) and not fullBarrier(node1, config) and not fullBarrier(node2, config) ) or exists(Node n | config.allowImplicitRead(n, _) and node1.asNode() = n and node2.isImplicitReadNode(n, false) ) }
from Constructor fileReader, Call call where fileReader.getDeclaringType().hasQualifiedName("java.io", "FileReader") and call.getCallee() = fileReader select call.getArgument(0)
from Constructor fileReader, Call call, Expr src where fileReader.getDeclaringType().hasQualifiedName("java.io", "FileReader") and call.getCallee() = fileReader and DataFlow::localFlow(DataFlow::exprNode(src), DataFlow::exprNode(call.getArgument(0))) select src
from Constructor fileReader, Call call, Parameter p where fileReader.getDeclaringType().hasQualifiedName("java.io", "FileReader") and call.getCallee() = fileReader and DataFlow::localFlow(DataFlow::parameterNode(p), DataFlow::exprNode(call.getArgument(0))) select p
This query finds calls to formatting functions where the format string is not hard-coded:
from StringFormatMethod format, MethodAccess call, Expr formatString where call.getMethod() = format and call.getArgument(format.getFormatStringIndex()) = formatString and not exists(DataFlow::Node source, DataFlow::Node sink | DataFlow::localFlow(source, sink) and source.asExpr() instanceof StringLiteral and sink.asExpr() = formatString ) select call, "Argument to String format method isn't hard-coded."
Global data flow
more powerful than local data flow, less precise than local data flow, need more time and memory to perform.
Using global data flow
对DataFlow::Configuration进行继承,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13
import semmle.code.java.dataflow.DataFlow
class MyDataFlowConfiguration extends DataFlow::Configuration { MyDataFlowConfiguration() { this = "MyDataFlowConfiguration" }
Write a query that finds all hard-coded strings used to create a java.net.URL, using local data flow.
1 2 3 4 5 6 7 8
import semmle.code.java.dataflow.DataFlow
from Constructor url, Call call, StringLiteral src where url.getDeclaringType().hasQualifiedName("java.net", "URL") and call.getCallee() = url and DataFlow::localFlow(DataFlow::exprNode(src), DataFlow::exprNode(call.getArgument(0))) select src
Exercises 2
Write a query that finds all hard-coded strings used to create a java.net.URL, using global data flow.
/** class representing java.util.Collection.toArray(T[]) */ class CollectionToArray extends Method { CollectionToArray() { this.getDeclaringType().hasQualifiedName("java.util", "Collection") and this.hasName("toArray") and this.getNumberOfParameters() = 1 } }
/** class representing calls to java.util.Collection.toArray(T[]) */ class CollectionToArrayCall extends MethodAccess { CollectionToArrayCall() { exists(CollectionToArray m | this.getMethod().getSourceDeclaration().overridesOrInstantiates*(m) ) }
/** the call's actual return type, as determined from its argument */ Array getActualReturnType() { result = this.getArgument(0).getType() } }
最终版本:
刨除掉那些对类型为A[]的对象调用toArray,然后将其再转型到A[]。
1 2 3 4 5 6 7 8 9 10
import java
// Insert the class definitions from above
from CastExpr ce, Array source, Array target where source = ce.getExpr().getType() and target = ce.getType() and target.getElementType().(RefType).getASupertype+() = source.getElementType() and not ce.getExpr().(CollectionToArrayCall).getActualReturnType() = target select ce, "Potentially problematic array downcast."
举例:查找不匹配的包含检查
1 2 3 4 5 6 7
Map<Object, Object> zkProp;
// ...
if (zkProp.entrySet().contains("dynamicConfigFile")){ // ... }
Type getArgumentType() { result = this.getArgument(0).getType() }
1 2 3 4 5 6 7
Type getCollectionElementType() { exists(RefType D, ParameterizedInterface S | D = this.getMethod().getDeclaringType() and D.hasSupertype*(S) and S.getSourceDeclaration() instanceof JavaUtilCollection and result = S.getTypeArgument(0) ) }
from JavaUtilCollectionContainsCall juccc, Type collEltType, Type argType where collEltType = juccc.getCollectionElementType() and argType = juccc.getArgumentType() and not haveCommonDescendant(collEltType, argType) select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType
from JavaUtilCollectionContainsCall juccc, Type collEltType, Type argType where collEltType = juccc.getCollectionElementType() and argType = juccc.getArgumentType() and not haveCommonDescendant(collEltType, argType) and not collEltType instanceof TypeVariable and not argType instanceof TypeVariable and not collEltType = argType.(PrimitiveType).getBoxedType() and not argType.hasName("<nulltype>") select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType
from Callable callee where not exists(Callable caller|caller.polyCalls(callee)) select callee
一个标准Java应用并不会对JDK中的每个库函数都调用可以用fromSource谓词来做过滤。
我们可以使用谓词fromSource来检查一个编译单元是否是一个源文件,并细化我们的查询。
1 2 3 4
import java from Callable callee where not exists(Callable caller|caller.polyCalls(callee)) and callee.getCompilationUnit().fromSource() select callee, "Not called."
from Callable callee where not exists(Callable caller | caller.polyCalls(callee)) and callee.getCompilationUnit().fromSource() and not callee.hasName("<clinit>") and not callee.hasName("finalize") select callee, "Not called."
还有一种特例就是
在单例模式中,一个类提供了private修饰的空构造方法,防止它被其他类实例化(单例模式)。
这些结果不应该出现在我们的查询结果中,因为他们的设计意图就是这样。
1 2 3 4 5 6 7 8 9
import java
from Callable callee where not exists(Callable caller | caller.polyCalls(callee)) and callee.getCompilationUnit().fromSource() and not callee.hasName("<clinit>") and not callee.hasName("finalize") and not callee.isPublic() and not callee.(Constructor).getNumberOfParameters() = 0 select callee, "Not called."
实际上很多方法其实都是可用通过反射的方式去调用的,一般来说很难检测这种方法。
但是CodeQL支持识别JUnit和其他框架的测试类,这些测试类被test runner调用。
1 2 3 4 5 6 7 8 9 10
import java
from Callable callee where not exists(Callable caller | caller.polyCalls(callee)) and callee.getCompilationUnit().fromSource() and not callee.hasName("<clinit>") and not callee.hasName("finalize") and not callee.isPublic() and not callee.(Constructor).getNumberOfParameters() = 0 and not callee.getDeclaringType() instanceof TestClass select callee, "Not called."
import java from Constructor c, Annotation ann, AnnotationType anntp where ann = c.getAnAnnotation() and anntp = ann.getType() and anntp.hasQualifiedName("java.lang", "SuppressWarnings") select ann, ann.getValue("value")
下面这个例子仅仅查询具有单个注解元素,并且名字是value:
1 2 3 4 5 6 7 8
import java
from AnnotationType anntp where forex(AnnotationElement elt | elt = anntp.getAnAnnotationElement() | elt.getName() = "value" ) select anntp
举例:查询缺失的@Override注解
1 2 3 4 5 6 7 8 9 10 11
classSuper{ publicvoidm(){} }
classSub1extendsSuper{ @Overridepublicvoidm(){} }
classSub2extendsSuper{ publicvoidm(){} }
我们需要找到那些Sub2.m,应该加上@Override注解的方法。
找到所有的@Override注解:
1 2 3 4 5
import java
from Annotation ann where ann.getType().hasQualifiedName("java.lang","Override") select ann
import java from Method overriding, Method overridden where overriding.overrides(overridden) and not overriding.getAnAnnotation() instanceof OverrideAnnotation and overriding.fromSource() select overriding, "Method overrides another method, but does not have an @Override annotation."
from Call call where call.getCallee() instanceof DeprecatedMethod and not call.getCaller() instanceof DeprecatedMethod select call, "This call invokes a deprecated method."
from Call call where call.getCallee() instanceof DeprecatedMethod and not call.getCaller() instanceof DeprecatedMethod and not call.getCaller().getAnAnnotation() instanceof SuppressDeprecationWarningAnnotation select call, "This call invokes a deprecated method."
import <language> // For some languages (Java/C++/Python) you need to explicitly import the data flow library, such as // import semmle.code.java.dataflow.DataFlow import DataFlow::PathGraph ...
from MyConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "<message>"
from Expr e, Callable c where c.getDeclaringType().hasQualifiedName("my.namespace.name", "MyClass") and c.getName() = "c" and e.getEnclosingCallable() = c select e, e.getAQlClass()
from MyConfig config, DataFlow::PathNode source, DataFlow::PathNode sink where config.hasFlowPath(source, sink) select sink.getNode(), source, sink, "Sink is reached from $@.", source.getNode(), "here"
简化版:
1 2 3
from MyConfig config, DataFlow::Node source, DataFlow::Node sink where config.hasPath(source, sink) select sink, "Sink is reached from $@.", source.getNode(), "here"
overrideint fieldFlowBranchLimit() { result = 5000 }
如果仍然没有结果,而且性能还可以使用,那么最好在做进一步调试时将此设置为高值。
步骤二:局部数据流
Configuration.hasPartialFlow谓词、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/** * Holds if there is a partial data flow path from `source` to `node`. The * approximate distance between `node` and the closest source is `dist` and * is restricted to be less than or equal to `explorationLimit()`. This * predicate completely disregards sink definitions. * * This predicate is intended for dataflow exploration and debugging and may * perform poorly if the number of sources is too big and/or the exploration * limit is set too high without using barriers. * * This predicate is disabled (has no results) by default. Override * `explorationLimit()` with a suitable number to enable this predicate. * * To use this in a`path-problem` query, import the module `PartialPathGraph`. */ final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) {