зеркало из https://github.com/github/codeql.git
Merge remote-tracking branch 'upstream/master' into typeAheadSink
This commit is contained in:
Коммит
c6c1ebe81a
|
@ -12,9 +12,9 @@
|
|||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Cross-site scripting through exception (`js/xss-through-exception`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where an exception is written to the DOM. Results are not shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
|
@ -25,3 +25,6 @@
|
|||
| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | This query now recognizes additional ways event handler receivers can be bound. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimick this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.
|
||||
|
||||
|
|
|
@ -84,10 +84,10 @@ predicate hasZeroParamDecl(Function f) {
|
|||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
predicate isCompiledAsC(Function f) {
|
||||
exists(File file | file.compiledAsC() |
|
||||
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
|
||||
)
|
||||
predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
from FunctionCall fc, Function f, Parameter p
|
||||
|
@ -95,7 +95,7 @@ where
|
|||
f = fc.getTarget() and
|
||||
p = f.getAParameter() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
p.getIndex() < fc.getNumberOfArguments() and
|
||||
|
|
|
@ -24,10 +24,10 @@ predicate hasZeroParamDecl(Function f) {
|
|||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
predicate isCompiledAsC(Function f) {
|
||||
exists(File file | file.compiledAsC() |
|
||||
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
|
||||
)
|
||||
predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
from FunctionCall fc, Function f
|
||||
|
@ -36,7 +36,7 @@ where
|
|||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
// There is an explicit declaration of the function whose parameter count is larger
|
||||
// than the number of call arguments
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
|
|
|
@ -25,10 +25,10 @@ predicate hasZeroParamDecl(Function f) {
|
|||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
predicate isCompiledAsC(Function f) {
|
||||
exists(File file | file.compiledAsC() |
|
||||
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
|
||||
)
|
||||
predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
from FunctionCall fc, Function f
|
||||
|
@ -36,7 +36,7 @@ where
|
|||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
exists(f.getBlock()) and
|
||||
// There must not exist a declaration with the number of parameters
|
||||
// at least as large as the number of call arguments
|
||||
|
|
|
@ -21,8 +21,10 @@ import com.semmle.util.language.LegacyLanguage;
|
|||
import com.semmle.util.process.Env;
|
||||
import com.semmle.util.projectstructure.ProjectLayout;
|
||||
import com.semmle.util.trap.TrapWriter;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.net.URI;
|
||||
|
@ -195,6 +197,11 @@ public class AutoBuild {
|
|||
private final String defaultEncoding;
|
||||
private ExecutorService threadPool;
|
||||
private volatile boolean seenCode = false;
|
||||
private boolean installDependencies = false;
|
||||
private int installDependenciesTimeout;
|
||||
|
||||
/** The default timeout when running <code>yarn</code>, in milliseconds. */
|
||||
public static final int INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT = 10 * 60 * 1000; // 10 minutes
|
||||
|
||||
public AutoBuild() {
|
||||
this.LGTM_SRC = toRealPath(getPathFromEnvVar("LGTM_SRC"));
|
||||
|
@ -204,6 +211,11 @@ public class AutoBuild {
|
|||
this.typeScriptMode =
|
||||
getEnumFromEnvVar("LGTM_INDEX_TYPESCRIPT", TypeScriptMode.class, TypeScriptMode.FULL);
|
||||
this.defaultEncoding = getEnvVar("LGTM_INDEX_DEFAULT_ENCODING");
|
||||
this.installDependencies = Boolean.valueOf(getEnvVar("LGTM_INDEX_TYPESCRIPT_INSTALL_DEPS"));
|
||||
this.installDependenciesTimeout =
|
||||
Env.systemEnv()
|
||||
.getInt(
|
||||
"LGTM_INDEX_TYPESCRIPT_INSTALL_DEPS_TIMEOUT", INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT);
|
||||
setupFileTypes();
|
||||
setupXmlMode();
|
||||
setupMatchers();
|
||||
|
@ -533,6 +545,10 @@ public class AutoBuild {
|
|||
List<Path> tsconfigFiles = new ArrayList<>();
|
||||
findFilesToExtract(defaultExtractor, filesToExtract, tsconfigFiles);
|
||||
|
||||
if (!tsconfigFiles.isEmpty() && this.installDependencies) {
|
||||
this.installDependencies(filesToExtract);
|
||||
}
|
||||
|
||||
// extract TypeScript projects and files
|
||||
Set<Path> extractedFiles = extractTypeScript(defaultExtractor, filesToExtract, tsconfigFiles);
|
||||
|
||||
|
@ -549,6 +565,61 @@ public class AutoBuild {
|
|||
}
|
||||
}
|
||||
|
||||
/** Returns true if yarn is installed, otherwise prints a warning and returns false. */
|
||||
private boolean verifyYarnInstallation() {
|
||||
ProcessBuilder pb = new ProcessBuilder(Arrays.asList("yarn", "-v"));
|
||||
try {
|
||||
Process process = pb.start();
|
||||
boolean completed = process.waitFor(this.installDependenciesTimeout, TimeUnit.MILLISECONDS);
|
||||
if (!completed) {
|
||||
System.err.println("Yarn could not be launched. Timeout during 'yarn -v'.");
|
||||
return false;
|
||||
}
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String version = reader.readLine();
|
||||
System.out.println("Found yarn version: " + version);
|
||||
return true;
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
System.err.println(
|
||||
"Yarn not found. Please put 'yarn' on the PATH for automatic dependency installation.");
|
||||
Exceptions.ignore(ex, "Continue without dependency installation");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected void installDependencies(Set<Path> filesToExtract) {
|
||||
if (!verifyYarnInstallation()) {
|
||||
return;
|
||||
}
|
||||
for (Path file : filesToExtract) {
|
||||
if (file.getFileName().toString().equals("package.json")) {
|
||||
System.out.println("Installing dependencies from " + file);
|
||||
ProcessBuilder pb =
|
||||
new ProcessBuilder(
|
||||
Arrays.asList(
|
||||
"yarn",
|
||||
"install",
|
||||
"--verbose",
|
||||
"--non-interactive",
|
||||
"--ignore-scripts",
|
||||
"--ignore-platform",
|
||||
"--ignore-engines",
|
||||
"--ignore-optional",
|
||||
"--no-default-rc",
|
||||
"--no-bin-links",
|
||||
"--pure-lockfile"));
|
||||
pb.directory(file.getParent().toFile());
|
||||
pb.redirectOutput(Redirect.INHERIT);
|
||||
pb.redirectError(Redirect.INHERIT);
|
||||
try {
|
||||
pb.start().waitFor(this.installDependenciesTimeout, TimeUnit.MILLISECONDS);
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
throw new ResourceError("Could not install dependencies from " + file, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ExtractorConfig mkExtractorConfig() {
|
||||
ExtractorConfig config = new ExtractorConfig(true);
|
||||
config = config.withSourceType(getSourceType());
|
||||
|
|
|
@ -128,6 +128,11 @@ public class AutoBuildTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installDependencies(Set<Path> filesToExtract) {
|
||||
// never install dependencies during testing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extractXml() throws IOException {
|
||||
Files.walkFileTree(
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Directly writing exceptions to a webpage without sanitization allows for a cross-site scripting
|
||||
vulnerability if the value of the exception can be influenced by a user.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
To guard against cross-site scripting, consider using contextual output encoding/escaping before
|
||||
writing user input to the page, or one of the other solutions that are mentioned in the
|
||||
references.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example shows an exception being written directly to the document,
|
||||
and this exception can potentially be influenced by the page URL,
|
||||
leaving the website vulnerable to cross-site scripting.
|
||||
</p>
|
||||
<sample src="examples/ExceptionXss.js" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html">DOM based
|
||||
XSS Prevention Cheat Sheet</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html">XSS
|
||||
(Cross Site Scripting) Prevention Cheat Sheet</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP
|
||||
<a href="https://www.owasp.org/index.php/DOM_Based_XSS">DOM Based XSS</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP
|
||||
<a href="https://www.owasp.org/index.php/Types_of_Cross-Site_Scripting">Types of Cross-Site
|
||||
Scripting</a>.
|
||||
</li>
|
||||
<li>
|
||||
Wikipedia: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-site scripting</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @name Cross-site scripting through exception
|
||||
* @description Inserting data from an exception containing user
|
||||
* input into the DOM may enable cross-site scripting.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
* @id js/xss-through-exception
|
||||
* @tags security
|
||||
* external/cwe/cwe-079
|
||||
* external/cwe/cwe-116
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.ExceptionXss::ExceptionXss
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from
|
||||
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where
|
||||
cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
sink.getNode().(Xss::Shared::Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(),
|
||||
"user-provided value"
|
|
@ -0,0 +1,10 @@
|
|||
function setLanguageOptions() {
|
||||
var href = document.location.href,
|
||||
deflt = href.substring(href.indexOf("default=")+8);
|
||||
|
||||
try {
|
||||
var parsed = unknownParseFunction(deflt);
|
||||
} catch(e) {
|
||||
document.write("Had an error: " + e + ".");
|
||||
}
|
||||
}
|
|
@ -245,6 +245,21 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
|
|||
ctx.(ConditionalExpr).inNullSensitiveContext()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data-flow node where exceptions thrown by this expression will
|
||||
* propagate if this expression causes an exception to be thrown.
|
||||
*/
|
||||
DataFlow::Node getExceptionTarget() {
|
||||
if exists(this.getEnclosingStmt().getEnclosingTryCatchStmt())
|
||||
then
|
||||
result = DataFlow::parameterNode(this
|
||||
.getEnclosingStmt()
|
||||
.getEnclosingTryCatchStmt()
|
||||
.getACatchClause()
|
||||
.getAParameter())
|
||||
else result = any(DataFlow::FunctionNode f | f.getFunction() = this.getContainer()).getExceptionalReturn()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -73,21 +73,21 @@ class RegExpTerm extends Locatable, @regexpterm {
|
|||
/** Holds if this regular expression term can match the empty string. */
|
||||
predicate isNullable() { none() } // Overridden in subclasses.
|
||||
|
||||
/** Gets the regular expression term that is matched before this one, if any. */
|
||||
/** Gets the regular expression term that is matched (textually) before this one, if any. */
|
||||
RegExpTerm getPredecessor() {
|
||||
exists(RegExpSequence seq, int i |
|
||||
seq.getChild(i) = this and
|
||||
seq.getChild(i - getDirection()) = result
|
||||
seq.getChild(i - 1) = result
|
||||
)
|
||||
or
|
||||
result = getParent().(RegExpTerm).getPredecessor()
|
||||
}
|
||||
|
||||
/** Gets the regular expression term that is matched after this one, if any. */
|
||||
/** Gets the regular expression term that is matched (textually) after this one, if any. */
|
||||
RegExpTerm getSuccessor() {
|
||||
exists(RegExpSequence seq, int i |
|
||||
seq.getChild(i) = this and
|
||||
seq.getChild(i + getDirection()) = result
|
||||
seq.getChild(i + 1) = result
|
||||
)
|
||||
or
|
||||
exists(RegExpTerm parent |
|
||||
|
@ -98,12 +98,6 @@ class RegExpTerm extends Locatable, @regexpterm {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the matching direction of this term: `1` if it is in a forward-matching
|
||||
* context, `-1` if it is in a backward-matching context.
|
||||
*/
|
||||
private int getDirection() { if isInBackwardMatchingContext() then result = -1 else result = 1 }
|
||||
|
||||
/**
|
||||
* Holds if this regular term is in a forward-matching context, that is,
|
||||
* it has no enclosing lookbehind assertions.
|
||||
|
|
|
@ -55,6 +55,18 @@ class Stmt extends @stmt, ExprOrStmt, Documentable {
|
|||
}
|
||||
|
||||
override predicate isAmbient() { hasDeclareKeyword(this) or getParent().isAmbient() }
|
||||
|
||||
/**
|
||||
* Gets the `try` statement with a catch block containing this statement without
|
||||
* crossing function boundaries or other `try ` statements with catch blocks.
|
||||
*/
|
||||
TryStmt getEnclosingTryCatchStmt() {
|
||||
getParentStmt+() = result.getBody() and
|
||||
exists(result.getACatchClause()) and
|
||||
not exists(TryStmt mid | exists(mid.getACatchClause()) |
|
||||
getParentStmt+() = mid.getBody() and mid.getParentStmt+() = result.getBody()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1125,6 +1125,9 @@ class MidPathNode extends PathNode, MkMidNode {
|
|||
// Skip to the top of big left-leaning string concatenation trees.
|
||||
nd = any(AddExpr add).flow() and
|
||||
nd = any(AddExpr add).getAnOperand().flow()
|
||||
or
|
||||
// Skip the exceptional return on functions, as this highlights the entire function.
|
||||
nd = any(DataFlow::FunctionNode f).getExceptionalReturn()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -558,6 +558,23 @@ module TaintTracking {
|
|||
succ = this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge arising from calling `String.prototype.match()`.
|
||||
*/
|
||||
private class StringMatchTaintStep extends AdditionalTaintStep, DataFlow::MethodCallNode {
|
||||
StringMatchTaintStep() {
|
||||
this.getMethodName() = "match" and
|
||||
this.getNumArgument() = 1 and
|
||||
this.getArgument(0) .analyze().getAType() = TTRegExp()
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = this.getReceiver() and
|
||||
succ = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge arising from JSON unparsing.
|
||||
|
|
|
@ -66,19 +66,7 @@ predicate localExceptionStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|||
or
|
||||
DataFlow::exceptionalInvocationReturnNode(pred, expr)
|
||||
|
|
||||
// Propagate out of enclosing function.
|
||||
not exists(getEnclosingTryStmt(expr.getEnclosingStmt())) and
|
||||
exists(Function f |
|
||||
f = expr.getEnclosingFunction() and
|
||||
DataFlow::exceptionalFunctionReturnNode(succ, f)
|
||||
)
|
||||
or
|
||||
// Propagate to enclosing try/catch.
|
||||
// To avoid false flow, we only propagate to an unguarded catch clause.
|
||||
exists(TryStmt try |
|
||||
try = getEnclosingTryStmt(expr.getEnclosingStmt()) and
|
||||
DataFlow::parameterNode(succ, try.getCatchClause().getAParameter())
|
||||
)
|
||||
succ = expr.getExceptionTarget()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -156,19 +144,6 @@ private module CachedSteps {
|
|||
cached
|
||||
predicate callStep(DataFlow::Node pred, DataFlow::Node succ) { argumentPassing(_, pred, _, succ) }
|
||||
|
||||
/**
|
||||
* Gets the `try` statement containing `stmt` without crossing function boundaries
|
||||
* or other `try ` statements.
|
||||
*/
|
||||
cached
|
||||
TryStmt getEnclosingTryStmt(Stmt stmt) {
|
||||
result.getBody() = stmt
|
||||
or
|
||||
not stmt instanceof Function and
|
||||
not stmt = any(TryStmt try).getBody() and
|
||||
result = getEnclosingTryStmt(stmt.getParentStmt())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a flow step from `pred` to `succ` through:
|
||||
* - returning a value from a function call, or
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import javascript
|
||||
|
||||
module DomBasedXss {
|
||||
import Xss::DomBasedXss
|
||||
import DomBasedXssCustomizations::DomBasedXss
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about XSS.
|
||||
|
@ -33,16 +33,4 @@ module DomBasedXss {
|
|||
node instanceof Sanitizer
|
||||
}
|
||||
}
|
||||
|
||||
/** A source of remote user input, considered as a flow source for DOM-based XSS. */
|
||||
class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access of the URL of this page, or of the referrer to this page.
|
||||
*/
|
||||
class LocationSource extends Source {
|
||||
LocationSource() { this = DOM::locationSource() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Provides default sources for reasoning about DOM-based
|
||||
* cross-site scripting vulnerabilities.
|
||||
*/
|
||||
|
||||
|
||||
import javascript
|
||||
|
||||
module DomBasedXss {
|
||||
import Xss::DomBasedXss
|
||||
|
||||
/** A source of remote user input, considered as a flow source for DOM-based XSS. */
|
||||
class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access of the URL of this page, or of the referrer to this page.
|
||||
*/
|
||||
class LocationSource extends Source {
|
||||
LocationSource() { this = DOM::locationSource() }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about cross-site
|
||||
* scripting vulnerabilities where the taint-flow passes through a thrown
|
||||
* exception.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
module ExceptionXss {
|
||||
import DomBasedXssCustomizations::DomBasedXss as DomBasedXssCustom
|
||||
import ReflectedXssCustomizations::ReflectedXss as ReflectedXssCustom
|
||||
import Xss as Xss
|
||||
|
||||
/**
|
||||
* Holds if `node` is unlikely to cause an exception containing sensitive information to be thrown.
|
||||
*/
|
||||
private predicate isUnlikelyToThrowSensitiveInformation(DataFlow::Node node) {
|
||||
node = any(DataFlow::CallNode call | call.getCalleeName() = "getElementById").getAnArgument()
|
||||
or
|
||||
node = any(DataFlow::CallNode call | call.getCalleeName() = "indexOf").getAnArgument()
|
||||
or
|
||||
node = any(DataFlow::CallNode call | call.getCalleeName() = "stringify").getAnArgument()
|
||||
or
|
||||
node = DataFlow::globalVarRef("console").getAMemberCall(_).getAnArgument()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` can possibly cause an exception containing sensitive information to be thrown.
|
||||
*/
|
||||
predicate canThrowSensitiveInformation(DataFlow::Node node) {
|
||||
not isUnlikelyToThrowSensitiveInformation(node) and
|
||||
(
|
||||
// in the case of reflective calls the below ensures that both InvokeNodes have no known callee.
|
||||
forex(DataFlow::InvokeNode call | node = call.getAnArgument() | not exists(call.getACallee()))
|
||||
or
|
||||
node.asExpr().getEnclosingStmt() instanceof ThrowStmt
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A FlowLabel representing tainted data that has not been thrown in an exception.
|
||||
* In the js/xss-through-exception query data-flow can only reach a sink after
|
||||
* the data has been thrown as an exception, and data that has not been thrown
|
||||
* as an exception therefore has this flow label, and only this flow label, associated with it.
|
||||
*/
|
||||
class NotYetThrown extends DataFlow::FlowLabel {
|
||||
NotYetThrown() { this = "NotYetThrown" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about XSS with possible exceptional flow.
|
||||
* Flow labels are used to ensure that we only report taint-flow that has been thrown in
|
||||
* an exception.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "ExceptionXss" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
|
||||
source instanceof Xss::Shared::Source and label instanceof NotYetThrown
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
|
||||
sink instanceof Xss::Shared::Sink and not label instanceof NotYetThrown
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Xss::Shared::Sanitizer }
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel inlbl,
|
||||
DataFlow::FlowLabel outlbl
|
||||
) {
|
||||
inlbl instanceof NotYetThrown and (outlbl.isTaint() or outlbl instanceof NotYetThrown) and
|
||||
succ = pred.asExpr().getExceptionTarget() and
|
||||
canThrowSensitiveInformation(pred)
|
||||
or
|
||||
// All the usual taint-flow steps apply on data-flow before it has been thrown in an exception.
|
||||
this.isAdditionalFlowStep(pred, succ) and inlbl instanceof NotYetThrown and outlbl instanceof NotYetThrown
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
import javascript
|
||||
|
||||
module ReflectedXss {
|
||||
import Xss::ReflectedXss
|
||||
import ReflectedXssCustomizations::ReflectedXss
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about XSS.
|
||||
|
@ -23,13 +23,4 @@ module ReflectedXss {
|
|||
node instanceof Sanitizer
|
||||
}
|
||||
}
|
||||
|
||||
/** A third-party controllable request input, considered as a flow source for reflected XSS. */
|
||||
class ThirdPartyRequestInputAccessAsSource extends Source {
|
||||
ThirdPartyRequestInputAccessAsSource() {
|
||||
this.(HTTP::RequestInputAccess).isThirdPartyControllable()
|
||||
or
|
||||
this.(HTTP::RequestHeaderAccess).getAHeaderName() = "referer"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Provides default sources for reasoning about reflected
|
||||
* cross-site scripting vulnerabilities.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
module ReflectedXss {
|
||||
import Xss::ReflectedXss
|
||||
|
||||
/** A third-party controllable request input, considered as a flow source for reflected XSS. */
|
||||
class ThirdPartyRequestInputAccessAsSource extends Source {
|
||||
ThirdPartyRequestInputAccessAsSource() {
|
||||
this.(HTTP::RequestInputAccess).isThirdPartyControllable()
|
||||
or
|
||||
this.(HTTP::RequestHeaderAccess).getAHeaderName() = "referer"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,4 +26,7 @@
|
|||
/^(^y|^z)(u$|v$)$/;
|
||||
|
||||
// OK
|
||||
/x*^y/;
|
||||
/x*^y/;
|
||||
|
||||
// OK
|
||||
/(?<=(^|\/)(\.|\.\.))$/;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
| tst.js:2:10:2:10 | $ | This assertion can never match. |
|
||||
| tst.js:11:3:11:3 | $ | This assertion can never match. |
|
||||
| tst.js:20:3:20:3 | $ | This assertion can never match. |
|
||||
| tst.js:38:6:38:6 | $ | This assertion can never match. |
|
||||
|
|
|
@ -32,4 +32,7 @@
|
|||
/x(?!y+$).*y.*/;
|
||||
|
||||
// OK
|
||||
/x(?=[yz]+$).*yz.*/;
|
||||
/x(?=[yz]+$).*yz.*/;
|
||||
|
||||
// NOT OK
|
||||
/(?<=$x)yz/;
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
nodes
|
||||
| exception-xss.js:2:9:2:31 | foo |
|
||||
| exception-xss.js:2:15:2:31 | document.location |
|
||||
| exception-xss.js:2:15:2:31 | document.location |
|
||||
| exception-xss.js:9:11:9:13 | foo |
|
||||
| exception-xss.js:10:10:10:10 | e |
|
||||
| exception-xss.js:11:18:11:18 | e |
|
||||
| exception-xss.js:11:18:11:18 | e |
|
||||
| exception-xss.js:15:3:15:12 | exceptional return of inner(foo) |
|
||||
| exception-xss.js:15:9:15:11 | foo |
|
||||
| exception-xss.js:16:10:16:10 | e |
|
||||
| exception-xss.js:17:18:17:18 | e |
|
||||
| exception-xss.js:17:18:17:18 | e |
|
||||
| exception-xss.js:21:11:21:13 | foo |
|
||||
| exception-xss.js:21:11:21:21 | foo + "bar" |
|
||||
| exception-xss.js:22:10:22:10 | e |
|
||||
| exception-xss.js:23:18:23:18 | e |
|
||||
| exception-xss.js:23:18:23:18 | e |
|
||||
| exception-xss.js:33:11:33:22 | ["bar", foo] |
|
||||
| exception-xss.js:33:19:33:21 | foo |
|
||||
| exception-xss.js:34:10:34:10 | e |
|
||||
| exception-xss.js:35:18:35:18 | e |
|
||||
| exception-xss.js:35:18:35:18 | e |
|
||||
| exception-xss.js:46:3:46:19 | exceptional return of deep("bar" + foo) |
|
||||
| exception-xss.js:46:8:46:18 | "bar" + foo |
|
||||
| exception-xss.js:46:16:46:18 | foo |
|
||||
| exception-xss.js:47:10:47:10 | e |
|
||||
| exception-xss.js:48:18:48:18 | e |
|
||||
| exception-xss.js:48:18:48:18 | e |
|
||||
| exception-xss.js:81:3:81:19 | exceptional return of myWeirdInner(foo) |
|
||||
| exception-xss.js:81:16:81:18 | foo |
|
||||
| exception-xss.js:82:10:82:10 | e |
|
||||
| exception-xss.js:83:18:83:18 | e |
|
||||
| exception-xss.js:83:18:83:18 | e |
|
||||
| exception-xss.js:89:11:89:13 | foo |
|
||||
| exception-xss.js:89:11:89:26 | foo.match(/foo/) |
|
||||
| exception-xss.js:90:10:90:10 | e |
|
||||
| exception-xss.js:91:18:91:18 | e |
|
||||
| exception-xss.js:91:18:91:18 | e |
|
||||
| exception-xss.js:95:11:95:22 | [foo, "bar"] |
|
||||
| exception-xss.js:95:12:95:14 | foo |
|
||||
| exception-xss.js:96:10:96:10 | e |
|
||||
| exception-xss.js:97:18:97:18 | e |
|
||||
| exception-xss.js:97:18:97:18 | e |
|
||||
| exception-xss.js:102:12:102:14 | foo |
|
||||
| exception-xss.js:106:10:106:10 | e |
|
||||
| exception-xss.js:107:18:107:18 | e |
|
||||
| exception-xss.js:107:18:107:18 | e |
|
||||
| exception-xss.js:117:13:117:25 | req.params.id |
|
||||
| exception-xss.js:117:13:117:25 | req.params.id |
|
||||
| exception-xss.js:118:11:118:11 | e |
|
||||
| exception-xss.js:119:14:119:30 | "Exception: " + e |
|
||||
| exception-xss.js:119:14:119:30 | "Exception: " + e |
|
||||
| exception-xss.js:119:30:119:30 | e |
|
||||
| exception-xss.js:125:48:125:64 | document.location |
|
||||
| exception-xss.js:125:48:125:64 | document.location |
|
||||
| exception-xss.js:125:48:125:71 | documen ... .search |
|
||||
| exception-xss.js:128:11:128:52 | session ... ssion') |
|
||||
| exception-xss.js:129:10:129:10 | e |
|
||||
| exception-xss.js:130:18:130:18 | e |
|
||||
| exception-xss.js:130:18:130:18 | e |
|
||||
| tst.js:298:9:298:16 | location |
|
||||
| tst.js:298:9:298:16 | location |
|
||||
| tst.js:299:10:299:10 | e |
|
||||
| tst.js:300:20:300:20 | e |
|
||||
| tst.js:300:20:300:20 | e |
|
||||
| tst.js:305:10:305:17 | location |
|
||||
| tst.js:305:10:305:17 | location |
|
||||
| tst.js:307:10:307:10 | e |
|
||||
| tst.js:308:20:308:20 | e |
|
||||
| tst.js:308:20:308:20 | e |
|
||||
edges
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:9:11:9:13 | foo |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:15:9:15:11 | foo |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:21:11:21:13 | foo |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:33:19:33:21 | foo |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:46:16:46:18 | foo |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:81:16:81:18 | foo |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:89:11:89:13 | foo |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:95:12:95:14 | foo |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:102:12:102:14 | foo |
|
||||
| exception-xss.js:2:15:2:31 | document.location | exception-xss.js:2:9:2:31 | foo |
|
||||
| exception-xss.js:2:15:2:31 | document.location | exception-xss.js:2:9:2:31 | foo |
|
||||
| exception-xss.js:9:11:9:13 | foo | exception-xss.js:10:10:10:10 | e |
|
||||
| exception-xss.js:10:10:10:10 | e | exception-xss.js:11:18:11:18 | e |
|
||||
| exception-xss.js:10:10:10:10 | e | exception-xss.js:11:18:11:18 | e |
|
||||
| exception-xss.js:15:3:15:12 | exceptional return of inner(foo) | exception-xss.js:16:10:16:10 | e |
|
||||
| exception-xss.js:15:9:15:11 | foo | exception-xss.js:15:3:15:12 | exceptional return of inner(foo) |
|
||||
| exception-xss.js:16:10:16:10 | e | exception-xss.js:17:18:17:18 | e |
|
||||
| exception-xss.js:16:10:16:10 | e | exception-xss.js:17:18:17:18 | e |
|
||||
| exception-xss.js:21:11:21:13 | foo | exception-xss.js:21:11:21:21 | foo + "bar" |
|
||||
| exception-xss.js:21:11:21:21 | foo + "bar" | exception-xss.js:22:10:22:10 | e |
|
||||
| exception-xss.js:22:10:22:10 | e | exception-xss.js:23:18:23:18 | e |
|
||||
| exception-xss.js:22:10:22:10 | e | exception-xss.js:23:18:23:18 | e |
|
||||
| exception-xss.js:33:11:33:22 | ["bar", foo] | exception-xss.js:34:10:34:10 | e |
|
||||
| exception-xss.js:33:19:33:21 | foo | exception-xss.js:33:11:33:22 | ["bar", foo] |
|
||||
| exception-xss.js:34:10:34:10 | e | exception-xss.js:35:18:35:18 | e |
|
||||
| exception-xss.js:34:10:34:10 | e | exception-xss.js:35:18:35:18 | e |
|
||||
| exception-xss.js:46:3:46:19 | exceptional return of deep("bar" + foo) | exception-xss.js:47:10:47:10 | e |
|
||||
| exception-xss.js:46:8:46:18 | "bar" + foo | exception-xss.js:46:3:46:19 | exceptional return of deep("bar" + foo) |
|
||||
| exception-xss.js:46:16:46:18 | foo | exception-xss.js:46:8:46:18 | "bar" + foo |
|
||||
| exception-xss.js:47:10:47:10 | e | exception-xss.js:48:18:48:18 | e |
|
||||
| exception-xss.js:47:10:47:10 | e | exception-xss.js:48:18:48:18 | e |
|
||||
| exception-xss.js:81:3:81:19 | exceptional return of myWeirdInner(foo) | exception-xss.js:82:10:82:10 | e |
|
||||
| exception-xss.js:81:16:81:18 | foo | exception-xss.js:81:3:81:19 | exceptional return of myWeirdInner(foo) |
|
||||
| exception-xss.js:82:10:82:10 | e | exception-xss.js:83:18:83:18 | e |
|
||||
| exception-xss.js:82:10:82:10 | e | exception-xss.js:83:18:83:18 | e |
|
||||
| exception-xss.js:89:11:89:13 | foo | exception-xss.js:89:11:89:26 | foo.match(/foo/) |
|
||||
| exception-xss.js:89:11:89:26 | foo.match(/foo/) | exception-xss.js:90:10:90:10 | e |
|
||||
| exception-xss.js:90:10:90:10 | e | exception-xss.js:91:18:91:18 | e |
|
||||
| exception-xss.js:90:10:90:10 | e | exception-xss.js:91:18:91:18 | e |
|
||||
| exception-xss.js:95:11:95:22 | [foo, "bar"] | exception-xss.js:96:10:96:10 | e |
|
||||
| exception-xss.js:95:12:95:14 | foo | exception-xss.js:95:11:95:22 | [foo, "bar"] |
|
||||
| exception-xss.js:96:10:96:10 | e | exception-xss.js:97:18:97:18 | e |
|
||||
| exception-xss.js:96:10:96:10 | e | exception-xss.js:97:18:97:18 | e |
|
||||
| exception-xss.js:102:12:102:14 | foo | exception-xss.js:106:10:106:10 | e |
|
||||
| exception-xss.js:106:10:106:10 | e | exception-xss.js:107:18:107:18 | e |
|
||||
| exception-xss.js:106:10:106:10 | e | exception-xss.js:107:18:107:18 | e |
|
||||
| exception-xss.js:117:13:117:25 | req.params.id | exception-xss.js:118:11:118:11 | e |
|
||||
| exception-xss.js:117:13:117:25 | req.params.id | exception-xss.js:118:11:118:11 | e |
|
||||
| exception-xss.js:118:11:118:11 | e | exception-xss.js:119:30:119:30 | e |
|
||||
| exception-xss.js:119:30:119:30 | e | exception-xss.js:119:14:119:30 | "Exception: " + e |
|
||||
| exception-xss.js:119:30:119:30 | e | exception-xss.js:119:14:119:30 | "Exception: " + e |
|
||||
| exception-xss.js:125:48:125:64 | document.location | exception-xss.js:125:48:125:71 | documen ... .search |
|
||||
| exception-xss.js:125:48:125:64 | document.location | exception-xss.js:125:48:125:71 | documen ... .search |
|
||||
| exception-xss.js:125:48:125:71 | documen ... .search | exception-xss.js:128:11:128:52 | session ... ssion') |
|
||||
| exception-xss.js:128:11:128:52 | session ... ssion') | exception-xss.js:129:10:129:10 | e |
|
||||
| exception-xss.js:129:10:129:10 | e | exception-xss.js:130:18:130:18 | e |
|
||||
| exception-xss.js:129:10:129:10 | e | exception-xss.js:130:18:130:18 | e |
|
||||
| tst.js:298:9:298:16 | location | tst.js:299:10:299:10 | e |
|
||||
| tst.js:298:9:298:16 | location | tst.js:299:10:299:10 | e |
|
||||
| tst.js:299:10:299:10 | e | tst.js:300:20:300:20 | e |
|
||||
| tst.js:299:10:299:10 | e | tst.js:300:20:300:20 | e |
|
||||
| tst.js:305:10:305:17 | location | tst.js:307:10:307:10 | e |
|
||||
| tst.js:305:10:305:17 | location | tst.js:307:10:307:10 | e |
|
||||
| tst.js:307:10:307:10 | e | tst.js:308:20:308:20 | e |
|
||||
| tst.js:307:10:307:10 | e | tst.js:308:20:308:20 | e |
|
||||
#select
|
||||
| exception-xss.js:11:18:11:18 | e | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:11:18:11:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| exception-xss.js:17:18:17:18 | e | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:17:18:17:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| exception-xss.js:23:18:23:18 | e | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:23:18:23:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| exception-xss.js:35:18:35:18 | e | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:35:18:35:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| exception-xss.js:48:18:48:18 | e | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:48:18:48:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| exception-xss.js:83:18:83:18 | e | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:83:18:83:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| exception-xss.js:91:18:91:18 | e | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:91:18:91:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| exception-xss.js:97:18:97:18 | e | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:97:18:97:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| exception-xss.js:107:18:107:18 | e | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:107:18:107:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| exception-xss.js:119:14:119:30 | "Exception: " + e | exception-xss.js:117:13:117:25 | req.params.id | exception-xss.js:119:14:119:30 | "Exception: " + e | Cross-site scripting vulnerability due to $@. | exception-xss.js:117:13:117:25 | req.params.id | user-provided value |
|
||||
| exception-xss.js:130:18:130:18 | e | exception-xss.js:125:48:125:64 | document.location | exception-xss.js:130:18:130:18 | e | Cross-site scripting vulnerability due to $@. | exception-xss.js:125:48:125:64 | document.location | user-provided value |
|
||||
| tst.js:300:20:300:20 | e | tst.js:298:9:298:16 | location | tst.js:300:20:300:20 | e | Cross-site scripting vulnerability due to $@. | tst.js:298:9:298:16 | location | user-provided value |
|
||||
| tst.js:308:20:308:20 | e | tst.js:305:10:305:17 | location | tst.js:308:20:308:20 | e | Cross-site scripting vulnerability due to $@. | tst.js:305:10:305:17 | location | user-provided value |
|
|
@ -0,0 +1 @@
|
|||
Security/CWE-079/ExceptionXss.ql
|
|
@ -15,6 +15,11 @@ nodes
|
|||
| addEventListener.js:12:24:12:28 | event |
|
||||
| addEventListener.js:12:24:12:33 | event.data |
|
||||
| addEventListener.js:12:24:12:33 | event.data |
|
||||
| exception-xss.js:2:9:2:31 | foo |
|
||||
| exception-xss.js:2:15:2:31 | document.location |
|
||||
| exception-xss.js:2:15:2:31 | document.location |
|
||||
| exception-xss.js:86:17:86:19 | foo |
|
||||
| exception-xss.js:86:17:86:19 | foo |
|
||||
| jquery.js:2:7:2:40 | tainted |
|
||||
| jquery.js:2:17:2:33 | document.location |
|
||||
| jquery.js:2:17:2:33 | document.location |
|
||||
|
@ -313,9 +318,19 @@ nodes
|
|||
| tst.js:282:19:282:29 | window.name |
|
||||
| tst.js:285:59:285:65 | tainted |
|
||||
| tst.js:285:59:285:65 | tainted |
|
||||
| tst.js:297:35:297:42 | location |
|
||||
| tst.js:297:35:297:42 | location |
|
||||
| tst.js:297:35:297:42 | location |
|
||||
| tst.js:298:9:298:16 | location |
|
||||
| tst.js:298:9:298:16 | location |
|
||||
| tst.js:299:10:299:10 | e |
|
||||
| tst.js:300:20:300:20 | e |
|
||||
| tst.js:300:20:300:20 | e |
|
||||
| tst.js:305:10:305:17 | location |
|
||||
| tst.js:305:10:305:17 | location |
|
||||
| tst.js:307:10:307:10 | e |
|
||||
| tst.js:308:20:308:20 | e |
|
||||
| tst.js:308:20:308:20 | e |
|
||||
| tst.js:313:35:313:42 | location |
|
||||
| tst.js:313:35:313:42 | location |
|
||||
| tst.js:313:35:313:42 | location |
|
||||
| typeahead.js:20:13:20:45 | target |
|
||||
| typeahead.js:20:22:20:38 | document.location |
|
||||
| typeahead.js:20:22:20:38 | document.location |
|
||||
|
@ -351,6 +366,10 @@ edges
|
|||
| addEventListener.js:10:21:10:25 | event | addEventListener.js:12:24:12:28 | event |
|
||||
| addEventListener.js:12:24:12:28 | event | addEventListener.js:12:24:12:33 | event.data |
|
||||
| addEventListener.js:12:24:12:28 | event | addEventListener.js:12:24:12:33 | event.data |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:86:17:86:19 | foo |
|
||||
| exception-xss.js:2:9:2:31 | foo | exception-xss.js:86:17:86:19 | foo |
|
||||
| exception-xss.js:2:15:2:31 | document.location | exception-xss.js:2:9:2:31 | foo |
|
||||
| exception-xss.js:2:15:2:31 | document.location | exception-xss.js:2:9:2:31 | foo |
|
||||
| jquery.js:2:7:2:40 | tainted | jquery.js:4:5:4:11 | tainted |
|
||||
| jquery.js:2:7:2:40 | tainted | jquery.js:4:5:4:11 | tainted |
|
||||
| jquery.js:2:7:2:40 | tainted | jquery.js:7:20:7:26 | tainted |
|
||||
|
@ -610,7 +629,15 @@ edges
|
|||
| tst.js:282:9:282:29 | tainted | tst.js:285:59:285:65 | tainted |
|
||||
| tst.js:282:19:282:29 | window.name | tst.js:282:9:282:29 | tainted |
|
||||
| tst.js:282:19:282:29 | window.name | tst.js:282:9:282:29 | tainted |
|
||||
| tst.js:297:35:297:42 | location | tst.js:297:35:297:42 | location |
|
||||
| tst.js:298:9:298:16 | location | tst.js:299:10:299:10 | e |
|
||||
| tst.js:298:9:298:16 | location | tst.js:299:10:299:10 | e |
|
||||
| tst.js:299:10:299:10 | e | tst.js:300:20:300:20 | e |
|
||||
| tst.js:299:10:299:10 | e | tst.js:300:20:300:20 | e |
|
||||
| tst.js:305:10:305:17 | location | tst.js:307:10:307:10 | e |
|
||||
| tst.js:305:10:305:17 | location | tst.js:307:10:307:10 | e |
|
||||
| tst.js:307:10:307:10 | e | tst.js:308:20:308:20 | e |
|
||||
| tst.js:307:10:307:10 | e | tst.js:308:20:308:20 | e |
|
||||
| tst.js:313:35:313:42 | location | tst.js:313:35:313:42 | location |
|
||||
| typeahead.js:20:13:20:45 | target | typeahead.js:21:12:21:17 | target |
|
||||
| typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search |
|
||||
| typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search |
|
||||
|
@ -634,6 +661,7 @@ edges
|
|||
| addEventListener.js:2:20:2:29 | event.data | addEventListener.js:1:43:1:47 | event | addEventListener.js:2:20:2:29 | event.data | Cross-site scripting vulnerability due to $@. | addEventListener.js:1:43:1:47 | event | user-provided value |
|
||||
| addEventListener.js:6:20:6:23 | data | addEventListener.js:5:43:5:48 | {data} | addEventListener.js:6:20:6:23 | data | Cross-site scripting vulnerability due to $@. | addEventListener.js:5:43:5:48 | {data} | user-provided value |
|
||||
| addEventListener.js:12:24:12:33 | event.data | addEventListener.js:10:21:10:25 | event | addEventListener.js:12:24:12:33 | event.data | Cross-site scripting vulnerability due to $@. | addEventListener.js:10:21:10:25 | event | user-provided value |
|
||||
| exception-xss.js:86:17:86:19 | foo | exception-xss.js:2:15:2:31 | document.location | exception-xss.js:86:17:86:19 | foo | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:15:2:31 | document.location | user-provided value |
|
||||
| jquery.js:4:5:4:11 | tainted | jquery.js:2:17:2:33 | document.location | jquery.js:4:5:4:11 | tainted | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value |
|
||||
| jquery.js:7:5:7:34 | "<div i ... + "\\">" | jquery.js:2:17:2:33 | document.location | jquery.js:7:5:7:34 | "<div i ... + "\\">" | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value |
|
||||
| jquery.js:8:18:8:34 | "XSS: " + tainted | jquery.js:2:17:2:33 | document.location | jquery.js:8:18:8:34 | "XSS: " + tainted | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value |
|
||||
|
@ -705,7 +733,9 @@ edges
|
|||
| tst.js:261:11:261:21 | window.name | tst.js:261:11:261:21 | window.name | tst.js:261:11:261:21 | window.name | Cross-site scripting vulnerability due to $@. | tst.js:261:11:261:21 | window.name | user-provided value |
|
||||
| tst.js:277:22:277:29 | location | tst.js:277:22:277:29 | location | tst.js:277:22:277:29 | location | Cross-site scripting vulnerability due to $@. | tst.js:277:22:277:29 | location | user-provided value |
|
||||
| tst.js:285:59:285:65 | tainted | tst.js:282:19:282:29 | window.name | tst.js:285:59:285:65 | tainted | Cross-site scripting vulnerability due to $@. | tst.js:282:19:282:29 | window.name | user-provided value |
|
||||
| tst.js:297:35:297:42 | location | tst.js:297:35:297:42 | location | tst.js:297:35:297:42 | location | Cross-site scripting vulnerability due to $@. | tst.js:297:35:297:42 | location | user-provided value |
|
||||
| tst.js:300:20:300:20 | e | tst.js:298:9:298:16 | location | tst.js:300:20:300:20 | e | Cross-site scripting vulnerability due to $@. | tst.js:298:9:298:16 | location | user-provided value |
|
||||
| tst.js:308:20:308:20 | e | tst.js:305:10:305:17 | location | tst.js:308:20:308:20 | e | Cross-site scripting vulnerability due to $@. | tst.js:305:10:305:17 | location | user-provided value |
|
||||
| tst.js:313:35:313:42 | location | tst.js:313:35:313:42 | location | tst.js:313:35:313:42 | location | Cross-site scripting vulnerability due to $@. | tst.js:313:35:313:42 | location | user-provided value |
|
||||
| typeahead.js:25:18:25:20 | val | typeahead.js:20:22:20:38 | document.location | typeahead.js:25:18:25:20 | val | Cross-site scripting vulnerability due to $@. | typeahead.js:20:22:20:38 | document.location | user-provided value |
|
||||
| v-html.vue:2:8:2:23 | v-html=tainted | v-html.vue:6:42:6:58 | document.location | v-html.vue:2:8:2:23 | v-html=tainted | Cross-site scripting vulnerability due to $@. | v-html.vue:6:42:6:58 | document.location | user-provided value |
|
||||
| winjs.js:3:43:3:49 | tainted | winjs.js:2:17:2:33 | document.location | winjs.js:3:43:3:49 | tainted | Cross-site scripting vulnerability due to $@. | winjs.js:2:17:2:33 | document.location | user-provided value |
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
(function() {
|
||||
var foo = document.location;
|
||||
|
||||
function inner(x) {
|
||||
unknown(x);
|
||||
}
|
||||
|
||||
try {
|
||||
unknown(foo);
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK!
|
||||
}
|
||||
|
||||
try {
|
||||
inner(foo);
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK!
|
||||
}
|
||||
|
||||
try {
|
||||
unknown(foo + "bar");
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK!
|
||||
}
|
||||
|
||||
try {
|
||||
unknown({prop: foo});
|
||||
} catch(e) {
|
||||
$('myId').html(e); // We don't flag this for now.
|
||||
}
|
||||
|
||||
try {
|
||||
unknown(["bar", foo]);
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK!
|
||||
}
|
||||
|
||||
function deep(x) {
|
||||
deep2(x);
|
||||
}
|
||||
function deep2(x) {
|
||||
inner(x);
|
||||
}
|
||||
|
||||
try {
|
||||
deep("bar" + foo);
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK!
|
||||
}
|
||||
|
||||
try {
|
||||
var tmp = "bar" + foo;
|
||||
} catch(e) {
|
||||
$('myId').html(e); // OK
|
||||
}
|
||||
|
||||
function safe(x) {
|
||||
var foo = x + "bar";
|
||||
}
|
||||
|
||||
try {
|
||||
safe(foo);
|
||||
} catch(e) {
|
||||
$('myId').html(e); // OK
|
||||
}
|
||||
|
||||
try {
|
||||
safe.call(null, foo);
|
||||
} catch(e) {
|
||||
$('myId').html(e); // OK
|
||||
}
|
||||
var myWeirdInner;
|
||||
try {
|
||||
myWeirdInner = function (x) {
|
||||
inner(x);
|
||||
}
|
||||
} catch(e) {
|
||||
$('myId').html(e); // OK
|
||||
}
|
||||
try {
|
||||
myWeirdInner(foo);
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK!
|
||||
}
|
||||
|
||||
$('myId').html(foo); // Direct leak, reported by other query.
|
||||
|
||||
try {
|
||||
unknown(foo.match(/foo/));
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK!
|
||||
}
|
||||
|
||||
try {
|
||||
unknown([foo, "bar"]);
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK!
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
unknown(foo);
|
||||
} finally {
|
||||
// nothing
|
||||
}
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK!
|
||||
}
|
||||
});
|
||||
|
||||
var express = require('express');
|
||||
|
||||
var app = express();
|
||||
|
||||
app.get('/user/:id', function(req, res) {
|
||||
try {
|
||||
unknown(req.params.id);
|
||||
} catch(e) {
|
||||
res.send("Exception: " + e); // NOT OK!
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
(function () {
|
||||
sessionStorage.setItem('exceptionSession', document.location.search);
|
||||
|
||||
try {
|
||||
unknown(sessionStorage.getItem('exceptionSession'));
|
||||
} catch(e) {
|
||||
$('myId').html(e); // NOT OK
|
||||
}
|
||||
})();
|
|
@ -293,6 +293,22 @@ function flowThroughPropertyNames() {
|
|||
$(p); // OK
|
||||
}
|
||||
|
||||
function basicExceptions() {
|
||||
try {
|
||||
throw location;
|
||||
} catch(e) {
|
||||
$("body").append(e); // NOT OK
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
throw location
|
||||
} finally {}
|
||||
} catch(e) {
|
||||
$("body").append(e); // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
function handlebarsSafeString() {
|
||||
return new Handlebars.SafeString(location); // NOT OK!
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* @kind file-classifier
|
||||
* @id py/not-generated-file-filter
|
||||
*/
|
||||
import python
|
||||
import external.DefectFilter
|
||||
import semmle.python.filters.GeneratedCode
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* @kind file-classifier
|
||||
* @id py/not-test-file-filter
|
||||
*/
|
||||
import python
|
||||
import external.DefectFilter
|
||||
import semmle.python.filters.Tests
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
* external/cwe/cwe-798
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.filters.Tests
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* @precision medium
|
||||
* @id py/duplicate-block
|
||||
*/
|
||||
import python
|
||||
import CodeDuplication
|
||||
|
||||
predicate sorted_by_location(DuplicateBlock x, DuplicateBlock y) {
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
import python
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
import TurboGears
|
||||
|
||||
private class ValidatedMethodParameter extends Parameter {
|
||||
|
||||
ValidatedMethodParameter() {
|
||||
exists(string name, TurboGearsControllerMethod method |
|
||||
method.getArgByName(name) = this and
|
||||
method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UnvalidatedControllerMethodParameter extends TaintSource {
|
||||
|
||||
UnvalidatedControllerMethodParameter() {
|
||||
exists(Parameter p |
|
||||
any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and
|
||||
|
@ -25,9 +21,5 @@ class UnvalidatedControllerMethodParameter extends TaintSource {
|
|||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof UntrustedStringKind
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind }
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
import semmle.python.web.Http
|
||||
import TurboGears
|
||||
|
||||
|
||||
|
||||
class ControllerMethodReturnValue extends HttpResponseTaintSink {
|
||||
|
||||
ControllerMethodReturnValue() {
|
||||
exists(TurboGearsControllerMethod m |
|
||||
m.getAReturnValueFlowNode() = this and
|
||||
|
@ -16,14 +12,10 @@ class ControllerMethodReturnValue extends HttpResponseTaintSink {
|
|||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringKind
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof StringKind }
|
||||
}
|
||||
|
||||
class ControllerMethodTemplatedReturnValue extends HttpResponseTaintSink {
|
||||
|
||||
ControllerMethodTemplatedReturnValue() {
|
||||
exists(TurboGearsControllerMethod m |
|
||||
m.getAReturnValueFlowNode() = this and
|
||||
|
@ -31,8 +23,5 @@ class ControllerMethodTemplatedReturnValue extends HttpResponseTaintSink {
|
|||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringDictKind
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof StringDictKind }
|
||||
}
|
||||
|
|
|
@ -1,55 +1,33 @@
|
|||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
private ClassObject theTurboGearsControllerClass() {
|
||||
result = ModuleObject::named("tg").attr("TGController")
|
||||
}
|
||||
|
||||
|
||||
ClassObject aTurboGearsControllerClass() {
|
||||
result.getASuperType() = theTurboGearsControllerClass()
|
||||
}
|
||||
private ClassValue theTurboGearsControllerClass() { result = Value::named("tg.TGController") }
|
||||
|
||||
ClassValue aTurboGearsControllerClass() { result.getABaseType+() = theTurboGearsControllerClass() }
|
||||
|
||||
class TurboGearsControllerMethod extends Function {
|
||||
|
||||
ControlFlowNode decorator;
|
||||
|
||||
TurboGearsControllerMethod() {
|
||||
aTurboGearsControllerClass().getPyClass() = this.getScope() and
|
||||
aTurboGearsControllerClass().getScope() = this.getScope() and
|
||||
decorator = this.getADecorator().getAFlowNode() and
|
||||
/* Is decorated with @expose() or @expose(path) */
|
||||
(
|
||||
decorator.(CallNode).getFunction().(NameNode).getId() = "expose"
|
||||
or
|
||||
decorator.refersTo(_, ModuleObject::named("tg").attr("expose"), _)
|
||||
decorator.pointsTo().getClass() = Value::named("tg.expose")
|
||||
)
|
||||
}
|
||||
|
||||
private ControlFlowNode templateName() {
|
||||
result = decorator.(CallNode).getArg(0)
|
||||
}
|
||||
private ControlFlowNode templateName() { result = decorator.(CallNode).getArg(0) }
|
||||
|
||||
predicate isTemplated() {
|
||||
exists(templateName())
|
||||
}
|
||||
|
||||
string getTemplateName() {
|
||||
exists(StringObject str |
|
||||
templateName().refersTo(str) and
|
||||
result = str.getText()
|
||||
)
|
||||
}
|
||||
predicate isTemplated() { exists(templateName()) }
|
||||
|
||||
Dict getValidationDict() {
|
||||
exists(Call call, Object dict |
|
||||
exists(Call call, Value dict |
|
||||
call = this.getADecorator() and
|
||||
call.getFunc().(Name).getId() = "validate" and
|
||||
call.getArg(0).refersTo(dict) and
|
||||
result = dict.getOrigin()
|
||||
call.getArg(0).pointsTo(dict, result)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
| test.py:13:5:13:50 | Function ok_validated |
|
||||
| test.py:18:5:18:57 | Function partially_validated |
|
||||
| test.py:22:5:22:51 | Function not_validated |
|
||||
| test.py:26:5:26:28 | Function with_template |
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
| test.py:14 | BinaryExpr | externally controlled string |
|
||||
| test.py:19 | BinaryExpr | externally controlled string |
|
||||
| test.py:23 | BinaryExpr | externally controlled string |
|
||||
| test.py:27 | Dict | {externally controlled string} |
|
||||
|
|
|
@ -21,3 +21,7 @@ class RootController(TGController):
|
|||
@expose()
|
||||
def not_validated(self, a=None, b=None, *args):
|
||||
return 'Values: %s, %s, %s' % (a, b, args)
|
||||
|
||||
@expose("<template_path>")
|
||||
def with_template(self):
|
||||
return {'template_var': 'foo'}
|
||||
|
|
Загрузка…
Ссылка в новой задаче