mirror of
https://github.com/github/codeql.git
synced 2026-04-23 15:55:18 +02:00
Revert "JavaScript: Improve double-escaping query"
This commit is contained in:
@@ -10,8 +10,8 @@ attacks such as cross-site scripting. One particular example of this is HTML ent
|
||||
where HTML special characters are replaced by HTML character entities to prevent them from being
|
||||
interpreted as HTML markup. For example, the less-than character is encoded as <code>&lt;</code>
|
||||
and the double-quote character as <code>&quot;</code>.
|
||||
Other examples include backslash escaping or JSON encoding for including untrusted data in string
|
||||
literals, and percent-encoding for URI components.
|
||||
Other examples include backslash-escaping for including untrusted data in string literals and
|
||||
percent-encoding for URI components.
|
||||
</p>
|
||||
<p>
|
||||
The reverse process of replacing escape sequences with the characters they represent is known as
|
||||
|
||||
@@ -46,12 +46,7 @@ string getStringValue(RegExpLiteral rl) {
|
||||
*/
|
||||
DataFlow::Node getASimplePredecessor(DataFlow::Node nd) {
|
||||
result = nd.getAPredecessor() and
|
||||
not exists(SsaDefinition ssa |
|
||||
ssa = nd.(DataFlow::SsaDefinitionNode).getSsaVariable().getDefinition()
|
||||
|
|
||||
ssa instanceof SsaPhiNode or
|
||||
ssa instanceof SsaVariableCapture
|
||||
)
|
||||
not nd.(DataFlow::SsaDefinitionNode).getSsaVariable().getDefinition() instanceof SsaPhiNode
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,31 +54,38 @@ DataFlow::Node getASimplePredecessor(DataFlow::Node nd) {
|
||||
* into a form described by regular expression `regex`.
|
||||
*/
|
||||
predicate escapingScheme(string metachar, string regex) {
|
||||
metachar = "&" and regex = "&.+;"
|
||||
metachar = "&" and regex = "&.*;"
|
||||
or
|
||||
metachar = "%" and regex = "%.+"
|
||||
metachar = "%" and regex = "%.*"
|
||||
or
|
||||
metachar = "\\" and regex = "\\\\.+"
|
||||
metachar = "\\" and regex = "\\\\.*"
|
||||
}
|
||||
|
||||
/**
|
||||
* A method call that performs string replacement.
|
||||
* A call to `String.prototype.replace` that replaces all instances of a pattern.
|
||||
*/
|
||||
abstract class Replacement extends DataFlow::Node {
|
||||
class Replacement extends DataFlow::Node {
|
||||
RegExpLiteral pattern;
|
||||
|
||||
Replacement() {
|
||||
exists(DataFlow::MethodCallNode mcn | this = mcn |
|
||||
mcn.getMethodName() = "replace" and
|
||||
pattern.flow().(DataFlow::SourceNode).flowsTo(mcn.getArgument(0)) and
|
||||
mcn.getNumArgument() = 2 and
|
||||
pattern.isGlobal()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this replacement replaces the string `input` with `output`.
|
||||
*/
|
||||
abstract predicate replaces(string input, string output);
|
||||
|
||||
/**
|
||||
* Gets the input of this replacement.
|
||||
*/
|
||||
abstract DataFlow::Node getInput();
|
||||
|
||||
/**
|
||||
* Gets the output of this replacement.
|
||||
*/
|
||||
abstract DataFlow::SourceNode getOutput();
|
||||
predicate replaces(string input, string output) {
|
||||
exists(DataFlow::MethodCallNode mcn |
|
||||
mcn = this and
|
||||
input = getStringValue(pattern) and
|
||||
output = mcn.getArgument(1).getStringValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this replacement escapes `char` using `metachar`.
|
||||
@@ -116,12 +118,9 @@ abstract class Replacement extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the previous replacement in this chain of replacements.
|
||||
*/
|
||||
Replacement getPreviousReplacement() { result.getOutput() = getASimplePredecessor*(getInput()) }
|
||||
|
||||
/**
|
||||
* Gets the next replacement in this chain of replacements.
|
||||
*/
|
||||
Replacement getNextReplacement() { this = result.getPreviousReplacement() }
|
||||
Replacement getPreviousReplacement() {
|
||||
result = getASimplePredecessor*(this.(DataFlow::MethodCallNode).getReceiver())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an earlier replacement in this chain of replacements that
|
||||
@@ -131,9 +130,7 @@ abstract class Replacement extends DataFlow::Node {
|
||||
exists(Replacement pred | pred = this.getPreviousReplacement() |
|
||||
if pred.escapes(_, metachar)
|
||||
then result = pred
|
||||
else (
|
||||
not pred.unescapes(metachar, _) and result = pred.getAnEarlierEscaping(metachar)
|
||||
)
|
||||
else result = pred.getAnEarlierEscaping(metachar)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -145,100 +142,11 @@ abstract class Replacement extends DataFlow::Node {
|
||||
exists(Replacement succ | this = succ.getPreviousReplacement() |
|
||||
if succ.unescapes(metachar, _)
|
||||
then result = succ
|
||||
else (
|
||||
not succ.escapes(_, metachar) and result = succ.getALaterUnescaping(metachar)
|
||||
)
|
||||
else result = succ.getALaterUnescaping(metachar)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `String.prototype.replace` that replaces all instances of a pattern.
|
||||
*/
|
||||
class GlobalStringReplacement extends Replacement, DataFlow::MethodCallNode {
|
||||
RegExpLiteral pattern;
|
||||
|
||||
GlobalStringReplacement() {
|
||||
this.getMethodName() = "replace" and
|
||||
pattern.flow().(DataFlow::SourceNode).flowsTo(this.getArgument(0)) and
|
||||
this.getNumArgument() = 2 and
|
||||
pattern.isGlobal()
|
||||
}
|
||||
|
||||
override predicate replaces(string input, string output) {
|
||||
input = getStringValue(pattern) and
|
||||
output = this.getArgument(1).getStringValue()
|
||||
or
|
||||
exists(DataFlow::FunctionNode replacer, DataFlow::PropRead pr, DataFlow::ObjectLiteralNode map |
|
||||
replacer = getCallback(1) and
|
||||
replacer.getParameter(0).flowsToExpr(pr.getPropertyNameExpr()) and
|
||||
pr = map.getAPropertyRead() and
|
||||
pr.flowsTo(replacer.getAReturn()) and
|
||||
map.asExpr().(ObjectExpr).getPropertyByName(input).getInit().getStringValue() = output
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput() { result = this.getReceiver() }
|
||||
|
||||
override DataFlow::SourceNode getOutput() { result = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `JSON.stringify`, viewed as a string replacement.
|
||||
*/
|
||||
class JsonStringifyReplacement extends Replacement, DataFlow::CallNode {
|
||||
JsonStringifyReplacement() { this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") }
|
||||
|
||||
override predicate replaces(string input, string output) {
|
||||
input = "\\" and output = "\\\\"
|
||||
// the other replacements are not relevant for this query
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput() { result = this.getArgument(0) }
|
||||
|
||||
override DataFlow::SourceNode getOutput() { result = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `JSON.parse`, viewed as a string replacement.
|
||||
*/
|
||||
class JsonParseReplacement extends Replacement {
|
||||
JsonParserCall self;
|
||||
|
||||
JsonParseReplacement() { this = self }
|
||||
|
||||
override predicate replaces(string input, string output) {
|
||||
input = "\\\\" and output = "\\"
|
||||
// the other replacements are not relevant for this query
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput() { result = self.getInput() }
|
||||
|
||||
override DataFlow::SourceNode getOutput() { result = self.getOutput() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A string replacement wrapped in a utility function.
|
||||
*/
|
||||
class WrappedReplacement extends Replacement, DataFlow::CallNode {
|
||||
int i;
|
||||
|
||||
Replacement inner;
|
||||
|
||||
WrappedReplacement() {
|
||||
exists(DataFlow::FunctionNode wrapped | wrapped.getFunction() = getACallee() |
|
||||
wrapped.getParameter(i).flowsTo(inner.getPreviousReplacement*().getInput()) and
|
||||
inner.getNextReplacement*().getOutput().flowsTo(wrapped.getAReturn())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate replaces(string input, string output) { inner.replaces(input, output) }
|
||||
|
||||
override DataFlow::Node getInput() { result = getArgument(i) }
|
||||
|
||||
override DataFlow::SourceNode getOutput() { result = this }
|
||||
}
|
||||
|
||||
from Replacement primary, Replacement supplementary, string message, string metachar
|
||||
where
|
||||
primary.escapes(metachar, _) and
|
||||
|
||||
Reference in New Issue
Block a user