Add relative filepath lookup

This commit is contained in:
Joe Farebrother
2023-09-29 15:16:23 +01:00
parent 40a7223620
commit 12a579e0aa

View File

@@ -3,7 +3,7 @@ private import codeql.util.Unit
private import semmle.code.csharp.frameworks.microsoft.AspNetCore
/** An additional flow step for cross-site scripting (XSS) vulnerabilities */
abstract class XssAdditionalFlowStep extends Unit {
class XssAdditionalFlowStep extends Unit {
abstract predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2);
}
@@ -73,9 +73,82 @@ private class ViewCallFlowStep extends XssAdditionalFlowStep {
}
private predicate viewCallRefersToPage(ViewCall vc, RazorPage rp) {
viewCallRefersToPageAbsolute(vc, rp)
viewCallRefersToPageAbsolute(vc, rp) or
viewCallRefersToPageRelative(vc, rp)
}
private predicate viewCallRefersToPageAbsolute(ViewCall vc, RazorPage rp) {
["/", "~/", ""] + vc.getNameArgument() = rp.getSourceFilepath()
["/", ""] + vc.getNameArgument() = ["", "~"] + rp.getSourceFilepath()
}
private predicate viewCallRefersToPageRelative(ViewCall vc, RazorPage rp) {
rp.getSourceFilepath() =
min(int i, RelativeViewCallFilepath fp |
fp.hasViewCallWithIndex(vc, i) and
exists(RazorPage rp2 | rp2.getSourceFilepath() = fp.getNormalizedPath())
|
fp.getNormalizedPath() order by i
)
}
private class RelativeViewCallFilepath extends NormalizableFilepath {
ViewCall vc;
int idx;
RelativeViewCallFilepath() {
exists(string actionName |
actionName = vc.getNameArgument() and
not actionName.matches("%.cshtml")
or
not exists(vc.getNameArgument()) and
actionName = vc.getActionMethod().getName()
|
idx = 0 and
this = "/Views/" + vc.getControllerName() + "/" + actionName + ".cshtml"
or
idx = 1 and
this = "/Views/Shared/" + actionName + ".cshtml"
)
}
predicate hasViewCallWithIndex(ViewCall vc2, int idx2) { vc = vc2 and idx = idx2 }
}
// TODO: this could be a shared library
/** A filepath that should be normalized. */
abstract private class NormalizableFilepath extends string {
bindingset[this]
NormalizableFilepath() { any() }
/** Gets the normalized filepath for this string; traversing `/../` paths. */
string getNormalizedPath() {
exists(string norm |
norm = this.getNormalizedUpTo(0).regexpReplaceAll("/+$", "") and
(if this.matches("/%") then result = "/" + norm else result = norm)
)
}
private string getComponent(int i) { result = this.splitAt("/", i) }
private int getNumComponents() { result = strictcount(int i | exists(this.getComponent(i))) }
private string getNormalizedUpTo(int i) {
i in [0 .. this.getNumComponents()] and
(
i = this.getNumComponents() and
result = ""
or
i < this.getNumComponents() and
exists(string comp, string sofar |
comp = this.getComponent(i) and sofar = this.getNormalizedUpTo(i + 1)
|
if comp = [".", ""]
then result = sofar
else
if comp = ".." or not sofar.matches("../%")
then result = comp + "/" + sofar
else exists(string base | sofar = "../" + base | result = base)
)
)
}
}