Go: Use FlowSummaryImpl from dataflow pack

This commit is contained in:
Tom Hvitved
2023-11-27 12:44:12 +01:00
parent faaa558ed9
commit 35c654aa76
9 changed files with 351 additions and 1952 deletions

View File

@@ -55,7 +55,6 @@
],
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
"go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
],
"SsaReadPosition Java/C#": [
@@ -464,7 +463,6 @@
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
],
"AccessPathSyntax": [
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/AccessPathSyntax.qll"
],

View File

@@ -76,10 +76,10 @@
private import go
import internal.ExternalFlowExtensions
private import internal.DataFlowPrivate
private import internal.FlowSummaryImpl
private import internal.FlowSummaryImpl::Private
private import internal.FlowSummaryImpl::Private::External
private import internal.FlowSummaryImplSpecific
private import internal.AccessPathSyntax
private import FlowSummary
private import internal.FlowSummaryImpl::Public
private import codeql.mad.ModelValidation as SharedModelVal
/** Holds if `package` have MaD framework coverage. */
@@ -274,7 +274,7 @@ private string interpretPackage(string p) {
}
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
SourceOrSinkElement interpretElement(
SourceSinkInterpretationInput::SourceOrSinkElement interpretElement(
string pkg, string type, boolean subtypes, string name, string signature, string ext
) {
elementSpec(pkg, type, subtypes, name, signature, ext) and
@@ -298,8 +298,9 @@ SourceOrSinkElement interpretElement(
predicate hasExternalSpecification(Function f) {
f = any(SummarizedCallable sc).asFunction()
or
exists(SourceOrSinkElement e | f = e.asEntity() |
sourceElement(e, _, _, _) or sinkElement(e, _, _, _)
exists(SourceSinkInterpretationInput::SourceOrSinkElement e | f = e.asEntity() |
SourceSinkInterpretationInput::sourceElement(e, _, _) or
SourceSinkInterpretationInput::sinkElement(e, _, _)
)
}
@@ -353,7 +354,9 @@ private module Cached {
*/
cached
predicate sourceNode(DataFlow::Node node, string kind) {
exists(InterpretNode n | isSourceNode(n, kind) and n.asNode() = node)
exists(SourceSinkInterpretationInput::InterpretNode n |
isSourceNode(n, kind) and n.asNode() = node
)
}
/**
@@ -362,8 +365,71 @@ private module Cached {
*/
cached
predicate sinkNode(DataFlow::Node node, string kind) {
exists(InterpretNode n | isSinkNode(n, kind) and n.asNode() = node)
exists(SourceSinkInterpretationInput::InterpretNode n |
isSinkNode(n, kind) and n.asNode() = node
)
}
}
import Cached
private predicate interpretSummary(
Callable c, string input, string output, string kind, string provenance
) {
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
c.asFunction() = interpretElement(namespace, type, subtypes, name, signature, ext).asEntity()
)
}
private class SummarizedCallableAdapter extends SummarizedCallable {
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _) }
private predicate relevantSummaryElementManual(string input, string output, string kind) {
exists(Provenance provenance |
interpretSummary(this, input, output, kind, provenance) and
provenance.isManual()
)
}
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
exists(Provenance provenance |
interpretSummary(this, input, output, kind, provenance) and
provenance.isGenerated()
)
}
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
exists(string kind |
this.relevantSummaryElementManual(input, output, kind)
or
not this.relevantSummaryElementManual(_, _, _) and
this.relevantSummaryElementGenerated(input, output, kind)
|
if kind = "value" then preservesValue = true else preservesValue = false
)
}
override predicate hasProvenance(Provenance provenance) {
interpretSummary(this, _, _, _, provenance)
}
}
private class NeutralCallableAdapter extends NeutralCallable {
string kind;
string provenance_;
NeutralCallableAdapter() {
// Neutral models have not been implemented for Go.
none() and
exists(this) and
exists(kind) and
exists(provenance_)
}
override string getKind() { result = kind }
override predicate hasProvenance(Provenance provenance) { provenance = provenance_ }
}

View File

