Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Shared: Re-factor summary, source and sink model generators into separate modules. #19382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 2, 2025
Prev Previous commit
Next Next commit
Java: Re-factor implementation to use the new model generator interface.
  • Loading branch information
michaelnebel committed Apr 29, 2025
commit 2535055de054b8fa0d92510dfa56c813efe4ae30
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import internal.CaptureModels
import SummaryModels

from DataFlowSummaryTargetApi api, string flow
where flow = ContentSensitive::captureFlow(api, _)
Expand Down
1 change: 1 addition & 0 deletions 1 java/ql/src/utils/modelgenerator/CaptureNeutralModels.ql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import internal.CaptureModels
import SummaryModels

from DataFlowSummaryTargetApi api, string noflow
where noflow = captureNeutral(api)
Expand Down
1 change: 1 addition & 0 deletions 1 java/ql/src/utils/modelgenerator/CaptureSinkModels.ql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import internal.CaptureModels
import SinkModels

from DataFlowSinkTargetApi api, string sink
where sink = Heuristic::captureSink(api)
Expand Down
1 change: 1 addition & 0 deletions 1 java/ql/src/utils/modelgenerator/CaptureSourceModels.ql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import internal.CaptureModels
import SourceModels

from DataFlowSourceTargetApi api, string source
where source = Heuristic::captureSource(api)
Expand Down
1 change: 1 addition & 0 deletions 1 java/ql/src/utils/modelgenerator/CaptureSummaryModels.ql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import internal.CaptureModels
import SummaryModels

from DataFlowSummaryTargetApi api, string flow
where flow = captureFlow(api, _)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java
import semmle.code.java.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import PartialFlow::PartialPathGraph

int explorationLimit() { result = 3 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java
import semmle.code.java.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import Heuristic
import PropagateFlow::PathGraph

Expand Down
291 changes: 149 additions & 142 deletions 291 java/ql/src/utils/modelgenerator/internal/CaptureModels.qll
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,20 @@ predicate isPrimitiveTypeUsedForBulkData(J::Type t) {
t.hasName(["byte", "char", "Byte", "Character"])
}

module ModelGeneratorInput implements ModelGeneratorInputSig<Location, JavaDataFlow> {
private predicate isInfrequentlyUsed(J::CompilationUnit cu) {
cu.getPackage().getName().matches("javax.swing%") or
cu.getPackage().getName().matches("java.awt%")
}

private predicate relevant(Callable api) {
api.isPublic() and
api.getDeclaringType().isPublic() and
api.fromSource() and
not isUninterestingForModels(api) and
not isInfrequentlyUsed(api.getCompilationUnit())
}

module ModelGeneratorCommonInput implements ModelGeneratorCommonInputSig<Location, JavaDataFlow> {
class Type = J::Type;

class Parameter = J::Parameter;
Expand All @@ -34,96 +47,8 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, JavaDataF

class NodeExtended = DataFlow::Node;

Callable getAsExprEnclosingCallable(NodeExtended node) {
result = node.asExpr().getEnclosingCallable()
}

Callable getEnclosingCallable(NodeExtended node) { result = node.getEnclosingCallable() }

Parameter asParameter(NodeExtended node) { result = node.asParameter() }

private predicate isInfrequentlyUsed(J::CompilationUnit cu) {
cu.getPackage().getName().matches("javax.swing%") or
cu.getPackage().getName().matches("java.awt%")
}

private predicate relevant(Callable api) {
api.isPublic() and
api.getDeclaringType().isPublic() and
api.fromSource() and
not isUninterestingForModels(api) and
not isInfrequentlyUsed(api.getCompilationUnit())
}

private J::Method getARelevantOverride(J::Method m) {
result = m.getAnOverride() and
relevant(result) and
// Other exclusions for overrides.
not m instanceof J::ToStringMethod
}

/**
* Gets the super implementation of `m` if it is relevant.
* If such a super implementations does not exist, returns `m` if it is relevant.
*/
private J::Callable liftedImpl(J::Callable m) {
(
result = getARelevantOverride(m)
or
result = m and relevant(m)
) and
not exists(getARelevantOverride(result))
}

private predicate hasManualSummaryModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()).asCallable() or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel()).asCallable()
}

