зеркало из https://github.com/github/codeql.git
Java: Autoformat libs outside semmle.code.java.
This commit is contained in:
Родитель
16b29b2d08
Коммит
67d1c72e64
|
@ -1,25 +1,23 @@
|
|||
import java
|
||||
|
||||
/** Holds if the given `Javadoc` contains a minimum of a few characters of text. */
|
||||
private
|
||||
predicate acceptableDocText(Javadoc j) {
|
||||
private predicate acceptableDocText(Javadoc j) {
|
||||
// Require minimum combined length of all non-tag elements.
|
||||
sum(JavadocElement e, int toSum |
|
||||
e = j.getAChild() and
|
||||
not e = j.getATag(_) and
|
||||
toSum = e.toString().length()
|
||||
|
|
||||
|
|
||||
toSum
|
||||
) >= 5
|
||||
}
|
||||
|
||||
/** Holds if the given `JavadocTag` contains a minimum of a few characters of text. */
|
||||
private
|
||||
predicate acceptableTag(JavadocTag t) {
|
||||
private predicate acceptableTag(JavadocTag t) {
|
||||
sum(JavadocElement e, int toSum |
|
||||
e = t.getAChild() and
|
||||
toSum = e.toString().length()
|
||||
|
|
||||
|
|
||||
toSum
|
||||
) >= 5
|
||||
}
|
||||
|
@ -31,9 +29,7 @@ class DocuRefType extends RefType {
|
|||
this.isPublic()
|
||||
}
|
||||
|
||||
predicate hasAcceptableDocText() {
|
||||
acceptableDocText(this.getDoc().getJavadoc())
|
||||
}
|
||||
predicate hasAcceptableDocText() { acceptableDocText(this.getDoc().getJavadoc()) }
|
||||
}
|
||||
|
||||
/** A public (non-getter, non-setter) `Callable` that does not override another method. */
|
||||
|
@ -50,12 +46,11 @@ class DocuCallable extends Callable {
|
|||
not this.getLocation() = this.getDeclaringType().getLocation()
|
||||
}
|
||||
|
||||
predicate hasAcceptableDocText() {
|
||||
acceptableDocText(this.getDoc().getJavadoc())
|
||||
}
|
||||
predicate hasAcceptableDocText() { acceptableDocText(this.getDoc().getJavadoc()) }
|
||||
|
||||
string toMethodOrConstructorString() {
|
||||
(this instanceof Method and result = "method") or
|
||||
(this instanceof Method and result = "method")
|
||||
or
|
||||
(this instanceof Constructor and result = "constructor")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ import semmle.code.xml.MavenPom
|
|||
* within the given container (folder, jar file etc.).
|
||||
*/
|
||||
predicate pomDependsOnContainer(Pom f, Container g) {
|
||||
exists(RefType source, RefType target |
|
||||
source.getFile().getParentContainer*() = f.getFile().getParentContainer() and
|
||||
target.getFile().getParentContainer*() = g and
|
||||
depends(source, target)
|
||||
)
|
||||
exists(RefType source, RefType target |
|
||||
source.getFile().getParentContainer*() = f.getFile().getParentContainer() and
|
||||
target.getFile().getParentContainer*() = g and
|
||||
depends(source, target)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,9 +18,9 @@ predicate pomDependsOnContainer(Pom f, Container g) {
|
|||
* within the project represented by the targetPom.
|
||||
*/
|
||||
predicate pomDependsOnPom(Pom sourcePom, Pom targetPom) {
|
||||
exists(RefType source, RefType target |
|
||||
source.getFile().getParentContainer*() = sourcePom.getFile().getParentContainer() and
|
||||
target.getFile().getParentContainer*() = targetPom.getFile().getParentContainer() and
|
||||
depends(source, target)
|
||||
)
|
||||
exists(RefType source, RefType target |
|
||||
source.getFile().getParentContainer*() = sourcePom.getFile().getParentContainer() and
|
||||
target.getFile().getParentContainer*() = targetPom.getFile().getParentContainer() and
|
||||
depends(source, target)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
*
|
||||
* http://hg.openjdk.java.net/jdk9/jdk9/langtools/file/6ba2130e87bd/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdk8_internals.txt
|
||||
*/
|
||||
|
||||
predicate jdkInternalApi(string p) {
|
||||
p = "apple.applescript" or
|
||||
p = "apple.laf" or
|
||||
|
|
|
@ -3,16 +3,14 @@
|
|||
*
|
||||
* http://hg.openjdk.java.net/jdk9/jdk9/langtools/file/6ba2130e87bd/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties
|
||||
*/
|
||||
|
||||
predicate jdkInternalReplacement(string old, string new) {
|
||||
exists(string r, int eqIdx | jdkInternalReplacement(r) and eqIdx = r.indexOf("=") |
|
||||
old = r.prefix(eqIdx) and
|
||||
new = r.suffix(eqIdx+1)
|
||||
new = r.suffix(eqIdx + 1)
|
||||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate jdkInternalReplacement(string r) {
|
||||
private predicate jdkInternalReplacement(string r) {
|
||||
r = "com.sun.crypto.provider.SunJCE=Use java.security.Security.getProvider(provider-name) @since 1.3" or
|
||||
r = "com.sun.org.apache.xml.internal.security=Use java.xml.crypto @since 1.6" or
|
||||
r = "com.sun.org.apache.xml.internal.security.utils.Base64=Use java.util.Base64 @since 1.8" or
|
||||
|
|
|
@ -15,7 +15,8 @@ class RefiningEquals extends EqualsMethod {
|
|||
// ... there is a `super` access that ...
|
||||
exists(MethodAccess sup, SuperAccess qual |
|
||||
// ... is of the form `super.something`, but not `A.super.something` ...
|
||||
qual = sup.getQualifier() and not exists(qual.getQualifier()) and
|
||||
qual = sup.getQualifier() and
|
||||
not exists(qual.getQualifier()) and
|
||||
// ... calls `super.equals` ...
|
||||
sup.getCallee() instanceof EqualsMethod and
|
||||
// ... on the (only) parameter of this method ...
|
||||
|
|
|
@ -14,40 +14,33 @@ library class BoundKind extends string {
|
|||
this = "<="
|
||||
}
|
||||
|
||||
predicate isEqual() {
|
||||
this = "="
|
||||
}
|
||||
predicate isEqual() { this = "=" }
|
||||
|
||||
predicate isNotEqual() {
|
||||
this = "!="
|
||||
}
|
||||
predicate isNotEqual() { this = "!=" }
|
||||
|
||||
predicate isLower() {
|
||||
this = ">="
|
||||
}
|
||||
predicate isLower() { this = ">=" }
|
||||
|
||||
predicate isUpper() {
|
||||
this = "<="
|
||||
}
|
||||
predicate isUpper() { this = "<=" }
|
||||
|
||||
predicate providesLowerBound() {
|
||||
isEqual() or isLower()
|
||||
}
|
||||
predicate providesLowerBound() { isEqual() or isLower() }
|
||||
|
||||
predicate providesUpperBound() {
|
||||
isEqual() or isUpper()
|
||||
}
|
||||
predicate providesUpperBound() { isEqual() or isUpper() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The information from `s1` implies that `test` always has the value `testIsTrue`.
|
||||
*/
|
||||
predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) {
|
||||
exists(ConditionBlock cb, SsaVariable v, BinaryExpr cond, boolean condIsTrue, int k1, int k2, CompileTimeConstantExpr c1, CompileTimeConstantExpr c2 |
|
||||
exists(
|
||||
ConditionBlock cb, SsaVariable v, BinaryExpr cond, boolean condIsTrue, int k1, int k2,
|
||||
CompileTimeConstantExpr c1, CompileTimeConstantExpr c2
|
||||
|
|
||||
s1 = cond and
|
||||
cb.getCondition() = cond and
|
||||
cond.hasOperands(v.getAUse(), c1) and c1.getIntValue() = k1 and
|
||||
test.hasOperands(v.getAUse(), c2) and c2.getIntValue() = k2 and
|
||||
cond.hasOperands(v.getAUse(), c1) and
|
||||
c1.getIntValue() = k1 and
|
||||
test.hasOperands(v.getAUse(), c2) and
|
||||
c2.getIntValue() = k2 and
|
||||
v.getSourceVariable().getVariable() instanceof LocalScopeVariable and
|
||||
cb.controls(test.getBasicBlock(), condIsTrue) and
|
||||
v.getSourceVariable().getType() instanceof IntegralType and
|
||||
|
@ -62,22 +55,36 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) {
|
|||
or
|
||||
exists(ComparisonExpr comp | comp = cond |
|
||||
comp.getLesserOperand() = v.getAUse() and
|
||||
(condIsTrue = true and boundKind.isUpper() and (if comp.isStrict() then bound = k1-1 else bound = k1)
|
||||
(
|
||||
condIsTrue = true and
|
||||
boundKind.isUpper() and
|
||||
(if comp.isStrict() then bound = k1 - 1 else bound = k1)
|
||||
or
|
||||
condIsTrue = false and boundKind.isLower() and (if comp.isStrict() then bound = k1 else bound = k1+1))
|
||||
condIsTrue = false and
|
||||
boundKind.isLower() and
|
||||
(if comp.isStrict() then bound = k1 else bound = k1 + 1)
|
||||
)
|
||||
or
|
||||
comp.getGreaterOperand() = v.getAUse() and
|
||||
(condIsTrue = true and boundKind.isLower() and (if comp.isStrict() then bound = k1+1 else bound = k1)
|
||||
(
|
||||
condIsTrue = true and
|
||||
boundKind.isLower() and
|
||||
(if comp.isStrict() then bound = k1 + 1 else bound = k1)
|
||||
or
|
||||
condIsTrue = false and boundKind.isUpper() and (if comp.isStrict() then bound = k1 else bound = k1-1))
|
||||
condIsTrue = false and
|
||||
boundKind.isUpper() and
|
||||
(if comp.isStrict() then bound = k1 else bound = k1 - 1)
|
||||
)
|
||||
)
|
||||
|
|
||||
// Given the bound we check if the `test` is either
|
||||
// always true (`testIsTrue = true`) or always false (`testIsTrue = false`).
|
||||
exists(EqualityTest testeq, boolean pol | testeq = test and pol = testeq.polarity() |
|
||||
(
|
||||
boundKind.providesLowerBound() and k2 < bound or
|
||||
boundKind.providesUpperBound() and bound < k2 or
|
||||
boundKind.providesLowerBound() and k2 < bound
|
||||
or
|
||||
boundKind.providesUpperBound() and bound < k2
|
||||
or
|
||||
boundKind.isNotEqual() and k2 = bound
|
||||
) and
|
||||
testIsTrue = pol.booleanNot()
|
||||
|
@ -87,14 +94,42 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) {
|
|||
or
|
||||
exists(ComparisonExpr comp | comp = test |
|
||||
comp.getLesserOperand() = v.getAUse() and
|
||||
(boundKind.providesLowerBound() and testIsTrue = false and (k2 < bound or k2 = bound and comp.isStrict())
|
||||
(
|
||||
boundKind.providesLowerBound() and
|
||||
testIsTrue = false and
|
||||
(
|
||||
k2 < bound
|
||||
or
|
||||
k2 = bound and comp.isStrict()
|
||||
)
|
||||
or
|
||||
boundKind.providesUpperBound() and testIsTrue = true and (bound < k2 or bound = k2 and not comp.isStrict()))
|
||||
boundKind.providesUpperBound() and
|
||||
testIsTrue = true and
|
||||
(
|
||||
bound < k2
|
||||
or
|
||||
bound = k2 and not comp.isStrict()
|
||||
)
|
||||
)
|
||||
or
|
||||
comp.getGreaterOperand() = v.getAUse() and
|
||||
(boundKind.providesLowerBound() and testIsTrue = true and (k2 < bound or k2 = bound and not comp.isStrict())
|
||||
(
|
||||
boundKind.providesLowerBound() and
|
||||
testIsTrue = true and
|
||||
(
|
||||
k2 < bound
|
||||
or
|
||||
k2 = bound and not comp.isStrict()
|
||||
)
|
||||
or
|
||||
boundKind.providesUpperBound() and testIsTrue = false and (bound < k2 or bound = k2 and comp.isStrict()))
|
||||
boundKind.providesUpperBound() and
|
||||
testIsTrue = false and
|
||||
(
|
||||
bound < k2
|
||||
or
|
||||
bound = k2 and comp.isStrict()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -6,11 +6,13 @@ import semmle.code.java.frameworks.Mockito
|
|||
/**
|
||||
* Expression `e` is assigned to variable `v`.
|
||||
*/
|
||||
private
|
||||
predicate flowsInto(Expr e, Variable v) {
|
||||
e = v.getAnAssignedValue() or
|
||||
exists(ParExpr p | flowsInto(p, v) | e = p.getExpr()) or
|
||||
exists(CastExpr c | flowsInto(c, v) | e = c.getExpr()) or
|
||||
private predicate flowsInto(Expr e, Variable v) {
|
||||
e = v.getAnAssignedValue()
|
||||
or
|
||||
exists(ParExpr p | flowsInto(p, v) | e = p.getExpr())
|
||||
or
|
||||
exists(CastExpr c | flowsInto(c, v) | e = c.getExpr())
|
||||
or
|
||||
exists(ConditionalExpr c | flowsInto(c, v) | e = c.getTrueExpr() or e = c.getFalseExpr())
|
||||
}
|
||||
|
||||
|
@ -30,8 +32,7 @@ predicate sqlType(RefType t) {
|
|||
* A (reflexive, transitive) subtype of `Closeable` or `AutoCloseable`,
|
||||
* or a closeable type in the `java.sql` package.
|
||||
*/
|
||||
private
|
||||
predicate closeableType(RefType t) {
|
||||
private predicate closeableType(RefType t) {
|
||||
exists(RefType supertype | supertype = t.getASupertype*() |
|
||||
supertype.hasName("Closeable") or
|
||||
supertype.hasName("AutoCloseable") or
|
||||
|
@ -73,14 +74,14 @@ class CloseableInitExpr extends Expr {
|
|||
*
|
||||
* The expression `parent` is the "closeable init" from which `e` is derived, if any, or `e` itself.
|
||||
*/
|
||||
private
|
||||
predicate closeableInit(Expr e, Expr parent) {
|
||||
private predicate closeableInit(Expr e, Expr parent) {
|
||||
exists(ClassInstanceExpr cie | cie = e |
|
||||
closeableType(cie.getType()) and
|
||||
(
|
||||
exists(Expr arg | arg = cie.getAnArgument() |
|
||||
closeableType(arg.getType()) and
|
||||
parent = arg)
|
||||
parent = arg
|
||||
)
|
||||
or
|
||||
(
|
||||
not exists(Expr arg | arg = cie.getAnArgument() | closeableType(arg.getType())) and
|
||||
|
@ -91,9 +92,7 @@ predicate closeableInit(Expr e, Expr parent) {
|
|||
or
|
||||
exists(SqlResourceOpeningMethodAccess ma | ma = e and parent = e)
|
||||
or
|
||||
exists(LocalVariableDecl v, Expr f |
|
||||
e = v.getAnAccess() and flowsInto(f, v)
|
||||
|
|
||||
exists(LocalVariableDecl v, Expr f | e = v.getAnAccess() and flowsInto(f, v) |
|
||||
closeableInit(f, parent)
|
||||
)
|
||||
}
|
||||
|
@ -101,20 +100,16 @@ predicate closeableInit(Expr e, Expr parent) {
|
|||
/**
|
||||
* The transitive closure of `closeableInit`.
|
||||
*/
|
||||
private
|
||||
predicate transitiveCloseableInit(Expr init, Expr transParent) {
|
||||
private predicate transitiveCloseableInit(Expr init, Expr transParent) {
|
||||
closeableInit+(init, transParent)
|
||||
}
|
||||
|
||||
/**
|
||||
* The expression `root` is the innermost "closeable init" expression of `cie` (possibly itself).
|
||||
*/
|
||||
private
|
||||
predicate closeableInitRootCause(Expr cie, Expr root) {
|
||||
private predicate closeableInitRootCause(Expr cie, Expr root) {
|
||||
transitiveCloseableInit(cie, root) and
|
||||
not exists(Expr other |
|
||||
transitiveCloseableInit(root, other) and other != root
|
||||
)
|
||||
not exists(Expr other | transitiveCloseableInit(root, other) and other != root)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,7 +117,8 @@ predicate closeableInitRootCause(Expr cie, Expr root) {
|
|||
* the type of any of the "closeable init" expressions that it is derived from.
|
||||
*/
|
||||
RefType typeInDerivation(ClassInstanceExpr cie) {
|
||||
result = cie.getType() or
|
||||
result = cie.getType()
|
||||
or
|
||||
exists(Expr transParent | transitiveCloseableInit(cie, transParent) |
|
||||
result = transParent.getType()
|
||||
)
|
||||
|
@ -131,20 +127,19 @@ RefType typeInDerivation(ClassInstanceExpr cie) {
|
|||
/**
|
||||
* A "closeable init" whose root cause is not a field or parameter.
|
||||
*/
|
||||
private
|
||||
predicate locallyInitializedCloseable(Expr cie) {
|
||||
private predicate locallyInitializedCloseable(Expr cie) {
|
||||
exists(Expr root | closeableInitRootCause(cie, root) |
|
||||
not exists(VarAccess va | va = root |
|
||||
va.getVariable() instanceof Parameter or
|
||||
va.getVariable() instanceof Field)
|
||||
va.getVariable() instanceof Field
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A locally initialized "closeable init" whose constructor does not have a throws clause.
|
||||
*/
|
||||
private
|
||||
predicate safeCloseableInit(ClassInstanceExpr cie) {
|
||||
private predicate safeCloseableInit(ClassInstanceExpr cie) {
|
||||
locallyInitializedCloseable(cie) and
|
||||
not exists(cie.getConstructor().getAnException())
|
||||
}
|
||||
|
@ -152,8 +147,7 @@ predicate safeCloseableInit(ClassInstanceExpr cie) {
|
|||
/**
|
||||
* A locally initialized "closeable init" that is neither assigned to a variable nor passed to a safe outer "closeable init".
|
||||
*/
|
||||
private
|
||||
predicate unassignedCloseableInit(CloseableInitExpr cie) {
|
||||
private predicate unassignedCloseableInit(CloseableInitExpr cie) {
|
||||
locallyInitializedCloseable(cie) and
|
||||
not flowsInto(cie, _) and
|
||||
not exists(ClassInstanceExpr outer | safeCloseableInit(outer) | cie = outer.getAnArgument())
|
||||
|
@ -162,23 +156,25 @@ predicate unassignedCloseableInit(CloseableInitExpr cie) {
|
|||
/**
|
||||
* A locally initialized "closeable init" that flows into a field or return statement.
|
||||
*/
|
||||
private
|
||||
predicate escapingCloseableInit(CloseableInitExpr cie) {
|
||||
private predicate escapingCloseableInit(CloseableInitExpr cie) {
|
||||
exists(Expr wrappingResource |
|
||||
locallyInitializedCloseable(cie) and (transitiveCloseableInit(wrappingResource, cie) or wrappingResource = cie)
|
||||
|
|
||||
exists(Field f | flowsInto(wrappingResource, f)) or
|
||||
locallyInitializedCloseable(cie) and
|
||||
(transitiveCloseableInit(wrappingResource, cie) or wrappingResource = cie)
|
||||
|
|
||||
exists(Field f | flowsInto(wrappingResource, f))
|
||||
or
|
||||
exists(ConstructorCall call | call.callsThis() or call.callsSuper() |
|
||||
closeableType(call.getConstructedType()) and
|
||||
call.getAnArgument() = wrappingResource
|
||||
) or
|
||||
wrappingResource.getEnclosingStmt() instanceof ReturnStmt or
|
||||
)
|
||||
or
|
||||
wrappingResource.getEnclosingStmt() instanceof ReturnStmt
|
||||
or
|
||||
getCloseableVariable(wrappingResource).getAnAccess().getEnclosingStmt() instanceof ReturnStmt
|
||||
or
|
||||
exists(Parameter p0 |
|
||||
escapingMethodParameterClosable(p0)
|
||||
|
|
||||
p0.getAnArgument() = wrappingResource or
|
||||
exists(Parameter p0 | escapingMethodParameterClosable(p0) |
|
||||
p0.getAnArgument() = wrappingResource
|
||||
or
|
||||
exists(LocalVariableDecl v |
|
||||
p0.getAnArgument() = v.getAnAccess() and flowsInto(wrappingResource, v)
|
||||
)
|
||||
|
@ -189,17 +185,20 @@ predicate escapingCloseableInit(CloseableInitExpr cie) {
|
|||
/**
|
||||
* Holds if `p` is a closable that escapes by an assignment to a field.
|
||||
*/
|
||||
private
|
||||
predicate escapingMethodParameterClosable(Parameter p) {
|
||||
private predicate escapingMethodParameterClosable(Parameter p) {
|
||||
p.getCallable() instanceof Method and
|
||||
exists(Expr wrappingResource |
|
||||
closeableType(p.getType()) and (transitiveCloseableInit(wrappingResource, p.getAnAccess()) or wrappingResource = p.getAnAccess())
|
||||
|
|
||||
exists(Field f | flowsInto(wrappingResource, f)) or
|
||||
exists(Parameter p0 |
|
||||
escapingMethodParameterClosable(p0)
|
||||
|
|
||||
p0.getAnArgument() = wrappingResource or
|
||||
closeableType(p.getType()) and
|
||||
(
|
||||
transitiveCloseableInit(wrappingResource, p.getAnAccess()) or
|
||||
wrappingResource = p.getAnAccess()
|
||||
)
|
||||
|
|
||||
exists(Field f | flowsInto(wrappingResource, f))
|
||||
or
|
||||
exists(Parameter p0 | escapingMethodParameterClosable(p0) |
|
||||
p0.getAnArgument() = wrappingResource
|
||||
or
|
||||
exists(LocalVariableDecl v |
|
||||
p0.getAnArgument() = v.getAnAccess() and flowsInto(wrappingResource, v)
|
||||
)
|
||||
|
@ -210,16 +209,14 @@ predicate escapingMethodParameterClosable(Parameter p) {
|
|||
/**
|
||||
* A local variable into which the specified (locally initialized) "closeable init" flows.
|
||||
*/
|
||||
private
|
||||
LocalVariableDecl getCloseableVariable(CloseableInitExpr cie) {
|
||||
private LocalVariableDecl getCloseableVariable(CloseableInitExpr cie) {
|
||||
locallyInitializedCloseable(cie) and flowsInto(cie, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable on which a "close" method is called, implicitly or explicitly, directly or indirectly.
|
||||
*/
|
||||
private
|
||||
predicate closeCalled(Variable v) {
|
||||
private predicate closeCalled(Variable v) {
|
||||
// `close()` is implicitly called on variables declared or referenced
|
||||
// in the resources clause of try-with-resource statements.
|
||||
exists(TryStmt try | try.getAResourceVariable() = v)
|
||||
|
@ -227,7 +224,7 @@ predicate closeCalled(Variable v) {
|
|||
// Otherwise, there should be an explicit call to a method whose name contains "close".
|
||||
exists(MethodAccess e |
|
||||
v = getCloseableVariable(_) or v instanceof Parameter or v instanceof LocalVariableDecl
|
||||
|
|
||||
|
|
||||
(
|
||||
e.getMethod().getName().toLowerCase().matches("%close%") and
|
||||
exists(VarAccess va | va = v.getAnAccess() |
|
||||
|
@ -239,10 +236,14 @@ predicate closeCalled(Variable v) {
|
|||
// The "close" call could happen indirectly inside a helper method of unknown name.
|
||||
exists(int i | exprs(v.getAnAccess(), _, _, e, i) |
|
||||
exists(Parameter p, int j | params(p, _, j, e.getMethod(), _) |
|
||||
(closeCalled(p) and i = j) or
|
||||
(closeCalled(p) and i = j)
|
||||
or
|
||||
// The helper method could be iterating over a varargs parameter.
|
||||
exists(EnhancedForStmt for | for.getExpr() = p.getAnAccess() |
|
||||
closeCalled(for.getVariable().getVariable())) and p.isVarargs() and j<=i
|
||||
closeCalled(for.getVariable().getVariable())
|
||||
) and
|
||||
p.isVarargs() and
|
||||
j <= i
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -252,17 +253,17 @@ predicate closeCalled(Variable v) {
|
|||
* A locally initialized "closeable init" that flows into a variable on which a "close" method is called
|
||||
* or is immediately closed after creation.
|
||||
*/
|
||||
private
|
||||
predicate closedResource(CloseableInitExpr cie) {
|
||||
private predicate closedResource(CloseableInitExpr cie) {
|
||||
locallyInitializedCloseable(cie) and
|
||||
(
|
||||
exists(LocalVariableDecl v | closeCalled(v) |
|
||||
exists(Expr wrappingResource |
|
||||
wrappingResource = cie or transitiveCloseableInit(wrappingResource, cie)
|
||||
|
|
||||
|
|
||||
flowsInto(wrappingResource, v)
|
||||
)
|
||||
) or
|
||||
)
|
||||
or
|
||||
immediatelyClosed(cie)
|
||||
)
|
||||
}
|
||||
|
@ -274,9 +275,9 @@ private predicate immediatelyClosed(ClassInstanceExpr cie) {
|
|||
/**
|
||||
* A unassigned or locally-assigned "closeable init" that does not escape and is not closed.
|
||||
*/
|
||||
private
|
||||
predicate badCloseableInitImpl(CloseableInitExpr cie) {
|
||||
(unassignedCloseableInit(cie) and not immediatelyClosed(cie) and not escapingCloseableInit(cie)) or
|
||||
private predicate badCloseableInitImpl(CloseableInitExpr cie) {
|
||||
(unassignedCloseableInit(cie) and not immediatelyClosed(cie) and not escapingCloseableInit(cie))
|
||||
or
|
||||
(locallyInitializedCloseable(cie) and not closedResource(cie) and not escapingCloseableInit(cie))
|
||||
}
|
||||
|
||||
|
@ -285,7 +286,9 @@ predicate badCloseableInitImpl(CloseableInitExpr cie) {
|
|||
*/
|
||||
predicate badCloseableInit(CloseableInitExpr cie) {
|
||||
badCloseableInitImpl(cie) and
|
||||
not exists(CloseableInitExpr cie2 | transitiveCloseableInit(cie, cie2) and cie2 != cie and badCloseableInitImpl(cie2))
|
||||
not exists(CloseableInitExpr cie2 |
|
||||
transitiveCloseableInit(cie, cie2) and cie2 != cie and badCloseableInitImpl(cie2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -294,12 +297,14 @@ predicate badCloseableInit(CloseableInitExpr cie) {
|
|||
predicate noNeedToClose(CloseableInitExpr cie) {
|
||||
locallyInitializedCloseable(cie) and
|
||||
(
|
||||
(cie instanceof ClassInstanceExpr and not exists(cie.(ClassInstanceExpr).getAnArgument())) or
|
||||
(cie instanceof ClassInstanceExpr and not exists(cie.(ClassInstanceExpr).getAnArgument()))
|
||||
or
|
||||
exists(RefType t | t = cie.getType() and t.fromSource() |
|
||||
exists(Method close | close.getDeclaringType() = t and close.getName() = "close" |
|
||||
not exists(close.getBody().getAChild())
|
||||
)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(CloseableInitExpr sqlStmt, LocalVariableDecl v |
|
||||
// If a `java.sql.Statement` is closed, an associated `java.sql.ResultSet` is implicitly closed.
|
||||
sqlStmt.getType().(RefType).getASupertype*() instanceof TypeStatement and
|
||||
|
@ -307,7 +312,8 @@ predicate noNeedToClose(CloseableInitExpr cie) {
|
|||
closedResource(sqlStmt) and
|
||||
cie.getType() instanceof TypeResultSet and
|
||||
cie.(SqlResourceOpeningMethodAccess).getQualifier() = v.getAnAccess()
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma | cie.(ClassInstanceExpr).getAnArgument() = ma |
|
||||
ma.getMethod() instanceof ServletResponseGetOutputStreamMethod or
|
||||
ma.getMethod() instanceof ServletResponseGetWriterMethod or
|
||||
|
@ -317,5 +323,7 @@ predicate noNeedToClose(CloseableInitExpr cie) {
|
|||
or
|
||||
exists(CloseableInitExpr inner | transitiveCloseableInit(cie, inner) | noNeedToClose(inner))
|
||||
or
|
||||
exists(CloseableInitExpr inner | transitiveCloseableInit(cie, inner) | closeCalled(getCloseableVariable(inner)))
|
||||
exists(CloseableInitExpr inner | transitiveCloseableInit(cie, inner) |
|
||||
closeCalled(getCloseableVariable(inner))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -9,9 +9,7 @@ import java
|
|||
* declared in an immutable type, and that every method overriding it is also
|
||||
* designed for chaining.
|
||||
*/
|
||||
predicate designedForChaining(Method m) {
|
||||
not nonChaining(m)
|
||||
}
|
||||
predicate designedForChaining(Method m) { not nonChaining(m) }
|
||||
|
||||
private predicate nonChaining(Method m) {
|
||||
// The method has a body, and at least one of the return values is not suitable for chaining.
|
||||
|
@ -22,9 +20,11 @@ private predicate nonChaining(Method m) {
|
|||
not exists(m.getBody()) and
|
||||
(
|
||||
// ... it has the wrong return type, ...
|
||||
not hasSubtype*(m.getReturnType(), m.getDeclaringType()) or
|
||||
not hasSubtype*(m.getReturnType(), m.getDeclaringType())
|
||||
or
|
||||
// ... it is defined on an immutable type, or ...
|
||||
m.getDeclaringType() instanceof ImmutableType or
|
||||
m.getDeclaringType() instanceof ImmutableType
|
||||
or
|
||||
// ... it has an override that is non-chaining.
|
||||
exists(Method override | override.overrides(m) | nonChaining(override))
|
||||
)
|
||||
|
@ -36,25 +36,31 @@ private predicate nonChainingReturn(Method m, ReturnStmt ret) {
|
|||
(
|
||||
ret.getResult() instanceof ThisAccess and
|
||||
ret.getResult().getType() != m.getDeclaringType()
|
||||
) or
|
||||
)
|
||||
or
|
||||
// A method call to the wrong method is returned.
|
||||
(
|
||||
ret.getResult() instanceof MethodAccess and
|
||||
exists(MethodAccess delegateCall, Method delegate |
|
||||
delegateCall = ret.getResult() and
|
||||
delegate = delegateCall.getMethod()
|
||||
|
|
||||
delegate.getDeclaringType() != m.getDeclaringType() or
|
||||
delegate.isStatic() or
|
||||
not hasSubtype*(m.getReturnType(), delegate.getReturnType()) or
|
||||
|
|
||||
delegate.getDeclaringType() != m.getDeclaringType()
|
||||
or
|
||||
delegate.isStatic()
|
||||
or
|
||||
not hasSubtype*(m.getReturnType(), delegate.getReturnType())
|
||||
or
|
||||
// A method on the wrong object is called.
|
||||
not (
|
||||
delegateCall.getQualifier().getProperExpr() instanceof ThisAccess or
|
||||
not exists(delegateCall.getQualifier())
|
||||
) or
|
||||
)
|
||||
or
|
||||
nonChaining(delegate)
|
||||
)
|
||||
) or
|
||||
)
|
||||
or
|
||||
// Something else is returned.
|
||||
not (
|
||||
ret.getResult() instanceof ThisAccess or
|
||||
|
|
|
@ -17,10 +17,16 @@ import java
|
|||
class RangeCallable extends Callable {
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(int elSuper, int ecSuper | super.hasLocationInfo(path, sl, sc, elSuper, ecSuper) |
|
||||
this.getBody().hasLocationInfo(path, _, _, el, ec) or
|
||||
(not exists(this.getBody()) and
|
||||
(lastParameter().hasLocationInfo(path, _, _, el, ec) or
|
||||
(not exists(this.getAParameter()) and el = elSuper and ec = ecSuper)))
|
||||
this.getBody().hasLocationInfo(path, _, _, el, ec)
|
||||
or
|
||||
(
|
||||
not exists(this.getBody()) and
|
||||
(
|
||||
lastParameter().hasLocationInfo(path, _, _, el, ec)
|
||||
or
|
||||
(not exists(this.getAParameter()) and el = elSuper and ec = ecSuper)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -37,7 +43,8 @@ class RangeCallable extends Callable {
|
|||
class RangeRefType extends RefType {
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
exists(int elSuper, int ecSuper | super.hasLocationInfo(path, sl, sc, elSuper, ecSuper) |
|
||||
lastMember().hasLocationInfo(path, _, _, el, ec) or
|
||||
lastMember().hasLocationInfo(path, _, _, el, ec)
|
||||
or
|
||||
(not exists(this.getAMember()) and el = elSuper and ec = ecSuper)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
|
||||
abstract class PathCreation extends Expr {
|
||||
abstract Expr getInput();
|
||||
}
|
||||
abstract class PathCreation extends Expr { abstract Expr getInput(); }
|
||||
|
||||
class PathsGet extends PathCreation, MethodAccess {
|
||||
PathsGet() {
|
||||
|
@ -28,9 +26,7 @@ class FileSystemGetPath extends PathCreation, MethodAccess {
|
|||
}
|
||||
|
||||
class FileCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileCreation() {
|
||||
this.getConstructedType() instanceof TypeFile
|
||||
}
|
||||
FileCreation() { this.getConstructedType() instanceof TypeFile }
|
||||
|
||||
override Expr getInput() {
|
||||
result = this.getAnArgument() and
|
||||
|
@ -40,9 +36,7 @@ class FileCreation extends PathCreation, ClassInstanceExpr {
|
|||
}
|
||||
|
||||
class FileWriterCreation extends PathCreation, ClassInstanceExpr {
|
||||
FileWriterCreation() {
|
||||
this.getConstructedType().getQualifiedName() = "java.io.FileWriter"
|
||||
}
|
||||
FileWriterCreation() { this.getConstructedType().getQualifiedName() = "java.io.FileWriter" }
|
||||
|
||||
override Expr getInput() {
|
||||
result = this.getAnArgument() and
|
||||
|
@ -58,11 +52,10 @@ predicate inWeakCheck(Expr e) {
|
|||
def.getName() = "endsWith" or
|
||||
def.getName() = "isEmpty" or
|
||||
def.getName() = "equals"
|
||||
) or
|
||||
// Checking against `null` has no bearing on path traversal.
|
||||
exists(EqualityTest b | b.getAnOperand() = e |
|
||||
b.getAnOperand() instanceof NullLiteral
|
||||
)
|
||||
or
|
||||
// Checking against `null` has no bearing on path traversal.
|
||||
exists(EqualityTest b | b.getAnOperand() = e | b.getAnOperand() instanceof NullLiteral)
|
||||
}
|
||||
|
||||
// Ignore cases where the variable has been checked somehow,
|
||||
|
|
|
@ -2,10 +2,17 @@ import semmle.code.java.dataflow.FlowSources
|
|||
import semmle.code.java.security.ExternalProcess
|
||||
|
||||
private class RemoteUserInputToArgumentToExecFlowConfig extends TaintTracking::Configuration {
|
||||
RemoteUserInputToArgumentToExecFlowConfig() { this = "ExecCommon::RemoteUserInputToArgumentToExecFlowConfig" }
|
||||
RemoteUserInputToArgumentToExecFlowConfig() {
|
||||
this = "ExecCommon::RemoteUserInputToArgumentToExecFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof RemoteUserInput }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,12 +6,13 @@ import semmle.code.java.frameworks.android.SQLite
|
|||
import semmle.code.java.frameworks.javaee.Persistence
|
||||
|
||||
/** A sink for database query language injection vulnerabilities. */
|
||||
abstract class QueryInjectionSink extends DataFlow::ExprNode {}
|
||||
abstract class QueryInjectionSink extends DataFlow::ExprNode { }
|
||||
|
||||
/** A sink for SQL injection vulnerabilities. */
|
||||
class SqlInjectionSink extends QueryInjectionSink {
|
||||
SqlInjectionSink() {
|
||||
this.getExpr() instanceof SqlExpr or
|
||||
this.getExpr() instanceof SqlExpr
|
||||
or
|
||||
exists(SQLiteRunner s, MethodAccess m | m.getMethod() = s |
|
||||
m.getArgument(s.sqlIndex()) = this.getExpr()
|
||||
)
|
||||
|
@ -33,9 +34,14 @@ class PersistenceQueryInjectionSink extends QueryInjectionSink {
|
|||
|
||||
private class QueryInjectionFlowConfig extends TaintTracking::Configuration {
|
||||
QueryInjectionFlowConfig() { this = "SqlInjectionLib::QueryInjectionFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof RemoteUserInput }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
|
||||
override predicate isSanitizer(DataFlow::Node node) { node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,7 +49,5 @@ private class QueryInjectionFlowConfig extends TaintTracking::Configuration {
|
|||
* can be excluded from `SqlUnescaped.ql` to avoid overlapping results.
|
||||
*/
|
||||
predicate queryTaintedBy(QueryInjectionSink query, RemoteUserInput source) {
|
||||
exists(QueryInjectionFlowConfig conf |
|
||||
conf.hasFlow(source, query)
|
||||
)
|
||||
exists(QueryInjectionFlowConfig conf | conf.hasFlow(source, query))
|
||||
}
|
||||
|
|
|
@ -10,18 +10,21 @@ class HeaderSplittingSink extends DataFlow::ExprNode {
|
|||
exists(ResponseAddCookieMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.getExpr() = ma.getArgument(0)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(ResponseAddHeaderMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.getExpr() = ma.getAnArgument()
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(ResponseSetHeaderMethod m, MethodAccess ma |
|
||||
ma.getMethod() = m and
|
||||
this.getExpr() = ma.getAnArgument()
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(JaxRsResponseBuilder builder, Method m |
|
||||
m = builder.getAMethod() and m.getName() = "header"
|
||||
|
|
||||
|
|
||||
this.getExpr() = m.getAReference().getArgument(1)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,8 +7,13 @@ private import BoundingChecks
|
|||
* If the `Array` accessed by the `ArrayAccess` is a fixed size, return the array size.
|
||||
*/
|
||||
int fixedArraySize(ArrayAccess arrayAccess) {
|
||||
result = arrayAccess.getArray().(VarAccess).getVariable().getAnAssignedValue()
|
||||
.(ArrayCreationExpr).getFirstDimensionSize()
|
||||
result = arrayAccess
|
||||
.getArray()
|
||||
.(VarAccess)
|
||||
.getVariable()
|
||||
.getAnAssignedValue()
|
||||
.(ArrayCreationExpr)
|
||||
.getFirstDimensionSize()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,8 +27,12 @@ private predicate arrayIndexOutOfBoundExceptionCaught(ArrayAccess arrayAccess) {
|
|||
ts.getAResourceExpr().getAChildExpr*() = arrayAccess
|
||||
) and
|
||||
cc = ts.getACatchClause()
|
||||
|
|
||||
cc.getVariable().getType().(RefType).hasQualifiedName("java.lang", "ArrayIndexOutOfBoundsException")
|
||||
|
|
||||
cc
|
||||
.getVariable()
|
||||
.getType()
|
||||
.(RefType)
|
||||
.hasQualifiedName("java.lang", "ArrayIndexOutOfBoundsException")
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -41,15 +50,11 @@ class PointlessLoop extends WhileStmt {
|
|||
PointlessLoop() {
|
||||
getCondition().(BooleanLiteral).getBooleanValue() = true and
|
||||
// The only `break` must be the last statement.
|
||||
forall(BreakStmt break |
|
||||
break.(JumpStmt).getTarget() = this
|
||||
|
|
||||
forall(BreakStmt break | break.(JumpStmt).getTarget() = this |
|
||||
this.getStmt().(Block).getLastStmt() = break
|
||||
) and
|
||||
// No `continue` statements.
|
||||
not exists(ContinueStmt continue |
|
||||
continue.(JumpStmt).getTarget() = this
|
||||
)
|
||||
not exists(ContinueStmt continue | continue.(JumpStmt).getTarget() = this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,11 +69,13 @@ class CheckableArrayAccess extends ArrayAccess {
|
|||
/*
|
||||
* We are not interested in array accesses that don't access the first dimension.
|
||||
*/
|
||||
|
||||
not this.getArray() instanceof ArrayAccess and
|
||||
/*
|
||||
* Array accesses within loops can make it difficult to verify whether the index is checked
|
||||
* prior to access. Ignore "pointless" loops of the sort found in Juliet test cases.
|
||||
*/
|
||||
|
||||
not exists(LoopStmt loop |
|
||||
loop.getBody().getAChild*() = getEnclosingStmt() and
|
||||
not loop instanceof PointlessLoop
|
||||
|
@ -84,8 +91,7 @@ class CheckableArrayAccess extends ArrayAccess {
|
|||
index = getIndexExpr() and
|
||||
not (
|
||||
// There is a condition dominating this expression ensuring that the index is >= 0.
|
||||
lowerBound(index) >= 0
|
||||
and
|
||||
lowerBound(index) >= 0 and
|
||||
// There is a condition dominating this expression that ensures the index is less than the length.
|
||||
lessthanLength(this)
|
||||
)
|
||||
|
@ -99,6 +105,7 @@ class CheckableArrayAccess extends ArrayAccess {
|
|||
/*
|
||||
* Find an `ArrayCreationExpr` for the array used in this indexing operation.
|
||||
*/
|
||||
|
||||
exists(VariableAssign assign |
|
||||
assign.getSource() = arrayCreation and
|
||||
defUsePair(assign, this.getArray())
|
||||
|
@ -107,10 +114,12 @@ class CheckableArrayAccess extends ArrayAccess {
|
|||
* If the array access is protected by a conditional that verifies the index is less than the array
|
||||
* length, then the array will never be accessed if the size is zero.
|
||||
*/
|
||||
|
||||
not lessthanLength(this) and
|
||||
/*
|
||||
* Verify that the size expression is never checked to be greater than 0.
|
||||
*/
|
||||
|
||||
sizeExpr = arrayCreation.getDimension(0) and
|
||||
not lowerBound(sizeExpr) > 0
|
||||
}
|
||||
|
@ -120,7 +129,6 @@ class CheckableArrayAccess extends ArrayAccess {
|
|||
* A source of "flow" which has an upper or lower bound.
|
||||
*/
|
||||
abstract class BoundedFlowSource extends DataFlow::Node {
|
||||
|
||||
/**
|
||||
* Return a lower bound for the input, if possible.
|
||||
*/
|
||||
|
@ -144,7 +152,7 @@ class RandomValueFlowSource extends BoundedFlowSource {
|
|||
RandomValueFlowSource() {
|
||||
exists(RefType random, MethodAccess nextAccess |
|
||||
random.hasQualifiedName("java.util", "Random")
|
||||
|
|
||||
|
|
||||
nextAccess.getCallee().getDeclaringType().getAnAncestor() = random and
|
||||
nextAccess.getCallee().getName().matches("next%") and
|
||||
nextAccess = this.asExpr()
|
||||
|
@ -163,31 +171,24 @@ class RandomValueFlowSource extends BoundedFlowSource {
|
|||
* If this call specified an argument to `nextInt()`, and that argument is a compile time constant,
|
||||
* it forms the upper bound.
|
||||
*/
|
||||
|
||||
this.asExpr().(MethodAccess).getCallee().hasName("nextInt") and
|
||||
this.asExpr().(MethodAccess).getNumArgument() = 1 and
|
||||
result = this.asExpr().(MethodAccess).getArgument(0).(CompileTimeConstantExpr).getIntValue()
|
||||
}
|
||||
|
||||
string getDescription() {
|
||||
result = "Random value"
|
||||
}
|
||||
string getDescription() { result = "Random value" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A compile time constant expression that evaluates to a numeric type.
|
||||
*/
|
||||
class NumericLiteralFlowSource extends BoundedFlowSource {
|
||||
NumericLiteralFlowSource() {
|
||||
exists(this.asExpr().(CompileTimeConstantExpr).getIntValue())
|
||||
}
|
||||
NumericLiteralFlowSource() { exists(this.asExpr().(CompileTimeConstantExpr).getIntValue()) }
|
||||
|
||||
int lowerBound() {
|
||||
result = this.asExpr().(CompileTimeConstantExpr).getIntValue()
|
||||
}
|
||||
int lowerBound() { result = this.asExpr().(CompileTimeConstantExpr).getIntValue() }
|
||||
|
||||
int upperBound() {
|
||||
result = this.asExpr().(CompileTimeConstantExpr).getIntValue()
|
||||
}
|
||||
int upperBound() { result = this.asExpr().(CompileTimeConstantExpr).getIntValue() }
|
||||
|
||||
string getDescription() {
|
||||
result = "Literal value " + this.asExpr().(CompileTimeConstantExpr).getIntValue()
|
||||
|
|
|
@ -24,9 +24,10 @@ int lowerBound(VarAccess va) {
|
|||
exists(ComparisonExpr greaterThanValue |
|
||||
// This condition should hold when the variable is later accessed.
|
||||
conditionHolds(greaterThanValue, va)
|
||||
|
|
||||
|
|
||||
greaterThanValue.getGreaterOperand() = va.getVariable().getAnAccess() and
|
||||
if greaterThanValue.isStrict() then
|
||||
if greaterThanValue.isStrict()
|
||||
then
|
||||
// value > i, so value has a lower bound of i + 1
|
||||
result = greaterThanValue.getLesserOperand().(CompileTimeConstantExpr).getIntValue() + 1
|
||||
else
|
||||
|
@ -43,7 +44,7 @@ predicate lessthanLength(ArrayAccess a) {
|
|||
exists(ComparisonExpr lessThanLength, VarAccess va |
|
||||
va = a.getIndexExpr() and
|
||||
conditionHolds(lessThanLength, va)
|
||||
|
|
||||
|
|
||||
lessThanLength.getGreaterOperand().(FieldAccess).getQualifier() = arrayReference(a) and
|
||||
lessThanLength.getGreaterOperand().(FieldAccess).getField().hasName("length") and
|
||||
lessThanLength.getLesserOperand() = va.getVariable().getAnAccess() and
|
||||
|
@ -54,7 +55,8 @@ predicate lessthanLength(ArrayAccess a) {
|
|||
/**
|
||||
* Return all other references to the array accessed in the `ArrayAccess`.
|
||||
*/
|
||||
private pragma[nomagic] Expr arrayReference(ArrayAccess arrayAccess) {
|
||||
pragma[nomagic]
|
||||
private Expr arrayReference(ArrayAccess arrayAccess) {
|
||||
// Array is stored in a variable.
|
||||
result = arrayAccess.getArray().(VarAccess).getVariable().getAnAccess() or
|
||||
// Array is returned from a method.
|
||||
|
|
|
@ -7,19 +7,18 @@ import semmle.code.java.controlflow.Guards
|
|||
* The type of `exp` is narrower than or equal to `numType`,
|
||||
* or there is an enclosing cast to a type at least as narrow as 'numType'.
|
||||
*/
|
||||
|
||||
predicate narrowerThanOrEqualTo(ArithExpr exp, NumType numType) {
|
||||
exp.getType().(NumType).widerThan(numType)
|
||||
implies
|
||||
exists(CastExpr cast | cast.getAChildExpr().getProperExpr() = exp |
|
||||
numType.widerThanOrEqualTo((NumType)cast.getType())
|
||||
numType.widerThanOrEqualTo(cast.getType().(NumType))
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the size of this use is guarded using `Math.abs`. */
|
||||
predicate guardedAbs(ArithExpr e, Expr use) {
|
||||
exists(MethodAccess m |
|
||||
m.getMethod() instanceof MethodAbs
|
||||
|
|
||||
exists(MethodAccess m | m.getMethod() instanceof MethodAbs |
|
||||
m.getArgument(0) = use and
|
||||
guardedLesser(e, m)
|
||||
)
|
||||
|
@ -31,7 +30,8 @@ predicate guardedLesser(ArithExpr e, Expr use) {
|
|||
use = guard.getLesserOperand() and
|
||||
guard = c.getCondition() and
|
||||
c.controls(e.getBasicBlock(), true)
|
||||
) or
|
||||
)
|
||||
or
|
||||
guardedAbs(e, use)
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,8 @@ predicate guardedGreater(ArithExpr e, Expr use) {
|
|||
use = guard.getGreaterOperand() and
|
||||
guard = c.getCondition() and
|
||||
c.controls(e.getBasicBlock(), true)
|
||||
) or
|
||||
)
|
||||
or
|
||||
guardedAbs(e, use)
|
||||
}
|
||||
|
||||
|
@ -55,25 +56,31 @@ predicate guarded(ArithExpr e, Expr use) {
|
|||
}
|
||||
|
||||
/** A prior use of the same variable that could see the same value. */
|
||||
VarAccess priorAccess(VarAccess access) {
|
||||
useUsePair(result, access)
|
||||
}
|
||||
VarAccess priorAccess(VarAccess access) { useUsePair(result, access) }
|
||||
|
||||
/** Holds if `e` is guarded against overflow by `use`. */
|
||||
predicate guardedAgainstOverflow(ArithExpr e, VarAccess use) {
|
||||
use = e.getAnOperand() and
|
||||
(
|
||||
// overflow possible if large
|
||||
e instanceof AddExpr and guardedLesser(e, priorAccess(use)) or
|
||||
e instanceof PreIncExpr and guardedLesser(e, priorAccess(use)) or
|
||||
e instanceof PostIncExpr and guardedLesser(e, priorAccess(use)) or
|
||||
e instanceof AddExpr and guardedLesser(e, priorAccess(use))
|
||||
or
|
||||
e instanceof PreIncExpr and guardedLesser(e, priorAccess(use))
|
||||
or
|
||||
e instanceof PostIncExpr and guardedLesser(e, priorAccess(use))
|
||||
or
|
||||
// overflow unlikely with subtraction
|
||||
e instanceof SubExpr or
|
||||
e instanceof PreDecExpr or
|
||||
e instanceof PostDecExpr or
|
||||
e instanceof SubExpr
|
||||
or
|
||||
e instanceof PreDecExpr
|
||||
or
|
||||
e instanceof PostDecExpr
|
||||
or
|
||||
// overflow possible if large or small
|
||||
e instanceof MulExpr and guardedLesser(e, priorAccess(use)) and
|
||||
guardedGreater(e, priorAccess(use)) or
|
||||
e instanceof MulExpr and
|
||||
guardedLesser(e, priorAccess(use)) and
|
||||
guardedGreater(e, priorAccess(use))
|
||||
or
|
||||
// overflow possible if MIN_VALUE
|
||||
e instanceof DivExpr and guardedGreater(e, priorAccess(use))
|
||||
)
|
||||
|
@ -84,16 +91,25 @@ predicate guardedAgainstUnderflow(ArithExpr e, VarAccess use) {
|
|||
use = e.getAnOperand() and
|
||||
(
|
||||
// underflow unlikely for addition
|
||||
e instanceof AddExpr or
|
||||
e instanceof PreIncExpr or
|
||||
e instanceof PostIncExpr or
|
||||
e instanceof AddExpr
|
||||
or
|
||||
e instanceof PreIncExpr
|
||||
or
|
||||
e instanceof PostIncExpr
|
||||
or
|
||||
// underflow possible if use is left operand and small
|
||||
e instanceof SubExpr and (use = e.getRightOperand() or guardedGreater(e, priorAccess(use))) or
|
||||
e instanceof PreDecExpr and guardedGreater(e, priorAccess(use)) or
|
||||
e instanceof PostDecExpr and guardedGreater(e, priorAccess(use)) or
|
||||
e instanceof SubExpr and
|
||||
(use = e.getRightOperand() or guardedGreater(e, priorAccess(use)))
|
||||
or
|
||||
e instanceof PreDecExpr and guardedGreater(e, priorAccess(use))
|
||||
or
|
||||
e instanceof PostDecExpr and guardedGreater(e, priorAccess(use))
|
||||
or
|
||||
// underflow possible if large or small
|
||||
e instanceof MulExpr and guardedLesser(e, priorAccess(use)) and
|
||||
guardedGreater(e, priorAccess(use)) or
|
||||
e instanceof MulExpr and
|
||||
guardedLesser(e, priorAccess(use)) and
|
||||
guardedGreater(e, priorAccess(use))
|
||||
or
|
||||
// underflow possible if MAX_VALUE
|
||||
e instanceof DivExpr and guardedLesser(e, priorAccess(use))
|
||||
)
|
||||
|
|
|
@ -9,8 +9,7 @@ import semmle.code.java.security.SensitiveActions
|
|||
/** Test code filter. */
|
||||
predicate testMethod(Method m) {
|
||||
(
|
||||
m instanceof TestMethod
|
||||
or
|
||||
m instanceof TestMethod or
|
||||
m.getDeclaringType() instanceof TestClass
|
||||
) and
|
||||
// Do report results in the Juliet tests.
|
||||
|
@ -19,14 +18,19 @@ predicate testMethod(Method m) {
|
|||
|
||||
private class SensitiveSourceFlowConfig extends TaintTracking::Configuration {
|
||||
SensitiveSourceFlowConfig() { this = "SensitiveStorage::SensitiveSourceFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SensitiveExpr }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = cookieInput(_) or
|
||||
sink.asExpr() = cookieInput(_)
|
||||
or
|
||||
exists(MethodAccess m |
|
||||
m.getMethod() instanceof PropertiesSetPropertyMethod and sink.asExpr() = m.getArgument(1)
|
||||
) or
|
||||
)
|
||||
or
|
||||
sink.asExpr() = getInstanceInput(_, _)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node n) {
|
||||
n.getType() instanceof NumericType or n.getType() instanceof BooleanType
|
||||
}
|
||||
|
@ -74,13 +78,13 @@ private predicate cookieStore(DataFlow::Node cookie, Expr store) {
|
|||
|
||||
private class CookieToStoreFlowConfig extends DataFlow2::Configuration {
|
||||
CookieToStoreFlowConfig() { this = "SensitiveStorage::CookieToStoreFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof Cookie }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { cookieStore(sink, _) }
|
||||
}
|
||||
|
||||
private Expr cookieInput(Cookie c) {
|
||||
result = c.getArgument(1)
|
||||
}
|
||||
private Expr cookieInput(Cookie c) { result = c.getArgument(1) }
|
||||
|
||||
/** The instantiation of a cookie, which can act as storage. */
|
||||
class Cookie extends Storable {
|
||||
|
@ -89,9 +93,7 @@ class Cookie extends Storable {
|
|||
}
|
||||
|
||||
/** Gets an input, for example `input` in `new Cookie("...", input);`. */
|
||||
override Expr getAnInput() {
|
||||
result = cookieInput(this)
|
||||
}
|
||||
override Expr getAnInput() { result = cookieInput(this) }
|
||||
|
||||
/** Gets a store, for example `response.addCookie(cookie);`. */
|
||||
override Expr getAStore() {
|
||||
|
@ -120,7 +122,9 @@ private predicate propertiesStore(DataFlow::Node prop, Expr store) {
|
|||
|
||||
private class PropertiesFlowConfig extends DataFlow3::Configuration {
|
||||
PropertiesFlowConfig() { this = "SensitiveStorage::PropertiesFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof Properties }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
propertiesInput(sink, _) or
|
||||
propertiesStore(sink, _)
|
||||
|
@ -129,9 +133,7 @@ private class PropertiesFlowConfig extends DataFlow3::Configuration {
|
|||
|
||||
/** The instantiation of a `Properties` object, which can be stored to disk. */
|
||||
class Properties extends Storable {
|
||||
Properties() {
|
||||
this.getConstructor().getDeclaringType() instanceof TypeProperty
|
||||
}
|
||||
Properties() { this.getConstructor().getDeclaringType() instanceof TypeProperty }
|
||||
|
||||
/** Gets an input, for example `input` in `props.setProperty("password", input);`. */
|
||||
override Expr getAnInput() {
|
||||
|
@ -167,7 +169,7 @@ private Expr getInstanceInput(DataFlow::Node instance, RefType t) {
|
|||
a.getDest() = fa and
|
||||
a.getSource() = result and
|
||||
fa.getField().getDeclaringType() = t
|
||||
|
|
||||
|
|
||||
t.getASourceSupertype*() instanceof TypeSerializable or
|
||||
t instanceof JAXBElement
|
||||
)
|
||||
|
@ -175,12 +177,15 @@ private Expr getInstanceInput(DataFlow::Node instance, RefType t) {
|
|||
|
||||
private class ClassStoreFlowConfig extends DataFlow4::Configuration {
|
||||
ClassStoreFlowConfig() { this = "SensitiveStorage::ClassStoreFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ClassStore }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(getInstanceInput(sink, _)) or
|
||||
serializableStore(sink, _) or
|
||||
marshallableStore(sink, _)
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 1 }
|
||||
}
|
||||
|
||||
|
@ -212,7 +217,9 @@ class Serializable extends ClassStore {
|
|||
// `Properties` are `Serializable`, but handled elsewhere.
|
||||
not this instanceof Properties and
|
||||
// restrict attention to tainted instances
|
||||
exists(SensitiveSource data | data.flowsToCached(getInstanceInput(_, this.getConstructor().getDeclaringType())))
|
||||
exists(SensitiveSource data |
|
||||
data.flowsToCached(getInstanceInput(_, this.getConstructor().getDeclaringType()))
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a store, for example `outputStream.writeObject(instance)`. */
|
||||
|
@ -226,9 +233,7 @@ class Serializable extends ClassStore {
|
|||
|
||||
/** The instantiation of a marshallable class, which can be stored to disk as XML. */
|
||||
class Marshallable extends ClassStore {
|
||||
Marshallable() {
|
||||
this.getConstructor().getDeclaringType() instanceof JAXBElement
|
||||
}
|
||||
Marshallable() { this.getConstructor().getDeclaringType() instanceof JAXBElement }
|
||||
|
||||
/** Gets a store, for example `marshaller.marshal(instance)`. */
|
||||
override Expr getAStore() {
|
||||
|
|
|
@ -18,9 +18,12 @@ class XMLDecoderReadObjectMethod extends Method {
|
|||
|
||||
class SafeXStream extends DataFlow2::Configuration {
|
||||
SafeXStream() { this = "UnsafeDeserialization::SafeXStream" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
any(XStreamEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() = src.asExpr()
|
||||
any(XStreamEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() = src
|
||||
.asExpr()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and
|
||||
|
@ -31,9 +34,12 @@ class SafeXStream extends DataFlow2::Configuration {
|
|||
|
||||
class SafeKryo extends DataFlow2::Configuration {
|
||||
SafeKryo() { this = "UnsafeDeserialization::SafeKryo" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
any(KryoEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() = src.asExpr()
|
||||
any(KryoEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() = src
|
||||
.asExpr()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and
|
||||
|
@ -64,8 +70,7 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
|
|||
}
|
||||
|
||||
class UnsafeDeserializationSink extends DataFlow::ExprNode {
|
||||
UnsafeDeserializationSink() {
|
||||
unsafeDeserialization(_, this.getExpr())
|
||||
}
|
||||
UnsafeDeserializationSink() { unsafeDeserialization(_, this.getExpr()) }
|
||||
|
||||
MethodAccess getMethodAccess() { unsafeDeserialization(result, this.getExpr()) }
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ class UrlRedirectSink extends DataFlow::ExprNode {
|
|||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof ResponseSetHeaderMethod or
|
||||
ma.getMethod() instanceof ResponseAddHeaderMethod
|
||||
|
|
||||
|
|
||||
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "Location" and
|
||||
this.asExpr() = ma.getArgument(1)
|
||||
)
|
||||
|
|
|
@ -20,6 +20,7 @@ abstract class XmlParserCall extends MethodAccess {
|
|||
* Gets the argument representing the XML content to be parsed.
|
||||
*/
|
||||
abstract Expr getSink();
|
||||
|
||||
/**
|
||||
* Holds if the call is safe.
|
||||
*/
|
||||
|
@ -30,7 +31,6 @@ abstract class XmlParserCall extends MethodAccess {
|
|||
* An access to a method use for configuring the parser.
|
||||
*/
|
||||
abstract class ParserConfig extends MethodAccess {
|
||||
|
||||
/**
|
||||
* Holds if the method disables a property.
|
||||
*/
|
||||
|
@ -54,16 +54,12 @@ abstract class ParserConfig extends MethodAccess {
|
|||
|
||||
/** The class `javax.xml.parsers.DocumentBuilderFactory`. */
|
||||
class DocumentBuilderFactory extends RefType {
|
||||
DocumentBuilderFactory() {
|
||||
this.hasQualifiedName("javax.xml.parsers", "DocumentBuilderFactory")
|
||||
}
|
||||
DocumentBuilderFactory() { this.hasQualifiedName("javax.xml.parsers", "DocumentBuilderFactory") }
|
||||
}
|
||||
|
||||
/** The class `javax.xml.parsers.DocumentBuilder`. */
|
||||
class DocumentBuilder extends RefType {
|
||||
DocumentBuilder() {
|
||||
this.hasQualifiedName("javax.xml.parsers", "DocumentBuilder")
|
||||
}
|
||||
DocumentBuilder() { this.hasQualifiedName("javax.xml.parsers", "DocumentBuilder") }
|
||||
}
|
||||
|
||||
/** A call to `DocumentBuilder.parse`. */
|
||||
|
@ -76,19 +72,26 @@ class DocumentBuilderParse extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeDocumentBuilderToDocumentBuilderParseFlowConfig conf | conf.hasFlowToExpr(this.getQualifier()))
|
||||
exists(SafeDocumentBuilderToDocumentBuilderParseFlowConfig conf |
|
||||
conf.hasFlowToExpr(this.getQualifier())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SafeDocumentBuilderToDocumentBuilderParseFlowConfig extends DataFlow2::Configuration {
|
||||
SafeDocumentBuilderToDocumentBuilderParseFlowConfig() { this = "XmlParsers::SafeDocumentBuilderToDocumentBuilderParseFlowConfig" }
|
||||
SafeDocumentBuilderToDocumentBuilderParseFlowConfig() {
|
||||
this = "XmlParsers::SafeDocumentBuilderToDocumentBuilderParseFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeDocumentBuilder }
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(DocumentBuilderParse dbp).getQualifier() }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(DocumentBuilderParse dbp).getQualifier()
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(RefType t, ReturnStmt ret, Method m |
|
||||
node2.asExpr().(ClassInstanceExpr).getConstructedType().getSourceDeclaration() = t and
|
||||
|
@ -97,7 +100,8 @@ private class SafeDocumentBuilderToDocumentBuilderParseFlowConfig extends DataFl
|
|||
ret.getEnclosingCallable() = m and
|
||||
m.hasName("initialValue") and
|
||||
m.getDeclaringType() = t
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, Method m |
|
||||
ma = node2.asExpr() and
|
||||
ma.getQualifier() = node1.asExpr() and
|
||||
|
@ -106,6 +110,7 @@ private class SafeDocumentBuilderToDocumentBuilderParseFlowConfig extends DataFl
|
|||
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.lang", "ThreadLocal")
|
||||
)
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -123,7 +128,8 @@ class DocumentBuilderFactoryConfig extends ParserConfig {
|
|||
}
|
||||
|
||||
private predicate constantStringExpr(Expr e, string val) {
|
||||
e.(CompileTimeConstantExpr).getStringValue() = val or
|
||||
e.(CompileTimeConstantExpr).getStringValue() = val
|
||||
or
|
||||
exists(SsaExplicitUpdate v, Expr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr().(VariableAssign).getSource() and
|
||||
|
@ -134,22 +140,21 @@ private predicate constantStringExpr(Expr e, string val) {
|
|||
/** An expression that always has the same string value. */
|
||||
private class ConstantStringExpr extends Expr {
|
||||
string value;
|
||||
ConstantStringExpr() {
|
||||
constantStringExpr(this, value)
|
||||
}
|
||||
|
||||
ConstantStringExpr() { constantStringExpr(this, value) }
|
||||
|
||||
/** Get the string value of this expression. */
|
||||
string getStringValue() {
|
||||
result = value
|
||||
}
|
||||
string getStringValue() { result = value }
|
||||
}
|
||||
|
||||
/**
|
||||
* A general configuration that is safe when enabled.
|
||||
*/
|
||||
Expr singleSafeConfig() {
|
||||
result.(ConstantStringExpr).getStringValue() = "http://apache.org/xml/features/disallow-doctype-decl" or
|
||||
result.(ConstantStringExpr).getStringValue() = "http://javax.xml.XMLConstants/feature/secure-processing" or
|
||||
result.(ConstantStringExpr).getStringValue() = "http://apache.org/xml/features/disallow-doctype-decl"
|
||||
or
|
||||
result.(ConstantStringExpr).getStringValue() = "http://javax.xml.XMLConstants/feature/secure-processing"
|
||||
or
|
||||
exists(Field f |
|
||||
result = f.getAnAccess() and
|
||||
f.hasName("FEATURE_SECURE_PROCESSING") and
|
||||
|
@ -170,10 +175,16 @@ class SafeDocumentBuilderFactory extends VarAccess {
|
|||
(
|
||||
//These two need to be set together to work
|
||||
exists(DocumentBuilderFactoryConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://xml.org/sax/features/external-general-entities"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://xml.org/sax/features/external-general-entities"
|
||||
))
|
||||
) and
|
||||
exists(DocumentBuilderFactoryConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -191,9 +202,18 @@ private class DocumentBuilderConstruction extends MethodAccess {
|
|||
}
|
||||
|
||||
private class SafeDocumentBuilderFactoryToDocumentBuilderConstructionFlowConfig extends DataFlow3::Configuration {
|
||||
SafeDocumentBuilderFactoryToDocumentBuilderConstructionFlowConfig() { this = "XmlParsers::SafeDocumentBuilderFactoryToDocumentBuilderConstructionFlowConfig" }
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeDocumentBuilderFactory }
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(DocumentBuilderConstruction dbc).getQualifier() }
|
||||
SafeDocumentBuilderFactoryToDocumentBuilderConstructionFlowConfig() {
|
||||
this = "XmlParsers::SafeDocumentBuilderFactoryToDocumentBuilderConstructionFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
src.asExpr() instanceof SafeDocumentBuilderFactory
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(DocumentBuilderConstruction dbc).getQualifier()
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -214,9 +234,7 @@ class SafeDocumentBuilder extends DocumentBuilderConstruction {
|
|||
|
||||
/** The class `javax.xml.stream.XMLInputFactory`. */
|
||||
class XmlInputFactory extends RefType {
|
||||
XmlInputFactory() {
|
||||
this.hasQualifiedName("javax.xml.stream", "XMLInputFactory")
|
||||
}
|
||||
XmlInputFactory() { this.hasQualifiedName("javax.xml.stream", "XMLInputFactory") }
|
||||
}
|
||||
|
||||
/** A call to `XMLInputFactory.createXMLStreamReader`. */
|
||||
|
@ -231,24 +249,29 @@ class XmlInputFactoryStreamReader extends XmlParserCall {
|
|||
|
||||
override Expr getSink() {
|
||||
if this.getMethod().getParameterType(0) instanceof TypeString
|
||||
then
|
||||
result = this.getArgument(1)
|
||||
else
|
||||
result = this.getArgument(0)
|
||||
then result = this.getArgument(1)
|
||||
else result = this.getArgument(0)
|
||||
}
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeXmlInputFactoryToXmlInputFactoryReaderFlowConfig conf | conf.hasFlowToExpr(this.getQualifier()))
|
||||
exists(SafeXmlInputFactoryToXmlInputFactoryReaderFlowConfig conf |
|
||||
conf.hasFlowToExpr(this.getQualifier())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SafeXmlInputFactoryToXmlInputFactoryReaderFlowConfig extends DataFlow2::Configuration {
|
||||
SafeXmlInputFactoryToXmlInputFactoryReaderFlowConfig() { this = "XmlParsers::SafeXmlInputFactoryToXmlInputFactoryReaderFlowConfig" }
|
||||
SafeXmlInputFactoryToXmlInputFactoryReaderFlowConfig() {
|
||||
this = "XmlParsers::SafeXmlInputFactoryToXmlInputFactoryReaderFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeXmlInputFactory }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(XmlInputFactoryStreamReader xifsr).getQualifier() or
|
||||
sink.asExpr() = any(XmlInputFactoryEventReader xifer).getQualifier()
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -264,14 +287,14 @@ class XmlInputFactoryEventReader extends XmlParserCall {
|
|||
|
||||
override Expr getSink() {
|
||||
if this.getMethod().getParameterType(0) instanceof TypeString
|
||||
then
|
||||
result = this.getArgument(1)
|
||||
else
|
||||
result = this.getArgument(0)
|
||||
then result = this.getArgument(1)
|
||||
else result = this.getArgument(0)
|
||||
}
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeXmlInputFactoryToXmlInputFactoryReaderFlowConfig conf | conf.hasFlowToExpr(this.getQualifier()))
|
||||
exists(SafeXmlInputFactoryToXmlInputFactoryReaderFlowConfig conf |
|
||||
conf.hasFlowToExpr(this.getQualifier())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,7 +315,8 @@ class XmlInputFactoryConfig extends ParserConfig {
|
|||
* An `XmlInputFactory` specific expression that indicates whether parsing external entities is supported.
|
||||
*/
|
||||
Expr configOptionIsSupportingExternalEntities() {
|
||||
result.(ConstantStringExpr).getStringValue() = "javax.xml.stream.isSupportingExternalEntities" or
|
||||
result.(ConstantStringExpr).getStringValue() = "javax.xml.stream.isSupportingExternalEntities"
|
||||
or
|
||||
exists(Field f |
|
||||
result = f.getAnAccess() and
|
||||
f.hasName("IS_SUPPORTING_EXTERNAL_ENTITIES") and
|
||||
|
@ -304,7 +328,8 @@ Expr configOptionIsSupportingExternalEntities() {
|
|||
* An `XmlInputFactory` specific expression that indicates whether DTD is supported.
|
||||
*/
|
||||
Expr configOptionSupportDTD() {
|
||||
result.(ConstantStringExpr).getStringValue() = "javax.xml.stream.supportDTD" or
|
||||
result.(ConstantStringExpr).getStringValue() = "javax.xml.stream.supportDTD"
|
||||
or
|
||||
exists(Field f |
|
||||
result = f.getAnAccess() and
|
||||
f.hasName("SUPPORT_DTD") and
|
||||
|
@ -329,7 +354,6 @@ class SafeXmlInputFactory extends VarAccess {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#SAXBuilder
|
||||
*/
|
||||
|
@ -356,9 +380,7 @@ class SAXBuilderParse extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeSAXBuilderToSAXBuilderParseFlowConfig conf | conf.hasFlowToExpr(this.getQualifier()))
|
||||
|
@ -366,9 +388,16 @@ class SAXBuilderParse extends XmlParserCall {
|
|||
}
|
||||
|
||||
private class SafeSAXBuilderToSAXBuilderParseFlowConfig extends DataFlow2::Configuration {
|
||||
SafeSAXBuilderToSAXBuilderParseFlowConfig() { this = "XmlParsers::SafeSAXBuilderToSAXBuilderParseFlowConfig" }
|
||||
SafeSAXBuilderToSAXBuilderParseFlowConfig() {
|
||||
this = "XmlParsers::SafeSAXBuilderToSAXBuilderParseFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeSAXBuilder }
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(SAXBuilderParse sax).getQualifier() }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(SAXBuilderParse sax).getQualifier()
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -391,7 +420,10 @@ class SafeSAXBuilder extends VarAccess {
|
|||
exists(Variable v |
|
||||
v = this.getVariable() and
|
||||
exists(SAXBuilderConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.enables(any(ConstantStringExpr s | s.getStringValue() = "http://apache.org/xml/features/disallow-doctype-decl"))
|
||||
config
|
||||
.enables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://apache.org/xml/features/disallow-doctype-decl"
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -407,16 +439,12 @@ class SafeSAXBuilder extends VarAccess {
|
|||
* The class `javax.xml.parsers.SAXParser`.
|
||||
*/
|
||||
class SAXParser extends RefType {
|
||||
SAXParser() {
|
||||
this.hasQualifiedName("javax.xml.parsers", "SAXParser")
|
||||
}
|
||||
SAXParser() { this.hasQualifiedName("javax.xml.parsers", "SAXParser") }
|
||||
}
|
||||
|
||||
/** The class `javax.xml.parsers.SAXParserFactory`. */
|
||||
class SAXParserFactory extends RefType {
|
||||
SAXParserFactory() {
|
||||
this.hasQualifiedName("javax.xml.parsers", "SAXParserFactory")
|
||||
}
|
||||
SAXParserFactory() { this.hasQualifiedName("javax.xml.parsers", "SAXParserFactory") }
|
||||
}
|
||||
|
||||
/** A call to `SAXParser.parse`. */
|
||||
|
@ -429,9 +457,7 @@ class SAXParserParse extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeSAXParserFlowConfig sp | sp.hasFlowToExpr(this.getQualifier()))
|
||||
|
@ -449,7 +475,6 @@ class SAXParserFactoryConfig extends ParserConfig {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A safely configured `SAXParserFactory`.
|
||||
*/
|
||||
|
@ -457,21 +482,34 @@ class SafeSAXParserFactory extends VarAccess {
|
|||
SafeSAXParserFactory() {
|
||||
exists(Variable v | v = this.getVariable() |
|
||||
exists(SAXParserFactoryConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://xml.org/sax/features/external-general-entities"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://xml.org/sax/features/external-general-entities"
|
||||
))
|
||||
) and
|
||||
exists(SAXParserFactoryConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"
|
||||
))
|
||||
) and
|
||||
exists(SAXParserFactoryConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://apache.org/xml/features/nonvalidating/load-external-dtd"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://apache.org/xml/features/nonvalidating/load-external-dtd"
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SafeSAXParserFactoryToNewSAXParserFlowConfig extends DataFlow5::Configuration {
|
||||
SafeSAXParserFactoryToNewSAXParserFlowConfig() { this = "XmlParsers::SafeSAXParserFactoryToNewSAXParserFlowConfig" }
|
||||
SafeSAXParserFactoryToNewSAXParserFlowConfig() {
|
||||
this = "XmlParsers::SafeSAXParserFactoryToNewSAXParserFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeSAXParserFactory }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
sink.asExpr() = ma.getQualifier() and
|
||||
|
@ -480,15 +518,21 @@ private class SafeSAXParserFactoryToNewSAXParserFlowConfig extends DataFlow5::Co
|
|||
m.hasName("newSAXParser")
|
||||
)
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
private class SafeSAXParserFlowConfig extends DataFlow4::Configuration {
|
||||
SafeSAXParserFlowConfig() { this = "XmlParsers::SafeSAXParserFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeSAXParser }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma | sink.asExpr() = ma.getQualifier() and ma.getMethod().getDeclaringType() instanceof SAXParser)
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and ma.getMethod().getDeclaringType() instanceof SAXParser
|
||||
)
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -504,14 +548,11 @@ class SafeSAXParser extends MethodAccess {
|
|||
}
|
||||
|
||||
/* SAXReader: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#SAXReader */
|
||||
|
||||
/**
|
||||
* The class `org.dom4j.io.SAXReader`.
|
||||
*/
|
||||
class SAXReader extends RefType {
|
||||
SAXReader() {
|
||||
this.hasQualifiedName("org.dom4j.io", "SAXReader")
|
||||
}
|
||||
SAXReader() { this.hasQualifiedName("org.dom4j.io", "SAXReader") }
|
||||
}
|
||||
|
||||
/** A call to `SAXReader.read`. */
|
||||
|
@ -524,9 +565,7 @@ class SAXReaderRead extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeSAXReaderFlowConfig sr | sr.hasFlowToExpr(this.getQualifier()))
|
||||
|
@ -546,10 +585,15 @@ class SAXReaderConfig extends ParserConfig {
|
|||
|
||||
private class SafeSAXReaderFlowConfig extends DataFlow4::Configuration {
|
||||
SafeSAXReaderFlowConfig() { this = "XmlParsers::SafeSAXReaderFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeSAXReader }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma | sink.asExpr() = ma.getQualifier() and ma.getMethod().getDeclaringType() instanceof SAXReader)
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and ma.getMethod().getDeclaringType() instanceof SAXReader
|
||||
)
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -558,25 +602,31 @@ class SafeSAXReader extends VarAccess {
|
|||
SafeSAXReader() {
|
||||
exists(Variable v | v = this.getVariable() |
|
||||
exists(SAXReaderConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://xml.org/sax/features/external-general-entities"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://xml.org/sax/features/external-general-entities"
|
||||
))
|
||||
) and
|
||||
exists(SAXReaderConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"
|
||||
))
|
||||
) and
|
||||
exists(SAXReaderConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.enables(any(ConstantStringExpr s | s.getStringValue() = "http://apache.org/xml/features/disallow-doctype-decl"))
|
||||
config
|
||||
.enables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://apache.org/xml/features/disallow-doctype-decl"
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/* https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#XMLReader */
|
||||
|
||||
/** The class `org.xml.sax.XMLReader`. */
|
||||
class XMLReader extends RefType {
|
||||
XMLReader() {
|
||||
this.hasQualifiedName("org.xml.sax", "XMLReader")
|
||||
}
|
||||
XMLReader() { this.hasQualifiedName("org.xml.sax", "XMLReader") }
|
||||
}
|
||||
|
||||
/** A call to `XMLReader.read`. */
|
||||
|
@ -589,9 +639,7 @@ class XMLReaderParse extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(ExplicitlySafeXMLReader sr | sr.flowsTo(this.getQualifier())) or
|
||||
|
@ -601,7 +649,7 @@ class XMLReaderParse extends XmlParserCall {
|
|||
|
||||
/** A `ParserConfig` specific to the `XMLReader`. */
|
||||
class XMLReaderConfig extends ParserConfig {
|
||||
XMLReaderConfig() {
|
||||
XMLReaderConfig() {
|
||||
exists(Method m |
|
||||
m = this.getMethod() and
|
||||
m.getDeclaringType() instanceof XMLReader and
|
||||
|
@ -612,8 +660,13 @@ class XMLReaderConfig extends ParserConfig {
|
|||
|
||||
private class ExplicitlySafeXMLReaderFlowConfig extends DataFlow3::Configuration {
|
||||
ExplicitlySafeXMLReaderFlowConfig() { this = "XmlParsers::ExplicitlySafeXMLReaderFlowConfig" }
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ExplicitlySafeXMLReader }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) {
|
||||
src.asExpr() instanceof ExplicitlySafeXMLReader
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof SafeXMLReaderFlowSink }
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -631,28 +684,47 @@ class ExplicitlySafeXMLReader extends VarAccess {
|
|||
exists(Variable v | v = this.getVariable() |
|
||||
(
|
||||
exists(XMLReaderConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://xml.org/sax/features/external-general-entities"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://xml.org/sax/features/external-general-entities"
|
||||
))
|
||||
) and
|
||||
exists(XMLReaderConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://xml.org/sax/features/external-parameter-entities"
|
||||
))
|
||||
) and
|
||||
exists(XMLReaderConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(any(ConstantStringExpr s | s.getStringValue() = "http://apache.org/xml/features/nonvalidating/load-external-dtd"))
|
||||
config
|
||||
.disables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://apache.org/xml/features/nonvalidating/load-external-dtd"
|
||||
))
|
||||
)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(XMLReaderConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.enables(any(ConstantStringExpr s | s.getStringValue() = "http://apache.org/xml/features/disallow-doctype-decl")))
|
||||
config
|
||||
.enables(any(ConstantStringExpr s |
|
||||
s.getStringValue() = "http://apache.org/xml/features/disallow-doctype-decl"
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate flowsTo(SafeXMLReaderFlowSink sink) {
|
||||
any(ExplicitlySafeXMLReaderFlowConfig conf).hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(sink))
|
||||
any(ExplicitlySafeXMLReaderFlowConfig conf)
|
||||
.hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(sink))
|
||||
}
|
||||
}
|
||||
|
||||
private class CreatedSafeXMLReaderFlowConfig extends DataFlow3::Configuration {
|
||||
CreatedSafeXMLReaderFlowConfig() { this = "XmlParsers::CreatedSafeXMLReaderFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CreatedSafeXMLReader }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof SafeXMLReaderFlowSink }
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -664,7 +736,8 @@ class CreatedSafeXMLReader extends MethodAccess {
|
|||
this.getMethod().getDeclaringType() instanceof SAXParser and
|
||||
this.getMethod().hasName("getXMLReader") and
|
||||
safeParser.hasFlowToExpr(this.getQualifier())
|
||||
) or
|
||||
)
|
||||
or
|
||||
//Obtained from SAXReader
|
||||
exists(SafeSAXReaderFlowConfig safeReader |
|
||||
this.getMethod().getDeclaringType() instanceof SAXReader and
|
||||
|
@ -672,8 +745,10 @@ class CreatedSafeXMLReader extends MethodAccess {
|
|||
safeReader.hasFlowToExpr(this.getQualifier())
|
||||
)
|
||||
}
|
||||
|
||||
predicate flowsTo(SafeXMLReaderFlowSink sink) {
|
||||
any(CreatedSafeXMLReaderFlowConfig conf).hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(sink))
|
||||
any(CreatedSafeXMLReaderFlowConfig conf)
|
||||
.hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(sink))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -684,9 +759,7 @@ class CreatedSafeXMLReader extends MethodAccess {
|
|||
|
||||
/** The class `javax.xml.transform.sax.SAXSource` */
|
||||
class SAXSource extends RefType {
|
||||
SAXSource() {
|
||||
this.hasQualifiedName("javax.xml.transform.sax", "SAXSource")
|
||||
}
|
||||
SAXSource() { this.hasQualifiedName("javax.xml.transform.sax", "SAXSource") }
|
||||
}
|
||||
|
||||
/** A call to the constructor of `SAXSource` with `XMLReader` and `InputSource`. */
|
||||
|
@ -696,12 +769,12 @@ class ConstructedSAXSource extends ClassInstanceExpr {
|
|||
this.getNumArgument() = 2 and
|
||||
this.getArgument(0).getType() instanceof XMLReader
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument representing the XML content to be parsed.
|
||||
*/
|
||||
Expr getSink() {
|
||||
result = this.getArgument(1)
|
||||
}
|
||||
Expr getSink() { result = this.getArgument(1) }
|
||||
|
||||
/** Holds if the resulting `SAXSource` is safe. */
|
||||
predicate isSafe() {
|
||||
exists(CreatedSafeXMLReader safeReader | safeReader.flowsTo(this.getArgument(0))) or
|
||||
|
@ -730,13 +803,13 @@ class SafeSAXSource extends Expr {
|
|||
exists(ExplicitlySafeXMLReader safeReader | safeReader.flowsTo(s.getArgument(0)))
|
||||
)
|
||||
)
|
||||
) or
|
||||
)
|
||||
or
|
||||
this.(ConstructedSAXSource).isSafe()
|
||||
}
|
||||
}
|
||||
|
||||
/* Transformer: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#TransformerFactory */
|
||||
|
||||
/** An access to a method use for configuring a transformer or schema. */
|
||||
abstract class TransformerConfig extends MethodAccess {
|
||||
/** Holds if the configuration is disabled */
|
||||
|
@ -748,14 +821,13 @@ abstract class TransformerConfig extends MethodAccess {
|
|||
|
||||
/** The class `javax.xml.XMLConstants`. */
|
||||
class XmlConstants extends RefType {
|
||||
XmlConstants() {
|
||||
this.hasQualifiedName("javax.xml", "XMLConstants")
|
||||
}
|
||||
XmlConstants() { this.hasQualifiedName("javax.xml", "XMLConstants") }
|
||||
}
|
||||
|
||||
/** A configuration specific for transformers and schema. */
|
||||
Expr configAccessExternalDTD() {
|
||||
result.(ConstantStringExpr).getStringValue() = "http://javax.xml.XMLConstants/property/accessExternalDTD" or
|
||||
result.(ConstantStringExpr).getStringValue() = "http://javax.xml.XMLConstants/property/accessExternalDTD"
|
||||
or
|
||||
exists(Field f |
|
||||
result = f.getAnAccess() and
|
||||
f.hasName("ACCESS_EXTERNAL_DTD") and
|
||||
|
@ -765,7 +837,8 @@ Expr configAccessExternalDTD() {
|
|||
|
||||
/** A configuration specific for transformers. */
|
||||
Expr configAccessExternalStyleSheet() {
|
||||
result.(ConstantStringExpr).getStringValue() = "http://javax.xml.XMLConstants/property/accessExternalStylesheet" or
|
||||
result.(ConstantStringExpr).getStringValue() = "http://javax.xml.XMLConstants/property/accessExternalStylesheet"
|
||||
or
|
||||
exists(Field f |
|
||||
result = f.getAnAccess() and
|
||||
f.hasName("ACCESS_EXTERNAL_STYLESHEET") and
|
||||
|
@ -775,7 +848,8 @@ Expr configAccessExternalStyleSheet() {
|
|||
|
||||
/** A configuration specific for schema. */
|
||||
Expr configAccessExternalSchema() {
|
||||
result.(ConstantStringExpr).getStringValue() = "http://javax.xml.XMLConstants/property/accessExternalSchema" or
|
||||
result.(ConstantStringExpr).getStringValue() = "http://javax.xml.XMLConstants/property/accessExternalSchema"
|
||||
or
|
||||
exists(Field f |
|
||||
result = f.getAnAccess() and
|
||||
f.hasName("ACCESS_EXTERNAL_SCHEMA") and
|
||||
|
@ -786,17 +860,14 @@ Expr configAccessExternalSchema() {
|
|||
/** The class `javax.xml.transform.TransformerFactory` or `javax.xml.transform.sax.SAXTransformerFactory`. */
|
||||
class TransformerFactory extends RefType {
|
||||
TransformerFactory() {
|
||||
this.hasQualifiedName("javax.xml.transform", "TransformerFactory")
|
||||
or
|
||||
this.hasQualifiedName("javax.xml.transform", "TransformerFactory") or
|
||||
this.hasQualifiedName("javax.xml.transform.sax", "SAXTransformerFactory")
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `javax.xml.transform.Transformer`. */
|
||||
class Transformer extends RefType {
|
||||
Transformer() {
|
||||
this.hasQualifiedName("javax.xml.transform", "Transformer")
|
||||
}
|
||||
Transformer() { this.hasQualifiedName("javax.xml.transform", "Transformer") }
|
||||
}
|
||||
|
||||
/** A call to `Transformer.transform`. */
|
||||
|
@ -809,19 +880,26 @@ class TransformerTransform extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeTransformerToTransformerTransformFlowConfig st | st.hasFlowToExpr(this.getQualifier()))
|
||||
exists(SafeTransformerToTransformerTransformFlowConfig st |
|
||||
st.hasFlowToExpr(this.getQualifier())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SafeTransformerToTransformerTransformFlowConfig extends DataFlow2::Configuration {
|
||||
SafeTransformerToTransformerTransformFlowConfig() { this = "XmlParsers::SafeTransformerToTransformerTransformFlowConfig" }
|
||||
SafeTransformerToTransformerTransformFlowConfig() {
|
||||
this = "XmlParsers::SafeTransformerToTransformerTransformFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeTransformer }
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(TransformerTransform tt).getQualifier() }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(TransformerTransform tt).getQualifier()
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -835,9 +913,7 @@ class TransformerFactorySource extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeTransformerFactoryFlowConfig stf | stf.hasFlowToExpr(this.getQualifier()))
|
||||
|
@ -857,10 +933,16 @@ class TransformerFactoryConfig extends TransformerConfig {
|
|||
|
||||
private class SafeTransformerFactoryFlowConfig extends DataFlow3::Configuration {
|
||||
SafeTransformerFactoryFlowConfig() { this = "XmlParsers::SafeTransformerFactoryFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeTransformerFactory }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma | sink.asExpr() = ma.getQualifier() and ma.getMethod().getDeclaringType() instanceof TransformerFactory)
|
||||
exists(MethodAccess ma |
|
||||
sink.asExpr() = ma.getQualifier() and
|
||||
ma.getMethod().getDeclaringType() instanceof TransformerFactory
|
||||
)
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -890,7 +972,8 @@ class SafeTransformer extends MethodAccess {
|
|||
}
|
||||
}
|
||||
|
||||
/* SAXTransformer: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#SAXTransformerFactory
|
||||
/*
|
||||
* SAXTransformer: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#SAXTransformerFactory
|
||||
* Has an extra method called newFilter.
|
||||
*/
|
||||
|
||||
|
@ -904,9 +987,7 @@ class SAXTransformerFactoryNewXMLFilter extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeTransformerFactoryFlowConfig stf | stf.hasFlowToExpr(this.getQualifier()))
|
||||
|
@ -916,9 +997,7 @@ class SAXTransformerFactoryNewXMLFilter extends XmlParserCall {
|
|||
/* Schema: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#SchemaFactory */
|
||||
/** The class `javax.xml.validation.SchemaFactory`. */
|
||||
class SchemaFactory extends RefType {
|
||||
SchemaFactory() {
|
||||
this.hasQualifiedName("javax.xml.validation", "SchemaFactory")
|
||||
}
|
||||
SchemaFactory() { this.hasQualifiedName("javax.xml.validation", "SchemaFactory") }
|
||||
}
|
||||
|
||||
/** A `ParserConfig` specific to `SchemaFactory`. */
|
||||
|
@ -942,19 +1021,26 @@ class SchemaFactoryNewSchema extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
exists(SafeSchemaFactoryToSchemaFactoryNewSchemaFlowConfig ssf | ssf.hasFlowToExpr(this.getQualifier()))
|
||||
exists(SafeSchemaFactoryToSchemaFactoryNewSchemaFlowConfig ssf |
|
||||
ssf.hasFlowToExpr(this.getQualifier())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SafeSchemaFactoryToSchemaFactoryNewSchemaFlowConfig extends DataFlow2::Configuration {
|
||||
SafeSchemaFactoryToSchemaFactoryNewSchemaFlowConfig() { this = "XmlParsers::SafeSchemaFactoryToSchemaFactoryNewSchemaFlowConfig" }
|
||||
SafeSchemaFactoryToSchemaFactoryNewSchemaFlowConfig() {
|
||||
this = "XmlParsers::SafeSchemaFactoryToSchemaFactoryNewSchemaFlowConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeSchemaFactory }
|
||||
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(SchemaFactoryNewSchema sfns).getQualifier() }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(SchemaFactoryNewSchema sfns).getQualifier()
|
||||
}
|
||||
|
||||
override int fieldFlowBranchLimit() { result = 0 }
|
||||
}
|
||||
|
||||
|
@ -963,20 +1049,19 @@ class SafeSchemaFactory extends VarAccess {
|
|||
SafeSchemaFactory() {
|
||||
exists(Variable v | v = this.getVariable() |
|
||||
exists(SchemaFactoryConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(configAccessExternalDTD())) and
|
||||
config.disables(configAccessExternalDTD())
|
||||
) and
|
||||
exists(SchemaFactoryConfig config | config.getQualifier() = v.getAnAccess() |
|
||||
config.disables(configAccessExternalSchema()))
|
||||
config.disables(configAccessExternalSchema())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/* Unmarshaller: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#Unmarshaller */
|
||||
|
||||
/** The class `javax.xml.bind.Unmarshaller`. */
|
||||
class XmlUnmarshaller extends RefType {
|
||||
XmlUnmarshaller() {
|
||||
this.hasQualifiedName("javax.xml.bind", "Unmarshaller")
|
||||
}
|
||||
XmlUnmarshaller() { this.hasQualifiedName("javax.xml.bind", "Unmarshaller") }
|
||||
}
|
||||
|
||||
/** A call to `Unmarshaller.unmarshal`. */
|
||||
|
@ -989,22 +1074,15 @@ class XmlUnmarshal extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
none()
|
||||
}
|
||||
override predicate isSafe() { none() }
|
||||
}
|
||||
|
||||
/* XPathExpression: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#XPathExpression */
|
||||
|
||||
/** The class `javax.xml.xpath.XPathExpression`. */
|
||||
class XPathExpression extends RefType {
|
||||
XPathExpression() {
|
||||
this.hasQualifiedName("javax.xml.xpath", "XPathExpression")
|
||||
}
|
||||
XPathExpression() { this.hasQualifiedName("javax.xml.xpath", "XPathExpression") }
|
||||
}
|
||||
|
||||
/** A call to `XPathExpression.evaluate`. */
|
||||
|
@ -1017,17 +1095,12 @@ class XPathEvaluate extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
none()
|
||||
}
|
||||
override predicate isSafe() { none() }
|
||||
}
|
||||
|
||||
// Sink methods in simplexml http://simple.sourceforge.net/home.php
|
||||
|
||||
/** A call to `read` or `validate` in `Persister`. */
|
||||
class SimpleXMLPersisterCall extends XmlParserCall {
|
||||
SimpleXMLPersisterCall() {
|
||||
|
@ -1038,13 +1111,9 @@ class SimpleXMLPersisterCall extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(1)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(1) }
|
||||
|
||||
override predicate isSafe() {
|
||||
none()
|
||||
}
|
||||
override predicate isSafe() { none() }
|
||||
}
|
||||
|
||||
/** A call to `provide` in `Provider`. */
|
||||
|
@ -1060,13 +1129,9 @@ class SimpleXMLProviderCall extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
none()
|
||||
}
|
||||
override predicate isSafe() { none() }
|
||||
}
|
||||
|
||||
/** A call to `read` in `NodeBuilder`. */
|
||||
|
@ -1079,13 +1144,9 @@ class SimpleXMLNodeBuilderCall extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
none()
|
||||
}
|
||||
override predicate isSafe() { none() }
|
||||
}
|
||||
|
||||
/** A call to the `format` method of the `Formatter`. */
|
||||
|
@ -1098,11 +1159,7 @@ class SimpleXMLFormatterCall extends XmlParserCall {
|
|||
)
|
||||
}
|
||||
|
||||
override Expr getSink() {
|
||||
result = this.getArgument(0)
|
||||
}
|
||||
override Expr getSink() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isSafe() {
|
||||
none()
|
||||
}
|
||||
override predicate isSafe() { none() }
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ class NumericNarrowingCastExpr extends CastExpr {
|
|||
NumericNarrowingCastExpr() {
|
||||
exists(NumericType sourceType, NumericType targetType |
|
||||
sourceType = getExpr().getType() and targetType = getType()
|
||||
|
|
||||
|
|
||||
not targetType.(NumType).widerThanOrEqualTo(sourceType.(NumType))
|
||||
)
|
||||
}
|
||||
|
@ -38,8 +38,9 @@ predicate boundedRead(RValue read) {
|
|||
read = v.getAUse() and
|
||||
cb.controls(read.getBasicBlock(), testIsTrue) and
|
||||
cb.getCondition() = comp
|
||||
|
|
||||
comp.getLesserOperand() = v.getAUse() and testIsTrue = true or
|
||||
|
|
||||
comp.getLesserOperand() = v.getAUse() and testIsTrue = true
|
||||
or
|
||||
comp.getGreaterOperand() = v.getAUse() and testIsTrue = false
|
||||
)
|
||||
}
|
||||
|
|
|
@ -9,9 +9,7 @@ import SensitiveApi
|
|||
private class HardcodedByteArray extends ArrayCreationExpr {
|
||||
HardcodedByteArray() {
|
||||
getType().(Array).getElementType().(PrimitiveType).getName() = "byte" and
|
||||
forex(Expr elem | elem = getInit().getAChildExpr() |
|
||||
elem instanceof CompileTimeConstantExpr
|
||||
)
|
||||
forex(Expr elem | elem = getInit().getAChildExpr() | elem instanceof CompileTimeConstantExpr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,9 +21,7 @@ private class HardcodedByteArray extends ArrayCreationExpr {
|
|||
private class HardcodedCharArray extends ArrayCreationExpr {
|
||||
HardcodedCharArray() {
|
||||
getType().(Array).getElementType().(PrimitiveType).getName() = "char" and
|
||||
forex(Expr elem | elem = getInit().getAChildExpr() |
|
||||
elem instanceof CompileTimeConstantExpr
|
||||
)
|
||||
forex(Expr elem | elem = getInit().getAChildExpr() | elem instanceof CompileTimeConstantExpr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,9 +41,7 @@ class HardcodedExpr extends Expr {
|
|||
* An argument to a sensitive call, expected to contain credentials.
|
||||
*/
|
||||
abstract class CredentialsSink extends Expr {
|
||||
Call getSurroundingCall() {
|
||||
this = result.getAnArgument()
|
||||
}
|
||||
Call getSurroundingCall() { this = result.getAnArgument() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,9 +76,7 @@ class PasswordVariable extends Variable {
|
|||
* A variable whose name indicates that it may hold a user name.
|
||||
*/
|
||||
class UsernameVariable extends Variable {
|
||||
UsernameVariable() {
|
||||
getName().regexpMatch("(?i)(user|username)")
|
||||
}
|
||||
UsernameVariable() { getName().regexpMatch("(?i)(user|username)") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,7 +5,8 @@ import java
|
|||
*/
|
||||
predicate javaApiCallablePasswordParam(Callable c, int i) {
|
||||
exists(c.getParameter(i)) and
|
||||
javaApiCallablePasswordParam(c.getDeclaringType().getQualifiedName() + ";" + c.getStringSignature() + ";" + i)
|
||||
javaApiCallablePasswordParam(c.getDeclaringType().getQualifiedName() + ";" +
|
||||
c.getStringSignature() + ";" + i)
|
||||
}
|
||||
|
||||
private predicate javaApiCallablePasswordParam(string s) {
|
||||
|
@ -128,7 +129,8 @@ private predicate javaApiCallablePasswordParam(string s) {
|
|||
*/
|
||||
predicate javaApiCallableUsernameParam(Callable c, int i) {
|
||||
exists(c.getParameter(i)) and
|
||||
javaApiCallableUsernameParam(c.getDeclaringType().getQualifiedName() + ";" + c.getStringSignature() + ";" + i)
|
||||
javaApiCallableUsernameParam(c.getDeclaringType().getQualifiedName() + ";" +
|
||||
c.getStringSignature() + ";" + i)
|
||||
}
|
||||
|
||||
private predicate javaApiCallableUsernameParam(string s) {
|
||||
|
@ -189,7 +191,8 @@ private predicate javaApiCallableUsernameParam(string s) {
|
|||
*/
|
||||
predicate javaApiCallableCryptoKeyParam(Callable c, int i) {
|
||||
exists(c.getParameter(i)) and
|
||||
javaApiCallableCryptoKeyParam(c.getDeclaringType().getQualifiedName() + ";" + c.getStringSignature() + ";" + i)
|
||||
javaApiCallableCryptoKeyParam(c.getDeclaringType().getQualifiedName() + ";" +
|
||||
c.getStringSignature() + ";" + i)
|
||||
}
|
||||
|
||||
private predicate javaApiCallableCryptoKeyParam(string s) {
|
||||
|
@ -413,7 +416,8 @@ private predicate javaApiCallableCryptoKeyParam(string s) {
|
|||
*/
|
||||
predicate otherApiCallableCredentialParam(Callable c, int i) {
|
||||
exists(c.getParameter(i)) and
|
||||
otherApiCallableCredentialParam(c.getDeclaringType().getQualifiedName() + ";" + c.getStringSignature() + ";" + i)
|
||||
otherApiCallableCredentialParam(c.getDeclaringType().getQualifiedName() + ";" +
|
||||
c.getStringSignature() + ";" + i)
|
||||
}
|
||||
|
||||
private predicate otherApiCallableCredentialParam(string s) {
|
||||
|
|
|
@ -14,9 +14,7 @@ import semmle.code.java.frameworks.j2objc.J2ObjC
|
|||
* - HTML entities in hexadecimal notation (e.g. `灟`)
|
||||
*/
|
||||
private predicate looksLikeCode(JavadocText line) {
|
||||
exists(string trimmed |
|
||||
trimmed = trimmedCommentText(line)
|
||||
|
|
||||
exists(string trimmed | trimmed = trimmedCommentText(line) |
|
||||
(
|
||||
trimmed.matches("%;") or
|
||||
trimmed.matches("%{") or
|
||||
|
@ -37,10 +35,12 @@ private predicate looksLikeCode(JavadocText line) {
|
|||
* - HTML entities in hexadecimal notation (e.g. `灟`)
|
||||
*/
|
||||
private string trimmedCommentText(JavadocText line) {
|
||||
result = line.getText().trim()
|
||||
.regexpReplaceAll("\\s*//.*$", "")
|
||||
.regexpReplaceAll("\\{@[^}]+\\}", "")
|
||||
.regexpReplaceAll("(?i)&#?[a-z0-9]{1,31};", "")
|
||||
result = line
|
||||
.getText()
|
||||
.trim()
|
||||
.regexpReplaceAll("\\s*//.*$", "")
|
||||
.regexpReplaceAll("\\{@[^}]+\\}", "")
|
||||
.regexpReplaceAll("(?i)&#?[a-z0-9]{1,31};", "")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,7 +49,7 @@ private string trimmedCommentText(JavadocText line) {
|
|||
private predicate hasCodeTags(Javadoc j) {
|
||||
exists(string tag | tag = "pre" or tag = "code" |
|
||||
j.getAChild().(JavadocText).getText().matches("%<" + tag + ">%") and
|
||||
j.getAChild().(JavadocText).getText().matches("%</"+ tag + ">%")
|
||||
j.getAChild().(JavadocText).getText().matches("%</" + tag + ">%")
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -57,9 +57,7 @@ private predicate hasCodeTags(Javadoc j) {
|
|||
* The comment immediately following `c`.
|
||||
*/
|
||||
private Javadoc getNextComment(Javadoc c) {
|
||||
exists(int n, File f | javadocLines(c, f, _, n) |
|
||||
javadocLines(result, f, n+1, _)
|
||||
)
|
||||
exists(int n, File f | javadocLines(c, f, _, n) | javadocLines(result, f, n + 1, _))
|
||||
}
|
||||
|
||||
private predicate javadocLines(Javadoc j, File f, int start, int end) {
|
||||
|
@ -69,9 +67,7 @@ private predicate javadocLines(Javadoc j, File f, int start, int end) {
|
|||
}
|
||||
|
||||
private class JavadocFirst extends Javadoc {
|
||||
JavadocFirst() {
|
||||
not exists(Javadoc prev | this = getNextComment(prev))
|
||||
}
|
||||
JavadocFirst() { not exists(Javadoc prev | this = getNextComment(prev)) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,10 +75,10 @@ private class JavadocFirst extends Javadoc {
|
|||
*/
|
||||
private int codeCount(JavadocFirst first) {
|
||||
result = sum(Javadoc following |
|
||||
following = getNextComment*(first) and not hasCodeTags(following)
|
||||
following = getNextComment*(first) and not hasCodeTags(following)
|
||||
|
|
||||
count(JavadocText line | line = following.getAChild() and looksLikeCode(line))
|
||||
)
|
||||
count(JavadocText line | line = following.getAChild() and looksLikeCode(line))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,15 +86,16 @@ private int codeCount(JavadocFirst first) {
|
|||
*/
|
||||
private int anyCount(JavadocFirst first) {
|
||||
result = sum(Javadoc following |
|
||||
following = getNextComment*(first) and not hasCodeTags(following)
|
||||
following = getNextComment*(first) and not hasCodeTags(following)
|
||||
|
|
||||
count(JavadocText line | line = following.getAChild() and
|
||||
not exists(string trimmed | trimmed = line.getText().trim() |
|
||||
trimmed.regexpMatch("(|/\\*|/\\*\\*|\\*|\\*/)") or
|
||||
trimmed.matches("@%")
|
||||
)
|
||||
count(JavadocText line |
|
||||
line = following.getAChild() and
|
||||
not exists(string trimmed | trimmed = line.getText().trim() |
|
||||
trimmed.regexpMatch("(|/\\*|/\\*\\*|\\*|\\*/)") or
|
||||
trimmed.matches("@%")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,7 +104,7 @@ private int anyCount(JavadocFirst first) {
|
|||
class CommentedOutCode extends JavadocFirst {
|
||||
CommentedOutCode() {
|
||||
anyCount(this) > 0 and
|
||||
((float)codeCount(this))/((float)anyCount(this)) > 0.5 and
|
||||
(codeCount(this).(float)) / (anyCount(this).(float)) > 0.5 and
|
||||
not this instanceof JSNIComment and
|
||||
not this instanceof OCNIComment
|
||||
}
|
||||
|
@ -115,9 +112,7 @@ class CommentedOutCode extends JavadocFirst {
|
|||
/**
|
||||
* The number of lines that appear to be commented-out code.
|
||||
*/
|
||||
int getCodeLines(){
|
||||
result = codeCount(this)
|
||||
}
|
||||
int getCodeLines() { result = codeCount(this) }
|
||||
|
||||
private Javadoc getLastSuccessor() {
|
||||
result = getNextComment*(this) and
|
||||
|
|
|
@ -19,7 +19,9 @@ private predicate emptyDecl(SsaExplicitUpdate ssa) {
|
|||
*/
|
||||
predicate deadLocal(SsaExplicitUpdate ssa) {
|
||||
ssa.getSourceVariable().getVariable() instanceof LocalScopeVariable and
|
||||
not exists(ssa.getAUse()) and not exists(SsaPhiNode phi | phi.getAPhiInput() = ssa) and not exists(SsaImplicitInit init | init.captures(ssa)) and
|
||||
not exists(ssa.getAUse()) and
|
||||
not exists(SsaPhiNode phi | phi.getAPhiInput() = ssa) and
|
||||
not exists(SsaImplicitInit init | init.captures(ssa)) and
|
||||
not emptyDecl(ssa) and
|
||||
not readImplicitly(ssa, _)
|
||||
}
|
||||
|
@ -44,8 +46,9 @@ predicate overwritten(SsaExplicitUpdate ssa) {
|
|||
exists(BasicBlock bb1, BasicBlock bb2, int i, int j |
|
||||
bb1.getNode(i) = ssa.getCFGNode() and
|
||||
bb2.getNode(j) = overwrite.getCFGNode()
|
||||
|
|
||||
bb1.getABBSuccessor+() = bb2 or
|
||||
|
|
||||
bb1.getABBSuccessor+() = bb2
|
||||
or
|
||||
bb1 = bb2 and i < j
|
||||
)
|
||||
)
|
||||
|
@ -55,7 +58,8 @@ predicate overwritten(SsaExplicitUpdate ssa) {
|
|||
* A local variable with a read access.
|
||||
*/
|
||||
predicate read(LocalScopeVariable v) {
|
||||
exists(VarAccess va | va = v.getAnAccess() | va.isRValue()) or
|
||||
exists(VarAccess va | va = v.getAnAccess() | va.isRValue())
|
||||
or
|
||||
readImplicitly(_, v)
|
||||
}
|
||||
|
||||
|
@ -77,16 +81,19 @@ predicate assigned(LocalScopeVariable v) {
|
|||
predicate exprHasNoEffect(Expr e) {
|
||||
inInitializer(e) and
|
||||
not exists(Expr bad | bad = e.getAChildExpr*() |
|
||||
bad instanceof Assignment or
|
||||
bad instanceof UnaryAssignExpr or
|
||||
bad instanceof Assignment
|
||||
or
|
||||
bad instanceof UnaryAssignExpr
|
||||
or
|
||||
exists(ClassInstanceExpr cie, Constructor c |
|
||||
bad = cie and c = cie.getConstructor().getSourceDeclaration()
|
||||
|
|
||||
|
|
||||
constructorHasEffect(c)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, Method m |
|
||||
bad = ma and m = ma.getMethod().getAPossibleImplementation()
|
||||
|
|
||||
|
|
||||
methodHasEffect(m) or not m.fromSource()
|
||||
)
|
||||
)
|
||||
|
@ -97,15 +104,17 @@ private predicate inInitializer(Expr e) {
|
|||
}
|
||||
|
||||
// The next two predicates are somewhat conservative.
|
||||
|
||||
private predicate constructorHasEffect(Constructor c) {
|
||||
// Only assign fields of the class - do not call methods,
|
||||
// create new objects or assign any other variables.
|
||||
exists(MethodAccess ma | ma.getEnclosingCallable() = c) or
|
||||
exists(ClassInstanceExpr cie | cie.getEnclosingCallable() = c) or
|
||||
exists(MethodAccess ma | ma.getEnclosingCallable() = c)
|
||||
or
|
||||
exists(ClassInstanceExpr cie | cie.getEnclosingCallable() = c)
|
||||
or
|
||||
exists(Assignment a | a.getEnclosingCallable() = c |
|
||||
not exists(VarAccess va | va = a.getDest() |
|
||||
va.getVariable() instanceof LocalVariableDecl or
|
||||
va.getVariable() instanceof LocalVariableDecl
|
||||
or
|
||||
exists(Field f | f = va.getVariable() |
|
||||
va.getQualifier() instanceof ThisAccess or
|
||||
not exists(va.getQualifier())
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import java
|
||||
|
||||
private Stmt getASwitchChild(SwitchStmt s) {
|
||||
result = s.getAChild() or
|
||||
exists(Stmt mid | mid = getASwitchChild(s) and not mid instanceof SwitchStmt and result = mid.getAChild())
|
||||
result = s.getAChild()
|
||||
or
|
||||
exists(Stmt mid |
|
||||
mid = getASwitchChild(s) and not mid instanceof SwitchStmt and result = mid.getAChild()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate blockInSwitch(SwitchStmt s, BasicBlock b) {
|
||||
|
@ -16,7 +19,8 @@ private predicate switchCaseControlFlow(SwitchStmt switch, BasicBlock b1, BasicB
|
|||
}
|
||||
|
||||
predicate switchCaseControlFlowPlus(SwitchStmt switch, BasicBlock b1, BasicBlock b2) {
|
||||
switchCaseControlFlow(switch, b1, b2) or
|
||||
switchCaseControlFlow(switch, b1, b2)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
switchCaseControlFlowPlus(switch, mid, b2) and
|
||||
switchCaseControlFlow(switch, b1, mid) and
|
||||
|
@ -35,22 +39,22 @@ predicate mayDropThroughWithoutComment(SwitchStmt switch, Stmt switchCase) {
|
|||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate fallThroughCommented(Stmt case) {
|
||||
private predicate fallThroughCommented(Stmt case) {
|
||||
exists(Location loc |
|
||||
loc = case.getLocation() and
|
||||
loc.getStartLine() = fallThroughCommentedLine(loc.getFile())
|
||||
)
|
||||
}
|
||||
|
||||
private
|
||||
int fallThroughCommentedLine(File f) {
|
||||
private int fallThroughCommentedLine(File f) {
|
||||
exists(Location loc, JavadocText text |
|
||||
loc.getFile() = f and
|
||||
text.getLocation() = loc and
|
||||
text.getText().toLowerCase().regexpMatch(".*falls?[ -]?(through|thru).*") and
|
||||
result = loc.getStartLine() + 1
|
||||
) or exists(int mid |
|
||||
)
|
||||
or
|
||||
exists(int mid |
|
||||
mid = fallThroughCommentedLine(f) and
|
||||
not stmtLine(f) = mid and
|
||||
mid < max(stmtLine(f)) and
|
||||
|
@ -58,8 +62,7 @@ int fallThroughCommentedLine(File f) {
|
|||
)
|
||||
}
|
||||
|
||||
private
|
||||
int stmtLine(File f) {
|
||||
private int stmtLine(File f) {
|
||||
exists(Stmt s, Location loc |
|
||||
s.getLocation() = loc and
|
||||
loc.getFile() = f and
|
||||
|
|
|
@ -1,93 +1,212 @@
|
|||
import java
|
||||
|
||||
/*
|
||||
*
|
||||
* Counting nontrivial literal occurrences
|
||||
*
|
||||
*/
|
||||
|
||||
private
|
||||
predicate trivialPositiveIntValue(string s) {
|
||||
s="0" or s="1" or s="2" or s="3" or s="4" or s="5" or s="6" or s="7" or s="8" or
|
||||
s="9" or s="10" or s="11" or s="12" or s="13" or s="14" or s="15" or s="16" or s="17" or
|
||||
s="18" or s="19" or s="20"
|
||||
|
||||
or
|
||||
|
||||
s="16" or s="32" or s="64" or s="128" or s="256" or s="512" or s="1024" or
|
||||
s="2048" or s="4096" or s="16384" or s="32768" or s="65536" or
|
||||
s="1048576" or s="2147483648" or s="4294967296"
|
||||
|
||||
or
|
||||
|
||||
s="15" or s="31" or s="63" or s="127" or s="255" or s="511" or s="1023" or
|
||||
s="2047" or s="4095" or s="16383" or s="32767" or s="65535" or
|
||||
s="1048577" or s="2147483647" or s="4294967295"
|
||||
|
||||
or
|
||||
|
||||
s = "0x00000001" or s = "0x00000002" or s = "0x00000004" or s = "0x00000008" or
|
||||
s = "0x00000010" or s = "0x00000020" or s = "0x00000040" or s = "0x00000080" or
|
||||
s = "0x00000100" or s = "0x00000200" or s = "0x00000400" or s = "0x00000800" or
|
||||
s = "0x00001000" or s = "0x00002000" or s = "0x00004000" or s = "0x00008000" or
|
||||
s = "0x00010000" or s = "0x00020000" or s = "0x00040000" or s = "0x00080000" or
|
||||
s = "0x00100000" or s = "0x00200000" or s = "0x00400000" or s = "0x00800000" or
|
||||
s = "0x01000000" or s = "0x02000000" or s = "0x04000000" or s = "0x08000000" or
|
||||
s = "0x10000000" or s = "0x20000000" or s = "0x40000000" or s = "0x80000000" or
|
||||
s = "0x00000001" or s = "0x00000003" or s = "0x00000007" or s = "0x0000000f" or
|
||||
s = "0x0000001f" or s = "0x0000003f" or s = "0x0000007f" or s = "0x000000ff" or
|
||||
s = "0x000001ff" or s = "0x000003ff" or s = "0x000007ff" or s = "0x00000fff" or
|
||||
s = "0x00001fff" or s = "0x00003fff" or s = "0x00007fff" or s = "0x0000ffff" or
|
||||
s = "0x0001ffff" or s = "0x0003ffff" or s = "0x0007ffff" or s = "0x000fffff" or
|
||||
s = "0x001fffff" or s = "0x003fffff" or s = "0x007fffff" or s = "0x00ffffff" or
|
||||
s = "0x01ffffff" or s = "0x03ffffff" or s = "0x07ffffff" or s = "0x0fffffff" or
|
||||
s = "0x1fffffff" or s = "0x3fffffff" or s = "0x7fffffff" or s = "0xffffffff" or
|
||||
s = "0x0001" or s = "0x0002" or s = "0x0004" or s = "0x0008" or
|
||||
s = "0x0010" or s = "0x0020" or s = "0x0040" or s = "0x0080" or
|
||||
s = "0x0100" or s = "0x0200" or s = "0x0400" or s = "0x0800" or
|
||||
s = "0x1000" or s = "0x2000" or s = "0x4000" or s = "0x8000" or
|
||||
s = "0x0001" or s = "0x0003" or s = "0x0007" or s = "0x000f" or
|
||||
s = "0x001f" or s = "0x003f" or s = "0x007f" or s = "0x00ff" or
|
||||
s = "0x01ff" or s = "0x03ff" or s = "0x07ff" or s = "0x0fff" or
|
||||
s = "0x1fff" or s = "0x3fff" or s = "0x7fff" or s = "0xffff" or
|
||||
s = "0x01" or s = "0x02" or s = "0x04" or s = "0x08" or
|
||||
s = "0x10" or s = "0x20" or s = "0x40" or s = "0x80" or
|
||||
s = "0x01" or s = "0x03" or s = "0x07" or s = "0x0f" or
|
||||
s = "0x1f" or s = "0x3f" or s = "0x7f" or s = "0xff" or
|
||||
s = "0x00"
|
||||
|
||||
or
|
||||
|
||||
s = "10" or s = "100" or s = "1000" or
|
||||
s = "10000" or s = "100000" or s = "1000000" or
|
||||
s = "10000000" or s = "100000000" or s = "1000000000"
|
||||
private predicate trivialPositiveIntValue(string s) {
|
||||
s = "0" or
|
||||
s = "1" or
|
||||
s = "2" or
|
||||
s = "3" or
|
||||
s = "4" or
|
||||
s = "5" or
|
||||
s = "6" or
|
||||
s = "7" or
|
||||
s = "8" or
|
||||
s = "9" or
|
||||
s = "10" or
|
||||
s = "11" or
|
||||
s = "12" or
|
||||
s = "13" or
|
||||
s = "14" or
|
||||
s = "15" or
|
||||
s = "16" or
|
||||
s = "17" or
|
||||
s = "18" or
|
||||
s = "19" or
|
||||
s = "20" or
|
||||
s = "16" or
|
||||
s = "32" or
|
||||
s = "64" or
|
||||
s = "128" or
|
||||
s = "256" or
|
||||
s = "512" or
|
||||
s = "1024" or
|
||||
s = "2048" or
|
||||
s = "4096" or
|
||||
s = "16384" or
|
||||
s = "32768" or
|
||||
s = "65536" or
|
||||
s = "1048576" or
|
||||
s = "2147483648" or
|
||||
s = "4294967296" or
|
||||
s = "15" or
|
||||
s = "31" or
|
||||
s = "63" or
|
||||
s = "127" or
|
||||
s = "255" or
|
||||
s = "511" or
|
||||
s = "1023" or
|
||||
s = "2047" or
|
||||
s = "4095" or
|
||||
s = "16383" or
|
||||
s = "32767" or
|
||||
s = "65535" or
|
||||
s = "1048577" or
|
||||
s = "2147483647" or
|
||||
s = "4294967295" or
|
||||
s = "0x00000001" or
|
||||
s = "0x00000002" or
|
||||
s = "0x00000004" or
|
||||
s = "0x00000008" or
|
||||
s = "0x00000010" or
|
||||
s = "0x00000020" or
|
||||
s = "0x00000040" or
|
||||
s = "0x00000080" or
|
||||
s = "0x00000100" or
|
||||
s = "0x00000200" or
|
||||
s = "0x00000400" or
|
||||
s = "0x00000800" or
|
||||
s = "0x00001000" or
|
||||
s = "0x00002000" or
|
||||
s = "0x00004000" or
|
||||
s = "0x00008000" or
|
||||
s = "0x00010000" or
|
||||
s = "0x00020000" or
|
||||
s = "0x00040000" or
|
||||
s = "0x00080000" or
|
||||
s = "0x00100000" or
|
||||
s = "0x00200000" or
|
||||
s = "0x00400000" or
|
||||
s = "0x00800000" or
|
||||
s = "0x01000000" or
|
||||
s = "0x02000000" or
|
||||
s = "0x04000000" or
|
||||
s = "0x08000000" or
|
||||
s = "0x10000000" or
|
||||
s = "0x20000000" or
|
||||
s = "0x40000000" or
|
||||
s = "0x80000000" or
|
||||
s = "0x00000001" or
|
||||
s = "0x00000003" or
|
||||
s = "0x00000007" or
|
||||
s = "0x0000000f" or
|
||||
s = "0x0000001f" or
|
||||
s = "0x0000003f" or
|
||||
s = "0x0000007f" or
|
||||
s = "0x000000ff" or
|
||||
s = "0x000001ff" or
|
||||
s = "0x000003ff" or
|
||||
s = "0x000007ff" or
|
||||
s = "0x00000fff" or
|
||||
s = "0x00001fff" or
|
||||
s = "0x00003fff" or
|
||||
s = "0x00007fff" or
|
||||
s = "0x0000ffff" or
|
||||
s = "0x0001ffff" or
|
||||
s = "0x0003ffff" or
|
||||
s = "0x0007ffff" or
|
||||
s = "0x000fffff" or
|
||||
s = "0x001fffff" or
|
||||
s = "0x003fffff" or
|
||||
s = "0x007fffff" or
|
||||
s = "0x00ffffff" or
|
||||
s = "0x01ffffff" or
|
||||
s = "0x03ffffff" or
|
||||
s = "0x07ffffff" or
|
||||
s = "0x0fffffff" or
|
||||
s = "0x1fffffff" or
|
||||
s = "0x3fffffff" or
|
||||
s = "0x7fffffff" or
|
||||
s = "0xffffffff" or
|
||||
s = "0x0001" or
|
||||
s = "0x0002" or
|
||||
s = "0x0004" or
|
||||
s = "0x0008" or
|
||||
s = "0x0010" or
|
||||
s = "0x0020" or
|
||||
s = "0x0040" or
|
||||
s = "0x0080" or
|
||||
s = "0x0100" or
|
||||
s = "0x0200" or
|
||||
s = "0x0400" or
|
||||
s = "0x0800" or
|
||||
s = "0x1000" or
|
||||
s = "0x2000" or
|
||||
s = "0x4000" or
|
||||
s = "0x8000" or
|
||||
s = "0x0001" or
|
||||
s = "0x0003" or
|
||||
s = "0x0007" or
|
||||
s = "0x000f" or
|
||||
s = "0x001f" or
|
||||
s = "0x003f" or
|
||||
s = "0x007f" or
|
||||
s = "0x00ff" or
|
||||
s = "0x01ff" or
|
||||
s = "0x03ff" or
|
||||
s = "0x07ff" or
|
||||
s = "0x0fff" or
|
||||
s = "0x1fff" or
|
||||
s = "0x3fff" or
|
||||
s = "0x7fff" or
|
||||
s = "0xffff" or
|
||||
s = "0x01" or
|
||||
s = "0x02" or
|
||||
s = "0x04" or
|
||||
s = "0x08" or
|
||||
s = "0x10" or
|
||||
s = "0x20" or
|
||||
s = "0x40" or
|
||||
s = "0x80" or
|
||||
s = "0x01" or
|
||||
s = "0x03" or
|
||||
s = "0x07" or
|
||||
s = "0x0f" or
|
||||
s = "0x1f" or
|
||||
s = "0x3f" or
|
||||
s = "0x7f" or
|
||||
s = "0xff" or
|
||||
s = "0x00" or
|
||||
s = "10" or
|
||||
s = "100" or
|
||||
s = "1000" or
|
||||
s = "10000" or
|
||||
s = "100000" or
|
||||
s = "1000000" or
|
||||
s = "10000000" or
|
||||
s = "100000000" or
|
||||
s = "1000000000"
|
||||
}
|
||||
|
||||
private
|
||||
predicate trivialIntValue(string s) {
|
||||
trivialPositiveIntValue(s) or
|
||||
private predicate trivialIntValue(string s) {
|
||||
trivialPositiveIntValue(s)
|
||||
or
|
||||
exists(string pos | trivialPositiveIntValue(pos) and s = "-" + pos)
|
||||
}
|
||||
|
||||
private
|
||||
predicate intTrivial(Literal lit) {
|
||||
private predicate intTrivial(Literal lit) {
|
||||
exists(string v | trivialIntValue(v) and v = lit.getLiteral())
|
||||
}
|
||||
|
||||
private
|
||||
predicate longTrivial(Literal lit) {
|
||||
private predicate longTrivial(Literal lit) {
|
||||
exists(string v | trivialIntValue(v) and v + "L" = lit.getLiteral())
|
||||
}
|
||||
|
||||
private
|
||||
predicate powerOfTen(float f) {
|
||||
f = 10 or f = 100 or f = 1000 or
|
||||
f = 10000 or f = 100000 or f = 1000000 or
|
||||
f = 10000000 or f = 100000000 or f = 1000000000
|
||||
private predicate powerOfTen(float f) {
|
||||
f = 10 or
|
||||
f = 100 or
|
||||
f = 1000 or
|
||||
f = 10000 or
|
||||
f = 100000 or
|
||||
f = 1000000 or
|
||||
f = 10000000 or
|
||||
f = 100000000 or
|
||||
f = 1000000000
|
||||
}
|
||||
|
||||
private
|
||||
predicate floatTrivial(Literal lit) {
|
||||
private predicate floatTrivial(Literal lit) {
|
||||
(lit instanceof FloatingPointLiteral or lit instanceof DoubleLiteral) and
|
||||
exists(float f |
|
||||
f = lit.getValue().toFloat() and
|
||||
|
@ -95,18 +214,11 @@ predicate floatTrivial(Literal lit) {
|
|||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate stringTrivial(StringLiteral lit) {
|
||||
lit.getLiteral().length() < 8
|
||||
}
|
||||
private predicate stringTrivial(StringLiteral lit) { lit.getLiteral().length() < 8 }
|
||||
|
||||
private
|
||||
predicate small(Literal lit) {
|
||||
lit.getLiteral().length() <= 1
|
||||
}
|
||||
private predicate small(Literal lit) { lit.getLiteral().length() <= 1 }
|
||||
|
||||
private
|
||||
predicate trivial(Literal lit) {
|
||||
private predicate trivial(Literal lit) {
|
||||
lit instanceof CharacterLiteral or
|
||||
lit instanceof BooleanLiteral or
|
||||
lit instanceof NullLiteral or
|
||||
|
@ -118,8 +230,7 @@ predicate trivial(Literal lit) {
|
|||
excludedLiteral(lit)
|
||||
}
|
||||
|
||||
private
|
||||
predicate literalIsConstantInitializer(Literal literal, Field f) {
|
||||
private predicate literalIsConstantInitializer(Literal literal, Field f) {
|
||||
exists(AssignExpr e, VarAccess access |
|
||||
access = e.getAChildExpr() and
|
||||
f = access.getVariable() and
|
||||
|
@ -127,12 +238,12 @@ predicate literalIsConstantInitializer(Literal literal, Field f) {
|
|||
f.isFinal() and
|
||||
f.isStatic() and
|
||||
literal = e.getAChildExpr() and
|
||||
literal.getIndex() = 1) and
|
||||
not trivial(literal)
|
||||
literal.getIndex() = 1
|
||||
) and
|
||||
not trivial(literal)
|
||||
}
|
||||
|
||||
private
|
||||
predicate nonTrivialValue(string value, Literal literal, string context) {
|
||||
private predicate nonTrivialValue(string value, Literal literal, string context) {
|
||||
value = literal.getLiteral() and
|
||||
not trivial(literal) and
|
||||
not literalIsConstantInitializer(literal, _) and
|
||||
|
@ -141,43 +252,37 @@ predicate nonTrivialValue(string value, Literal literal, string context) {
|
|||
exists(MethodAccess ma | literal = ma.getAnArgument() and ma.getMethod().getName() = context)
|
||||
}
|
||||
|
||||
|
||||
private
|
||||
predicate valueOccurrenceCount(string value, int n, string context) {
|
||||
private predicate valueOccurrenceCount(string value, int n, string context) {
|
||||
n = strictcount(Literal lit | nonTrivialValue(value, lit, context)) and
|
||||
n > 20
|
||||
}
|
||||
|
||||
private
|
||||
predicate occurenceCount(Literal lit, string value, int n, string context) {
|
||||
private predicate occurenceCount(Literal lit, string value, int n, string context) {
|
||||
valueOccurrenceCount(value, n, context) and
|
||||
value = lit.getLiteral() and
|
||||
nonTrivialValue(_, lit, context)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Literals repeated frequently
|
||||
*
|
||||
*/
|
||||
|
||||
private
|
||||
predicate check(Literal lit, string value, int n, string context, CompilationUnit f) {
|
||||
private predicate check(Literal lit, string value, int n, string context, CompilationUnit f) {
|
||||
// Check that the literal is nontrivial
|
||||
not trivial(lit) and
|
||||
// Check that it is repeated a number of times
|
||||
occurenceCount(lit, value, n, context) and n > 20 and
|
||||
occurenceCount(lit, value, n, context) and
|
||||
n > 20 and
|
||||
f = lit.getCompilationUnit()
|
||||
}
|
||||
|
||||
private
|
||||
predicate checkWithFileCount(string value, int overallCount, int fileCount, string context, CompilationUnit f) {
|
||||
private predicate checkWithFileCount(
|
||||
string value, int overallCount, int fileCount, string context, CompilationUnit f
|
||||
) {
|
||||
fileCount = strictcount(Literal lit | check(lit, value, overallCount, context, f))
|
||||
}
|
||||
|
||||
private
|
||||
predicate firstOccurrence(Literal lit, string value, string context, int n) {
|
||||
private predicate firstOccurrence(Literal lit, string value, string context, int n) {
|
||||
exists(CompilationUnit f, int fileCount |
|
||||
checkWithFileCount(value, n, fileCount, context, f) and
|
||||
fileCount < 100 and
|
||||
|
@ -186,7 +291,8 @@ predicate firstOccurrence(Literal lit, string value, string context, int n) {
|
|||
check(lit2, value, n, context, f) and
|
||||
lit.getLocation().getStartLine() = start1 and
|
||||
lit2.getLocation().getStartLine() = start2 and
|
||||
start2 < start1)
|
||||
start2 < start1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -202,38 +308,38 @@ predicate isNumber(Literal lit) {
|
|||
predicate magicConstant(Literal e, string msg) {
|
||||
exists(string value, int n, string context |
|
||||
firstOccurrence(e, value, context, n) and
|
||||
msg = "Magic constant: literal '" + value + "' is used " + n.toString() + " times in calls to " + context
|
||||
msg = "Magic constant: literal '" + value + "' is used " + n.toString() + " times in calls to " +
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Literals where there is a defined constant with the same value
|
||||
*
|
||||
*/
|
||||
|
||||
private
|
||||
predicate relevantField(Field f, string value) {
|
||||
exists(Literal lit | not trivial(lit) and value = lit.getLiteral() and literalIsConstantInitializer(lit, f))
|
||||
private predicate relevantField(Field f, string value) {
|
||||
exists(Literal lit |
|
||||
not trivial(lit) and value = lit.getLiteral() and literalIsConstantInitializer(lit, f)
|
||||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate relevantType(RefType t, string value, Package p) {
|
||||
private predicate relevantType(RefType t, string value, Package p) {
|
||||
exists(Literal lit | nonTrivialValue(value, lit, _) |
|
||||
lit.getEnclosingCallable().getDeclaringType() = t and p = t.getPackage()
|
||||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate fieldUsedInContext(Field constField, string context) {
|
||||
private predicate fieldUsedInContext(Field constField, string context) {
|
||||
literalIsConstantInitializer(_, constField) and
|
||||
exists(MethodAccess ma |
|
||||
constField.getAnAccess() = ma.getAnArgument() and
|
||||
ma.getMethod().getName() = context)
|
||||
ma.getMethod().getName() = context
|
||||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate candidateConstantForLiteral(Field constField, RefType literalType, Literal magicLiteral, string context) {
|
||||
private predicate candidateConstantForLiteral(
|
||||
Field constField, RefType literalType, Literal magicLiteral, string context
|
||||
) {
|
||||
exists(Literal initLiteral |
|
||||
literalIsConstantInitializer(initLiteral, constField) and
|
||||
exists(string value |
|
||||
|
@ -245,14 +351,15 @@ predicate candidateConstantForLiteral(Field constField, RefType literalType, Lit
|
|||
)
|
||||
}
|
||||
|
||||
private
|
||||
RefType inheritsProtected(Field f) {
|
||||
(f.isProtected() and result.getASupertype() = f.getDeclaringType()) or
|
||||
private RefType inheritsProtected(Field f) {
|
||||
(f.isProtected() and result.getASupertype() = f.getDeclaringType())
|
||||
or
|
||||
exists(RefType mid | mid = inheritsProtected(f) and result.getASupertype() = mid)
|
||||
}
|
||||
|
||||
private
|
||||
predicate constantForLiteral(Field field, string value, RefType fromType, Literal magicLiteral, string context) {
|
||||
private predicate constantForLiteral(
|
||||
Field field, string value, RefType fromType, Literal magicLiteral, string context
|
||||
) {
|
||||
//public fields in public classes
|
||||
(
|
||||
candidateConstantForLiteral(field, fromType, magicLiteral, context) and
|
||||
|
@ -291,24 +398,22 @@ predicate constantForLiteral(Field field, string value, RefType fromType, Litera
|
|||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate canUseFieldInsteadOfLiteral(Field constField, Literal magicLiteral, string context) {
|
||||
private predicate canUseFieldInsteadOfLiteral(Field constField, Literal magicLiteral, string context) {
|
||||
constantForLiteral(constField, _, _, magicLiteral, context)
|
||||
}
|
||||
|
||||
predicate literalInsteadOfConstant(Literal magicLiteral, string message, Field constField, string linkText) {
|
||||
predicate literalInsteadOfConstant(
|
||||
Literal magicLiteral, string message, Field constField, string linkText
|
||||
) {
|
||||
exists(string context |
|
||||
canUseFieldInsteadOfLiteral(constField, magicLiteral, context) and
|
||||
message =
|
||||
"Literal value '" + magicLiteral.getLiteral() + "' used " +
|
||||
" in a call to " + context +
|
||||
"; consider using the defined constant $@." and linkText = constField.getName()
|
||||
and
|
||||
message = "Literal value '" + magicLiteral.getLiteral() + "' used " + " in a call to " + context
|
||||
+ "; consider using the defined constant $@." and
|
||||
linkText = constField.getName() and
|
||||
(
|
||||
constField.getCompilationUnit() = magicLiteral.getCompilationUnit() or
|
||||
not almostPrivate(constField)
|
||||
)
|
||||
and
|
||||
) and
|
||||
(
|
||||
constField.getCompilationUnit().getPackage() = magicLiteral.getCompilationUnit().getPackage() or
|
||||
not almostPackageProtected(constField)
|
||||
|
@ -317,7 +422,9 @@ predicate literalInsteadOfConstant(Literal magicLiteral, string message, Field c
|
|||
}
|
||||
|
||||
private predicate almostPrivate(Field f) {
|
||||
not exists(VarAccess va | va.getVariable() = f and va.getCompilationUnit() != f.getCompilationUnit())
|
||||
not exists(VarAccess va |
|
||||
va.getVariable() = f and va.getCompilationUnit() != f.getCompilationUnit()
|
||||
)
|
||||
or
|
||||
exists(Interface i | i = f.getDeclaringType() |
|
||||
forall(VarAccess va | va.getVariable() = f |
|
||||
|
@ -333,13 +440,10 @@ private predicate almostPackageProtected(Field f) {
|
|||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Removing literals from uninteresting contexts
|
||||
*
|
||||
*/
|
||||
|
||||
private
|
||||
predicate excludedLiteral(Literal lit) {
|
||||
private predicate excludedLiteral(Literal lit) {
|
||||
// Remove test cases
|
||||
lit.getEnclosingCallable().getDeclaringType() instanceof TestClass
|
||||
or
|
||||
|
|
|
@ -47,11 +47,12 @@ predicate assignmentToShadowingLocal(LocalVariableDecl d, Field f) {
|
|||
shadows(d, _, _, _) and
|
||||
exists(Expr assignedValue, Expr use |
|
||||
d.getAnAssignedValue() = assignedValue and getARelevantChild(assignedValue) = use
|
||||
|
|
||||
|
|
||||
exists(FieldAccess access, Field ff | access = assignedValue |
|
||||
ff = access.getField() and
|
||||
ff.getSourceDeclaration() = f
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(MethodAccess get, Method getter | get = assignedValue and getter = get.getMethod() |
|
||||
getterFor(getter, f)
|
||||
)
|
||||
|
@ -66,7 +67,8 @@ predicate assignmentFromShadowingLocal(LocalVariableDecl d, Field f) {
|
|||
arg = set.getAnArgument() and
|
||||
setter = set.getMethod() and
|
||||
setterFor(setter, f)
|
||||
) or
|
||||
)
|
||||
or
|
||||
exists(Field instance, Expr assignedValue |
|
||||
access = getARelevantChild(assignedValue) and
|
||||
assignedValue = instance.getAnAssignedValue() and
|
||||
|
@ -75,9 +77,10 @@ predicate assignmentFromShadowingLocal(LocalVariableDecl d, Field f) {
|
|||
)
|
||||
}
|
||||
|
||||
private
|
||||
Expr getARelevantChild(Expr parent) {
|
||||
exists(MethodAccess ma | parent = ma.getAnArgument() and result = parent) or
|
||||
exists(Variable v | parent = v.getAnAccess() and result = parent) or
|
||||
private Expr getARelevantChild(Expr parent) {
|
||||
exists(MethodAccess ma | parent = ma.getAnArgument() and result = parent)
|
||||
or
|
||||
exists(Variable v | parent = v.getAnAccess() and result = parent)
|
||||
or
|
||||
exists(Expr mid | mid = getARelevantChild(parent) and result = mid.getAChildExpr())
|
||||
}
|
||||
|
|
|
@ -5,11 +5,7 @@ import java
|
|||
* top-level children (usually, in fact, there is only one) is
|
||||
* a tag with the name "coverage".
|
||||
*/
|
||||
class CloverReport extends XMLFile {
|
||||
CloverReport() {
|
||||
this.getAChild().getName() = "coverage"
|
||||
}
|
||||
}
|
||||
class CloverReport extends XMLFile { CloverReport() { this.getAChild().getName() = "coverage" } }
|
||||
|
||||
/**
|
||||
* The Clover "coverage" tag contains one or more "projects".
|
||||
|
@ -20,9 +16,7 @@ class CloverCoverage extends XMLElement {
|
|||
this.getName() = "coverage"
|
||||
}
|
||||
|
||||
CloverProject getAProject() {
|
||||
result = this.getAChild()
|
||||
}
|
||||
CloverProject getAProject() { result = this.getAChild() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,9 +25,7 @@ class CloverCoverage extends XMLElement {
|
|||
* all subclasses of this class, to share code.
|
||||
*/
|
||||
abstract class CloverMetricsContainer extends XMLElement {
|
||||
CloverMetrics getMetrics() {
|
||||
result = this.getAChild()
|
||||
}
|
||||
CloverMetrics getMetrics() { result = this.getAChild() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,33 +38,47 @@ class CloverMetrics extends XMLElement {
|
|||
this.getName() = "metrics"
|
||||
}
|
||||
|
||||
private
|
||||
int attr(string name) { result = this.getAttribute(name).getValue().toInt() }
|
||||
private int attr(string name) { result = this.getAttribute(name).getValue().toInt() }
|
||||
|
||||
private
|
||||
float ratio(string name) { result = attr("covered" + name)/(float)attr(name) }
|
||||
private float ratio(string name) { result = attr("covered" + name) / attr(name).(float) }
|
||||
|
||||
int getNumConditionals() { result = attr("conditionals") }
|
||||
|
||||
int getNumCoveredConditionals() { result = attr("coveredconditionals") }
|
||||
|
||||
int getNumStatements() { result = attr("statements") }
|
||||
|
||||
int getNumCoveredStatements() { result = attr("coveredstatements") }
|
||||
|
||||
int getNumElements() { result = attr("elements") }
|
||||
|
||||
int getNumCoveredElements() { result = attr("coveredelements") }
|
||||
|
||||
int getNumMethods() { result = attr("methods") }
|
||||
|
||||
int getNumCoveredMethods() { result = attr("coveredmethods") }
|
||||
|
||||
int getNumLoC() { result = attr("loc") }
|
||||
|
||||
int getNumNonCommentedLoC() { result = attr("ncloc") }
|
||||
|
||||
int getNumPackages() { result = attr("packages") }
|
||||
|
||||
int getNumFiles() { result = attr("files") }
|
||||
|
||||
int getNumClasses() { result = attr("classes") }
|
||||
|
||||
int getCloverComplexity() { result = attr("complexity") }
|
||||
|
||||
float getConditionalCoverage() { result = ratio("conditionals") }
|
||||
|
||||
float getStatementCoverage() { result = ratio("statements") }
|
||||
|
||||
float getElementCoverage() { result = ratio("elements") }
|
||||
|
||||
float getMethodCoverage() { result = ratio("methods") }
|
||||
|
||||
float getNonCommentedLoCRatio() { result = attr("ncloc")/attr("loc")}
|
||||
float getNonCommentedLoCRatio() { result = attr("ncloc") / attr("loc") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,9 +86,7 @@ class CloverMetrics extends XMLElement {
|
|||
* groups together several "package" (or "testpackage") elements.
|
||||
*/
|
||||
class CloverProject extends CloverMetricsContainer {
|
||||
CloverProject() {
|
||||
this.getParent() instanceof CloverCoverage
|
||||
}
|
||||
CloverProject() { this.getParent() instanceof CloverCoverage }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,9 +98,7 @@ class CloverPackage extends CloverMetricsContainer {
|
|||
this.getName() = "package"
|
||||
}
|
||||
|
||||
Package getRealPackage() {
|
||||
result.hasName(getAttribute("name").getValue())
|
||||
}
|
||||
Package getRealPackage() { result.hasName(getAttribute("name").getValue()) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,12 +120,12 @@ class CloverClass extends CloverMetricsContainer {
|
|||
this.getName() = "class"
|
||||
}
|
||||
|
||||
CloverPackage getPackage() {
|
||||
result = ((CloverFile)getParent()).getParent()
|
||||
}
|
||||
CloverPackage getPackage() { result = (getParent().(CloverFile)).getParent() }
|
||||
|
||||
RefType getRealClass() {
|
||||
result.hasQualifiedName(getPackage().getAttribute("name").getValue(), getAttribute("name").getValue())
|
||||
result
|
||||
.hasQualifiedName(getPackage().getAttribute("name").getValue(),
|
||||
getAttribute("name").getValue())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +133,5 @@ class CloverClass extends CloverMetricsContainer {
|
|||
* Get the clover metrics associated with the given class, if any.
|
||||
*/
|
||||
CloverMetrics cloverInfo(RefType t) {
|
||||
exists(CloverClass c | c.getRealClass() = t |
|
||||
result = c.getMetrics()
|
||||
)
|
||||
exists(CloverClass c | c.getRealClass() = t | result = c.getMetrics())
|
||||
}
|
||||
|
|
|
@ -1,22 +1,15 @@
|
|||
import java
|
||||
|
||||
private
|
||||
string relativePath(File file) {
|
||||
result = file.getRelativePath().replaceAll("\\", "/")
|
||||
}
|
||||
private string relativePath(File file) { result = file.getRelativePath().replaceAll("\\", "/") }
|
||||
|
||||
private cached
|
||||
predicate tokenLocation(File file, int sl, int sc, int ec, int el, Copy copy, int index) {
|
||||
cached
|
||||
private predicate tokenLocation(File file, int sl, int sc, int ec, int el, Copy copy, int index) {
|
||||
file = copy.sourceFile() and
|
||||
tokens(copy, index, sl, sc, ec, el)
|
||||
}
|
||||
|
||||
class Copy extends @duplication_or_similarity
|
||||
{
|
||||
private
|
||||
int lastToken() {
|
||||
result = max(int i | tokens(this, i, _, _, _, _) | i)
|
||||
}
|
||||
class Copy extends @duplication_or_similarity {
|
||||
private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) }
|
||||
|
||||
int tokenStartingAt(Location loc) {
|
||||
tokenLocation(loc.getFile(), loc.getStartLine(), loc.getStartColumn(), _, _, this, result)
|
||||
|
@ -26,29 +19,17 @@ class Copy extends @duplication_or_similarity
|
|||
tokenLocation(loc.getFile(), _, _, loc.getEndLine(), loc.getEndColumn(), this, result)
|
||||
}
|
||||
|
||||
int sourceStartLine() {
|
||||
tokens(this, 0, result, _, _, _)
|
||||
}
|
||||
int sourceStartLine() { tokens(this, 0, result, _, _, _) }
|
||||
|
||||
int sourceStartColumn() {
|
||||
tokens(this, 0, _, result, _, _)
|
||||
}
|
||||
int sourceStartColumn() { tokens(this, 0, _, result, _, _) }
|
||||
|
||||
int sourceEndLine() {
|
||||
tokens(this, lastToken(), _, _, result, _)
|
||||
}
|
||||
int sourceEndLine() { tokens(this, lastToken(), _, _, result, _) }
|
||||
|
||||
int sourceEndColumn() {
|
||||
tokens(this, lastToken(), _, _, _, result)
|
||||
}
|
||||
int sourceEndColumn() { tokens(this, lastToken(), _, _, _, result) }
|
||||
|
||||
int sourceLines() {
|
||||
result = this.sourceEndLine() + 1 - this.sourceStartLine()
|
||||
}
|
||||
int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() }
|
||||
|
||||
int getEquivalenceClass() {
|
||||
duplicateCode(this, _, result) or similarCode(this, _, result)
|
||||
}
|
||||
int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) }
|
||||
|
||||
File sourceFile() {
|
||||
exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) |
|
||||
|
@ -56,7 +37,9 @@ class Copy extends @duplication_or_similarity
|
|||
)
|
||||
}
|
||||
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
sourceFile().getAbsolutePath() = filepath and
|
||||
startline = sourceStartLine() and
|
||||
startcolumn = sourceStartColumn() and
|
||||
|
@ -67,30 +50,21 @@ class Copy extends @duplication_or_similarity
|
|||
string toString() { none() }
|
||||
}
|
||||
|
||||
class DuplicateBlock extends Copy, @duplication
|
||||
{
|
||||
string toString() {
|
||||
result = "Duplicate code: " + sourceLines() + " duplicated lines."
|
||||
}
|
||||
class DuplicateBlock extends Copy, @duplication {
|
||||
string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." }
|
||||
}
|
||||
|
||||
class SimilarBlock extends Copy, @similarity
|
||||
{
|
||||
string toString() {
|
||||
result = "Similar code: " + sourceLines() + " almost duplicated lines."
|
||||
}
|
||||
class SimilarBlock extends Copy, @similarity {
|
||||
string toString() { result = "Similar code: " + sourceLines() + " almost duplicated lines." }
|
||||
}
|
||||
|
||||
Method sourceMethod() {
|
||||
hasLocation(result, _) and numlines(result, _, _, _)
|
||||
}
|
||||
Method sourceMethod() { hasLocation(result, _) and numlines(result, _, _, _) }
|
||||
|
||||
int numberOfSourceMethods(Class c) {
|
||||
result = count(Method m | m = sourceMethod() and m.getDeclaringType() = c)
|
||||
}
|
||||
|
||||
private
|
||||
predicate blockCoversStatement(int equivClass, int first, int last, Stmt stmt) {
|
||||
private predicate blockCoversStatement(int equivClass, int first, int last, Stmt stmt) {
|
||||
exists(DuplicateBlock b, Location loc |
|
||||
stmt.getLocation() = loc and
|
||||
first = b.tokenStartingAt(loc) and
|
||||
|
@ -99,20 +73,19 @@ predicate blockCoversStatement(int equivClass, int first, int last, Stmt stmt) {
|
|||
)
|
||||
}
|
||||
|
||||
private
|
||||
Stmt statementInMethod(Method m) {
|
||||
private Stmt statementInMethod(Method m) {
|
||||
result.getEnclosingCallable() = m and
|
||||
not result instanceof Block
|
||||
}
|
||||
|
||||
private
|
||||
predicate duplicateStatement(Method m1, Method m2, Stmt s1, Stmt s2) {
|
||||
private predicate duplicateStatement(Method m1, Method m2, Stmt s1, Stmt s2) {
|
||||
exists(int equivClass, int first, int last |
|
||||
s1 = statementInMethod(m1) and
|
||||
s2 = statementInMethod(m2) and
|
||||
blockCoversStatement(equivClass, first, last, s1) and
|
||||
blockCoversStatement(equivClass, first, last, s2) and
|
||||
s1 != s2 and m1 != m2
|
||||
s1 != s2 and
|
||||
m1 != m2
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -129,34 +102,30 @@ predicate duplicateMethod(Method m, Method other) {
|
|||
}
|
||||
|
||||
predicate similarLines(File f, int line) {
|
||||
exists(SimilarBlock b |
|
||||
b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]
|
||||
)
|
||||
exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()])
|
||||
}
|
||||
|
||||
private
|
||||
predicate similarLinesPerEquivalenceClass(int equivClass, int lines, File f)
|
||||
{
|
||||
private predicate similarLinesPerEquivalenceClass(int equivClass, int lines, File f) {
|
||||
lines = strictsum(SimilarBlock b, int toSum |
|
||||
(b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and (toSum = b.sourceLines()) | toSum)
|
||||
(b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and
|
||||
(toSum = b.sourceLines())
|
||||
|
|
||||
toSum
|
||||
)
|
||||
}
|
||||
|
||||
private pragma[noopt]
|
||||
predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
pragma[noopt]
|
||||
private predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getTotalNumberOfLines() |
|
||||
exists(int coveredApprox |
|
||||
coveredApprox = strictsum(int num |
|
||||
exists(int equivClass |
|
||||
similarLinesPerEquivalenceClass(equivClass, num, f) and
|
||||
similarLinesPerEquivalenceClass(equivClass, num, otherFile) and
|
||||
f != otherFile
|
||||
)
|
||||
) and
|
||||
exists(int n, int product |
|
||||
product = coveredApprox * 100 and n = product / numLines
|
||||
|
|
||||
n > 75
|
||||
)
|
||||
exists(int equivClass |
|
||||
similarLinesPerEquivalenceClass(equivClass, num, f) and
|
||||
similarLinesPerEquivalenceClass(equivClass, num, otherFile) and
|
||||
f != otherFile
|
||||
)
|
||||
) and
|
||||
exists(int n, int product | product = coveredApprox * 100 and n = product / numLines | n > 75)
|
||||
) and
|
||||
exists(int notCovered |
|
||||
notCovered = count(int j | j in [1 .. numLines] and not similarLines(f, j)) and
|
||||
|
@ -171,24 +140,26 @@ predicate duplicateLines(File f, int line) {
|
|||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, File f)
|
||||
{
|
||||
private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, File f) {
|
||||
lines = strictsum(DuplicateBlock b, int toSum |
|
||||
(b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and (toSum = b.sourceLines()) | toSum)
|
||||
(b.sourceFile() = f and b.getEquivalenceClass() = equivClass) and
|
||||
(toSum = b.sourceLines())
|
||||
|
|
||||
toSum
|
||||
)
|
||||
}
|
||||
|
||||
private pragma[noopt]
|
||||
predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
pragma[noopt]
|
||||
private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getTotalNumberOfLines() |
|
||||
exists(int coveredApprox |
|
||||
coveredApprox = strictsum(int num |
|
||||
exists(int equivClass |
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, f) and
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, otherFile) and
|
||||
f != otherFile
|
||||
)
|
||||
) and
|
||||
exists(int equivClass |
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, f) and
|
||||
duplicateLinesPerEquivalenceClass(equivClass, num, otherFile) and
|
||||
f != otherFile
|
||||
)
|
||||
) and
|
||||
exists(int n, int product | product = coveredApprox * 100 and n = product / numLines | n > 75)
|
||||
) and
|
||||
exists(int notCovered |
|
||||
|
@ -220,14 +191,14 @@ predicate duplicateFiles(File f, File other, int percent) {
|
|||
predicate duplicateAnonymousClass(AnonymousClass c, AnonymousClass other) {
|
||||
exists(int numDup |
|
||||
numDup = strictcount(Method m1 |
|
||||
exists(Method m2 |
|
||||
duplicateMethod(m1, m2) and
|
||||
m1 = sourceMethod() and
|
||||
m1.getDeclaringType() = c and
|
||||
m2.getDeclaringType() = other and
|
||||
c != other
|
||||
)
|
||||
) and
|
||||
exists(Method m2 |
|
||||
duplicateMethod(m1, m2) and
|
||||
m1 = sourceMethod() and
|
||||
m1.getDeclaringType() = c and
|
||||
m2.getDeclaringType() = other and
|
||||
c != other
|
||||
)
|
||||
) and
|
||||
numDup = numberOfSourceMethods(c) and
|
||||
numDup = numberOfSourceMethods(other) and
|
||||
forall(Type t | c.getASupertype() = t | t = other.getASupertype())
|
||||
|
@ -237,15 +208,15 @@ predicate duplicateAnonymousClass(AnonymousClass c, AnonymousClass other) {
|
|||
pragma[noopt]
|
||||
predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) {
|
||||
numDup = strictcount(Method m1 |
|
||||
exists(Method m2 |
|
||||
duplicateMethod(m1, m2) and
|
||||
m1 = sourceMethod() and
|
||||
m1.getDeclaringType() = c and
|
||||
m2.getDeclaringType() = other and
|
||||
other instanceof Class and
|
||||
c != other
|
||||
)
|
||||
) and
|
||||
exists(Method m2 |
|
||||
duplicateMethod(m1, m2) and
|
||||
m1 = sourceMethod() and
|
||||
m1.getDeclaringType() = c and
|
||||
m2.getDeclaringType() = other and
|
||||
other instanceof Class and
|
||||
c != other
|
||||
)
|
||||
) and
|
||||
total = numberOfSourceMethods(c) and
|
||||
exists(int n, int product | product = 100 * numDup and n = product / total | n > 80)
|
||||
}
|
||||
|
@ -259,8 +230,11 @@ predicate mostlyDuplicateClass(Class c, Class other, string message) {
|
|||
(
|
||||
total != numDup and
|
||||
exists(string s1, string s2, string s3, string name |
|
||||
s1 = " out of " and s2 = " methods in " and s3 = " are duplicated in $@." and name = c.getName()
|
||||
|
|
||||
s1 = " out of " and
|
||||
s2 = " methods in " and
|
||||
s3 = " are duplicated in $@." and
|
||||
name = c.getName()
|
||||
|
|
||||
message = numDup + s1 + total + s2 + name + s3
|
||||
)
|
||||
)
|
||||
|
@ -269,7 +243,7 @@ predicate mostlyDuplicateClass(Class c, Class other, string message) {
|
|||
total = numDup and
|
||||
exists(string s1, string s2, string name |
|
||||
s1 = "All methods in " and s2 = " are identical in $@." and name = c.getName()
|
||||
|
|
||||
|
|
||||
message = s1 + name + s2
|
||||
)
|
||||
)
|
||||
|
|
|
@ -10,9 +10,10 @@ import java
|
|||
*
|
||||
* For more information, see [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
*/
|
||||
external predicate defectResults(int id, string queryPath,
|
||||
string file, int startline, int startcol, int endline, int endcol,
|
||||
string message);
|
||||
external predicate defectResults(
|
||||
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
|
||||
string message
|
||||
);
|
||||
|
||||
/**
|
||||
* A defect query result stored in a dashboard database.
|
||||
|
@ -25,7 +26,9 @@ class DefectResult extends int {
|
|||
|
||||
/** Gets the file in which this query result was reported. */
|
||||
File getFile() {
|
||||
exists(string path | defectResults(this, _, path, _, _, _, _, _) | result.getAbsolutePath() = path)
|
||||
exists(string path | defectResults(this, _, path, _, _, _, _, _) |
|
||||
result.getAbsolutePath() = path
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the line on which the location of this query result starts. */
|
||||
|
@ -45,7 +48,7 @@ class DefectResult extends int {
|
|||
|
||||
/** Gets the URL corresponding to the location of this query result. */
|
||||
string getURL() {
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" +
|
||||
getStartLine() + ":" + getStartColumn() + ":" + getEndLine() + ":" + getEndColumn()
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn()
|
||||
+ ":" + getEndLine() + ":" + getEndColumn()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import java
|
||||
|
||||
class ExternalData extends @externalDataElement {
|
||||
|
||||
string getDataPath() { externalData(this, result, _, _) }
|
||||
|
||||
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
|
||||
|
@ -16,16 +15,13 @@ class ExternalData extends @externalDataElement {
|
|||
|
||||
date getFieldAsDate(int index) { result = getField(index).toDate() }
|
||||
|
||||
string toString() {
|
||||
result = getQueryPath() + ": " + buildTupleString(0)
|
||||
}
|
||||
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
|
||||
|
||||
private string buildTupleString(int start) {
|
||||
(start = getNumFields() - 1 and result = getField(start))
|
||||
or
|
||||
(start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start+1))
|
||||
(start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,4 +37,3 @@ class DefectExternalData extends ExternalData {
|
|||
|
||||
string getMessage() { result = getField(1) }
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
import java
|
||||
|
||||
external predicate metricResults(int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, float value);
|
||||
external predicate metricResults(
|
||||
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
|
||||
float value
|
||||
);
|
||||
|
||||
class MetricResult extends int {
|
||||
|
||||
MetricResult() { metricResults(this, _, _, _, _, _, _, _) }
|
||||
|
||||
string getQueryPath() { metricResults(this, result, _, _, _, _, _, _) }
|
||||
|
||||
File getFile() { exists(string path | metricResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path) }
|
||||
File getFile() {
|
||||
exists(string path |
|
||||
metricResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
|
||||
)
|
||||
}
|
||||
|
||||
int getStartLine() { metricResults(this, _, _, result, _, _, _, _) }
|
||||
|
||||
|
@ -31,7 +37,7 @@ class MetricResult extends int {
|
|||
float getValue() { metricResults(this, _, _, _, _, _, _, result) }
|
||||
|
||||
string getURL() {
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + getEndLine() + ":" + getEndColumn()
|
||||
result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn()
|
||||
+ ":" + getEndLine() + ":" + getEndColumn()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import java
|
||||
|
||||
class Commit extends @svnentry {
|
||||
|
||||
Commit() {
|
||||
svnaffectedfiles(this, _, _) and
|
||||
exists(date svnDate, date snapshotDate |
|
||||
|
@ -31,30 +30,22 @@ class Commit extends @svnentry {
|
|||
|
||||
string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) }
|
||||
|
||||
File getAnAffectedFile(string action) {
|
||||
svnaffectedfiles(this,result,action)
|
||||
}
|
||||
File getAnAffectedFile(string action) { svnaffectedfiles(this, result, action) }
|
||||
|
||||
File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) }
|
||||
|
||||
predicate isRecent() { recentCommit(this) }
|
||||
|
||||
int daysToNow() {
|
||||
exists(date now | snapshotDate(now) |
|
||||
result = getDate().daysTo(now) and result >= 0
|
||||
)
|
||||
exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0)
|
||||
}
|
||||
|
||||
int getRecentAdditionsForFile(File f) {
|
||||
svnchurn(this, f, result, _)
|
||||
}
|
||||
int getRecentAdditionsForFile(File f) { svnchurn(this, f, result, _) }
|
||||
|
||||
int getRecentDeletionsForFile(File f) {
|
||||
svnchurn(this, f, _, result)
|
||||
}
|
||||
int getRecentDeletionsForFile(File f) { svnchurn(this, f, _, result) }
|
||||
|
||||
int getRecentChurnForFile(File f) {
|
||||
exists(int added, int deleted | svnchurn(this, f, added, deleted) and result = added+deleted)
|
||||
exists(int added, int deleted | svnchurn(this, f, added, deleted) and result = added + deleted)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +62,8 @@ predicate recentCommit(Commit e) {
|
|||
snapshotDate(snapshotDate) and
|
||||
e.getDate() = commitDate and
|
||||
days = commitDate.daysTo(snapshotDate) and
|
||||
days >= 0 and days <= 60
|
||||
days >= 0 and
|
||||
days <= 60
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -80,11 +72,7 @@ date firstChange(File f) {
|
|||
}
|
||||
|
||||
predicate firstCommit(Commit e) {
|
||||
not exists(File f | f = e.getAnAffectedFile() |
|
||||
firstChange(f) < e.getDate()
|
||||
)
|
||||
not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate())
|
||||
}
|
||||
|
||||
predicate artificialChange(Commit e) {
|
||||
firstCommit(e) or e.getChangeSize() >= 50000
|
||||
}
|
||||
predicate artificialChange(Commit e) { firstCommit(e) or e.getChangeSize() >= 50000 }
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import semmle.code.FileSystem
|
||||
import semmle.code.Location
|
||||
|
||||
import semmle.code.java.Annotation
|
||||
import semmle.code.java.CompilationUnit
|
||||
import semmle.code.java.ControlFlowGraph
|
||||
|
@ -26,15 +25,12 @@ import semmle.code.java.Statement
|
|||
import semmle.code.java.Type
|
||||
import semmle.code.java.UnitTests
|
||||
import semmle.code.java.Variable
|
||||
|
||||
import semmle.code.java.controlflow.BasicBlocks
|
||||
|
||||
import semmle.code.java.metrics.MetricCallable
|
||||
import semmle.code.java.metrics.MetricElement
|
||||
import semmle.code.java.metrics.MetricField
|
||||
import semmle.code.java.metrics.MetricPackage
|
||||
import semmle.code.java.metrics.MetricRefType
|
||||
import semmle.code.java.metrics.MetricStmt
|
||||
|
||||
import semmle.code.xml.Ant
|
||||
import semmle.code.xml.XML
|
||||
|
|
|
@ -48,7 +48,7 @@ class Container extends @container, Top {
|
|||
string getRelativePath() {
|
||||
exists(string absPath, string pref |
|
||||
absPath = getAbsolutePath() and sourceLocationPrefix(pref)
|
||||
|
|
||||
|
|
||||
absPath = pref and result = ""
|
||||
or
|
||||
absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
|
||||
|
@ -100,9 +100,7 @@ class Container extends @container, Top {
|
|||
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getExtension() {
|
||||
result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
|
||||
}
|
||||
string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) }
|
||||
|
||||
/**
|
||||
* Gets the stem of this container, that is, the prefix of its base name up to
|
||||
|
@ -121,24 +119,16 @@ class Container extends @container, Top {
|
|||
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getStem() {
|
||||
result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
|
||||
}
|
||||
string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) }
|
||||
|
||||
/** Gets the parent container of this file or folder, if any. */
|
||||
Container getParentContainer() {
|
||||
containerparent(result, this)
|
||||
}
|
||||
Container getParentContainer() { containerparent(result, this) }
|
||||
|
||||
/** Gets a file or sub-folder in this container. */
|
||||
Container getAChildContainer() {
|
||||
this = result.getParentContainer()
|
||||
}
|
||||
Container getAChildContainer() { this = result.getParentContainer() }
|
||||
|
||||
/** Gets a file in this container. */
|
||||
File getAFile() {
|
||||
result = getAChildContainer()
|
||||
}
|
||||
File getAFile() { result = getAChildContainer() }
|
||||
|
||||
/** Gets the file in this container that has the given `baseName`, if any. */
|
||||
File getFile(string baseName) {
|
||||
|
@ -147,9 +137,7 @@ class Container extends @container, Top {
|
|||
}
|
||||
|
||||
/** Gets a sub-folder in this container. */
|
||||
Folder getAFolder() {
|
||||
result = getAChildContainer()
|
||||
}
|
||||
Folder getAFolder() { result = getAChildContainer() }
|
||||
|
||||
/** Gets the sub-folder in this container that has the given `baseName`, if any. */
|
||||
Folder getFolder(string baseName) {
|
||||
|
@ -162,19 +150,14 @@ class Container extends @container, Top {
|
|||
*
|
||||
* This is the absolute path of the container.
|
||||
*/
|
||||
override string toString() {
|
||||
result = getAbsolutePath()
|
||||
}
|
||||
override string toString() { result = getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath()`, `getBaseName()` or `getStem()` instead.
|
||||
*
|
||||
* Gets the name of this container.
|
||||
*/
|
||||
deprecated
|
||||
string getName() {
|
||||
result = getAbsolutePath()
|
||||
}
|
||||
deprecated string getName() { result = getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getBaseName()` or `getStem()` instead.
|
||||
|
@ -184,10 +167,9 @@ class Container extends @container, Top {
|
|||
* For folders, the short name includes the extension (if any), so the short name
|
||||
* of the folder with absolute path `/home/user/.m2` is `.m2`.
|
||||
*/
|
||||
deprecated
|
||||
string getShortName() {
|
||||
folders(this,_,result) or
|
||||
files(this,_,result,_,_)
|
||||
deprecated string getShortName() {
|
||||
folders(this, _, result) or
|
||||
files(this, _, result, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,22 +177,15 @@ class Container extends @container, Top {
|
|||
*
|
||||
* Gets the full name of this container, including its path and extension (if any).
|
||||
*/
|
||||
deprecated
|
||||
string getFullName() {
|
||||
result = getAbsolutePath()
|
||||
}
|
||||
deprecated string getFullName() { result = getAbsolutePath() }
|
||||
}
|
||||
|
||||
/** A folder. */
|
||||
class Folder extends Container, @folder {
|
||||
override string getAbsolutePath() {
|
||||
folders(this, result, _)
|
||||
}
|
||||
override string getAbsolutePath() { folders(this, result, _) }
|
||||
|
||||
/** Gets the URL of this folder. */
|
||||
override string getURL() {
|
||||
result = "folder://" + getAbsolutePath()
|
||||
}
|
||||
override string getURL() { result = "folder://" + getAbsolutePath() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -219,55 +194,42 @@ class Folder extends Container, @folder {
|
|||
* Note that `File` extends `Container` as it may be a `jar` file.
|
||||
*/
|
||||
class File extends Container, @file {
|
||||
override string getAbsolutePath() {
|
||||
files(this, result, _, _, _)
|
||||
}
|
||||
override string getAbsolutePath() { files(this, result, _, _, _) }
|
||||
|
||||
/** Gets the URL of this file. */
|
||||
override string getURL() {
|
||||
result = "file://" + this.getAbsolutePath() + ":0:0:0:0"
|
||||
}
|
||||
override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath()`, `getBaseName()` or `getStem()` instead.
|
||||
*
|
||||
* Holds if this file has the specified `name`.
|
||||
*/
|
||||
deprecated
|
||||
predicate hasName(string name) { name = this.getAbsolutePath() }
|
||||
deprecated predicate hasName(string name) { name = this.getAbsolutePath() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Java archive file with a ".jar" extension.
|
||||
*/
|
||||
class JarFile extends File {
|
||||
JarFile() {
|
||||
getExtension() = "jar"
|
||||
}
|
||||
JarFile() { getExtension() = "jar" }
|
||||
|
||||
/**
|
||||
* Gets the main attribute with the specified `key`
|
||||
* from this JAR file's manifest.
|
||||
*/
|
||||
string getManifestMainAttribute(string key) {
|
||||
jarManifestMain(this, key, result)
|
||||
}
|
||||
string getManifestMainAttribute(string key) { jarManifestMain(this, key, result) }
|
||||
|
||||
/**
|
||||
* Gets the "Specification-Version" main attribute
|
||||
* from this JAR file's manifest.
|
||||
*/
|
||||
string getSpecificationVersion() {
|
||||
result = getManifestMainAttribute("Specification-Version")
|
||||
}
|
||||
string getSpecificationVersion() { result = getManifestMainAttribute("Specification-Version") }
|
||||
|
||||
/**
|
||||
* Gets the "Implementation-Version" main attribute
|
||||
* from this JAR file's manifest.
|
||||
*/
|
||||
string getImplementationVersion() {
|
||||
result = getManifestMainAttribute("Implementation-Version")
|
||||
}
|
||||
string getImplementationVersion() { result = getManifestMainAttribute("Implementation-Version") }
|
||||
|
||||
/**
|
||||
* Gets the per-entry attribute for the specified `entry` and `key`
|
||||
|
|
|
@ -9,25 +9,39 @@ import semmle.code.java.Element
|
|||
|
||||
/** Holds if element `e` has name `name`. */
|
||||
predicate hasName(Element e, string name) {
|
||||
classes(e,name,_,_) or
|
||||
interfaces(e,name,_,_) or
|
||||
primitives(e,name) or
|
||||
constrs(e,name,_,_,_,_) or
|
||||
methods(e,name,_,_,_,_) or
|
||||
fields(e,name,_,_,_) or
|
||||
packages(e,name) or
|
||||
files(e,_,name,_,_) or
|
||||
paramName(e,name) or
|
||||
classes(e, name, _, _)
|
||||
or
|
||||
interfaces(e, name, _, _)
|
||||
or
|
||||
primitives(e, name)
|
||||
or
|
||||
constrs(e, name, _, _, _, _)
|
||||
or
|
||||
methods(e, name, _, _, _, _)
|
||||
or
|
||||
fields(e, name, _, _, _)
|
||||
or
|
||||
packages(e, name)
|
||||
or
|
||||
files(e, _, name, _, _)
|
||||
or
|
||||
paramName(e, name)
|
||||
or
|
||||
exists(int pos |
|
||||
params(e,_,pos,_,_) and
|
||||
not paramName(e,_) and
|
||||
name = "p"+pos
|
||||
) or
|
||||
localvars(e,name,_,_) or
|
||||
typeVars(e,name,_,_,_) or
|
||||
wildcards(e,name,_) or
|
||||
arrays(e,name,_,_,_) or
|
||||
modifiers(e,name)
|
||||
params(e, _, pos, _, _) and
|
||||
not paramName(e, _) and
|
||||
name = "p" + pos
|
||||
)
|
||||
or
|
||||
localvars(e, name, _, _)
|
||||
or
|
||||
typeVars(e, name, _, _, _)
|
||||
or
|
||||
wildcards(e, name, _)
|
||||
or
|
||||
arrays(e, name, _, _, _)
|
||||
or
|
||||
modifiers(e, name)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,35 +59,29 @@ class Top extends @top {
|
|||
* For more information, see
|
||||
* [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
exists(File f, Location l | fixedHasLocation(this, l, f) |
|
||||
locations_default(l,f,startline,startcolumn,endline,endcolumn) and
|
||||
locations_default(l, f, startline, startcolumn, endline, endcolumn) and
|
||||
filepath = f.getAbsolutePath()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the file associated with this element. */
|
||||
File getFile() {
|
||||
fixedHasLocation(this, _, result)
|
||||
}
|
||||
File getFile() { fixedHasLocation(this, _, result) }
|
||||
|
||||
/**
|
||||
* Gets the total number of lines that this element ranges over,
|
||||
* including lines of code, comment and whitespace-only lines.
|
||||
*/
|
||||
int getTotalNumberOfLines() {
|
||||
numlines(this, result, _, _)
|
||||
}
|
||||
int getTotalNumberOfLines() { numlines(this, result, _, _) }
|
||||
|
||||
/** Gets the number of lines of code that this element ranges over. */
|
||||
int getNumberOfLinesOfCode() {
|
||||
numlines(this, _, result, _)
|
||||
}
|
||||
int getNumberOfLinesOfCode() { numlines(this, _, result, _) }
|
||||
|
||||
/** Gets the number of comment lines that this element ranges over. */
|
||||
int getNumberOfCommentLines() {
|
||||
numlines(this, _, _, result)
|
||||
}
|
||||
int getNumberOfCommentLines() { numlines(this, _, _, result) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { hasName(this, result) }
|
||||
|
@ -82,16 +90,16 @@ class Top extends @top {
|
|||
/** A location maps language elements to positions in source files. */
|
||||
class Location extends @location {
|
||||
/** Gets the line number where this location starts. */
|
||||
int getStartLine() { locations_default(this,_,result,_,_,_) }
|
||||
int getStartLine() { locations_default(this, _, result, _, _, _) }
|
||||
|
||||
/** Gets the column number where this location starts. */
|
||||
int getStartColumn() { locations_default(this,_,_,result,_,_) }
|
||||
int getStartColumn() { locations_default(this, _, _, result, _, _) }
|
||||
|
||||
/** Gets the line number where this location ends. */
|
||||
int getEndLine() { locations_default(this,_,_,_,result,_) }
|
||||
int getEndLine() { locations_default(this, _, _, _, result, _) }
|
||||
|
||||
/** Gets the column number where this location ends. */
|
||||
int getEndColumn() { locations_default(this,_,_,_,_,result) }
|
||||
int getEndColumn() { locations_default(this, _, _, _, _, result) }
|
||||
|
||||
/**
|
||||
* Gets the total number of lines that this location ranges over,
|
||||
|
@ -99,37 +107,41 @@ class Location extends @location {
|
|||
*/
|
||||
int getNumberOfLines() {
|
||||
exists(@sourceline s | hasLocation(s, this) |
|
||||
numlines(s,result,_,_) or
|
||||
(not numlines(s,_,_,_) and result = 0)
|
||||
numlines(s, result, _, _)
|
||||
or
|
||||
(not numlines(s, _, _, _) and result = 0)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the number of lines of code that this location ranges over. */
|
||||
int getNumberOfLinesOfCode() {
|
||||
exists(@sourceline s | hasLocation(s, this) |
|
||||
numlines(s,_,result,_) or
|
||||
(not numlines(s,_,_,_) and result = 0)
|
||||
numlines(s, _, result, _)
|
||||
or
|
||||
(not numlines(s, _, _, _) and result = 0)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the number of comment lines that this location ranges over. */
|
||||
int getNumberOfCommentLines() {
|
||||
exists(@sourceline s | hasLocation(s, this) |
|
||||
numlines(s,_,_,result) or
|
||||
(not numlines(s,_,_,_) and result = 0)
|
||||
numlines(s, _, _, result)
|
||||
or
|
||||
(not numlines(s, _, _, _) and result = 0)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the file containing this location. */
|
||||
File getFile() { locations_default(this,result,_,_,_,_) }
|
||||
File getFile() { locations_default(this, result, _, _, _, _) }
|
||||
|
||||
/** Gets a string representation containing the file and range for this location. */
|
||||
string toString() {
|
||||
exists(File f, int startLine, int endLine | locations_default(this,f,startLine,_,endLine,_) |
|
||||
if endLine = startLine then
|
||||
result = f.toString() + ":" + startLine.toString()
|
||||
else
|
||||
result = f.toString() + ":" + startLine.toString() + "-" + endLine.toString()
|
||||
exists(File f, int startLine, int endLine |
|
||||
locations_default(this, f, startLine, _, endLine, _)
|
||||
|
|
||||
if endLine = startLine
|
||||
then result = f.toString() + ":" + startLine.toString()
|
||||
else result = f.toString() + ":" + startLine.toString() + "-" + endLine.toString()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -140,8 +152,10 @@ class Location extends @location {
|
|||
* For more information, see
|
||||
* [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
exists(File f | locations_default(this,f,startline,startcolumn,endline,endcolumn) |
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
exists(File f | locations_default(this, f, startline, startcolumn, endline, endcolumn) |
|
||||
filepath = f.getAbsolutePath()
|
||||
)
|
||||
}
|
||||
|
@ -153,6 +167,7 @@ private predicate hasSourceLocation(Top l, Location loc, File f) {
|
|||
|
||||
cached
|
||||
private predicate fixedHasLocation(Top l, Location loc, File f) {
|
||||
hasSourceLocation(l, loc, f) or
|
||||
hasSourceLocation(l, loc, f)
|
||||
or
|
||||
(hasLocation(l, loc) and not hasSourceLocation(l, _, _) and locations_default(loc, f, _, _, _, _))
|
||||
}
|
||||
|
|
|
@ -19,20 +19,22 @@ class AntTarget extends XMLElement {
|
|||
*/
|
||||
string getDependsString() {
|
||||
result = "," +
|
||||
this.getAttributeValue("depends").replaceAll(" ", "")
|
||||
.replaceAll("\r","").replaceAll("\n","").replaceAll("\t","") + ","
|
||||
this
|
||||
.getAttributeValue("depends")
|
||||
.replaceAll(" ", "")
|
||||
.replaceAll("\r", "")
|
||||
.replaceAll("\n", "")
|
||||
.replaceAll("\t", "") + ","
|
||||
}
|
||||
|
||||
/** Holds if this Ant target depends on the specified target. */
|
||||
predicate dependsOn(AntTarget that) {
|
||||
this.getFile() = that.getFile() and
|
||||
this.getDependsString().matches("%,"+that.getName()+",%")
|
||||
this.getDependsString().matches("%," + that.getName() + ",%")
|
||||
}
|
||||
|
||||
/** Gets an Ant target on which this Ant target depends. */
|
||||
AntTarget getADependency() {
|
||||
this.dependsOn(result)
|
||||
}
|
||||
AntTarget getADependency() { this.dependsOn(result) }
|
||||
}
|
||||
|
||||
/** An Ant target that occurs in an Ant build file with the default name `build.xml`. */
|
||||
|
|
|
@ -20,24 +20,24 @@ private string normalize(string path) {
|
|||
class ProtoPom extends XMLElement {
|
||||
/** Gets a child XML element named "groupId". */
|
||||
Group getGroup() { result = this.getAChild() }
|
||||
|
||||
/** Gets a child XML element named "artifactId". */
|
||||
Artifact getArtifact() { result = this.getAChild() }
|
||||
|
||||
/** Gets a child XML element named "version". */
|
||||
Version getVersion() { result=this.getAChild() }
|
||||
Version getVersion() { result = this.getAChild() }
|
||||
|
||||
/**
|
||||
* Gets a string representing the version, or an empty string if no version
|
||||
* tag was provided.
|
||||
*/
|
||||
string getVersionString() {
|
||||
if exists(getVersion().getValue()) then
|
||||
result = getVersion().getValue()
|
||||
else
|
||||
result = ""
|
||||
if exists(getVersion().getValue()) then result = getVersion().getValue() else result = ""
|
||||
}
|
||||
|
||||
/** Gets a Maven coordinate of the form `groupId:artifactId`. */
|
||||
string getShortCoordinate() {
|
||||
result = this.getGroup().getValue() + ":" + this.getArtifact().getValue()
|
||||
result = this.getGroup().getValue() + ":" + this.getArtifact().getValue()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,34 +51,33 @@ class ProtoPom extends XMLElement {
|
|||
*/
|
||||
class Pom extends ProtoPom {
|
||||
Pom() {
|
||||
this.getName()="project" and
|
||||
this.getName() = "project" and
|
||||
/*
|
||||
* Ignore "dependency-reduced-pom" files - these are generated by the
|
||||
* shading plugin, and duplicate existing pom files.
|
||||
*/
|
||||
|
||||
this.getFile().getStem() != "dependency-reduced-pom"
|
||||
}
|
||||
|
||||
override Group getGroup() {
|
||||
// For a project element, the group may be defined in the parent tags instead
|
||||
if not(exists(super.getGroup())) then
|
||||
exists(Parent p | p = this.getAChild() and result = p.getAChild())
|
||||
else
|
||||
result = super.getGroup()
|
||||
if not (exists(super.getGroup()))
|
||||
then exists(Parent p | p = this.getAChild() and result = p.getAChild())
|
||||
else result = super.getGroup()
|
||||
}
|
||||
|
||||
/** Gets a Maven coordinate of the form `groupId:artifactId:version`. */
|
||||
string getCoordinate() {
|
||||
result = this.getGroup().getValue() + ":" +
|
||||
this.getArtifact().getValue() + ":" +
|
||||
this.getVersion().getValue()
|
||||
result = this.getGroup().getValue() + ":" + this.getArtifact().getValue() + ":" +
|
||||
this.getVersion().getValue()
|
||||
}
|
||||
|
||||
/** Gets a child XML element named "name". */
|
||||
Named getNamed() { result= this.getAChild() }
|
||||
Named getNamed() { result = this.getAChild() }
|
||||
|
||||
/** Gets a child XML element named "dependencies". */
|
||||
Dependencies getDependencies() { result=this.getAChild() }
|
||||
Dependencies getDependencies() { result = this.getAChild() }
|
||||
|
||||
/** Gets a child XML element named `dependencyManagement`. */
|
||||
DependencyManagement getDependencyManagement() { result = getAChild() }
|
||||
|
@ -89,16 +88,15 @@ class Pom extends ProtoPom {
|
|||
/**
|
||||
* Gets a property defined in the `<properties>` section of this POM.
|
||||
*/
|
||||
PomProperty getALocalProperty() {
|
||||
result = getAChild().(PomProperties).getAProperty()
|
||||
}
|
||||
PomProperty getALocalProperty() { result = getAChild().(PomProperties).getAProperty() }
|
||||
|
||||
/**
|
||||
* Gets a property value defined for this project, either in a local `<properties>` section, or
|
||||
* in the `<properties>` section of an ancestor POM.
|
||||
*/
|
||||
PomProperty getAProperty() {
|
||||
result = getALocalProperty() or
|
||||
result = getALocalProperty()
|
||||
or
|
||||
(
|
||||
result = getParentPom().getAProperty() and
|
||||
not getALocalProperty().getName() = result.getName()
|
||||
|
@ -119,7 +117,8 @@ class Pom extends ProtoPom {
|
|||
PomElement getProjectProperty() {
|
||||
(
|
||||
// It must either be a child of the pom, or a child of the parent node of the pom
|
||||
result = getAChild() or
|
||||
result = getAChild()
|
||||
or
|
||||
(
|
||||
result = getParentPom().getAChild() and
|
||||
// The parent project property is not shadowed by a local project property
|
||||
|
@ -135,14 +134,17 @@ class Pom extends ProtoPom {
|
|||
* occurs by considering the properties defined by this project.
|
||||
*/
|
||||
string resolvePlaceholder(string name) {
|
||||
if name.prefix(8) = "project." then
|
||||
if name.prefix(8) = "project."
|
||||
then
|
||||
exists(PomElement p |
|
||||
p = getProjectProperty() and
|
||||
"project." + p.getName() = name and
|
||||
result = p.getValue()
|
||||
)
|
||||
else
|
||||
exists(PomProperty prop | prop = getAProperty() and prop.getName() = name and result = prop.getValue())
|
||||
exists(PomProperty prop |
|
||||
prop = getAProperty() and prop.getName() = name and result = prop.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,35 +159,29 @@ class Pom extends ProtoPom {
|
|||
* Gets a pom dependency that is exported by this pom. An exported dependency is one that
|
||||
* is transitively available, i.e. one with scope compile.
|
||||
*/
|
||||
Pom getAnExportedPom() {
|
||||
result = getAnExportedDependency().getPom()
|
||||
}
|
||||
Pom getAnExportedPom() { result = getAnExportedDependency().getPom() }
|
||||
|
||||
/**
|
||||
* Gets the `<parent>` element of this pom, if any.
|
||||
*/
|
||||
Parent getParentElement() {
|
||||
result = getAChild()
|
||||
}
|
||||
Parent getParentElement() { result = getAChild() }
|
||||
|
||||
/**
|
||||
* Gets the pom referred to by the `<parent>` element of this pom, if any.
|
||||
*/
|
||||
Pom getParentPom() {
|
||||
result = getParentElement().getPom()
|
||||
}
|
||||
Pom getParentPom() { result = getParentElement().getPom() }
|
||||
|
||||
/**
|
||||
* Gets the version specified for dependency `dep` in a `dependencyManagement`
|
||||
* section if this pom or one of its ancestors.
|
||||
*/
|
||||
string getVersionStringForDependency(Dependency dep) {
|
||||
if exists(getDependencyManagement().getDependency(dep)) then
|
||||
result = getDependencyManagement().getDependency(dep).getVersionString()
|
||||
else if exists(getParentPom()) then
|
||||
result = getParentPom().getVersionStringForDependency(dep)
|
||||
if exists(getDependencyManagement().getDependency(dep))
|
||||
then result = getDependencyManagement().getDependency(dep).getVersionString()
|
||||
else
|
||||
result = ""
|
||||
if exists(getParentPom())
|
||||
then result = getParentPom().getVersionStringForDependency(dep)
|
||||
else result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,24 +193,24 @@ class Pom extends ProtoPom {
|
|||
*/
|
||||
Folder getSourceDirectory() {
|
||||
exists(string relativePath |
|
||||
if exists(getProperty("sourceDirectory")) then
|
||||
if exists(getProperty("sourceDirectory"))
|
||||
then
|
||||
// A custom source directory has been specified.
|
||||
relativePath = getProperty("sourceDirectory").getValue()
|
||||
else
|
||||
// The Maven default source directory.
|
||||
relativePath = "src"
|
||||
|
|
||||
|
|
||||
// Resolve the relative path against the base directory for this POM
|
||||
result.getAbsolutePath() = normalize(getFile().getParentContainer().getAbsolutePath() + "/" + relativePath)
|
||||
result.getAbsolutePath() = normalize(getFile().getParentContainer().getAbsolutePath() + "/" +
|
||||
relativePath)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `RefType` contained in the source directory.
|
||||
*/
|
||||
RefType getASourceRefType() {
|
||||
result.getFile().getParentContainer*() = getSourceDirectory()
|
||||
}
|
||||
RefType getASourceRefType() { result.getFile().getParentContainer*() = getSourceDirectory() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,45 +220,37 @@ class Pom extends ProtoPom {
|
|||
* via inherited methods from the super-class.
|
||||
*/
|
||||
class Dependency extends ProtoPom {
|
||||
Dependency() {this.getName()="dependency" }
|
||||
Dependency() { this.getName() = "dependency" }
|
||||
|
||||
/**
|
||||
* Gets an XML element with the same Maven short coordinate
|
||||
* (of the form `groupId:artifactId`) as this element.
|
||||
*/
|
||||
Pom getPom() {
|
||||
result.getShortCoordinate() = this.getShortCoordinate()
|
||||
}
|
||||
Pom getPom() { result.getShortCoordinate() = this.getShortCoordinate() }
|
||||
|
||||
/**
|
||||
* Gets the jar file that we think maven resolved this dependency to (if any).
|
||||
*/
|
||||
File getJar() {
|
||||
exists(MavenRepo mr |
|
||||
result = mr.getAnArtifact(this)
|
||||
)
|
||||
}
|
||||
File getJar() { exists(MavenRepo mr | result = mr.getAnArtifact(this)) }
|
||||
|
||||
/**
|
||||
* Gets the scope of this dependency. If the scope tag is present, this will
|
||||
* be the string contents of that tag, otherwise it defaults to "compile".
|
||||
*/
|
||||
string getScope() {
|
||||
if exists(getAChild().(Scope)) then
|
||||
exists(Scope s | s = getAChild() and result = s.getValue())
|
||||
else
|
||||
result = "compile"
|
||||
if exists(getAChild().(Scope))
|
||||
then exists(Scope s | s = getAChild() and result = s.getValue())
|
||||
else result = "compile"
|
||||
}
|
||||
|
||||
override string getVersionString() {
|
||||
if exists(getVersion()) then
|
||||
result = super.getVersionString()
|
||||
else if exists(Pom p | this = p.getADependency()) then
|
||||
exists(Pom p | this = p.getADependency() |
|
||||
result = p.getVersionStringForDependency(this)
|
||||
)
|
||||
if exists(getVersion())
|
||||
then result = super.getVersionString()
|
||||
else
|
||||
result = ""
|
||||
if exists(Pom p | this = p.getADependency())
|
||||
then
|
||||
exists(Pom p | this = p.getADependency() | result = p.getVersionStringForDependency(this))
|
||||
else result = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,16 +265,19 @@ class PomDependency extends Dependency {
|
|||
* management section, where they do not directly contribute to the dependencies of the containing
|
||||
* pom.
|
||||
*/
|
||||
|
||||
source.getADependency() = this and
|
||||
/*
|
||||
* Consider dependencies that can be used at compile time.
|
||||
*/
|
||||
|
||||
(
|
||||
getScope() = "compile" or
|
||||
/*
|
||||
* Provided dependencies are like compile time dependencies except (a) they are not packaged
|
||||
* when creating the jar and (b) they are not transitive.
|
||||
*/
|
||||
|
||||
getScope() = "provided"
|
||||
// We ignore "test" dependencies because they can be runtime or compile time dependencies
|
||||
)
|
||||
|
@ -305,59 +296,50 @@ class PomElement extends XMLElement {
|
|||
string getValue() {
|
||||
exists(string s |
|
||||
s = allCharactersString() and
|
||||
if s.matches("${%") then
|
||||
if s.matches("${%")
|
||||
then
|
||||
// Resolve the placeholder in the parent pom
|
||||
result = getParent*().(Pom).resolvePlaceholder(s.substring(2, s.length() - 1))
|
||||
else
|
||||
result = s
|
||||
else result = s
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An XML element named "groupId", as found in Maven POM XML files. */
|
||||
class Group extends PomElement {
|
||||
Group() {this.getName()="groupId"}
|
||||
}
|
||||
class Group extends PomElement { Group() { this.getName() = "groupId" } }
|
||||
|
||||
/** An XML element named "artifactId", as found in Maven POM XML files. */
|
||||
class Artifact extends PomElement {
|
||||
Artifact() {this.getName()="artifactId"}
|
||||
}
|
||||
class Artifact extends PomElement { Artifact() { this.getName() = "artifactId" } }
|
||||
|
||||
/** An XML element named "parent", as found in Maven POM XML files. */
|
||||
class Parent extends ProtoPom {
|
||||
Parent() {this.getName()="parent"}
|
||||
Parent() { this.getName() = "parent" }
|
||||
|
||||
Pom getPom() {
|
||||
result.getShortCoordinate() = this.getShortCoordinate()
|
||||
}
|
||||
Pom getPom() { result.getShortCoordinate() = this.getShortCoordinate() }
|
||||
}
|
||||
|
||||
/** An XML element named "version", as found in Maven POM XML files. */
|
||||
class Version extends PomElement {
|
||||
Version() {this.getName()="version"}
|
||||
}
|
||||
class Version extends PomElement { Version() { this.getName() = "version" } }
|
||||
|
||||
/** An XML element named "name", as found in Maven POM XML files. */
|
||||
class Named extends PomElement {
|
||||
Named() {this.getName()="name"}
|
||||
}
|
||||
class Named extends PomElement { Named() { this.getName() = "name" } }
|
||||
|
||||
/** An XML element named "scope", as found in Maven POM XML files. */
|
||||
class Scope extends PomElement {
|
||||
Scope() {this.getName()="scope"}
|
||||
}
|
||||
class Scope extends PomElement { Scope() { this.getName() = "scope" } }
|
||||
|
||||
/** An XML element named "dependencies", as found in Maven POM XML files. */
|
||||
class Dependencies extends PomElement {
|
||||
Dependencies() {this.getName()="dependencies"}
|
||||
Dependency getADependency() {result=this.getAChild()}
|
||||
Dependencies() { this.getName() = "dependencies" }
|
||||
|
||||
Dependency getADependency() { result = this.getAChild() }
|
||||
}
|
||||
|
||||
/** An XML element named `dependencyManagement`, as found in Maven POM XML files. */
|
||||
class DependencyManagement extends PomElement {
|
||||
DependencyManagement() { getName() = "dependencyManagement" }
|
||||
|
||||
Dependencies getDependencies() { result = getAChild() }
|
||||
|
||||
Dependency getADependency() { result = getDependencies().getADependency() }
|
||||
|
||||
/**
|
||||
|
@ -374,35 +356,28 @@ class DependencyManagement extends PomElement {
|
|||
* An XML element name "properties", as found in Maven POM XML files.
|
||||
*/
|
||||
class PomProperties extends PomElement {
|
||||
PomProperties() {this.getName()="properties"}
|
||||
PomProperty getAProperty() {result=this.getAChild()}
|
||||
PomProperties() { this.getName() = "properties" }
|
||||
|
||||
PomProperty getAProperty() { result = this.getAChild() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An XML element that is the child of a PomProperties element, as found in Maven POM XML files.
|
||||
* Represents a single property.
|
||||
*/
|
||||
class PomProperty extends PomElement {
|
||||
PomProperty() {
|
||||
getParent() instanceof PomProperties
|
||||
}
|
||||
}
|
||||
class PomProperty extends PomElement { PomProperty() { getParent() instanceof PomProperties } }
|
||||
|
||||
/**
|
||||
* A folder that represents a maven local repository using the standard layout. Any folder called
|
||||
* "repository" with a parent name ".m2" is considered to be a maven repository.
|
||||
*/
|
||||
class MavenRepo extends Folder {
|
||||
MavenRepo() {
|
||||
getBaseName() = "repository" and getParentContainer().getBaseName() = ".m2"
|
||||
}
|
||||
MavenRepo() { getBaseName() = "repository" and getParentContainer().getBaseName() = ".m2" }
|
||||
|
||||
/**
|
||||
* Gets a Jar file contained within this repository.
|
||||
*/
|
||||
File getAJarFile() {
|
||||
result = getAChildContainer*().(File) and result.getExtension() = "jar"
|
||||
}
|
||||
File getAJarFile() { result = getAChildContainer*().(File) and result.getExtension() = "jar" }
|
||||
|
||||
/**
|
||||
* Gets any jar artifacts in this repository that match the pom project definition. This is an
|
||||
|
@ -412,11 +387,11 @@ class MavenRepo extends Folder {
|
|||
*/
|
||||
MavenRepoJar getAnArtifact(ProtoPom pom) {
|
||||
result = getAJarFile() and
|
||||
if exists(MavenRepoJar mrj | mrj.preciseMatch(pom)) or versionHardMatch(pom) then
|
||||
if exists(MavenRepoJar mrj | mrj.preciseMatch(pom)) or versionHardMatch(pom)
|
||||
then
|
||||
// Either a hard match qualifier, or soft and there is at least one precise match
|
||||
result.preciseMatch(pom)
|
||||
else
|
||||
result.artefactMatches(pom)
|
||||
else result.artefactMatches(pom)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,29 +408,27 @@ private predicate versionHardMatch(ProtoPom pom) {
|
|||
* See: https://cwiki.apache.org/confluence/display/MAVENOLD/Repository+Layout+-+Final
|
||||
*/
|
||||
class MavenRepoJar extends File {
|
||||
MavenRepoJar() {
|
||||
exists(MavenRepo mr | mr.getAJarFile() = this)
|
||||
}
|
||||
MavenRepoJar() { exists(MavenRepo mr | mr.getAJarFile() = this) }
|
||||
|
||||
string getGroupID() {
|
||||
exists(MavenRepo mr |
|
||||
mr.getAJarFile() = this
|
||||
|
|
||||
exists(MavenRepo mr | mr.getAJarFile() = this |
|
||||
/*
|
||||
* Assuming the standard layout, the first part of the directory structure from the maven
|
||||
* repository will be the groupId converted to a path by replacing "." with "/".
|
||||
*/
|
||||
result = getParentContainer().getParentContainer().getParentContainer().getAbsolutePath().suffix(mr.getAbsolutePath().length() + 1).replaceAll("/", ".")
|
||||
|
||||
result = getParentContainer()
|
||||
.getParentContainer()
|
||||
.getParentContainer()
|
||||
.getAbsolutePath()
|
||||
.suffix(mr.getAbsolutePath().length() + 1)
|
||||
.replaceAll("/", ".")
|
||||
)
|
||||
}
|
||||
|
||||
string getArtefactID() {
|
||||
result = getParentContainer().getParentContainer().getBaseName()
|
||||
}
|
||||
string getArtefactID() { result = getParentContainer().getParentContainer().getBaseName() }
|
||||
|
||||
string getVersion() {
|
||||
result = getParentContainer().getBaseName()
|
||||
}
|
||||
string getVersion() { result = getParentContainer().getBaseName() }
|
||||
|
||||
/**
|
||||
* Holds if this jar is an artefact for the given pom or dependency, regardless of which version it is.
|
||||
|
@ -471,9 +444,8 @@ class MavenRepoJar extends File {
|
|||
*/
|
||||
predicate preciseMatch(ProtoPom pom) {
|
||||
artefactMatches(pom) and
|
||||
if versionHardMatch(pom) then
|
||||
("[" + getVersion() + "]").matches(pom.getVersionString() + "%")
|
||||
else
|
||||
getVersion().matches(pom.getVersionString() + "%")
|
||||
if versionHardMatch(pom)
|
||||
then ("[" + getVersion() + "]").matches(pom.getVersionString() + "%")
|
||||
else getVersion().matches(pom.getVersionString() + "%")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,7 @@ import java
|
|||
/**
|
||||
* Holds if any `web.xml` files are included in this snapshot.
|
||||
*/
|
||||
predicate isWebXMLIncluded() {
|
||||
exists(WebXMLFile webXML)
|
||||
}
|
||||
predicate isWebXMLIncluded() { exists(WebXMLFile webXML) }
|
||||
|
||||
/**
|
||||
* A deployment descriptor file, typically called `web.xml`.
|
||||
|
@ -34,67 +32,49 @@ class WebXMLFile extends XMLFile {
|
|||
* An XML element in a `WebXMLFile`.
|
||||
*/
|
||||
class WebXMLElement extends XMLElement {
|
||||
WebXMLElement() {
|
||||
this.getFile() instanceof WebXMLFile
|
||||
}
|
||||
WebXMLElement() { this.getFile() instanceof WebXMLFile }
|
||||
|
||||
/**
|
||||
* Gets the value for this element, with leading and trailing whitespace trimmed.
|
||||
*/
|
||||
string getValue() {
|
||||
result = allCharactersString().trim()
|
||||
}
|
||||
string getValue() { result = allCharactersString().trim() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<context-param>` element in a `web.xml` file.
|
||||
*/
|
||||
class WebContextParameter extends WebXMLElement {
|
||||
WebContextParameter() {
|
||||
this.getName() = "context-param"
|
||||
}
|
||||
WebContextParameter() { this.getName() = "context-param" }
|
||||
|
||||
/**
|
||||
* Gets the `<param-name>` element of this `<context-param>`.
|
||||
*/
|
||||
WebContextParamName getParamName() {
|
||||
result = getAChild()
|
||||
}
|
||||
WebContextParamName getParamName() { result = getAChild() }
|
||||
|
||||
/**
|
||||
* Gets the `<param-value>` element of this `<context-param>`.
|
||||
*/
|
||||
WebContextParamValue getParamValue() {
|
||||
result = getAChild()
|
||||
}
|
||||
WebContextParamValue getParamValue() { result = getAChild() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<param-name>` element in a `web.xml` file.
|
||||
*/
|
||||
class WebContextParamName extends WebXMLElement {
|
||||
WebContextParamName() {
|
||||
getName() = "param-name"
|
||||
}
|
||||
WebContextParamName() { getName() = "param-name" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<param-value>` element in a `web.xml` file.
|
||||
*/
|
||||
class WebContextParamValue extends WebXMLElement {
|
||||
WebContextParamValue() {
|
||||
getName() = "param-value"
|
||||
}
|
||||
WebContextParamValue() { getName() = "param-value" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<filter>` element in a `web.xml` file.
|
||||
*/
|
||||
class WebFilter extends WebXMLElement {
|
||||
WebFilter() {
|
||||
getName() = "filter"
|
||||
}
|
||||
}
|
||||
class WebFilter extends WebXMLElement { WebFilter() { getName() = "filter" } }
|
||||
|
||||
/**
|
||||
* A `<filter-class>` element in a `web.xml` file, nested under a `<filter>` element.
|
||||
|
@ -105,19 +85,13 @@ class WebFilterClass extends WebXMLElement {
|
|||
getParent() instanceof WebFilter
|
||||
}
|
||||
|
||||
Class getClass() {
|
||||
result.getQualifiedName() = getValue()
|
||||
}
|
||||
Class getClass() { result.getQualifiedName() = getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<servlet>` element in a `web.xml` file.
|
||||
*/
|
||||
class WebServlet extends WebXMLElement {
|
||||
WebServlet() {
|
||||
getName() = "servlet"
|
||||
}
|
||||
}
|
||||
class WebServlet extends WebXMLElement { WebServlet() { getName() = "servlet" } }
|
||||
|
||||
/**
|
||||
* A `<servlet-class>` element in a `web.xml` file, nested under a `<servlet>` element.
|
||||
|
@ -128,19 +102,13 @@ class WebServletClass extends WebXMLElement {
|
|||
getParent() instanceof WebServlet
|
||||
}
|
||||
|
||||
Class getClass() {
|
||||
result.getQualifiedName() = getValue()
|
||||
}
|
||||
Class getClass() { result.getQualifiedName() = getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `<listener>` element in a `web.xml` file.
|
||||
*/
|
||||
class WebListener extends WebXMLElement {
|
||||
WebListener() {
|
||||
getName() = "listener"
|
||||
}
|
||||
}
|
||||
class WebListener extends WebXMLElement { WebListener() { getName() = "listener" } }
|
||||
|
||||
/**
|
||||
* A `<listener-class>` element in a `web.xml` file, nested under a `<listener>` element.
|
||||
|
@ -154,7 +122,5 @@ class WebListenerClass extends WebXMLElement {
|
|||
/**
|
||||
* Gets the `Class` instance associated with this element.
|
||||
*/
|
||||
Class getClass() {
|
||||
result.getQualifiedName() = getValue()
|
||||
}
|
||||
Class getClass() { result.getQualifiedName() = getValue() }
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import semmle.code.Location
|
|||
/** An XML element that has a location. */
|
||||
abstract class XMLLocatable extends @xmllocatable {
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { xmllocations(this,result) }
|
||||
Location getLocation() { xmllocations(this, result) }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
|
@ -16,9 +16,11 @@ abstract class XMLLocatable extends @xmllocatable {
|
|||
* For more information, see
|
||||
* [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
exists(File f, Location l | l = this.getLocation() |
|
||||
locations_default(l,f,startline,startcolumn,endline,endcolumn) and
|
||||
locations_default(l, f, startline, startcolumn, endline, endcolumn) and
|
||||
filepath = f.getAbsolutePath()
|
||||
)
|
||||
}
|
||||
|
@ -39,35 +41,31 @@ class XMLParent extends @xmlparent {
|
|||
abstract string getName();
|
||||
|
||||
/** Gets the file to which this XML parent belongs. */
|
||||
XMLFile getFile() { result = this or xmlElements(this,_,_,_,result) }
|
||||
XMLFile getFile() { result = this or xmlElements(this, _, _, _, result) }
|
||||
|
||||
/** Gets the child element at a specified index of this XML parent. */
|
||||
XMLElement getChild(int index) { xmlElements(result, _, this, index, _) }
|
||||
|
||||
/** Gets a child element of this XML parent. */
|
||||
XMLElement getAChild() { xmlElements(result,_,this,_,_) }
|
||||
XMLElement getAChild() { xmlElements(result, _, this, _, _) }
|
||||
|
||||
/** Gets a child element of this XML parent with the given `name`. */
|
||||
XMLElement getAChild(string name) { xmlElements(result,_,this,_,_) and result.hasName(name) }
|
||||
XMLElement getAChild(string name) { xmlElements(result, _, this, _, _) and result.hasName(name) }
|
||||
|
||||
/** Gets a comment that is a child of this XML parent. */
|
||||
XMLComment getAComment() { xmlComments(result,_,this,_) }
|
||||
XMLComment getAComment() { xmlComments(result, _, this, _) }
|
||||
|
||||
/** Gets a character sequence that is a child of this XML parent. */
|
||||
XMLCharacters getACharactersSet() { xmlChars(result,_,this,_,_,_) }
|
||||
XMLCharacters getACharactersSet() { xmlChars(result, _, this, _, _, _) }
|
||||
|
||||
/** Gets the depth in the tree. (Overridden in XMLElement.) */
|
||||
int getDepth() { result = 0 }
|
||||
|
||||
/** Gets the number of child XML elements of this XML parent. */
|
||||
int getNumberOfChildren() {
|
||||
result = count(XMLElement e | xmlElements(e,_,this,_,_))
|
||||
}
|
||||
int getNumberOfChildren() { result = count(XMLElement e | xmlElements(e, _, this, _, _)) }
|
||||
|
||||
/** Gets the number of places in the body of this XML parent where text occurs. */
|
||||
int getNumberOfCharacterSets() {
|
||||
result = count(int pos | xmlChars(_,_,this,pos,_,_))
|
||||
}
|
||||
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Internal.
|
||||
|
@ -75,25 +73,28 @@ class XMLParent extends @xmlparent {
|
|||
* Append the character sequences of this XML parent from left to right, separated by a space,
|
||||
* up to a specified (zero-based) index.
|
||||
*/
|
||||
deprecated
|
||||
string charsSetUpTo(int n) {
|
||||
n = 0 and xmlChars(_,result,this,0,_,_)
|
||||
deprecated string charsSetUpTo(int n) {
|
||||
n = 0 and xmlChars(_, result, this, 0, _, _)
|
||||
or
|
||||
n > 0 and
|
||||
exists(string chars | xmlChars(_,chars,this,n,_,_) |
|
||||
result = this.charsSetUpTo(n-1) + " " + chars
|
||||
exists(string chars | xmlChars(_, chars, this, n, _, _) |
|
||||
result = this.charsSetUpTo(n - 1) + " " + chars
|
||||
)
|
||||
}
|
||||
|
||||
/** Append all the character sequences of this XML parent from left to right, separated by a space. */
|
||||
string allCharactersString() {
|
||||
result = concat(string chars, int pos | xmlChars(_, chars, this, pos, _, _) | chars, " " order by pos)
|
||||
result = concat(string chars, int pos |
|
||||
xmlChars(_, chars, this, pos, _, _)
|
||||
|
|
||||
chars, " "
|
||||
order by
|
||||
pos
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the text value contained in this XML parent. */
|
||||
string getTextValue() {
|
||||
result = allCharactersString()
|
||||
}
|
||||
string getTextValue() { result = allCharactersString() }
|
||||
|
||||
/** Gets a printable representation of this XML parent. */
|
||||
string toString() { result = this.getName() }
|
||||
|
@ -101,57 +102,55 @@ class XMLParent extends @xmlparent {
|
|||
|
||||
/** An XML file. */
|
||||
class XMLFile extends XMLParent, File {
|
||||
XMLFile() {
|
||||
xmlEncoding(this,_)
|
||||
}
|
||||
XMLFile() { xmlEncoding(this, _) }
|
||||
|
||||
/** Gets a printable representation of this XML file. */
|
||||
override
|
||||
string toString() { result = XMLParent.super.toString() }
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
|
||||
/** Gets the name of this XML file. */
|
||||
override
|
||||
string getName() { result = File.super.getAbsolutePath() }
|
||||
override string getName() { result = File.super.getAbsolutePath() }
|
||||
|
||||
/** Gets the encoding of this XML file. */
|
||||
string getEncoding() { xmlEncoding(this,result) }
|
||||
string getEncoding() { xmlEncoding(this, result) }
|
||||
|
||||
/** Gets the XML file itself. */
|
||||
override
|
||||
XMLFile getFile() { result = this }
|
||||
override XMLFile getFile() { result = this }
|
||||
|
||||
/** Gets a top-most element in an XML file. */
|
||||
XMLElement getARootElement() { result = this.getAChild() }
|
||||
|
||||
/** Gets a DTD associated with this XML file. */
|
||||
XMLDTD getADTD() { xmlDTDs(result,_,_,_,this) }
|
||||
XMLDTD getADTD() { xmlDTDs(result, _, _, _, this) }
|
||||
}
|
||||
|
||||
/** A "Document Type Definition" of an XML file. */
|
||||
class XMLDTD extends @xmldtd {
|
||||
/** Gets the name of the root element of this DTD. */
|
||||
string getRoot() { xmlDTDs(this,result,_,_,_) }
|
||||
string getRoot() { xmlDTDs(this, result, _, _, _) }
|
||||
|
||||
/** Gets the public ID of this DTD. */
|
||||
string getPublicId() { xmlDTDs(this,_,result,_,_) }
|
||||
string getPublicId() { xmlDTDs(this, _, result, _, _) }
|
||||
|
||||
/** Gets the system ID of this DTD. */
|
||||
string getSystemId() { xmlDTDs(this,_,_,result,_) }
|
||||
string getSystemId() { xmlDTDs(this, _, _, result, _) }
|
||||
|
||||
/** Holds if this DTD is public. */
|
||||
predicate isPublic() { not xmlDTDs(this,_,"",_,_) }
|
||||
predicate isPublic() { not xmlDTDs(this, _, "", _, _) }
|
||||
|
||||
/** Gets the parent of this DTD. */
|
||||
XMLParent getParent() { xmlDTDs(this,_,_,_,result) }
|
||||
XMLParent getParent() { xmlDTDs(this, _, _, _, result) }
|
||||
|
||||
/** Gets a printable representation of this DTD. */
|
||||
string toString() {
|
||||
(this.isPublic() and result = this.getRoot() + " PUBLIC '" +
|
||||
this.getPublicId() + "' '" +
|
||||
this.getSystemId() + "'") or
|
||||
(not this.isPublic() and result = this.getRoot() +
|
||||
" SYSTEM '" +
|
||||
this.getSystemId() + "'")
|
||||
(
|
||||
this.isPublic() and
|
||||
result = this.getRoot() + " PUBLIC '" + this.getPublicId() + "' '" + this.getSystemId() + "'"
|
||||
)
|
||||
or
|
||||
(
|
||||
not this.isPublic() and
|
||||
result = this.getRoot() + " SYSTEM '" + this.getSystemId() + "'"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,71 +160,61 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
|||
predicate hasName(string name) { name = getName() }
|
||||
|
||||
/** Gets the name of this XML element. */
|
||||
override
|
||||
string getName() { xmlElements(this,result,_,_,_) }
|
||||
override string getName() { xmlElements(this, result, _, _, _) }
|
||||
|
||||
/** Gets the XML file in which this XML element occurs. */
|
||||
override
|
||||
XMLFile getFile() { xmlElements(this,_,_,_,result) }
|
||||
override XMLFile getFile() { xmlElements(this, _, _, _, result) }
|
||||
|
||||
/** Gets the parent of this XML element. */
|
||||
XMLParent getParent() { xmlElements(this,_,result,_,_) }
|
||||
XMLParent getParent() { xmlElements(this, _, result, _, _) }
|
||||
|
||||
/** Gets the index of this XML element among its parent's children. */
|
||||
int getIndex() { xmlElements(this, _, _, result, _) }
|
||||
|
||||
/** Holds if this XML element has a namespace. */
|
||||
predicate hasNamespace() { xmlHasNs(this,_,_) }
|
||||
predicate hasNamespace() { xmlHasNs(this, _, _) }
|
||||
|
||||
/** Gets the namespace of this XML element, if any. */
|
||||
XMLNamespace getNamespace() { xmlHasNs(this,result,_) }
|
||||
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
|
||||
|
||||
/** Gets the index of this XML element among its parent's children. */
|
||||
int getElementPositionIndex() { xmlElements(this,_,_,result,_) }
|
||||
int getElementPositionIndex() { xmlElements(this, _, _, result, _) }
|
||||
|
||||
/** Gets the depth of this element within the XML file tree structure. */
|
||||
override
|
||||
int getDepth() { result = this.getParent().getDepth() + 1 }
|
||||
override int getDepth() { result = this.getParent().getDepth() + 1 }
|
||||
|
||||
/** Gets an XML attribute of this XML element. */
|
||||
XMLAttribute getAnAttribute() { result.getElement() = this }
|
||||
|
||||
/** Gets the attribute with the specified `name`, if any. */
|
||||
XMLAttribute getAttribute(string name) {
|
||||
result.getElement() = this and result.getName() = name
|
||||
}
|
||||
XMLAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
|
||||
|
||||
/** Holds if this XML element has an attribute with the specified `name`. */
|
||||
predicate hasAttribute(string name) {
|
||||
exists(XMLAttribute a| a = this.getAttribute(name))
|
||||
}
|
||||
predicate hasAttribute(string name) { exists(XMLAttribute a | a = this.getAttribute(name)) }
|
||||
|
||||
/** Gets the value of the attribute with the specified `name`, if any. */
|
||||
string getAttributeValue(string name) {
|
||||
result = this.getAttribute(name).getValue()
|
||||
}
|
||||
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||
|
||||
/** Gets a printable representation of this XML element. */
|
||||
override
|
||||
string toString() { result = XMLParent.super.toString() }
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
}
|
||||
|
||||
/** An attribute that occurs inside an XML element. */
|
||||
class XMLAttribute extends @xmlattribute, XMLLocatable {
|
||||
/** Gets the name of this attribute. */
|
||||
string getName() { xmlAttrs(this,_,result,_,_,_) }
|
||||
string getName() { xmlAttrs(this, _, result, _, _, _) }
|
||||
|
||||
/** Gets the XML element to which this attribute belongs. */
|
||||
XMLElement getElement() { xmlAttrs(this,result,_,_,_,_) }
|
||||
XMLElement getElement() { xmlAttrs(this, result, _, _, _, _) }
|
||||
|
||||
/** Holds if this attribute has a namespace. */
|
||||
predicate hasNamespace() { xmlHasNs(this,_,_) }
|
||||
predicate hasNamespace() { xmlHasNs(this, _, _) }
|
||||
|
||||
/** Gets the namespace of this attribute, if any. */
|
||||
XMLNamespace getNamespace() { xmlHasNs(this,result,_) }
|
||||
XMLNamespace getNamespace() { xmlHasNs(this, result, _) }
|
||||
|
||||
/** Gets the value of this attribute. */
|
||||
string getValue() { xmlAttrs(this,_,_,result,_,_) }
|
||||
string getValue() { xmlAttrs(this, _, _, result, _, _) }
|
||||
|
||||
/** Gets a printable representation of this XML attribute. */
|
||||
override string toString() { result = this.getName() + "=" + this.getValue() }
|
||||
|
@ -234,17 +223,18 @@ class XMLAttribute extends @xmlattribute, XMLLocatable {
|
|||
/** A namespace used in an XML file */
|
||||
class XMLNamespace extends @xmlnamespace {
|
||||
/** Gets the prefix of this namespace. */
|
||||
string getPrefix() { xmlNs(this,result,_,_) }
|
||||
string getPrefix() { xmlNs(this, result, _, _) }
|
||||
|
||||
/** Gets the URI of this namespace. */
|
||||
string getURI() { xmlNs(this,_,result,_) }
|
||||
string getURI() { xmlNs(this, _, result, _) }
|
||||
|
||||
/** Holds if this namespace has no prefix. */
|
||||
predicate isDefault() { this.getPrefix() = "" }
|
||||
|
||||
/** Gets a printable representation of this XML namespace. */
|
||||
string toString() {
|
||||
(this.isDefault() and result = this.getURI()) or
|
||||
(this.isDefault() and result = this.getURI())
|
||||
or
|
||||
(not this.isDefault() and result = this.getPrefix() + ":" + this.getURI())
|
||||
}
|
||||
}
|
||||
|
@ -252,10 +242,10 @@ class XMLNamespace extends @xmlnamespace {
|
|||
/** A comment of the form `<!-- ... -->` is an XML comment. */
|
||||
class XMLComment extends @xmlcomment, XMLLocatable {
|
||||
/** Gets the text content of this XML comment. */
|
||||
string getText() { xmlComments(this,result,_,_) }
|
||||
string getText() { xmlComments(this, result, _, _) }
|
||||
|
||||
/** Gets the parent of this XML comment. */
|
||||
XMLParent getParent() { xmlComments(this,_,result,_) }
|
||||
XMLParent getParent() { xmlComments(this, _, result, _) }
|
||||
|
||||
/** Gets a printable representation of this XML comment. */
|
||||
override string toString() { result = this.getText() }
|
||||
|
@ -267,13 +257,13 @@ class XMLComment extends @xmlcomment, XMLLocatable {
|
|||
*/
|
||||
class XMLCharacters extends @xmlcharacters, XMLLocatable {
|
||||
/** Gets the content of this character sequence. */
|
||||
string getCharacters() { xmlChars(this,result,_,_,_,_) }
|
||||
string getCharacters() { xmlChars(this, result, _, _, _, _) }
|
||||
|
||||
/** Gets the parent of this character sequence. */
|
||||
XMLParent getParent() { xmlChars(this,_,result,_,_,_) }
|
||||
XMLParent getParent() { xmlChars(this, _, result, _, _, _) }
|
||||
|
||||
/** Holds if this character sequence is CDATA. */
|
||||
predicate isCDATA() { xmlChars(this,_,_,_,1,_) }
|
||||
predicate isCDATA() { xmlChars(this, _, _, _, 1, _) }
|
||||
|
||||
/** Gets a printable representation of this XML character sequence. */
|
||||
override string toString() { result = this.getCharacters() }
|
||||
|
|
Загрузка…
Ссылка в новой задаче