@@ -10,40 +10,14 @@ private import internal.DataFlowUtil
// import all instances below
private module Summaries { }
class SummaryComponent = Impl::Public::SummaryComponent;
deprecated class SummaryComponent = Impl::Private::SummaryComponent;
/** Provides predicates for constructing summary components. */
module SummaryComponent {
import Impl::Public::SummaryComponent
deprecated module SummaryComponent = Impl::Private::SummaryComponent;
/** Gets a summary component that represents a qualifier. */
SummaryComponent qualifier() { result = argument(-1) }
deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
/** Gets a summary component for field `f`. */
SummaryComponent field(Field f) { result = content(any(FieldContent c | c.getField() = f)) }
/** Gets a summary component that represents the return value of a call. */
SummaryComponent return() { result = return(_) }
}
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
/** Provides predicates for constructing stacks of summary components. */
module SummaryComponentStack {
import Impl::Public::SummaryComponentStack
/** Gets a singleton stack representing a qualifier. */
SummaryComponentStack qualifier() { result = singleton(SummaryComponent::qualifier()) }
/** Gets a stack representing a field `f` of `object`. */
SummaryComponentStack fieldOf(Field f, SummaryComponentStack object) {
result = push(SummaryComponent::field(f), object)
}
/** Gets a singleton stack representing a (normal) return. */
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
}
deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
class SummarizedCallable = Impl::Public::SummarizedCallable;
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
deprecated class RequiredSummaryComponentStack = Impl::Private::RequiredSummaryComponentStack;

View File

@@ -1,182 +0,0 @@
/**
* Module for parsing access paths from MaD models, both the identifying access path used
* by dynamic languages, and the input/output specifications for summary steps.
*
* This file is used by the shared data flow library and by the JavaScript libraries
* (which does not use the shared data flow libraries).
*/
/**
* Convenience-predicate for extracting two capture groups at once.
*/
bindingset[input, regexp]
private predicate regexpCaptureTwo(string input, string regexp, string capture1, string capture2) {
capture1 = input.regexpCapture(regexp, 1) and
capture2 = input.regexpCapture(regexp, 2)
}
/** Companion module to the `AccessPath` class. */
module AccessPath {
/** A string that should be parsed as an access path. */
abstract class Range extends string {
bindingset[this]
Range() { any() }
}
/**
* Parses an integer constant `n` or interval `n1..n2` (inclusive) and gets the value
* of the constant or any value contained in the interval.
*/
bindingset[arg]
int parseInt(string arg) {
result = arg.toInt()
or
// Match "n1..n2"
exists(string lo, string hi |
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.(-?\\d+)", lo, hi) and
result = [lo.toInt() .. hi.toInt()]
)
}
/**
* Parses a lower-bounded interval `n..` and gets the lower bound.
*/
bindingset[arg]
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
/**
* Parses an integer constant or interval (bounded or unbounded) that explicitly
* references the arity, such as `N-1` or `N-3..N-1`.
*
* Note that expressions of form `N-x` will never resolve to a negative index,
* even if `N` is zero (it will have no result in that case).
*/
bindingset[arg, arity]
private int parseIntWithExplicitArity(string arg, int arity) {
result >= 0 and // do not allow N-1 to resolve to a negative index
exists(string lo |
// N-x
lo = arg.regexpCapture("N-(\\d+)", 1) and
result = arity - lo.toInt()
or
// N-x..
lo = arg.regexpCapture("N-(\\d+)\\.\\.", 1) and
result = [arity - lo.toInt(), arity - 1]
)
or
exists(string lo, string hi |
// x..N-y
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.N-(\\d+)", lo, hi) and
result = [lo.toInt() .. arity - hi.toInt()]
or
// N-x..N-y
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.N-(\\d+)", lo, hi) and
result = [arity - lo.toInt() .. arity - hi.toInt()] and
result >= 0
or
// N-x..y
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.(\\d+)", lo, hi) and
result = [arity - lo.toInt() .. hi.toInt()] and
result >= 0
)
}
/**
* Parses an integer constant or interval (bounded or unbounded) and gets any
* of the integers contained within (of which there may be infinitely many).
*
* Has no result for arguments involving an explicit arity, such as `N-1`.
*/
bindingset[arg, result]
int parseIntUnbounded(string arg) {
result = parseInt(arg)
or
result >= parseLowerBound(arg)
}
/**
* Parses an integer constant or interval (bounded or unbounded) that
* may reference the arity of a call, such as `N-1` or `N-3..N-1`.
*
* Note that expressions of form `N-x` will never resolve to a negative index,
* even if `N` is zero (it will have no result in that case).
*/
bindingset[arg, arity]
int parseIntWithArity(string arg, int arity) {
result = parseInt(arg)
or
result in [parseLowerBound(arg) .. arity - 1]
or
result = parseIntWithExplicitArity(arg, arity)
}
}
/** Gets the `n`th token on the access path as a string. */
private string getRawToken(AccessPath path, int n) {
// Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`.
// Instead use regexpFind to match valid tokens, and supplement with a final length
// check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token.
result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _)
}
/**
* A string that occurs as an access path (either identifying or input/output spec)
* which might be relevant for this database.
*/
class AccessPath extends string instanceof AccessPath::Range {
/** Holds if this string is not a syntactically valid access path. */
predicate hasSyntaxError() {
// If the lengths match, all characters must haven been included in a token
// or seen by the `.` lookahead pattern.
this != "" and
not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1
}
/** Gets the `n`th token on the access path (if there are no syntax errors). */
AccessPathToken getToken(int n) {
result = getRawToken(this, n) and
not this.hasSyntaxError()
}
/** Gets the number of tokens on the path (if there are no syntax errors). */
int getNumToken() {
result = count(int n | exists(getRawToken(this, n))) and
not this.hasSyntaxError()
}
}
/**
* An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
*/
class AccessPathToken extends string {
AccessPathToken() { this = getRawToken(_, _) }
private string getPart(int part) {
result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part)
}
/** Gets the name of the token, such as `Member` from `Member[x]` */
string getName() { result = this.getPart(1) }
/**
* Gets the argument list, such as `1,2` from `Member[1,2]`,
* or has no result if there are no arguments.
*/
string getArgumentList() { result = this.getPart(2) }
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
pragma[nomagic]
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument() { result = this.getArgument(_) }
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument(string name) { result = this.getArgument(name, _) }
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
}