private predicate hasManualSourceModel(Callable api) {
api = any(ExternalFlow::SourceCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel()).asCallable()
}

private predicate hasManualSinkModel(Callable api) {
api = any(ExternalFlow::SinkCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel()).asCallable()
}

predicate isUninterestingForDataFlowModels(Callable api) {
api.getDeclaringType() instanceof J::Interface and not exists(api.getBody())
}

predicate isUninterestingForHeuristicDataFlowModels(Callable api) { none() }

class SourceOrSinkTargetApi extends Callable {
SourceOrSinkTargetApi() { relevant(this) }
}

class SinkTargetApi extends SourceOrSinkTargetApi {
SinkTargetApi() { not hasManualSinkModel(this) }
}

class SourceTargetApi extends SourceOrSinkTargetApi {
SourceTargetApi() { not hasManualSourceModel(this) }
}

class SummaryTargetApi extends Callable {
private Callable lift;

SummaryTargetApi() {
lift = liftedImpl(this) and
not hasManualSummaryModel(lift)
}

Callable lift() { result = lift }

predicate isRelevant() {
relevant(this) and
not hasManualSummaryModel(this)
}
}

private string isExtensible(Callable c) {
if c.getDeclaringType().isFinal() then result = "false" else result = "true"
}
Expand Down Expand Up @@ -204,49 +129,89 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, JavaDataF
node.asExpr().(J::ThisAccess).isOwnInstanceAccess()
}

predicate sinkModelSanitizer(DataFlow::Node node) {
// exclude variable capture jump steps
exists(Ssa::SsaImplicitInit closure |
closure.captures(_) and
node.asExpr() = closure.getAFirstUse()
)
predicate containerContent = DataFlowPrivate::containerContent/1;

string partialModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _) // package
or
i = 1 and qualifiedName(api, _, result) // type
or
i = 2 and result = isExtensible(api) // extensible
or
i = 3 and result = api.getName() // name
or
i = 4 and result = ExternalFlow::paramsString(api) // parameters
or
i = 5 and result = "" and exists(api) // ext
}

predicate apiSource(DataFlow::Node source) {
string partialNeutralModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _) // package
or
i = 1 and qualifiedName(api, _, result) // type
or
i = 2 and result = api.getName() // name
or
i = 3 and result = ExternalFlow::paramsString(api) // parameters
}
}

private import ModelGeneratorCommonInput
private import MakeModelGeneratorFactory<Location, JavaDataFlow, JavaTaintTracking, ModelGeneratorCommonInput>

module SummaryModelGeneratorInput implements SummaryModelGeneratorInputSig {
Callable getAsExprEnclosingCallable(NodeExtended node) {
result = node.asExpr().getEnclosingCallable()
}

Parameter asParameter(NodeExtended node) { result = node.asParameter() }

private J::Method getARelevantOverride(J::Method m) {
result = m.getAnOverride() and
relevant(result) and
// Other exclusions for overrides.
not m instanceof J::ToStringMethod
}

/**
* Gets the super implementation of `m` if it is relevant.
* If such a super implementations does not exist, returns `m` if it is relevant.
*/
private J::Callable liftedImpl(J::Callable m) {
(
source.asExpr().(J::FieldAccess).isOwnFieldAccess() or
source instanceof DataFlow::ParameterNode
result = getARelevantOverride(m)
or
result = m and relevant(m)
) and
exists(J::RefType t |
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
)
not exists(getARelevantOverride(result))
}

predicate irrelevantSourceSinkApi(Callable source, SourceTargetApi api) { none() }

string getInputArgument(DataFlow::Node source) {
exists(int pos |
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
if pos >= 0 then result = "Argument[" + pos + "]" else result = qualifierString()
)
or
source.asExpr() instanceof J::FieldAccess and
result = qualifierString()
private predicate hasManualSummaryModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()).asCallable() or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel()).asCallable()
}

