diff --git a/java/ql/lib/semmle/code/Location.qll b/java/ql/lib/semmle/code/Location.qll index 14fc7a995328..7265164e8e15 100644 --- a/java/ql/lib/semmle/code/Location.qll +++ b/java/ql/lib/semmle/code/Location.qll @@ -8,6 +8,7 @@ module; import FileSystem import semmle.code.java.Element +private import semmle.code.java.Overlay private import semmle.code.SMAP /** Holds if element `e` has name `name`. */ @@ -221,3 +222,16 @@ private predicate fixedHasLocation(Top l, Location loc, File f) { not hasSourceLocation(l, _, _) and locations_default(loc, f, _, _, _, _) } + +overlay[local] +private predicate discardableLocation(string file, @location l) { + not isOverlay() and + file = getRawFileForLoc(l) and + not exists(@file f | hasLocation(f, l)) +} + +/** Discard base locations in files fully extracted in the overlay. */ +overlay[discard_entity] +private predicate discardLocation(@location l) { + exists(string file | discardableLocation(file, l) and extractedInOverlay(file)) +} diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll index 182bf5b7001f..cafffae52bd2 100644 --- a/java/ql/lib/semmle/code/java/Expr.qll +++ b/java/ql/lib/semmle/code/java/Expr.qll @@ -7,6 +7,7 @@ module; import java private import semmle.code.java.frameworks.android.Compose private import semmle.code.java.Constants +private import semmle.code.java.Overlay /** A common super-class that represents all kinds of expressions. */ class Expr extends ExprParent, @expr { @@ -2701,3 +2702,15 @@ class RecordPatternExpr extends Expr, @recordpatternexpr { ) } } + +overlay[local] +private predicate discardableExpr(string file, @expr e) { + not isOverlay() and + file = getRawFile(e) +} + +/** Discard base expressions in files fully extracted in the overlay. */ +overlay[discard_entity] +private predicate discardExpr(@expr e) { + exists(string file | discardableExpr(file, e) and extractedInOverlay(file)) +} diff --git a/java/ql/lib/semmle/code/java/Javadoc.qll b/java/ql/lib/semmle/code/java/Javadoc.qll index ef8f77bf9baf..101bab2723e0 100644 --- a/java/ql/lib/semmle/code/java/Javadoc.qll +++ b/java/ql/lib/semmle/code/java/Javadoc.qll @@ -5,6 +5,7 @@ overlay[local?] module; import semmle.code.Location +private import semmle.code.java.Overlay /** A Javadoc parent is an element whose child can be some Javadoc documentation. */ class JavadocParent extends @javadocParent, Top { @@ -196,3 +197,15 @@ class KtCommentSection extends @ktcommentsection { /** Gets the string representation of this section. */ string toString() { result = this.getContent() } } + +overlay[local] +private predicate discardableJavadoc(string file, @javadoc d) { + not isOverlay() and + exists(@member m | file = getRawFile(m) and hasJavadoc(m, d)) +} + +/** Discard javadoc entities in files fully extracted in the overlay. */ +overlay[discard_entity] +private predicate discardJavadoc(@javadoc d) { + exists(string file | discardableJavadoc(file, d) and extractedInOverlay(file)) +} diff --git a/java/ql/lib/semmle/code/java/Member.qll b/java/ql/lib/semmle/code/java/Member.qll index 662eab06bd1e..805ab0bf9402 100644 --- a/java/ql/lib/semmle/code/java/Member.qll +++ b/java/ql/lib/semmle/code/java/Member.qll @@ -11,6 +11,7 @@ import Annotation import Exception import metrics.MetricField private import dispatch.VirtualDispatch +private import semmle.code.java.Overlay /** * A common abstraction for type member declarations, @@ -623,7 +624,13 @@ class SrcMethod extends Method { then implementsInterfaceMethod(result, this) else result.getASourceOverriddenMethod*() = this ) and - (exists(result.getBody()) or result.hasModifier("native")) + ( + // We allow empty method bodies for the local overlay variant to allow + // calls to methods only fully extracted in base. + isOverlay() or + exists(result.getBody()) or + result.hasModifier("native") + ) } } @@ -897,3 +904,33 @@ class ExtensionMethod extends Method { else result = 0 } } + +overlay[local] +private predicate discardableMethod(string file, @method m) { + not isOverlay() and + file = getRawFile(m) and + exists(@classorinterface c | methods(m, _, _, _, c, _) and isAnonymClass(c, _)) +} + +/** Discard base methods on anonymous classes in files fully extracted in the overlay. */ +overlay[discard_entity] +private predicate discardAnonMethod(@method m) { + exists(string file | discardableMethod(file, m) and extractedInOverlay(file)) +} + +overlay[local] +private predicate discardableBaseMethod(string file, @method m) { + not isOverlay() and + file = getRawFile(m) +} + +overlay[local] +private predicate usedOverlayMethod(@method m) { isOverlay() and methods(m, _, _, _, _, _) } + +/** Discard base methods in files fully extracted in the overlay that were not extracted in the overlay. */ +overlay[discard_entity] +private predicate discardMethod(@method m) { + exists(string file | + discardableBaseMethod(file, m) and extractedInOverlay(file) and not usedOverlayMethod(m) + ) +} diff --git a/java/ql/lib/semmle/code/java/Overlay.qll b/java/ql/lib/semmle/code/java/Overlay.qll new file mode 100644 index 000000000000..713bd9f7623d --- /dev/null +++ b/java/ql/lib/semmle/code/java/Overlay.qll @@ -0,0 +1,39 @@ +overlay[local?] +module; + +import java + +/** + * A local predicate that always holds for the overlay variant and + * never holds for the base variant. This is used to define local + * predicates that behave differently for the base and overlay variant. + */ +overlay[local] +predicate isOverlay() { databaseMetadata("isOverlay", "true") } + +/** Gets the raw file for a locatable. */ +overlay[local] +string getRawFile(@locatable el) { + exists(@location loc, @file file | + hasLocation(el, loc) and + locations_default(loc, file, _, _, _, _) and + files(file, result) + ) +} + +/** Gets the raw file for a location. */ +overlay[local] +string getRawFileForLoc(@location l) { + exists(@file f | locations_default(l, f, _, _, _, _) and files(f, result)) +} + +/** Holds for files fully extracted in the overlay. */ +overlay[local] +predicate extractedInOverlay(string file) { + isOverlay() and + // The incremental Java extractor extracts skeletons without method + // bodies for dependencies. To approximate fully extracted files in + // the overlay, we restrict attention to files that contain an expression + // with an enclosing callable. + exists(@expr e | callableEnclosingExpr(e, _) and file = getRawFile(e)) +} diff --git a/java/ql/lib/semmle/code/java/Statement.qll b/java/ql/lib/semmle/code/java/Statement.qll index 73b0aac5cbdd..d1e7e748bc35 100644 --- a/java/ql/lib/semmle/code/java/Statement.qll +++ b/java/ql/lib/semmle/code/java/Statement.qll @@ -6,6 +6,7 @@ module; import Expr import metrics.MetricStmt +private import semmle.code.java.Overlay /** A common super-class of all statements. */ class Stmt extends StmtParent, ExprParent, @stmt { @@ -987,3 +988,15 @@ class SuperConstructorInvocationStmt extends Stmt, ConstructorCall, @superconstr override string getAPrimaryQlClass() { result = "SuperConstructorInvocationStmt" } } + +overlay[local] +private predicate discardableStmt(string file, @stmt s) { + not isOverlay() and + file = getRawFile(s) +} + +/** Discard base statements in files fully extracted in the overlay. */ +overlay[discard_entity] +private predicate discardStmt(@stmt s) { + exists(string file | discardableStmt(file, s) and extractedInOverlay(file)) +} diff --git a/java/ql/lib/semmle/code/java/Variable.qll b/java/ql/lib/semmle/code/java/Variable.qll index 50fd7a064846..9b8b42b71fba 100644 --- a/java/ql/lib/semmle/code/java/Variable.qll +++ b/java/ql/lib/semmle/code/java/Variable.qll @@ -5,6 +5,7 @@ overlay[local?] module; import Element +private import semmle.code.java.Overlay /** A variable is a field, a local variable or a parameter. */ class Variable extends @variable, Annotatable, Element, Modifiable { @@ -133,3 +134,15 @@ class Parameter extends Element, @param, LocalScopeVariable { /** Holds if this is an anonymous parameter, `_` */ predicate isAnonymous() { this.getName() = "" } } + +overlay[local] +private predicate discardableLocalVarDecl(string file, @localscopevariable l) { + not isOverlay() and + file = getRawFile(l) +} + +/** Discard base local scoped variables in files fully extracted in the overlay. */ +overlay[discard_entity] +private predicate discardLocalVarDecl(@localscopevariable l) { + exists(string file | discardableLocalVarDecl(file, l) and extractedInOverlay(file)) +} diff --git a/java/ql/src/experimental/Security/CWE/CWE-020/Log4jJndiInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-020/Log4jJndiInjection.ql index 84c4bb01c126..3abaa7bdcfa3 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-020/Log4jJndiInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-020/Log4jJndiInjection.ql @@ -22,6 +22,7 @@ import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.security.Sanitizers import Log4jInjectionFlow::PathGraph +overlay[local?] deprecated private class ActivateModels extends ActiveExperimentalModels { ActivateModels() { this = "log4j-injection" } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-036/OpenStream.ql b/java/ql/src/experimental/Security/CWE/CWE-036/OpenStream.ql index c84037719da9..0929ca3eb805 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-036/OpenStream.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-036/OpenStream.ql @@ -17,6 +17,7 @@ import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.ExternalFlow import RemoteUrlToOpenStreamFlow::PathGraph +overlay[local?] deprecated private class ActivateModels extends ActiveExperimentalModels { ActivateModels() { this = "openstream-called-on-tainted-url" } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql index c87097458520..11bb600ffe85 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql @@ -22,6 +22,7 @@ import semmle.code.java.security.PathSanitizer private import semmle.code.java.security.Sanitizers import InjectFilePathFlow::PathGraph +overlay[local?] deprecated private class ActivateModels extends ActiveExperimentalModels { ActivateModels() { this = "file-path-injection" } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql index 08f7631af828..c13bc3bb245c 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql @@ -18,6 +18,7 @@ import semmle.code.java.security.CommandLineQuery import InputToArgumentToExecFlow::PathGraph private import semmle.code.java.dataflow.ExternalFlow +overlay[local?] deprecated private class ActivateModels extends ActiveExperimentalModels { ActivateModels() { this = "jsch-os-injection" } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-200/AndroidWebResourceResponse.qll b/java/ql/src/experimental/Security/CWE/CWE-200/AndroidWebResourceResponse.qll index bd898df205a8..b988398e4c26 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-200/AndroidWebResourceResponse.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-200/AndroidWebResourceResponse.qll @@ -7,6 +7,7 @@ private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.dataflow.FlowSteps private import semmle.code.java.frameworks.android.WebView +overlay[local?] private class ActivateModels extends ActiveExperimentalModels { ActivateModels() { this = "android-web-resource-response" } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-400/ThreadResourceAbuse.qll b/java/ql/src/experimental/Security/CWE/CWE-400/ThreadResourceAbuse.qll index ce6de1a06798..12ba6769f742 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-400/ThreadResourceAbuse.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-400/ThreadResourceAbuse.qll @@ -8,6 +8,7 @@ import semmle.code.java.arithmetic.Overflow import semmle.code.java.dataflow.FlowSteps import semmle.code.java.controlflow.Guards +overlay[local?] private class ActivateModels extends ActiveExperimentalModels { ActivateModels() { this = "thread-resource-abuse" } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-625/PermissiveDotRegexQuery.qll b/java/ql/src/experimental/Security/CWE/CWE-625/PermissiveDotRegexQuery.qll index 8fe997793f4a..f8e328902504 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-625/PermissiveDotRegexQuery.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-625/PermissiveDotRegexQuery.qll @@ -9,6 +9,7 @@ import semmle.code.java.controlflow.Guards import semmle.code.java.security.UrlRedirect import Regex +overlay[local?] private class ActivateModels extends ActiveExperimentalModels { ActivateModels() { this = "permissive-dot-regex-query" } } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll index 5cb92d7ba8da..288814c4c511 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplCommon.qll @@ -291,6 +291,7 @@ module MakeImplCommon Lang> { * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing * callable of `lambdaCall`. */ + overlay[global] pragma[nomagic] predicate revLambdaFlow( Call lambdaCall, LambdaCallKind kind, Node node, Type t, boolean toReturn, boolean toJump,