Skip to content

Commit c0e562d

Browse files
authored
Merge pull request github#5979 from hvitved/java/shared-external-summaries
Java: Move some CSV flow summary code into shared library
2 parents 348fab8 + ecf7f24 commit c0e562d

File tree

5 files changed

+255
-253
lines changed

5 files changed

+255
-253
lines changed

csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll

Lines changed: 89 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -580,88 +580,112 @@ module Private {
580580
* summaries into a `SummarizedCallable`s.
581581
*/
582582
module External {
583-
/**
584-
* Provides a means of translating an externally (e.g., CSV) defined flow
585-
* summary into a `SummarizedCallable`.
586-
*/
587-
abstract class ExternalSummaryCompilation extends string {
588-
bindingset[this]
589-
ExternalSummaryCompilation() { any() }
590-
591-
/** Holds if this flow summary is for callable `c`. */
592-
abstract predicate callable(DataFlowCallable c, boolean preservesValue);
593-
594-
/** Holds if the `i`th input component is `c`. */
595-
abstract predicate input(int i, SummaryComponent c);
596-
597-
/** Holds if the `i`th output component is `c`. */
598-
abstract predicate output(int i, SummaryComponent c);
599-
600-
/**
601-
* Holds if the input components starting from index `i` translate into `suffix`.
602-
*/
603-
final predicate translateInput(int i, SummaryComponentStack suffix) {
604-
exists(SummaryComponent comp | this.input(i, comp) |
605-
i = max(int j | this.input(j, _)) and
606-
suffix = TSingletonSummaryComponentStack(comp)
607-
or
608-
exists(TSummaryComponent head, SummaryComponentStack tail |
609-
this.translateInputCons(i, head, tail) and
610-
suffix = TConsSummaryComponentStack(head, tail)
611-
)
612-
)
613-
}
583+
/** Holds if the `n`th component of specification `s` is `c`. */
584+
predicate specSplit(string s, string c, int n) { relevantSpec(s) and s.splitAt(" of ", n) = c }
614585

615-
final predicate translateInputCons(int i, SummaryComponent head, SummaryComponentStack tail) {
616-
this.input(i, head) and
617-
this.translateInput(i + 1, tail)
618-
}
586+
/** Holds if specification `s` has length `len`. */
587+
predicate specLength(string s, int len) { len = 1 + max(int n | specSplit(s, _, n)) }
619588

620-
/**
621-
* Holds if the output components starting from index `i` translate into `suffix`.
622-
*/
623-
predicate translateOutput(int i, SummaryComponentStack suffix) {
624-
exists(SummaryComponent comp | this.output(i, comp) |
625-
i = max(int j | this.output(j, _)) and
626-
suffix = TSingletonSummaryComponentStack(comp)
627-
or
628-
exists(TSummaryComponent head, SummaryComponentStack tail |
629-
this.translateOutputCons(i, head, tail) and
630-
suffix = TConsSummaryComponentStack(head, tail)
631-
)
589+
/** Gets the last component of specification `s`. */
590+
string specLast(string s) {
591+
exists(int len |
592+
specLength(s, len) and
593+
specSplit(s, result, len - 1)
594+
)
595+
}
596+
597+
/** Holds if specification component `c` parses as parameter `n`. */
598+
predicate parseParam(string c, int n) {
599+
specSplit(_, c, _) and
600+
(
601+
c.regexpCapture("Parameter\\[([-0-9]+)\\]", 1).toInt() = n
602+
or
603+
exists(int n1, int n2 |
604+
c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
605+
c.regexpCapture("Parameter\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
606+
n = [n1 .. n2]
632607
)
633-
}
608+
)
609+
}
634610

635-
predicate translateOutputCons(int i, SummaryComponent head, SummaryComponentStack tail) {
636-
this.output(i, head) and
637-
this.translateOutput(i + 1, tail)
638-
}
611+
/** Holds if specification component `c` parses as argument `n`. */
612+
predicate parseArg(string c, int n) {
613+
specSplit(_, c, _) and
614+
(
615+
c.regexpCapture("Argument\\[([-0-9]+)\\]", 1).toInt() = n
616+
or
617+
exists(int n1, int n2 |
618+
c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 1).toInt() = n1 and
619+
c.regexpCapture("Argument\\[([-0-9]+)\\.\\.([0-9]+)\\]", 2).toInt() = n2 and
620+
n = [n1 .. n2]
621+
)
622+
)
639623
}
640624

641-
private class ExternalRequiredSummaryComponentStack extends RequiredSummaryComponentStack {
642-
private SummaryComponent head;
625+
private SummaryComponent interpretComponent(string c) {
626+
specSplit(_, c, _) and
627+
(
628+
exists(int pos | parseArg(c, pos) and result = SummaryComponent::argument(pos))
629+
or
630+
exists(int pos | parseParam(c, pos) and result = SummaryComponent::parameter(pos))
631+
or
632+
result = interpretComponentSpecific(c)
633+
)
634+
}
643635

644-
ExternalRequiredSummaryComponentStack() {
645-
any(ExternalSummaryCompilation s).translateInputCons(_, head, this) or
646-
any(ExternalSummaryCompilation s).translateOutputCons(_, head, this)
647-
}
636+
private predicate interpretSpec(string spec, int idx, SummaryComponentStack stack) {
637+
exists(string c |
638+
relevantSpec(spec) and
639+
specLength(spec, idx + 1) and
640+
specSplit(spec, c, idx) and
641+
stack = SummaryComponentStack::singleton(interpretComponent(c))
642+
)
643+
or
644+
exists(SummaryComponent head, SummaryComponentStack tail |
645+
interpretSpec(spec, idx, head, tail) and
646+
stack = SummaryComponentStack::push(head, tail)
647+
)
648+
}
648649

649-
override predicate required(SummaryComponent c) { c = head }
650+
private predicate interpretSpec(
651+
string output, int idx, SummaryComponent head, SummaryComponentStack tail
652+
) {
653+
exists(string c |
654+
interpretSpec(output, idx + 1, tail) and
655+
specSplit(output, c, idx) and
656+
head = interpretComponent(c)
657+
)
650658
}
651659

652-
class ExternalSummarizedCallableAdaptor extends SummarizedCallable {
653-
ExternalSummarizedCallableAdaptor() { any(ExternalSummaryCompilation s).callable(this, _) }
660+
private class MkStack extends RequiredSummaryComponentStack {
661+
MkStack() { interpretSpec(_, _, _, this) }
662+
663+
override predicate required(SummaryComponent c) { interpretSpec(_, _, c, this) }
664+
}
665+
666+
private class SummarizedCallableExternal extends SummarizedCallable {
667+
SummarizedCallableExternal() { externalSummary(this, _, _, _) }
654668

655669
override predicate propagatesFlow(
656670
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
657671
) {
658-
exists(ExternalSummaryCompilation s |
659-
s.callable(this, preservesValue) and
660-
s.translateInput(0, input) and
661-
s.translateOutput(0, output)
672+
exists(string inSpec, string outSpec, string kind |
673+
externalSummary(this, inSpec, outSpec, kind) and
674+
interpretSpec(inSpec, 0, input) and
675+
interpretSpec(outSpec, 0, output)
676+
|
677+
kind = "value" and preservesValue = true
678+
or
679+
kind = "taint" and preservesValue = false
662680
)
663681
}
664682
}
683+
684+
/** Holds if component `c` of specification `spec` cannot be parsed. */
685+
predicate invalidSpecComponent(string spec, string c) {
686+
specSplit(spec, c, _) and
687+
not exists(interpretComponent(c))
688+
}
665689
}
666690

667691
/** Provides a query predicate for outputting a set of relevant flow summaries. */

csharp/ql/src/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,30 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
7777
result = Gvn::getGlobalValueNumber(dt.getDelegateType().getReturnType())
7878
)
7979
}
80+
81+
/** Holds if `spec` is a relevant external specification. */
82+
predicate relevantSpec(string spec) { none() }
83+
84+
/**
85+
* Holds if an external flow summary exists for `c` with input specification
86+
* `input`, output specification `output`, and kind `kind`.
87+
*/
88+
predicate externalSummary(DataFlowCallable c, string input, string output, string kind) { none() }
89+
90+
/** Gets the summary component for specification component `c`, if any. */
91+
bindingset[c]
92+
SummaryComponent interpretComponentSpecific(string c) {
93+
c = "ReturnValue" and result = SummaryComponent::return(any(NormalReturnKind nrk))
94+
or
95+
c = "Element" and result = SummaryComponent::content(any(ElementContent ec))
96+
or
97+
exists(Field f |
98+
c.regexpCapture("Field\\[(.+)\\]", 1) = f.getQualifiedName() and
99+
result = SummaryComponent::content(any(FieldContent fc | fc.getField() = f))
100+
)
101+
or
102+
exists(Property p |
103+
c.regexpCapture("Property\\[(.+)\\]", 1) = p.getQualifiedName() and
104+
result = SummaryComponent::content(any(PropertyContent pc | pc.getProperty() = p))
105+
)
106+
}

0 commit comments

Comments
 (0)