View File

@@ -86,7 +86,8 @@ module Private {
/** Holds if this summary node is the `i`th argument of `call`. */
predicate isArgumentOf(DataFlowCall call, int i) {
FlowSummaryImpl::Private::summaryArgumentNode(call, this.getSummaryNode(), i)
// We do not currently have support for callback-based library models.
none()
}
/** Holds if this summary node is a return node. */
@@ -96,7 +97,8 @@ module Private {
/** Holds if this summary node is an out node for `call`. */
predicate isOut(DataFlowCall call) {
FlowSummaryImpl::Private::summaryOutNode(call, this.getSummaryNode(), _)
// We do not currently have support for callback-based library models.
none()
}
}
}

View File

@@ -420,7 +420,10 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
* by default as a heuristic.
*/
predicate allowParameterReturnInSelf(ParameterNode p) {
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
exists(DataFlowCallable c, int pos |
p.isParameterOf(c, pos) and
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asSummarizedCallable(), pos)
)
}
/** An approximated `Content`. */

File diff suppressed because it is too large Load Diff

View File

@@ -1,292 +0,0 @@
/**
* Provides Go-specific classes and predicates for defining flow summaries.
*/
private import go
private import DataFlowDispatch
private import DataFlowPrivate
private import DataFlowUtil
private import FlowSummaryImpl::Private
private import FlowSummaryImpl::Public
private import semmle.go.dataflow.ExternalFlow
private import DataFlowImplCommon
private module FlowSummaries {
private import semmle.go.dataflow.FlowSummary as F
}
/**
* A class of callables that are candidates for flow summary modeling.
*/
class SummarizedCallableBase = Callable;
/**
* A class of callables that are candidates for neutral modeling.
*/
class NeutralCallableBase = Callable;
DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c or none() }
/** Gets the parameter position of the instance parameter. */
ArgumentPosition callbackSelfParameterPosition() { result = -1 }
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
string getParameterPosition(ParameterPosition pos) { result = pos.toString() }
/** Gets the textual representation of an argument position in the format used for flow summaries. */
string getArgumentPosition(ArgumentPosition pos) { result = pos.toString() }
/** Gets the synthesized data-flow call for `receiver`. */
DataFlowCall summaryDataFlowCall(SummaryNode receiver) {
// We do not currently have support for callback-based library models.
none()
}
/** Gets the type of content `c`. */
DataFlowType getContentType(Content c) { result = c.getType() }
/** Gets the type of the parameter at the given position. */
DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) { any() }
/** Gets the return type of kind `rk` for callable `c`. */
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() }
/**
* Gets the type of the `i`th parameter in a synthesized call that targets a
* callback of type `t`.
*/
bindingset[t, pos]
DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) { any() }
/**
* Gets the return type of kind `rk` in a synthesized call that targets a
* callback of type `t`.
*/
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() }
/** Gets the type of synthetic global `sg`. */
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) { any() }
/**
* Holds if an external flow summary exists for `c` with input specification
* `input`, output specification `output`, kind `kind`, and provenance `provenance`.
*/
predicate summaryElement(
SummarizedCallableBase c, string input, string output, string kind, string provenance
) {
exists(string package, string type, boolean subtypes, string name, string signature, string ext |
summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance) and
c.asFunction() = interpretElement(package, type, subtypes, name, signature, ext).asEntity()
)
}
/**
* Holds if a neutral model exists for `c` of kind `kind`
* and with provenance `provenance`.
* Note. Neutral models have not been implemented for Go.
*/
predicate neutralElement(NeutralCallableBase c, string kind, string provenance) { none() }
/** Gets the summary component for specification component `c`, if any. */
bindingset[c]
SummaryComponent interpretComponentSpecific(string c) {
exists(int pos | parseReturn(c, pos) and result = SummaryComponent::return(getReturnKind(pos)))
or
exists(Content content | parseContent(c, content) and result = SummaryComponent::content(content))
}
/** Gets the summary component for specification component `c`, if any. */
private string getContentSpecific(Content c) {
exists(Field f, string package, string className, string fieldName |
f = c.(FieldContent).getField() and
f.hasQualifiedName(package, className, fieldName) and
result = "Field[" + package + "." + className + "." + fieldName + "]"
)
or
exists(SyntheticField f |
f = c.(SyntheticFieldContent).getField() and result = "SyntheticField[" + f + "]"
)
or
c instanceof ArrayContent and result = "ArrayElement"
or
c instanceof CollectionContent and result = "Element"
or
c instanceof MapKeyContent and result = "MapKey"
or
c instanceof MapValueContent and result = "MapValue"
or
c instanceof PointerContent and result = "Dereference"
}
/** Gets the textual representation of the content in the format used for MaD models. */
string getMadRepresentationSpecific(SummaryComponent sc) {
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c))
or
exists(ReturnKind rk |
sc = TReturnSummaryComponent(rk) and
not rk = getReturnValueKind() and
result = "ReturnValue[" + rk.getIndex() + "]"
)
}
/** Holds if input specification component `c` needs a reference. */
predicate inputNeedsReferenceSpecific(string c) { none() }
/** Holds if output specification component `c` needs a reference. */
predicate outputNeedsReferenceSpecific(string c) { parseReturn(c, _) }
private newtype TSourceOrSinkElement =
TEntityElement(Entity e) or
TAstElement(AstNode n)
/** An element representable by CSV modeling. */
class SourceOrSinkElement extends TSourceOrSinkElement {
/** Gets this source or sink element as an entity, if it is one. */
Entity asEntity() { this = TEntityElement(result) }
/** Gets this source or sink element as an AST node, if it is one. */
AstNode asAstNode() { this = TAstElement(result) }
/** Gets a textual representation of this source or sink element. */
string toString() {
result = "element representing " + [this.asEntity().toString(), this.asAstNode().toString()]
}
predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) {
this.asEntity().hasLocationInfo(fp, sl, sc, el, ec) or
this.asAstNode().hasLocationInfo(fp, sl, sc, el, ec)
}
}
/**
* Holds if an external source specification exists for `e` with output specification
* `output`, kind `kind`, and provenance `provenance`.
*/
predicate sourceElement(SourceOrSinkElement e, string output, string kind, string provenance) {
exists(string package, string type, boolean subtypes, string name, string signature, string ext |
sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance) and
e = interpretElement(package, type, subtypes, name, signature, ext)
)
}
/**
* Holds if an external sink specification exists for `e` with input specification
* `input`, kind `kind` and provenance `provenance`.
*/
predicate sinkElement(SourceOrSinkElement e, string input, string kind, string provenance) {
exists(string package, string type, boolean subtypes, string name, string signature, string ext |
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance) and
e = interpretElement(package, type, subtypes, name, signature, ext)
)
}
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
ReturnKind getReturnValueKind() { result = getReturnKind(0) }
private newtype TInterpretNode =
TElement(SourceOrSinkElement n) or
TNode(Node n)
/** 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() { result = this.asElement().asAstNode() }
/** Gets the callable that this node corresponds to, if any. */
DataFlowCallable asCallable() {
result.asSummarizedCallable().asFunction() = this.asElement().asEntity()
}
/** Gets the target of this call, if any. */
SourceOrSinkElement getCallTarget() {
result.asEntity() = this.asCall().getNode().(DataFlow::CallNode).getTarget()
}
/** Gets a textual representation of this node. */
string toString() {
result = this.asElement().toString()
or
result = this.asNode().toString()
}
/** Gets the location of this node. */
predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) {
this.asElement().hasLocationInfo(fp, sl, sc, el, ec)
or
this.asNode().hasLocationInfo(fp, sl, sc, el, ec)
}
}
/** Provides additional sink specification logic required for annotations. */
pragma[inline]
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
exists(int pos | node.asNode() = getAnOutNodeExt(mid.asCall(), TValueReturn(getReturnKind(pos))) |
parseReturn(c, pos)
)
or
exists(Node n, SourceOrSinkElement e |
n = node.asNode() and
e = mid.asElement()
|
(c = "Parameter" or c = "") and
node.asNode().asParameter() = e.asEntity()
or
c = "" and
n.(DataFlow::FieldReadNode).getField() = e.asEntity()
)
}
/** Provides additional source specification logic required for annotations. */
pragma[inline]
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) {
exists(int pos, ReturnNodeExt ret |
parseReturn(c, pos) and
ret = n.asNode() and
ret.getKind().(ValueReturnKind).getKind() = getReturnKind(pos) and
mid.asCallable() = getNodeEnclosingCallable(ret)
)
or
exists(DataFlow::Write fw, Field f |
c = "" and
f = mid.asElement().asEntity() and
fw.writesField(_, f, n.asNode())
)
}
/**
* Holds if specification component `c` parses as return value `n` or a range
* containing `n`.
*/
predicate parseReturn(AccessPathToken c, int n) {
(
c = "ReturnValue" and n = 0
or
c.getName() = "ReturnValue" and
n = parseConstantOrRange(c.getAnArgument())
)
}
bindingset[arg]
private int parseConstantOrRange(string arg) {
result = arg.toInt()
or
exists(int n1, int n2 |
arg.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 1).toInt() = n1 and
arg.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 2).toInt() = n2 and
result = [n1 .. n2]
)
}
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
bindingset[arg]
ArgumentPosition parseParamBody(string arg) { result = parseConstantOrRange(arg) }
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
bindingset[arg]
ParameterPosition parseArgBody(string arg) { result = parseConstantOrRange(arg) }

View File

@@ -3,8 +3,8 @@
*/
import go
private import semmle.go.dataflow.FlowSummary
private import semmle.go.dataflow.internal.DataFlowPrivate
private import semmle.go.dataflow.internal.FlowSummaryImpl::Private
/** Provides models of commonly used functions in the `net/http` package. */
module NetHttp {
@@ -154,7 +154,7 @@ module NetHttp {
)
or
exists(
SummarizedCallable callable, DataFlow::CallNode call, SummaryComponentStack input,
SummarizedCallableImpl callable, DataFlow::CallNode call, SummaryComponentStack input,
SummaryComponentStack output
|
this = call.getASyntacticArgument() and