bindingset[kind]
predicate isRelevantSinkKind(string kind) {
not kind = "log-injection" and
not kind.matches("regex-use%") and
not kind = "file-content-store"
class SummaryTargetApi extends Callable {
private Callable lift;

SummaryTargetApi() {
lift = liftedImpl(this) and
not hasManualSummaryModel(lift)
}

Callable lift() { result = lift }

predicate isRelevant() {
relevant(this) and
not hasManualSummaryModel(this)
}
}

bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }
predicate isUninterestingForDataFlowModels(Callable api) {
api.getDeclaringType() instanceof J::Interface and not exists(api.getBody())
}

predicate containerContent = DataFlowPrivate::containerContent/1;
predicate isUninterestingForHeuristicDataFlowModels(Callable api) { none() }

predicate isAdditionalContentFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
TaintTracking::defaultAdditionalTaintStep(node1, node2, _) and
Expand Down Expand Up @@ -287,34 +252,76 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, JavaDataF
or
c instanceof DataFlowUtil::MapKeyContent and result = "MapKey"
}
}

string partialModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _) // package
or
i = 1 and qualifiedName(api, _, result) // type
or
i = 2 and result = isExtensible(api) // extensible
or
i = 3 and result = api.getName() // name
or
i = 4 and result = ExternalFlow::paramsString(api) // parameters
or
i = 5 and result = "" and exists(api) // ext
private module SourceModelGeneratorInput implements SourceModelGeneratorInputSig {
private predicate hasManualSourceModel(Callable api) {
api = any(ExternalFlow::SourceCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel()).asCallable()
}

string partialNeutralModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _) // package
or
i = 1 and qualifiedName(api, _, result) // type
or
i = 2 and result = api.getName() // name
or
i = 3 and result = ExternalFlow::paramsString(api) // parameters
class SourceTargetApi extends Callable {
SourceTargetApi() { relevant(this) and not hasManualSourceModel(this) }
}

predicate irrelevantSourceSinkApi(Callable source, SourceTargetApi api) { none() }

bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }

predicate sourceNode = ExternalFlow::sourceNode/2;
}

private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
private predicate hasManualSinkModel(Callable api) {
api = any(ExternalFlow::SinkCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel()).asCallable()
}

class SinkTargetApi extends Callable {
SinkTargetApi() { relevant(this) and not hasManualSinkModel(this) }
}

predicate sinkModelSanitizer(DataFlow::Node node) {
// exclude variable capture jump steps
exists(Ssa::SsaImplicitInit closure |
closure.captures(_) and
node.asExpr() = closure.getAFirstUse()
)
}

predicate apiSource(DataFlow::Node source) {
(
source.asExpr().(J::FieldAccess).isOwnFieldAccess() or
source instanceof DataFlow::ParameterNode
) and
exists(J::RefType t |
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
)
}

string getInputArgument(DataFlow::Node source) {
exists(int pos |
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
if pos >= 0 then result = "Argument[" + pos + "]" else result = qualifierString()
)
or
source.asExpr() instanceof J::FieldAccess and
result = qualifierString()
}

bindingset[kind]
predicate isRelevantSinkKind(string kind) {
not kind = "log-injection" and
not kind.matches("regex-use%") and
not kind = "file-content-store"
}

predicate sinkNode = ExternalFlow::sinkNode/2;
}

import MakeModelGenerator<Location, JavaDataFlow, JavaTaintTracking, ModelGeneratorInput>
import MakeSummaryModelGenerator<SummaryModelGeneratorInput> as SummaryModels
import MakeSourceModelGenerator<SourceModelGeneratorInput> as SourceModels
import MakeSinkModelGenerator<SinkModelGeneratorInput> as SinkModels
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.