Skip to content

Java: Move common CSV logic for sources and sinks into shared library #5991

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 3 commits into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
private import FlowSummaryImplSpecific
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
private import DataFlowImplCommon as DataFlowImplCommon

/** Provides classes and predicates for defining flow summaries. */
module Public {
Expand Down Expand Up @@ -178,7 +179,6 @@ module Public {
*/
module Private {
private import Public
private import DataFlowImplCommon as DataFlowImplCommon

newtype TSummaryComponent =
TContentSummaryComponent(Content c) or
Expand Down Expand Up @@ -580,6 +580,14 @@ module Private {
* summaries into a `SummarizedCallable`s.
*/
module External {
/** Holds if `spec` is a relevant external specification. */
private predicate relevantSpec(string spec) {
summaryElement(_, spec, _, _) or
summaryElement(_, _, spec, _) or
sourceElement(_, spec, _) or
sinkElement(_, spec, _)
}

/** Holds if the `n`th component of specification `s` is `c`. */
predicate specSplit(string s, string c, int n) { relevantSpec(s) and s.splitAt(" of ", n) = c }

Expand Down Expand Up @@ -629,6 +637,8 @@ module Private {
or
exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos))
or
c = "ReturnValue" and result = SummaryComponent::return(getReturnValueKind())
or
result = interpretComponentSpecific(c)
)
}
Expand Down Expand Up @@ -664,13 +674,13 @@ module Private {
}

private class SummarizedCallableExternal extends SummarizedCallable {
SummarizedCallableExternal() { externalSummary(this, _, _, _) }
SummarizedCallableExternal() { summaryElement(this, _, _, _) }

override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
exists(string inSpec, string outSpec, string kind |
externalSummary(this, inSpec, outSpec, kind) and
summaryElement(this, inSpec, outSpec, kind) and
interpretSpec(inSpec, 0, input) and
interpretSpec(outSpec, 0, output)
|
Expand All @@ -686,6 +696,111 @@ module Private {
specSplit(spec, c, _) and
not exists(interpretComponent(c))
}

private predicate inputNeedsReference(string c) {
c = "Argument" or
parseArg(c, _)
}

private predicate outputNeedsReference(string c) {
c = "Argument" or
parseArg(c, _) or
c = "ReturnValue"
}

private predicate sourceElementRef(InterpretNode ref, string output, string kind) {
exists(SourceOrSinkElement e |
sourceElement(e, output, kind) and
if outputNeedsReference(specLast(output))
then e = ref.getCallTarget()
else e = ref.asElement()
)
}

private predicate sinkElementRef(InterpretNode ref, string input, string kind) {
exists(SourceOrSinkElement e |
sinkElement(e, input, kind) and
if inputNeedsReference(specLast(input))
then e = ref.getCallTarget()
else e = ref.asElement()
)
}

private predicate interpretOutput(string output, int idx, InterpretNode ref, InterpretNode node) {
sourceElementRef(ref, output, _) and
specLength(output, idx) and
node = ref
or
exists(InterpretNode mid, string c |
interpretOutput(output, idx + 1, ref, mid) and
specSplit(output, c, idx)
|
exists(int pos |
node.asNode()
.(PostUpdateNode)
.getPreUpdateNode()
.(ArgumentNode)
.argumentOf(mid.asCall(), pos)
|
c = "Argument" or parseArg(c, pos)
)
or
exists(int pos | node.asNode().(ParameterNode).isParameterOf(mid.asCallable(), pos) |
c = "Parameter" or parseParam(c, pos)
)
or
c = "ReturnValue" and
node.asNode() = getAnOutNode(mid.asCall(), getReturnValueKind())
or
interpretOutputSpecific(c, mid, node)
)
}

private predicate interpretInput(string input, int idx, InterpretNode ref, InterpretNode node) {
sinkElementRef(ref, input, _) and
specLength(input, idx) and
node = ref
or
exists(InterpretNode mid, string c |
interpretInput(input, idx + 1, ref, mid) and
specSplit(input, c, idx)
|
exists(int pos | node.asNode().(ArgumentNode).argumentOf(mid.asCall(), pos) |
c = "Argument" or parseArg(c, pos)
)
or
exists(ReturnNode ret |
c = "ReturnValue" and
ret = node.asNode() and
ret.getKind() = getReturnValueKind() and
mid.asCallable() = DataFlowImplCommon::getNodeEnclosingCallable(ret)
)
or
interpretInputSpecific(c, mid, node)
)
}

/**
* Holds if `node` is specified as a source with the given kind in a CSV flow
* model.
*/
predicate isSourceNode(InterpretNode node, string kind) {
exists(InterpretNode ref, string output |
sourceElementRef(ref, output, kind) and
interpretOutput(output, 0, ref, node)
)
}

/**
* Holds if `node` is specified as a sink with the given kind in a CSV flow
* model.
*/
predicate isSinkNode(InterpretNode node, string kind) {
exists(InterpretNode ref, string input |
sinkElementRef(ref, input, kind) and
interpretInput(input, 0, ref, node)
)
}
}

/** Provides a query predicate for outputting a set of relevant flow summaries. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,15 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
)
}

/** Holds if `spec` is a relevant external specification. */
predicate relevantSpec(string spec) { none() }

/**
* Holds if an external flow summary exists for `c` with input specification
* `input`, output specification `output`, and kind `kind`.
*/
predicate externalSummary(DataFlowCallable c, string input, string output, string kind) { none() }
predicate summaryElement(DataFlowCallable c, string input, string output, string kind) { none() }

/** Gets the summary component for specification component `c`, if any. */
bindingset[c]
SummaryComponent interpretComponentSpecific(string c) {
c = "ReturnValue" and result = SummaryComponent::return(any(NormalReturnKind nrk))
or
c = "Element" and result = SummaryComponent::content(any(ElementContent ec))
or
exists(Field f |
Expand All @@ -104,3 +99,81 @@ SummaryComponent interpretComponentSpecific(string c) {
result = SummaryComponent::content(any(PropertyContent pc | pc.getProperty() = p))
)
}

class SourceOrSinkElement = Element;

/**
* Holds if an external source specification exists for `e` with output specification
* `output` and kind `kind`.
*/
predicate sourceElement(Element e, string output, string kind) { none() }

/**
* Holds if an external sink specification exists for `n` with input specification
* `input` and kind `kind`.
*/
predicate sinkElement(Element e, string input, string kind) { none() }

/** Gets the return kind corresponding to specification `"ReturnValue"`. */
NormalReturnKind getReturnValueKind() { any() }

private newtype TInterpretNode =
TElement_(Element n) or
TNode_(Node n) or
TDataFlowCall_(DataFlowCall c)

/** An entity used to interpret a source/sink specification. */
class InterpretNode extends TInterpretNode {
/** Gets the element that this node corresponds to, if any. */
SourceOrSinkElement asElement() { this = TElement_(result) }

/** Gets the data-flow node that this node corresponds to, if any. */
Node asNode() { this = TNode_(result) }

/** Gets the call that this node corresponds to, if any. */
DataFlowCall asCall() { this = TDataFlowCall_(result) }

/** Gets the callable that this node corresponds to, if any. */
DataFlowCallable asCallable() { result = this.asElement() }

/** Gets the target of this call, if any. */
Callable getCallTarget() { result = this.asCall().getARuntimeTarget() }

/** Gets a textual representation of this node. */
string toString() {
result = this.asElement().toString()
or
result = this.asNode().toString()
or
result = this.asCall().toString()
}

/** Gets the location of this node. */
Location getLocation() {
result = this.asElement().getLocation()
or
result = this.asNode().getLocation()
or
result = this.asCall().getLocation()
}
}

/** Provides additional sink specification logic required for attributes. */
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
exists(Node n | n = node.asNode() |
(c = "Parameter" or c = "") and
n.asParameter() = mid.asElement()
or
c = "" and
n.asExpr().(AssignableRead).getTarget().getUnboundDeclaration() = mid.asElement()
)
}

/** Provides additional sink specification logic required for attributes. */
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) {
c = "" and
exists(Assignable a |
n.asNode().asExpr() = a.getAnAssignedValue() and
a.getUnboundDeclaration() = mid.asElement()
)
}
Loading