mirror of
https://github.com/github/codeql.git
synced 2026-05-24 08:07:07 +02:00
Merge commit 'f3814c6468' into js/redux-less
This commit is contained in:
4
javascript/change-notes/2021-02-08-immutable.md
Normal file
4
javascript/change-notes/2021-02-08-immutable.md
Normal file
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* The dataflow libraries now model dataflow in the Immutable.js library.
|
||||
Affected packages are
|
||||
[Immutable.js](https://npmjs.com/package/immutable)
|
||||
8
javascript/change-notes/2021-02-08-xml-parser-taint.md
Normal file
8
javascript/change-notes/2021-02-08-xml-parser-taint.md
Normal file
@@ -0,0 +1,8 @@
|
||||
lgtm,codescanning
|
||||
* The security queries now track taint through XML parsers.
|
||||
Affected packages are
|
||||
[xml2js](https://www.npmjs.com/package/xml2js),
|
||||
[sax](https://www.npmjs.com/package/sax),
|
||||
[xml-js](https://www.npmjs.com/package/xml-js),
|
||||
[htmlparser2](https://www.npmjs.com/package/htmlparser2), and
|
||||
[node-expat](https://www.npmjs.com/package/node-expat)
|
||||
@@ -0,0 +1,6 @@
|
||||
lgtm,codescanning
|
||||
* The `js/xss-through-dom` query now recognizes form inputs as sources.
|
||||
Affected packages are
|
||||
[formik](https://www.npmjs.com/package/formik) and
|
||||
[react-final-form](https://www.npmjs.com/package/react-final-form) and
|
||||
[react-hook-form](https://www.npmjs.com/package/react-hook-form)
|
||||
7
javascript/change-notes/2021-02-09-form-parsers.md
Normal file
7
javascript/change-notes/2021-02-09-form-parsers.md
Normal file
@@ -0,0 +1,7 @@
|
||||
lgtm,codescanning
|
||||
* Server side form parsing libraries are now recognized as source of remote user input.
|
||||
Affected packages are
|
||||
[multer](https://www.npmjs.com/package/multer),
|
||||
[busboy](https://www.npmjs.com/package/busboy),
|
||||
[formidable](https://www.npmjs.com/package/formidable), and
|
||||
[multiparty](https://www.npmjs.com/package/formidable).
|
||||
9
javascript/change-notes/2021-02-10-markdown.md
Normal file
9
javascript/change-notes/2021-02-10-markdown.md
Normal file
@@ -0,0 +1,9 @@
|
||||
lgtm,codescanning
|
||||
* The security queries now track taint through markdown parsers.
|
||||
Affected packages are
|
||||
[marked](https://npmjs.com/package/marked),
|
||||
[markdown-table](https://npmjs.com/package/markdown-table),
|
||||
[showdown](https://npmjs.com/package/showdown),
|
||||
[snarkdown](https://npmjs.com/package/snarkdown),
|
||||
[unified](https://npmjs.com/package/unified), and
|
||||
[remark](https://npmjs.com/package/remark)
|
||||
8
javascript/change-notes/2021-02-11-apollo-client.md
Normal file
8
javascript/change-notes/2021-02-11-apollo-client.md
Normal file
@@ -0,0 +1,8 @@
|
||||
lgtm,codescanning
|
||||
* URIs used in the Apollo-link libraries are now recognized as sinks for `js/request-forgery`.
|
||||
Affected packages are
|
||||
[apollo-link-http](https://www.npmjs.com/package/apollo-link-http),
|
||||
[apollo-client](https://www.npmjs.com/package/apollo-client),
|
||||
[apollo-boost](https://www.npmjs.com/package/apollo-boost),
|
||||
[apollo-client-preset](https://www.npmjs.com/package/apollo-client-preset), and
|
||||
[apollo-link-ws](https://www.npmjs.com/package/apollo-link-ws)
|
||||
@@ -88,59 +88,59 @@ public class DeclarationFlags {
|
||||
return (flags & declareKeyword) != 0;
|
||||
}
|
||||
|
||||
/** Returns a mask with the computed bit set to the value of <tt>enable</tt>. */
|
||||
/** Returns a mask with the computed bit set to the value of <code>enable</code>. */
|
||||
public static int getComputed(boolean enable) {
|
||||
return enable ? computed : 0;
|
||||
}
|
||||
|
||||
/** Returns a mask with the abstract bit set to the value of <tt>enable</tt>. */
|
||||
/** Returns a mask with the abstract bit set to the value of <code>enable</code>. */
|
||||
public static int getAbstract(boolean enable) {
|
||||
return enable ? abstract_ : 0;
|
||||
}
|
||||
|
||||
/** Returns a mask with the static bit set to the value of <tt>enable</tt>. */
|
||||
/** Returns a mask with the static bit set to the value of <code>enable</code>. */
|
||||
public static int getStatic(boolean enable) {
|
||||
return enable ? static_ : 0;
|
||||
}
|
||||
|
||||
/** Returns a mask with the public bit set to the value of <tt>enable</tt>. */
|
||||
/** Returns a mask with the public bit set to the value of <code>enable</code>. */
|
||||
public static int getPublic(boolean enable) {
|
||||
return enable ? public_ : 0;
|
||||
}
|
||||
|
||||
/** Returns a mask with the readonly bit set to the value of <tt>enable</tt>. */
|
||||
/** Returns a mask with the readonly bit set to the value of <code>enable</code>. */
|
||||
public static int getReadonly(boolean enable) {
|
||||
return enable ? readonly : 0;
|
||||
}
|
||||
|
||||
/** Returns a mask with the private bit set to the value of <tt>enable</tt>. */
|
||||
/** Returns a mask with the private bit set to the value of <code>enable</code>. */
|
||||
public static int getPrivate(boolean enable) {
|
||||
return enable ? private_ : 0;
|
||||
}
|
||||
|
||||
/** Returns a mask with the protected bit set to the value of <tt>enable</tt>. */
|
||||
/** Returns a mask with the protected bit set to the value of <code>enable</code>. */
|
||||
public static int getProtected(boolean enable) {
|
||||
return enable ? protected_ : 0;
|
||||
}
|
||||
|
||||
/** Returns a mask with the optional bit set to the value of <tt>enable</tt>. */
|
||||
/** Returns a mask with the optional bit set to the value of <code>enable</code>. */
|
||||
public static int getOptional(boolean enable) {
|
||||
return enable ? optional : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mask with the definite assignment assertion bit set to the value of <tt>enable</tt>.
|
||||
* Returns a mask with the definite assignment assertion bit set to the value of <code>enable</code>.
|
||||
*/
|
||||
public static int getDefiniteAssignmentAssertion(boolean enable) {
|
||||
return enable ? definiteAssignmentAssertion : 0;
|
||||
}
|
||||
|
||||
/** Returns a mask with the declare keyword bit set to the value of <tt>enable</tt>. */
|
||||
/** Returns a mask with the declare keyword bit set to the value of <code>enable</code>. */
|
||||
public static int getDeclareKeyword(boolean enable) {
|
||||
return enable ? declareKeyword : 0;
|
||||
}
|
||||
|
||||
/** Returns true if the <tt>n</tt>th bit is set in <tt>flags</tt>. */
|
||||
/** Returns true if the <code>n</code>th bit is set in <code>flags</code>. */
|
||||
public static boolean hasNthFlag(int flags, int n) {
|
||||
return (flags & (1 << n)) != 0;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.semmle.ts.ast.ITypeExpression;
|
||||
/**
|
||||
* A literal constant.
|
||||
*
|
||||
* <p>A <tt>null</tt> literal may occur as a TypeScript type annotation - other literals are always
|
||||
* <p>A <code>null</code> literal may occur as a TypeScript type annotation - other literals are always
|
||||
* expressions.
|
||||
*/
|
||||
public class Literal extends Expression implements ITypeExpression {
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class MemberDefinition<V extends Expression> extends Node {
|
||||
return flags;
|
||||
}
|
||||
|
||||
/** Returns true if this has the <tt>static</tt> modifier. */
|
||||
/** Returns true if this has the <code>static</code> modifier. */
|
||||
public boolean isStatic() {
|
||||
return DeclarationFlags.isStatic(flags);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public abstract class MemberDefinition<V extends Expression> extends Node {
|
||||
return DeclarationFlags.isAbstract(flags);
|
||||
}
|
||||
|
||||
/** Returns true if has the <tt>public</tt> modifier (not true for implicitly public members). */
|
||||
/** Returns true if has the <code>public</code> modifier (not true for implicitly public members). */
|
||||
public boolean hasPublicKeyword() {
|
||||
return DeclarationFlags.isPublic(flags);
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public abstract class MemberDefinition<V extends Expression> extends Node {
|
||||
return DeclarationFlags.isReadonly(flags);
|
||||
}
|
||||
|
||||
/** Returns true if this has the <tt>declare</tt> modifier. */
|
||||
/** Returns true if this has the <code>declare</code> modifier. */
|
||||
public boolean hasDeclareKeyword() {
|
||||
return DeclarationFlags.hasDeclareKeyword(flags);
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ public class ASTExtractor {
|
||||
|
||||
/**
|
||||
* An identifier that refers to a variable from inside a type, i.e. the operand to a
|
||||
* <tt>typeof</tt> type or left operand to an <tt>is</tt> type.
|
||||
* <code>typeof</code> type or left operand to an <code>is</code> type.
|
||||
*
|
||||
* <p>This is generally treated as a type, except a variable binding will be emitted for it.
|
||||
*/
|
||||
@@ -288,7 +288,7 @@ public class ASTExtractor {
|
||||
VAR_AND_TYPE_AND_NAMESPACE_DECL,
|
||||
|
||||
/**
|
||||
* An identifier that occurs as part of a named export, such as <tt>export { A }</tt>.
|
||||
* An identifier that occurs as part of a named export, such as <code>export { A }</code>.
|
||||
*
|
||||
* <p>This may refer to a variable, type, and/or a namespace, and will export exactly those that
|
||||
* can be resolved.
|
||||
@@ -301,7 +301,7 @@ public class ASTExtractor {
|
||||
|
||||
/**
|
||||
* An identifier that occurs as a qualified name in a default export expression, such as
|
||||
* <tt>A</tt> in <tt>export default A.B</tt>.
|
||||
* <code>A</code> in <code>export default A.B</code>.
|
||||
*
|
||||
* <p>This acts like {@link #EXPORT}, except it cannot refer to a type (i.e. it must be a
|
||||
* variable and/or a namespace).
|
||||
|
||||
@@ -722,13 +722,13 @@ public class AutoBuild {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares <tt>package.json</tt> files in a virtual source root, and, if enabled,
|
||||
* Prepares <code>package.json</code> files in a virtual source root, and, if enabled,
|
||||
* installs dependencies for use by the TypeScript type checker.
|
||||
* <p>
|
||||
* Some packages must be downloaded while others exist within the same repo ("monorepos")
|
||||
* but are not in a location where TypeScript would look for it.
|
||||
* <p>
|
||||
* Downloaded packages are intalled under <tt>SCRATCH_DIR</tt>, in a mirrored directory hierarchy
|
||||
* Downloaded packages are intalled under <code>SCRATCH_DIR</code>, in a mirrored directory hierarchy
|
||||
* we call the "virtual source root".
|
||||
* <p>
|
||||
* Packages that exists within the repo are not downloaded. Since they are part of the main source tree,
|
||||
|
||||
@@ -331,7 +331,10 @@ public class HTMLExtractor implements IExtractor {
|
||||
textualExtractor.getMetrics(),
|
||||
textualExtractor.getExtractedFile());
|
||||
Pair<Label, LoCInfo> result = extractor.extract(tx, source, toplevelKind, scopeManager);
|
||||
emitTopLevelXmlNodeBinding(parentHtmlNode, result.fst(), context, trapWriter);
|
||||
Label toplevelLabel = result.fst();
|
||||
if (toplevelLabel != null) { // can be null when script ends up being parsed as JSON
|
||||
emitTopLevelXmlNodeBinding(parentHtmlNode, toplevelLabel, context, trapWriter);
|
||||
}
|
||||
locInfo.add(result.snd());
|
||||
} catch (ParseError e) {
|
||||
e.setPosition(scriptLocationManager.translatePosition(e.getPosition()));
|
||||
|
||||
@@ -43,7 +43,7 @@ public class Main {
|
||||
* A version identifier that should be updated every time the extractor changes in such a way that
|
||||
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
|
||||
*/
|
||||
public static final String EXTRACTOR_VERSION = "2020-12-11";
|
||||
public static final String EXTRACTOR_VERSION = "2021-02-05";
|
||||
|
||||
public static final Pattern NEWLINE = Pattern.compile("\n");
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ public class ScopeManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enters a scope for a block of form <tt>declare global { ... }</tt>.
|
||||
* Enters a scope for a block of form <code>declare global { ... }</code>.
|
||||
*
|
||||
* <p>Declarations in this block will contribute to the global scope, but references can still be
|
||||
* resolved in the scope enclosing the declaration itself. The scope itself does not have its own
|
||||
|
||||
@@ -14,7 +14,7 @@ public class ParsedProject {
|
||||
this.allFiles = allFiles;
|
||||
}
|
||||
|
||||
/** Returns the <tt>tsconfig.json</tt> file that defines this project. */
|
||||
/** Returns the <code>tsconfig.json</code> file that defines this project. */
|
||||
public File getTsConfigFile() {
|
||||
return tsConfigFile;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/**
|
||||
* An array type, such as <tt>number[]</tt>, or in general <tt>T[]</tt> where <tt>T</tt> is a type.
|
||||
* An array type, such as <code>number[]</code>, or in general <code>T[]</code> where <code>T</code> is a type.
|
||||
*/
|
||||
public class ArrayTypeExpr extends TypeExpression {
|
||||
private final ITypeExpression elementType;
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.semmle.ts.ast;
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** A conditional type annotation, such as <tt>T extends any[] ? A : B</tt>. */
|
||||
/** A conditional type annotation, such as <code>T extends any[] ? A : B</code>. */
|
||||
public class ConditionalTypeExpr extends TypeExpression {
|
||||
private ITypeExpression checkType;
|
||||
private ITypeExpression extendsType;
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Statement;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** A statement of form <tt>export as namespace X</tt> where <tt>X</tt> is an identifier. */
|
||||
/** A statement of form <code>export as namespace X</code> where <code>X</code> is an identifier. */
|
||||
public class ExportAsNamespaceDeclaration extends Statement {
|
||||
private Identifier id;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
* class StringList extends List<string> {}
|
||||
* </pre>
|
||||
*
|
||||
* Above, <tt>List</tt> is a concrete expression whereas its type argument is a type.
|
||||
* Above, <code>List</code> is a concrete expression whereas its type argument is a type.
|
||||
*/
|
||||
public class ExpressionWithTypeArguments extends Expression {
|
||||
private final Expression expression;
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.semmle.js.ast.Statement;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
import java.util.List;
|
||||
|
||||
/** A statement of form <tt>declare module "X" {...}</tt>. */
|
||||
/** A statement of form <code>declare module "X" {...}</code>. */
|
||||
public class ExternalModuleDeclaration extends Statement {
|
||||
private final Literal name;
|
||||
private final List<Statement> body;
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
import java.util.List;
|
||||
|
||||
/** An instantiation of a named type, such as <tt>Array<number></tt> */
|
||||
/** An instantiation of a named type, such as <code>Array<number></code> */
|
||||
public class GenericTypeExpr extends TypeExpression {
|
||||
private final ITypeExpression typeName; // Always Identifier or MemberExpression
|
||||
private final List<ITypeExpression> typeArguments;
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.semmle.js.ast.Statement;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
import java.util.List;
|
||||
|
||||
/** A statement of form: <tt>declare global { ... }</tt> */
|
||||
/** A statement of form: <code>declare global { ... }</code> */
|
||||
public class GlobalAugmentationDeclaration extends Statement {
|
||||
private final List<Statement> body;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ package com.semmle.ts.ast;
|
||||
*/
|
||||
public interface INodeWithSymbol {
|
||||
/**
|
||||
* Gets a number identifying the symbol associated with this AST node, or <tt>-1</tt> if there is
|
||||
* Gets a number identifying the symbol associated with this AST node, or <code>-1</code> if there is
|
||||
* no such symbol.
|
||||
*/
|
||||
int getSymbol();
|
||||
|
||||
@@ -8,6 +8,6 @@ import com.semmle.js.ast.Literal;
|
||||
*
|
||||
* <p>At the QL level, expressions and type annotations are completely separate. In the extractor,
|
||||
* however, some expressions such as {@link Literal} type may occur in a type annotation because the
|
||||
* TypeScript AST does not distinguish <tt>null</tt> literals from the <tt>null</tt> type.
|
||||
* TypeScript AST does not distinguish <code>null</code> literals from the <code>null</code> type.
|
||||
*/
|
||||
public interface ITypeExpression extends INode, ITypedAstNode {}
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.semmle.js.ast.Expression;
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** An import type such as in <tt>import("http").ServerRequest</tt>. */
|
||||
/** An import type such as in <code>import("http").ServerRequest</code>. */
|
||||
public class ImportTypeExpr extends Expression implements ITypeExpression {
|
||||
private final ITypeExpression path;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Statement;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** An import of form <tt>import a = E</tt>. */
|
||||
/** An import of form <code>import a = E</code>. */
|
||||
public class ImportWholeDeclaration extends Statement {
|
||||
private final Identifier lhs;
|
||||
private final Expression rhs;
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.semmle.ts.ast;
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** A type of form <tt>T[K]</tt> where <tt>T</tt> and <tt>K</tt> are types. */
|
||||
/** A type of form <code>T[K]</code> where <code>T</code> and <code>K</code> are types. */
|
||||
public class IndexedAccessTypeExpr extends TypeExpression {
|
||||
private final ITypeExpression objectType;
|
||||
private final ITypeExpression indexType;
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.semmle.ts.ast;
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** A type annotation of form <tt>infer R</tt> */
|
||||
/** A type annotation of form <code>infer R</code> */
|
||||
public class InferTypeExpr extends TypeExpression {
|
||||
private TypeParameter typeParameter;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
import java.util.List;
|
||||
|
||||
/** An inline interface type, such as <tt>{x: number; y: number}</tt>. */
|
||||
/** An inline interface type, such as <code>{x: number; y: number}</code>. */
|
||||
public class InterfaceTypeExpr extends TypeExpression {
|
||||
private final List<MemberDefinition<?>> body;
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import com.semmle.js.ast.Visitor;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An intersection type such as <tt>T&S</tt>, denoting the intersection of type <tt>T</tt> and
|
||||
* type <tt>S</tt>.
|
||||
* An intersection type such as <code>T&S</code>, denoting the intersection of type <code>T</code> and
|
||||
* type <code>S</code>.
|
||||
*/
|
||||
public class IntersectionTypeExpr extends TypeExpression {
|
||||
private final List<ITypeExpression> elementTypes;
|
||||
|
||||
@@ -4,14 +4,14 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/**
|
||||
* One of the TypeScript keyword types, such as <tt>string</tt> or <tt>any</tt>.
|
||||
* One of the TypeScript keyword types, such as <code>string</code> or <code>any</code>.
|
||||
*
|
||||
* <p>This includes the type <tt>unique symbol</tt> which consists of two keywords but is
|
||||
* <p>This includes the type <code>unique symbol</code> which consists of two keywords but is
|
||||
* represented as a keyword single type expression.
|
||||
*
|
||||
* <p>At the QL level, the <tt>null</tt> type is also a keyword type. In the extractor, however,
|
||||
* <p>At the QL level, the <code>null</code> type is also a keyword type. In the extractor, however,
|
||||
* this is represented by a Literal, because the TypeScript AST does not distinguish those two uses
|
||||
* of <tt>null</tt>.
|
||||
* of <code>null</code>.
|
||||
*/
|
||||
public class KeywordTypeExpr extends TypeExpression {
|
||||
private final String keyword;
|
||||
|
||||
@@ -4,10 +4,10 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/**
|
||||
* A type of form <tt>{ [K in C]: T }</tt>, where <tt>T</tt> is a type that may refer to <tt>K</tt>.
|
||||
* A type of form <code>{ [K in C]: T }</code>, where <code>T</code> is a type that may refer to <code>K</code>.
|
||||
*
|
||||
* <p>As with the TypeScript AST, the <tt>K in C</tt> part is represented as a type parameter with
|
||||
* <tt>C</tt> as its upper bound.
|
||||
* <p>As with the TypeScript AST, the <code>K in C</code> part is represented as a type parameter with
|
||||
* <code>C</code> as its upper bound.
|
||||
*/
|
||||
public class MappedTypeExpr extends TypeExpression {
|
||||
private final TypeParameter typeParameter;
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.semmle.js.ast.Expression;
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** A TypeScript expression of form <tt>E!</tt>, asserting that <tt>E</tt> is not null. */
|
||||
/** A TypeScript expression of form <code>E!</code>, asserting that <code>E</code> is not null. */
|
||||
public class NonNullAssertion extends Expression {
|
||||
private final Expression expression;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.semmle.ts.ast;
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** An optional type in a tuple type, such as <tt>number?</tt> in <tt>[string, number?]</tt>. */
|
||||
/** An optional type in a tuple type, such as <code>number?</code> in <code>[string, number?]</code>. */
|
||||
public class OptionalTypeExpr extends TypeExpression {
|
||||
private final ITypeExpression elementType;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.semmle.ts.ast;
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** A type expression in parentheses, such as <tt>("foo" | "bar")</tt>. */
|
||||
/** A type expression in parentheses, such as <code>("foo" | "bar")</code>. */
|
||||
public class ParenthesizedTypeExpr extends TypeExpression {
|
||||
private final ITypeExpression elementType;
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/**
|
||||
* A type of form <tt>E is T</tt>, <tt>asserts E is T</tt> or <tt>asserts E</tt> where <tt>E</tt> is
|
||||
* a parameter name or <tt>this</tt> and <tt>T</tt> is a type.
|
||||
* A type of form <code>E is T</code>, <code>asserts E is T</code> or <code>asserts E</code> where <code>E</code> is
|
||||
* a parameter name or <code>this</code> and <code>T</code> is a type.
|
||||
*/
|
||||
public class PredicateTypeExpr extends TypeExpression {
|
||||
private final ITypeExpression expression;
|
||||
@@ -23,12 +23,12 @@ public class PredicateTypeExpr extends TypeExpression {
|
||||
this.hasAssertsKeyword = hasAssertsKeyword;
|
||||
}
|
||||
|
||||
/** Returns the <tt>E</tt> in <tt>E is T</tt>. */
|
||||
/** Returns the <code>E</code> in <code>E is T</code>. */
|
||||
public ITypeExpression getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
/** Returns the <tt>T</tt> in <tt>E is T</tt>. */
|
||||
/** Returns the <code>T</code> in <code>E is T</code>. */
|
||||
public ITypeExpression getTypeExpr() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.semmle.ts.ast;
|
||||
import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/** A rest type in a tuple type, such as <tt>number[]</tt> in <tt>[string, ...number[]]</tt>. */
|
||||
/** A rest type in a tuple type, such as <code>number[]</code> in <code>[string, ...number[]]</code>. */
|
||||
public class RestTypeExpr extends TypeExpression {
|
||||
private final ITypeExpression arrayType;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
import java.util.List;
|
||||
|
||||
/** A tuple type, such as <tt>[number, string]</tt>. */
|
||||
/** A tuple type, such as <code>[number, string]</code>. */
|
||||
public class TupleTypeExpr extends TypeExpression {
|
||||
private final List<ITypeExpression> elementTypes;
|
||||
private final List<Identifier> elementNames;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class TypeAssertion extends Expression {
|
||||
}
|
||||
|
||||
/**
|
||||
* True if this is an assertion of form <tt>E as T</tt>, as opposed to the old syntax <code>
|
||||
* True if this is an assertion of form <code>E as T</code>, as opposed to the old syntax <code>
|
||||
* <T> E</code>.
|
||||
*/
|
||||
public boolean isAsExpression() {
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.semmle.js.ast.Visitor;
|
||||
/**
|
||||
* A type parameter declared on a class, interface, function, or type alias.
|
||||
*
|
||||
* <p>The general form of a type parameter is: <tt>S extends T = U</tt>.
|
||||
* <p>The general form of a type parameter is: <code>S extends T = U</code>.
|
||||
*/
|
||||
public class TypeParameter extends TypeExpression {
|
||||
private final Identifier id;
|
||||
@@ -29,7 +29,7 @@ public class TypeParameter extends TypeExpression {
|
||||
/**
|
||||
* Returns the bound on the type parameter, or {@code null} if there is no bound.
|
||||
*
|
||||
* <p>For example, in <tt>T extends Array = number[]</tt> the bound is <tt>Array</tt>.
|
||||
* <p>For example, in <code>T extends Array = number[]</code> the bound is <code>Array</code>.
|
||||
*/
|
||||
public ITypeExpression getBound() {
|
||||
return bound;
|
||||
@@ -38,7 +38,7 @@ public class TypeParameter extends TypeExpression {
|
||||
/**
|
||||
* Returns the type parameter default, or {@code null} if there is no default,
|
||||
*
|
||||
* <p>For example, in <tt>T extends Array = number[]</tt> the default is <tt>number[]</tt>.
|
||||
* <p>For example, in <code>T extends Array = number[]</code> the default is <code>number[]</code>.
|
||||
*/
|
||||
public ITypeExpression getDefault() {
|
||||
return default_;
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
|
||||
/**
|
||||
* A type of form <tt>typeof E</tt> where <tt>E</tt> is an expression that takes the form of a
|
||||
* A type of form <code>typeof E</code> where <code>E</code> is an expression that takes the form of a
|
||||
* qualified name.
|
||||
*/
|
||||
public class TypeofTypeExpr extends TypeExpression {
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.semmle.js.ast.Visitor;
|
||||
/**
|
||||
* A unary operator applied to a type.
|
||||
*
|
||||
* <p>This can be <tt>keyof T</tt> or <tt>readonly T</tt>.
|
||||
* <p>This can be <code>keyof T</code> or <code>readonly T</code>.
|
||||
*/
|
||||
public class UnaryTypeExpr extends TypeExpression {
|
||||
private final ITypeExpression elementType;
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.semmle.js.ast.SourceLocation;
|
||||
import com.semmle.js.ast.Visitor;
|
||||
import java.util.List;
|
||||
|
||||
/** A union type such as <tt>number | string | boolean</tt>. */
|
||||
/** A union type such as <code>number | string | boolean</code>. */
|
||||
public class UnionTypeExpr extends TypeExpression {
|
||||
private final List<ITypeExpression> elementTypes;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Extracts type and symbol information into TRAP files.
|
||||
*
|
||||
* <p>This is closely coupled with the <tt>type_table.ts</tt> file in the parser-wrapper. Type
|
||||
* <p>This is closely coupled with the <code>type_table.ts</code> file in the parser-wrapper. Type
|
||||
* strings and symbol strings generated in that file are parsed here. See that file for reference
|
||||
* and documentation.
|
||||
*/
|
||||
|
||||
@@ -703,14 +703,14 @@ public class TypeScriptASTConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given child to an AST node of the given type or <tt>null</tt>. A ParseError is
|
||||
* Converts the given child to an AST node of the given type or <code>null</code>. A ParseError is
|
||||
* thrown if a different type of node was found.
|
||||
*
|
||||
* <p>This is used to detect syntax errors that are not reported as syntax errors by the
|
||||
* TypeScript parser. Usually they are reported as errors in a later compiler stage, which the
|
||||
* extractor does not run.
|
||||
*
|
||||
* <p>Returns <tt>null</tt> if the child is absent.
|
||||
* <p>Returns <code>null</code> if the child is absent.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends Node> T tryConvertChild(JsonObject node, String prop, Class<T> expectedType)
|
||||
@@ -2515,8 +2515,8 @@ public class TypeScriptASTConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific modifier from the given node (or <tt>null</tt> if absent), as defined by its
|
||||
* <tt>modifiers</tt> property and the <tt>kind</tt> property of the modifier AST node.
|
||||
* Returns a specific modifier from the given node (or <code>null</code> if absent), as defined by its
|
||||
* <code>modifiers</code> property and the <code>kind</code> property of the modifier AST node.
|
||||
*/
|
||||
private JsonObject getModifier(JsonObject node, String modKind) {
|
||||
for (JsonElement mod : getModifiers(node))
|
||||
@@ -2526,8 +2526,8 @@ public class TypeScriptASTConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a node has a particular modifier, as defined by its <tt>modifiers</tt> property
|
||||
* and the <tt>kind</tt> property of the modifier AST node.
|
||||
* Check whether a node has a particular modifier, as defined by its <code>modifiers</code> property
|
||||
* and the <code>kind</code> property of the modifier AST node.
|
||||
*/
|
||||
private boolean hasModifier(JsonObject node, String modKind) {
|
||||
return getModifier(node, modKind) != null;
|
||||
@@ -2568,8 +2568,8 @@ public class TypeScriptASTConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a node has a particular flag, as defined by its <tt>flags</tt> property and the
|
||||
* <tt>ts.NodeFlags</tt> in enum.
|
||||
* Check whether a node has a particular flag, as defined by its <code>flags</code> property and the
|
||||
* <code>ts.NodeFlags</code> in enum.
|
||||
*/
|
||||
private boolean hasFlag(JsonObject node, String flagName) {
|
||||
int flagId = metadata.getNodeFlagId(flagName);
|
||||
|
||||
@@ -97,7 +97,7 @@ public class TypeScriptParser {
|
||||
public static final String TYPESCRIPT_RETRIES_VAR = "SEMMLE_TYPESCRIPT_RETRIES";
|
||||
|
||||
/**
|
||||
* An environment variable (without the <tt>SEMMLE_</tt> or <tt>LGTM_</tt> prefix), that can be
|
||||
* An environment variable (without the <code>SEMMLE_</code> or <code>LGTM_</code> prefix), that can be
|
||||
* set to indicate the maximum heap space usable by the Node.js process, in addition to its
|
||||
* "reserve memory".
|
||||
*
|
||||
@@ -106,7 +106,7 @@ public class TypeScriptParser {
|
||||
public static final String TYPESCRIPT_RAM_SUFFIX = "TYPESCRIPT_RAM";
|
||||
|
||||
/**
|
||||
* An environment variable (without the <tt>SEMMLE_</tt> or <tt>LGTM_</tt> prefix), that can be
|
||||
* An environment variable (without the <code>SEMMLE_</code> or <code>LGTM_</code> prefix), that can be
|
||||
* set to indicate the amount of heap space the Node.js process should reserve for extracting
|
||||
* individual files.
|
||||
*
|
||||
|
||||
@@ -4,9 +4,9 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* Holds the output of the <tt>get-type-table</tt> command.
|
||||
* Holds the output of the <code>get-type-table</code> command.
|
||||
*
|
||||
* <p>See documentation in <tt>parser-wrapper/src/type_table.ts</tt>.
|
||||
* <p>See documentation in <code>parser-wrapper/src/type_table.ts</code>.
|
||||
*/
|
||||
public class TypeTable {
|
||||
private final JsonArray typeStrings;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<script>
|
||||
{"Hello": 123}
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
#10000=@"/json_in_script.html;sourcefile"
|
||||
files(#10000,"/json_in_script.html","json_in_script","html",0)
|
||||
#10001=@"/;folder"
|
||||
folders(#10001,"/","")
|
||||
containerparent(#10001,#10000)
|
||||
#10002=@"loc,{#10000},0,0,0,0"
|
||||
locations_default(#10002,#10000,0,0,0,0)
|
||||
hasLocation(#10000,#10002)
|
||||
#20000=@"global_scope"
|
||||
scopes(#20000,0)
|
||||
#20001=*
|
||||
json(#20001,5,#10000,0,"{""Hello"": 123}")
|
||||
#20002=@"loc,{#10000},3,1,3,14"
|
||||
locations_default(#20002,#10000,3,1,3,14)
|
||||
json_locations(#20001,#20002)
|
||||
#20003=*
|
||||
json(#20003,2,#20001,0,"123")
|
||||
#20004=@"loc,{#10000},3,11,3,13"
|
||||
locations_default(#20004,#10000,3,11,3,13)
|
||||
json_locations(#20003,#20004)
|
||||
json_literals("123","123",#20003)
|
||||
json_properties(#20001,"Hello",#20003)
|
||||
#20005=*
|
||||
xmlElements(#20005,"html",#10000,0,#10000)
|
||||
#20006=@"loc,{#10000},1,1,5,7"
|
||||
locations_default(#20006,#10000,1,1,5,7)
|
||||
xmllocations(#20005,#20006)
|
||||
#20007=*
|
||||
xmlElements(#20007,"script",#20005,0,#10000)
|
||||
#20008=@"loc,{#10000},2,1,4,9"
|
||||
locations_default(#20008,#10000,2,1,4,9)
|
||||
xmllocations(#20007,#20008)
|
||||
numlines(#10000,5,0,0)
|
||||
filetype(#10000,"html")
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @name Template Object Injection
|
||||
* @description Instantiating a template using a user-controlled object is vulnerable to local file read and potential remote code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id js/template-object-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-073
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
import semmle.javascript.security.TaintedObject
|
||||
|
||||
class TemplateObjInjectionConfig extends TaintTracking::Configuration {
|
||||
TemplateObjInjectionConfig() { this = "TemplateObjInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
|
||||
TaintedObject::isSource(source, label)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
|
||||
label = TaintedObject::label() and
|
||||
exists(MethodCallExpr mc |
|
||||
Express::isResponse(mc.getReceiver()) and
|
||||
mc.getMethodName() = "render" and
|
||||
sink.asExpr() = mc.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
|
||||
guard instanceof TaintedObject::SanitizerGuard
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node src, DataFlow::Node trg, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl
|
||||
) {
|
||||
TaintedObject::step(src, trg, inlbl, outlbl)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Template object injection due to $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
@@ -0,0 +1,10 @@
|
||||
var app = require('express')();
|
||||
app.set('view engine', 'hbs');
|
||||
|
||||
app.post('/path', function(req, res) {
|
||||
var bodyParameter = req.body.bodyParameter
|
||||
var queryParameter = req.query.queryParameter
|
||||
|
||||
res.render('template', bodyParameter)
|
||||
res.render('template', queryParameter)
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
var app = require('express')();
|
||||
app.set('view engine', 'hbs');
|
||||
|
||||
app.post('/path', function(req, res) {
|
||||
var bodyParameter = req.body.bodyParameter
|
||||
var queryParameter = req.query.queryParameter
|
||||
|
||||
res.render('template', {bodyParameter})
|
||||
res.render('template', {queryParameter})
|
||||
});
|
||||
@@ -85,13 +85,16 @@ import semmle.javascript.frameworks.Electron
|
||||
import semmle.javascript.frameworks.EventEmitter
|
||||
import semmle.javascript.frameworks.Files
|
||||
import semmle.javascript.frameworks.Firebase
|
||||
import semmle.javascript.frameworks.FormParsers
|
||||
import semmle.javascript.frameworks.jQuery
|
||||
import semmle.javascript.frameworks.JWT
|
||||
import semmle.javascript.frameworks.Handlebars
|
||||
import semmle.javascript.frameworks.Immutable
|
||||
import semmle.javascript.frameworks.LazyCache
|
||||
import semmle.javascript.frameworks.LodashUnderscore
|
||||
import semmle.javascript.frameworks.Logging
|
||||
import semmle.javascript.frameworks.HttpFrameworks
|
||||
import semmle.javascript.frameworks.Markdown
|
||||
import semmle.javascript.frameworks.NoSQL
|
||||
import semmle.javascript.frameworks.PkgCloud
|
||||
import semmle.javascript.frameworks.PropertyProjection
|
||||
|
||||
@@ -357,6 +357,25 @@ module DOM {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to a DOM event.
|
||||
*/
|
||||
private DataFlow::SourceNode domEventSource() {
|
||||
// e.g. <form onSubmit={e => e.target}/>
|
||||
exists(JSXAttribute attr | attr.getName().matches("on%") |
|
||||
result = attr.getValue().flow().getABoundFunctionValue(0).getParameter(0)
|
||||
)
|
||||
or
|
||||
// node.addEventListener("submit", e => e.target)
|
||||
result = domValueRef().getAMethodCall("addEventListener").getABoundCallbackParameter(1, 0)
|
||||
or
|
||||
// node.onSubmit = (e => e.target);
|
||||
exists(DataFlow::PropWrite write | write = domValueRef().getAPropertyWrite() |
|
||||
write.getPropertyName().matches("on%") and
|
||||
result = write.getRhs().getAFunctionValue().getParameter(0)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a data flow node that refers directly to a value from the DOM. */
|
||||
DataFlow::SourceNode domValueSource() { result instanceof DomValueSource::Range }
|
||||
|
||||
@@ -368,6 +387,9 @@ module DOM {
|
||||
t.start() and
|
||||
result = domValueRef().getAMethodCall(["item", "namedItem"])
|
||||
or
|
||||
t.startInProp("target") and
|
||||
result = domEventSource()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = domValueRef(t2).track(t2, t))
|
||||
}
|
||||
|
||||
|
||||
@@ -659,9 +659,15 @@ module PseudoProperties {
|
||||
*/
|
||||
pragma[inline]
|
||||
string mapValueKnownKey(DataFlow::Node key) {
|
||||
result = pseudoProperty("mapValue", any(string s | key.mayHaveStringValue(s)))
|
||||
result = mapValueKey(any(string s | key.mayHaveStringValue(s)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of a map value where the key is `key`.
|
||||
*/
|
||||
bindingset[key]
|
||||
string mapValueKey(string key) { result = pseudoProperty("mapValue", key) }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of a map value where the key is `key`.
|
||||
*/
|
||||
|
||||
@@ -1079,8 +1079,19 @@ module DataFlow {
|
||||
|
||||
override DataFlow::Node getCalleeNode() { result = DataFlow::valueNode(astNode.getCallee()) }
|
||||
|
||||
/**
|
||||
* Whether i is an index that occurs after a spread argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate isIndexAfterSpread(int i) {
|
||||
astNode.isSpreadArgument(i)
|
||||
or
|
||||
exists(astNode.getArgument(i)) and
|
||||
isIndexAfterSpread(i - 1)
|
||||
}
|
||||
|
||||
override DataFlow::Node getArgument(int i) {
|
||||
not astNode.isSpreadArgument([0 .. i]) and
|
||||
not isIndexAfterSpread(i) and
|
||||
result = DataFlow::valueNode(astNode.getArgument(i))
|
||||
}
|
||||
|
||||
|
||||
@@ -823,4 +823,35 @@ module ClientRequest {
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes and predicates modelling the `apollo-client` library.
|
||||
*/
|
||||
private module ApolloClient {
|
||||
/**
|
||||
* A function from `apollo-client` that accepts an options object that may contain a `uri` property.
|
||||
*/
|
||||
API::Node apolloUriCallee() {
|
||||
result = API::moduleImport("apollo-link-http").getMember(["HttpLink", "createHttpLink"])
|
||||
or
|
||||
result =
|
||||
API::moduleImport(["apollo-boost", "apollo-client", "apollo-client-preset"])
|
||||
.getMember(["ApolloClient", "HttpLink", "createNetworkInterface"])
|
||||
or
|
||||
result = API::moduleImport("apollo-link-ws").getMember("WebSocketLink")
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of a URL request made using apollo-client.
|
||||
*/
|
||||
class ApolloClientRequest extends ClientRequest::Range, API::InvokeNode {
|
||||
ApolloClientRequest() { this = apolloUriCallee().getAnInvocation() }
|
||||
|
||||
override DataFlow::Node getUrl() { result = getParameter(0).getMember("uri").getARhs() }
|
||||
|
||||
override DataFlow::Node getHost() { none() }
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,8 +509,9 @@ module Express {
|
||||
this = request.getAPropertyRead("cookies")
|
||||
or
|
||||
// `req.files`, treated the same as `req.body`.
|
||||
// `express-fileupload` uses .files, and `multer` uses .files or .file
|
||||
kind = "body" and
|
||||
this = request.getAPropertyRead("files")
|
||||
this = request.getAPropertyRead(["files", "file"])
|
||||
)
|
||||
or
|
||||
kind = "body" and
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Provides classes for modelling the server-side form/file parsing libraries.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A source of remote flow from the `Busboy` library.
|
||||
*/
|
||||
private class BusBoyRemoteFlow extends RemoteFlowSource {
|
||||
BusBoyRemoteFlow() {
|
||||
this =
|
||||
API::moduleImport("busboy")
|
||||
.getInstance()
|
||||
.getMember("on")
|
||||
.getParameter(1)
|
||||
.getAParameter()
|
||||
.getAnImmediateUse()
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Busbuy" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of remote flow from the `Formidable` library parsing a HTTP request.
|
||||
*/
|
||||
private class FormidableRemoteFlow extends RemoteFlowSource {
|
||||
FormidableRemoteFlow() {
|
||||
exists(API::Node formidable |
|
||||
formidable = API::moduleImport("formidable").getReturn()
|
||||
or
|
||||
formidable = API::moduleImport("formidable").getMember("formidable").getReturn()
|
||||
or
|
||||
formidable =
|
||||
API::moduleImport("formidable").getMember(["IncomingForm", "Formidable"]).getInstance()
|
||||
|
|
||||
this =
|
||||
formidable.getMember("parse").getACall().getABoundCallbackParameter(1, any(int i | i > 0))
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Formidable" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of remote flow from the `Multiparty` library.
|
||||
*/
|
||||
private class MultipartyRemoteFlow extends RemoteFlowSource {
|
||||
MultipartyRemoteFlow() {
|
||||
exists(API::Node form | form = API::moduleImport("multiparty").getMember("Form").getInstance() |
|
||||
exists(API::CallNode parse | parse = form.getMember("parse").getACall() |
|
||||
this = parse.getParameter(1).getAParameter().getAnImmediateUse()
|
||||
)
|
||||
or
|
||||
exists(API::CallNode on | on = form.getMember("on").getACall() |
|
||||
on.getArgument(0).mayHaveStringValue(["part", "file", "field"]) and
|
||||
this = on.getParameter(1).getAParameter().getAnImmediateUse()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "parsed user value from Multiparty" }
|
||||
}
|
||||
185
javascript/ql/src/semmle/javascript/frameworks/Immutable.qll
Normal file
185
javascript/ql/src/semmle/javascript/frameworks/Immutable.qll
Normal file
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Provides classes and predicates for reasoning about [immutable](https://www.npmjs.com/package/immutable).
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Provides classes implementing data-flow for Immutable.
|
||||
*
|
||||
* The implemention rely on the flowsteps implemented in `Collections.qll`.
|
||||
*/
|
||||
private module Immutable {
|
||||
/**
|
||||
* An API entrypoint for the global `Immutable` variable.
|
||||
*/
|
||||
private class ImmutableGlobalEntry extends API::EntryPoint {
|
||||
ImmutableGlobalEntry() { this = "ImmutableGlobalEntry" }
|
||||
|
||||
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("Immutable") }
|
||||
|
||||
override DataFlow::Node getARhs() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An import of the `Immutable` library.
|
||||
*/
|
||||
API::Node immutableImport() {
|
||||
result = API::moduleImport("immutable")
|
||||
or
|
||||
result = API::root().getASuccessor(any(ImmutableGlobalEntry i))
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of any immutable collection.
|
||||
*
|
||||
* This predicate keeps track of which values in the program are Immutable collections.
|
||||
*/
|
||||
API::Node immutableCollection() {
|
||||
// keep this predicate in sync with the constructors defined in `storeStep`/`step`.
|
||||
result =
|
||||
immutableImport()
|
||||
.getMember(["Map", "OrderedMap", "List", "Stack", "Set", "OrderedSet", "fromJS", "merge"])
|
||||
.getReturn()
|
||||
or
|
||||
result = immutableImport().getMember("Record").getReturn().getReturn()
|
||||
or
|
||||
result =
|
||||
immutableImport()
|
||||
.getMember(["List", "Set", "OrderedSet", "Stack"])
|
||||
.getMember("of")
|
||||
.getReturn()
|
||||
or
|
||||
result = immutableCollection().getMember(["set", "map", "filter", "push", "merge"]).getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the immutable collection where `pred` has been stored using the name `prop`.
|
||||
*/
|
||||
DataFlow::SourceNode storeStep(DataFlow::Node pred, string prop) {
|
||||
// Immutable.Map() and Immutable.fromJS().
|
||||
exists(DataFlow::CallNode call |
|
||||
call = immutableImport().getMember(["Map", "OrderedMap", "fromJS"]).getACall()
|
||||
|
|
||||
pred = call.getOptionArgument(0, prop) and
|
||||
result = call
|
||||
)
|
||||
or
|
||||
// Immutable.List()
|
||||
exists(DataFlow::CallNode call, DataFlow::ArrayCreationNode arr |
|
||||
call = immutableImport().getMember(["List", "Stack", "Set", "OrderedSet"]).getACall()
|
||||
|
|
||||
arr = call.getArgument(0).getALocalSource() and
|
||||
exists(int i |
|
||||
prop = DataFlow::PseudoProperties::arrayElement(i) and
|
||||
pred = arr.getElement(i) and
|
||||
result = call
|
||||
)
|
||||
)
|
||||
or
|
||||
// collection.set(key, value)
|
||||
exists(DataFlow::CallNode call | call = immutableCollection().getMember("set").getACall() |
|
||||
call.getArgument(0).mayHaveStringValue(prop) and
|
||||
pred = call.getArgument(1) and
|
||||
result = call
|
||||
)
|
||||
or
|
||||
// list.push(x)
|
||||
exists(DataFlow::CallNode call | call = immutableCollection().getMember("push").getACall() |
|
||||
pred = call.getArgument(0) and
|
||||
result = call and
|
||||
prop = DataFlow::PseudoProperties::arrayElement()
|
||||
)
|
||||
or
|
||||
// Immutable.Record({defaults})({values}).
|
||||
exists(API::CallNode factoryCall, API::CallNode recordCall |
|
||||
factoryCall = immutableImport().getMember("Record").getACall() and
|
||||
recordCall = factoryCall.getReturn().getACall()
|
||||
|
|
||||
pred = [factoryCall, recordCall].getOptionArgument(0, prop) and
|
||||
result = recordCall
|
||||
)
|
||||
or
|
||||
// List/Set/Stack.of(values)
|
||||
exists(API::CallNode call |
|
||||
call =
|
||||
immutableImport()
|
||||
.getMember(["List", "Set", "OrderedSet", "Stack"])
|
||||
.getMember("of")
|
||||
.getACall()
|
||||
|
|
||||
pred = call.getAnArgument() and
|
||||
result = call and
|
||||
prop = DataFlow::PseudoProperties::arrayElement()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value that was stored in the immutable collection `pred` under the name `prop`.
|
||||
*/
|
||||
DataFlow::Node loadStep(DataFlow::Node pred, string prop) {
|
||||
// map.get()
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call = immutableCollection().getMember("get").getACall()
|
||||
|
|
||||
call.getArgument(0).mayHaveStringValue(prop) and
|
||||
pred = call.getReceiver() and
|
||||
result = call
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an immutable collection that contains all the elements from `pred`.
|
||||
*/
|
||||
DataFlow::SourceNode step(DataFlow::Node pred) {
|
||||
// map.set() / list.push() copies all existing values
|
||||
exists(DataFlow::CallNode call |
|
||||
call = immutableCollection().getMember(["set", "push"]).getACall()
|
||||
|
|
||||
pred = call.getReceiver() and
|
||||
result = call
|
||||
)
|
||||
or
|
||||
// toJS()/toList() on any immutable collection converts it to a plain JavaScript object/array (and vice versa for `fromJS`).
|
||||
exists(DataFlow::CallNode call |
|
||||
call = immutableCollection().getMember(["toJS", "toList"]).getACall()
|
||||
|
|
||||
pred = call.getReceiver() and
|
||||
result = call
|
||||
)
|
||||
or
|
||||
// Immutable.merge(x, y)
|
||||
exists(DataFlow::CallNode call | call = immutableImport().getMember("merge").getACall() |
|
||||
pred = call.getAnArgument() and
|
||||
result = call
|
||||
)
|
||||
or
|
||||
// collection.merge(other)
|
||||
exists(DataFlow::CallNode call | call = immutableCollection().getMember("merge").getACall() |
|
||||
pred = [call.getAnArgument(), call.getReceiver()] and
|
||||
result = call
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A dataflow step for an immutable collection.
|
||||
*/
|
||||
class ImmutableConstructionStep extends DataFlow::AdditionalFlowStep {
|
||||
ImmutableConstructionStep() { this = [loadStep(_, _), storeStep(_, _), step(_)] }
|
||||
|
||||
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
this = loadStep(pred, prop) and
|
||||
succ = this
|
||||
}
|
||||
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
this = storeStep(pred, prop) and
|
||||
succ = this
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
this = step(pred) and
|
||||
succ = this
|
||||
}
|
||||
}
|
||||
}
|
||||
120
javascript/ql/src/semmle/javascript/frameworks/Markdown.qll
Normal file
120
javascript/ql/src/semmle/javascript/frameworks/Markdown.qll
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Provides classes for modelling common markdown parsers and generators.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A taint step for the `marked` library, that converts markdown to HTML.
|
||||
*/
|
||||
private class MarkedStep extends TaintTracking::AdditionalTaintStep, DataFlow::CallNode {
|
||||
MarkedStep() {
|
||||
this = DataFlow::globalVarRef("marked").getACall()
|
||||
or
|
||||
this = DataFlow::moduleImport("marked").getACall()
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
pred = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint step for the `markdown-table` library.
|
||||
*/
|
||||
private class MarkdownTableStep extends TaintTracking::AdditionalTaintStep, DataFlow::CallNode {
|
||||
MarkdownTableStep() { this = DataFlow::moduleImport("markdown-table").getACall() }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
pred = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint step for the `showdown` library.
|
||||
*/
|
||||
private class ShowDownStep extends TaintTracking::AdditionalTaintStep, DataFlow::CallNode {
|
||||
ShowDownStep() {
|
||||
this =
|
||||
[DataFlow::globalVarRef("showdown"), DataFlow::moduleImport("showdown")]
|
||||
.getAConstructorInvocation("Converter")
|
||||
.getAMemberCall(["makeHtml", "makeMd"])
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
pred = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes and predicates for modelling taint steps in `unified` and `remark`.
|
||||
*/
|
||||
private module Unified {
|
||||
/**
|
||||
* The creation of a parser from `unified`.
|
||||
* The `remark` module is a shorthand that initializes `unified` with a markdown parser.
|
||||
*/
|
||||
DataFlow::CallNode unified() { result = DataFlow::moduleImport(["unified", "remark"]).getACall() }
|
||||
|
||||
/**
|
||||
* A chain of method calls that process an input with `unified`.
|
||||
*/
|
||||
class UnifiedChain extends DataFlow::CallNode {
|
||||
DataFlow::CallNode root;
|
||||
|
||||
UnifiedChain() {
|
||||
root = unified() and
|
||||
this = root.getAChainedMethodCall(["process", "processSync"])
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a plugin that is used in this chain.
|
||||
*/
|
||||
DataFlow::Node getAUsedPlugin() { result = root.getAChainedMethodCall("use").getArgument(0) }
|
||||
|
||||
/**
|
||||
* Gets the input that is processed.
|
||||
*/
|
||||
DataFlow::Node getInput() { result = getArgument(0) }
|
||||
|
||||
/**
|
||||
* Gets the processed output.
|
||||
*/
|
||||
DataFlow::Node getOutput() {
|
||||
this.getCalleeName() = "process" and result = getABoundCallbackParameter(1, 1)
|
||||
or
|
||||
this.getCalleeName() = "processSync" and result = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint step for the `unified` library.
|
||||
*/
|
||||
class UnifiedStep extends TaintTracking::AdditionalTaintStep, UnifiedChain {
|
||||
UnifiedStep() {
|
||||
// sanitizer. Mostly looking for `rehype-sanitize`, but also other plugins with `sanitize` in their name.
|
||||
not this.getAUsedPlugin().getALocalSource() =
|
||||
DataFlow::moduleImport(any(string s | s.matches("%sanitize%")))
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = getInput() and
|
||||
succ = getOutput()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint step for the `snarkdown` library.
|
||||
*/
|
||||
private class SnarkdownStep extends TaintTracking::AdditionalTaintStep, DataFlow::CallNode {
|
||||
SnarkdownStep() { this = DataFlow::moduleImport("snarkdown").getACall() }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
this = succ and
|
||||
pred = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,9 @@ module XML {
|
||||
|
||||
/** Holds if this call to the XML parser resolves entities of the given `kind`. */
|
||||
abstract predicate resolvesEntities(EntityKind kind);
|
||||
|
||||
/** Gets a reference to a value resulting from parsing the XML. */
|
||||
js::DataFlow::Node getAResult() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,10 +101,11 @@ module XML {
|
||||
* An invocation of `expat.Parser.parse` or `expat.Parser.write`.
|
||||
*/
|
||||
class ExpatParserInvocation extends ParserInvocation {
|
||||
js::DataFlow::NewNode parser;
|
||||
|
||||
ExpatParserInvocation() {
|
||||
exists(string m | m = "parse" or m = "write" |
|
||||
this = moduleMethodCall("node-expat", "Parser", m)
|
||||
)
|
||||
parser = js::DataFlow::moduleMember("node-expat", "Parser").getAnInstantiation() and
|
||||
this = parser.getAMemberCall(["parse", "write"]).asExpr()
|
||||
}
|
||||
|
||||
override js::Expr getSourceArgument() { result = getArgument(0) }
|
||||
@@ -110,6 +114,10 @@ module XML {
|
||||
// only internal entities are resolved by default
|
||||
kind = InternalEntity()
|
||||
}
|
||||
|
||||
override js::DataFlow::Node getAResult() {
|
||||
result = parser.getAMemberCall("on").getABoundCallbackParameter(1, _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,4 +168,122 @@ module XML {
|
||||
|
||||
override predicate resolvesEntities(XML::EntityKind kind) { kind = InternalEntity() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of `xml2js`.
|
||||
*/
|
||||
private class Xml2JSInvocation extends XML::ParserInvocation {
|
||||
js::DataFlow::CallNode call;
|
||||
|
||||
Xml2JSInvocation() {
|
||||
exists(js::API::Node imp | imp = js::API::moduleImport("xml2js") |
|
||||
call = [imp, imp.getMember("Parser").getInstance()].getMember("parseString").getACall() and
|
||||
this = call.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override js::Expr getSourceArgument() { result = getArgument(0) }
|
||||
|
||||
override predicate resolvesEntities(XML::EntityKind kind) {
|
||||
// sax-js (the parser used) does not expand entities.
|
||||
none()
|
||||
}
|
||||
|
||||
override js::DataFlow::Node getAResult() {
|
||||
result = call.getABoundCallbackParameter(call.getNumArgument() - 1, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of `sax`.
|
||||
*/
|
||||
private class SaxInvocation extends XML::ParserInvocation {
|
||||
js::DataFlow::InvokeNode parser;
|
||||
|
||||
SaxInvocation() {
|
||||
exists(js::API::Node imp | imp = js::API::moduleImport("sax") |
|
||||
parser = imp.getMember("parser").getACall()
|
||||
or
|
||||
parser = imp.getMember("SAXParser").getAnInstantiation()
|
||||
) and
|
||||
this = parser.getAMemberCall("write").asExpr()
|
||||
}
|
||||
|
||||
override js::Expr getSourceArgument() { result = getArgument(0) }
|
||||
|
||||
override predicate resolvesEntities(XML::EntityKind kind) {
|
||||
// sax-js does not expand entities.
|
||||
none()
|
||||
}
|
||||
|
||||
override js::DataFlow::Node getAResult() {
|
||||
result =
|
||||
parser
|
||||
.getAPropertyWrite(any(string s | s.matches("on%")))
|
||||
.getRhs()
|
||||
.getAFunctionValue()
|
||||
.getAParameter()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of `xml-js`.
|
||||
*/
|
||||
private class XmlJSInvocation extends XML::ParserInvocation {
|
||||
XmlJSInvocation() {
|
||||
this =
|
||||
js::DataFlow::moduleMember("xml-js", ["xml2json", "xml2js", "json2xml", "js2xml"])
|
||||
.getACall()
|
||||
.asExpr()
|
||||
}
|
||||
|
||||
override js::Expr getSourceArgument() { result = getArgument(0) }
|
||||
|
||||
override predicate resolvesEntities(XML::EntityKind kind) {
|
||||
// xml-js does not expand custom entities.
|
||||
none()
|
||||
}
|
||||
|
||||
override js::DataFlow::Node getAResult() { result.asExpr() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* An invocation of `htmlparser2`.
|
||||
*/
|
||||
private class HtmlParser2Invocation extends XML::ParserInvocation {
|
||||
js::DataFlow::NewNode parser;
|
||||
|
||||
HtmlParser2Invocation() {
|
||||
parser = js::DataFlow::moduleMember("htmlparser2", "Parser").getAnInstantiation() and
|
||||
this = parser.getAMemberCall("write").asExpr()
|
||||
}
|
||||
|
||||
override js::Expr getSourceArgument() { result = getArgument(0) }
|
||||
|
||||
override predicate resolvesEntities(XML::EntityKind kind) {
|
||||
// htmlparser2 does not expand entities.
|
||||
none()
|
||||
}
|
||||
|
||||
override js::DataFlow::Node getAResult() {
|
||||
result =
|
||||
parser
|
||||
.getArgument(0)
|
||||
.getALocalSource()
|
||||
.getAPropertySource()
|
||||
.getAFunctionValue()
|
||||
.getAParameter()
|
||||
}
|
||||
}
|
||||
|
||||
private class XMLParserTaintStep extends js::TaintTracking::AdditionalTaintStep {
|
||||
XML::ParserInvocation parser;
|
||||
|
||||
XMLParserTaintStep() { this.asExpr() = parser }
|
||||
|
||||
override predicate step(js::DataFlow::Node pred, js::DataFlow::Node succ) {
|
||||
pred.asExpr() = parser.getSourceArgument() and
|
||||
succ = parser.getAResult()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,20 +221,16 @@ module CleartextLogging {
|
||||
/**
|
||||
* Holds if `name` is filtered by e.g. a regular-expression test or a filter call.
|
||||
*/
|
||||
private predicate isFilteredPropertyName(DataFlow::Node name) {
|
||||
private predicate isFilteredPropertyName(DataFlow::SourceNode name) {
|
||||
exists(DataFlow::MethodCallNode reduceCall |
|
||||
reduceCall.getABoundCallbackParameter(0, 1).flowsTo(name) and
|
||||
reduceCall.getMethodName() = "reduce"
|
||||
reduceCall.getMethodName() = "reduce" and
|
||||
reduceCall.getABoundCallbackParameter(0, 1) = name
|
||||
|
|
||||
reduceCall.getReceiver+().(DataFlow::MethodCallNode).getMethodName() = "filter"
|
||||
)
|
||||
or
|
||||
exists(StringOps::RegExpTest test |
|
||||
test.getStringOperand().getALocalSource() = name.getALocalSource()
|
||||
)
|
||||
exists(StringOps::RegExpTest test | test.getStringOperand().getALocalSource() = name)
|
||||
or
|
||||
exists(MembershipCandidate test |
|
||||
test.getAMemberNode().getALocalSource() = name.getALocalSource()
|
||||
)
|
||||
exists(MembershipCandidate test | test.getAMemberNode().getALocalSource() = name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ private import semmle.javascript.dataflow.InferredTypes
|
||||
*/
|
||||
module XssThroughDom {
|
||||
import Xss::XssThroughDom
|
||||
private import XssThroughDomCustomizations::XssThroughDom
|
||||
private import semmle.javascript.security.dataflow.Xss::DomBasedXss as DomBasedXss
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
private import semmle.javascript.security.dataflow.UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin as UnsafeJQuery
|
||||
|
||||
/**
|
||||
@@ -40,88 +40,4 @@ module XssThroughDom {
|
||||
DomBasedXss::isOptionallySanitizedEdge(pred, succ)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an attribute name that could store user-controlled data.
|
||||
*
|
||||
* Attributes such as "id", "href", and "src" are often used as input to HTML.
|
||||
* However, they are either rarely controlable by a user, or already a sink for other XSS vulnerabilities.
|
||||
* Such attributes are therefore ignored.
|
||||
*/
|
||||
bindingset[result]
|
||||
string unsafeAttributeName() {
|
||||
result.regexpMatch("data-.*") or
|
||||
result.regexpMatch("aria-.*") or
|
||||
result = ["name", "value", "title", "alt"]
|
||||
}
|
||||
|
||||
/**
|
||||
* A source for text from the DOM from a JQuery method call.
|
||||
*/
|
||||
class JQueryTextSource extends Source, JQuery::MethodCall {
|
||||
JQueryTextSource() {
|
||||
(
|
||||
this.getMethodName() = ["text", "val"] and this.getNumArgument() = 0
|
||||
or
|
||||
this.getMethodName() = "attr" and
|
||||
this.getNumArgument() = 1 and
|
||||
forex(InferredType t | t = this.getArgument(0).analyze().getAType() | t = TTString()) and
|
||||
this.getArgument(0).mayHaveStringValue(unsafeAttributeName())
|
||||
) and
|
||||
// looks like a $("<p>" + ... ) source, which is benign for this query.
|
||||
not exists(DataFlow::Node prefix |
|
||||
DomBasedXss::isPrefixOfJQueryHtmlString(this.getReceiver()
|
||||
.(DataFlow::CallNode)
|
||||
.getAnArgument(), prefix)
|
||||
|
|
||||
prefix.getStringValue().regexpMatch("\\s*<.*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A source for text from the DOM from a DOM property read or call to `getAttribute()`.
|
||||
*/
|
||||
class DOMTextSource extends Source {
|
||||
DOMTextSource() {
|
||||
exists(DataFlow::PropRead read | read = this |
|
||||
read.getBase().getALocalSource() = DOM::domValueRef() and
|
||||
read.mayHavePropertyName(["innerText", "textContent", "value", "name"])
|
||||
)
|
||||
or
|
||||
exists(DataFlow::MethodCallNode mcn | mcn = this |
|
||||
mcn.getReceiver().getALocalSource() = DOM::domValueRef() and
|
||||
mcn.getMethodName() = "getAttribute" and
|
||||
mcn.getArgument(0).mayHaveStringValue(unsafeAttributeName())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A test of form `typeof x === "something"`, preventing `x` from being a string in some cases.
|
||||
*
|
||||
* This sanitizer helps prune infeasible paths in type-overloaded functions.
|
||||
*/
|
||||
class TypeTestGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
|
||||
override EqualityTest astNode;
|
||||
Expr operand;
|
||||
boolean polarity;
|
||||
|
||||
TypeTestGuard() {
|
||||
exists(TypeofTag tag | TaintTracking::isTypeofGuard(astNode, operand, tag) |
|
||||
// typeof x === "string" sanitizes `x` when it evaluates to false
|
||||
tag = "string" and
|
||||
polarity = astNode.getPolarity().booleanNot()
|
||||
or
|
||||
// typeof x === "object" sanitizes `x` when it evaluates to true
|
||||
tag != "string" and
|
||||
polarity = astNode.getPolarity()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
polarity = outcome and
|
||||
e = operand
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Provides default sources for reasoning about
|
||||
* cross-site scripting vulnerabilities through the DOM.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Sources for cross-site scripting vulnerabilities through the DOM.
|
||||
*/
|
||||
module XssThroughDom {
|
||||
import Xss::XssThroughDom
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
private import semmle.javascript.security.dataflow.Xss::DomBasedXss as DomBasedXss
|
||||
|
||||
/**
|
||||
* Gets an attribute name that could store user-controlled data.
|
||||
*
|
||||
* Attributes such as "id", "href", and "src" are often used as input to HTML.
|
||||
* However, they are either rarely controlable by a user, or already a sink for other XSS vulnerabilities.
|
||||
* Such attributes are therefore ignored.
|
||||
*/
|
||||
bindingset[result]
|
||||
string unsafeAttributeName() {
|
||||
result.regexpMatch("data-.*") or
|
||||
result.regexpMatch("aria-.*") or
|
||||
result = ["name", "value", "title", "alt"]
|
||||
}
|
||||
|
||||
/**
|
||||
* A source for text from the DOM from a JQuery method call.
|
||||
*/
|
||||
class JQueryTextSource extends Source, JQuery::MethodCall {
|
||||
JQueryTextSource() {
|
||||
(
|
||||
this.getMethodName() = ["text", "val"] and this.getNumArgument() = 0
|
||||
or
|
||||
this.getMethodName() = "attr" and
|
||||
this.getNumArgument() = 1 and
|
||||
forex(InferredType t | t = this.getArgument(0).analyze().getAType() | t = TTString()) and
|
||||
this.getArgument(0).mayHaveStringValue(unsafeAttributeName())
|
||||
) and
|
||||
// looks like a $("<p>" + ... ) source, which is benign for this query.
|
||||
not exists(DataFlow::Node prefix |
|
||||
DomBasedXss::isPrefixOfJQueryHtmlString(this.getReceiver()
|
||||
.(DataFlow::CallNode)
|
||||
.getAnArgument(), prefix)
|
||||
|
|
||||
prefix.getStringValue().regexpMatch("\\s*<.*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A source for text from the DOM from a DOM property read or call to `getAttribute()`.
|
||||
*/
|
||||
class DOMTextSource extends Source {
|
||||
DOMTextSource() {
|
||||
exists(DataFlow::PropRead read | read = this |
|
||||
read.getBase().getALocalSource() = DOM::domValueRef() and
|
||||
read.mayHavePropertyName(["innerText", "textContent", "value", "name"])
|
||||
)
|
||||
or
|
||||
exists(DataFlow::MethodCallNode mcn | mcn = this |
|
||||
mcn.getReceiver().getALocalSource() = DOM::domValueRef() and
|
||||
mcn.getMethodName() = "getAttribute" and
|
||||
mcn.getArgument(0).mayHaveStringValue(unsafeAttributeName())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A test of form `typeof x === "something"`, preventing `x` from being a string in some cases.
|
||||
*
|
||||
* This sanitizer helps prune infeasible paths in type-overloaded functions.
|
||||
*/
|
||||
class TypeTestGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
|
||||
override EqualityTest astNode;
|
||||
Expr operand;
|
||||
boolean polarity;
|
||||
|
||||
TypeTestGuard() {
|
||||
exists(TypeofTag tag | TaintTracking::isTypeofGuard(astNode, operand, tag) |
|
||||
// typeof x === "string" sanitizes `x` when it evaluates to false
|
||||
tag = "string" and
|
||||
polarity = astNode.getPolarity().booleanNot()
|
||||
or
|
||||
// typeof x === "object" sanitizes `x` when it evaluates to true
|
||||
tag != "string" and
|
||||
polarity = astNode.getPolarity()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
polarity = outcome and
|
||||
e = operand
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A module for form inputs seen as sources for xss-through-dom.
|
||||
*/
|
||||
module Forms {
|
||||
/**
|
||||
* A reference to an import of `Formik`.
|
||||
*/
|
||||
private DataFlow::SourceNode formik() {
|
||||
result = DataFlow::moduleImport("formik")
|
||||
or
|
||||
result = DataFlow::globalVarRef("Formik")
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing input values from a form build with `Formik`.
|
||||
*/
|
||||
class FormikSource extends Source {
|
||||
FormikSource() {
|
||||
exists(JSXElement elem |
|
||||
formik().getAPropertyRead("Formik").flowsToExpr(elem.getNameExpr())
|
||||
|
|
||||
this =
|
||||
elem.getAttributeByName(["validate", "onSubmit"])
|
||||
.getValue()
|
||||
.flow()
|
||||
.getAFunctionValue()
|
||||
.getParameter(0)
|
||||
)
|
||||
or
|
||||
this =
|
||||
formik()
|
||||
.getAMemberCall("withFormik")
|
||||
.getOptionArgument(0, ["validate", "handleSubmit"])
|
||||
.getAFunctionValue()
|
||||
.getParameter(0)
|
||||
or
|
||||
this = formik().getAMemberCall("useFormikContext").getAPropertyRead("values")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing input values from a form build with `react-final-form`.
|
||||
*/
|
||||
class ReactFinalFormSource extends Source {
|
||||
ReactFinalFormSource() {
|
||||
exists(JSXElement elem |
|
||||
DataFlow::moduleMember("react-final-form", "Form").flowsToExpr(elem.getNameExpr())
|
||||
|
|
||||
this =
|
||||
elem.getAttributeByName("onSubmit")
|
||||
.getValue()
|
||||
.flow()
|
||||
.getAFunctionValue()
|
||||
.getParameter(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing input values from a form build with `react-hook-form`.
|
||||
*/
|
||||
class ReactHookFormSource extends Source {
|
||||
ReactHookFormSource() {
|
||||
exists(API::Node useForm |
|
||||
useForm = API::moduleImport("react-hook-form").getMember("useForm").getReturn()
|
||||
|
|
||||
this =
|
||||
useForm.getMember("handleSubmit").getParameter(0).getParameter(0).getAnImmediateUse()
|
||||
or
|
||||
this = useForm.getMember("getValues").getACall()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
nodes
|
||||
| tst.js:5:9:5:46 | bodyParameter |
|
||||
| tst.js:5:25:5:32 | req.body |
|
||||
| tst.js:5:25:5:32 | req.body |
|
||||
| tst.js:5:25:5:46 | req.bod ... rameter |
|
||||
| tst.js:6:9:6:49 | queryParameter |
|
||||
| tst.js:6:9:6:49 | queryParameter |
|
||||
| tst.js:6:26:6:49 | req.que ... rameter |
|
||||
| tst.js:6:26:6:49 | req.que ... rameter |
|
||||
| tst.js:6:26:6:49 | req.que ... rameter |
|
||||
| tst.js:8:28:8:40 | bodyParameter |
|
||||
| tst.js:8:28:8:40 | bodyParameter |
|
||||
| tst.js:9:28:9:41 | queryParameter |
|
||||
| tst.js:9:28:9:41 | queryParameter |
|
||||
| tst.js:18:19:18:32 | queryParameter |
|
||||
| tst.js:18:19:18:32 | queryParameter |
|
||||
| tst.js:21:24:21:26 | obj |
|
||||
| tst.js:21:24:21:26 | obj |
|
||||
| tst.js:22:28:22:30 | obj |
|
||||
| tst.js:22:28:22:30 | obj |
|
||||
| tst.js:24:11:24:24 | str |
|
||||
| tst.js:24:17:24:19 | obj |
|
||||
| tst.js:24:17:24:24 | obj + "" |
|
||||
| tst.js:27:28:27:42 | JSON.parse(str) |
|
||||
| tst.js:27:28:27:42 | JSON.parse(str) |
|
||||
| tst.js:27:39:27:41 | str |
|
||||
edges
|
||||
| tst.js:5:9:5:46 | bodyParameter | tst.js:8:28:8:40 | bodyParameter |
|
||||
| tst.js:5:9:5:46 | bodyParameter | tst.js:8:28:8:40 | bodyParameter |
|
||||
| tst.js:5:25:5:32 | req.body | tst.js:5:25:5:46 | req.bod ... rameter |
|
||||
| tst.js:5:25:5:32 | req.body | tst.js:5:25:5:46 | req.bod ... rameter |
|
||||
| tst.js:5:25:5:46 | req.bod ... rameter | tst.js:5:9:5:46 | bodyParameter |
|
||||
| tst.js:6:9:6:49 | queryParameter | tst.js:9:28:9:41 | queryParameter |
|
||||
| tst.js:6:9:6:49 | queryParameter | tst.js:9:28:9:41 | queryParameter |
|
||||
| tst.js:6:9:6:49 | queryParameter | tst.js:18:19:18:32 | queryParameter |
|
||||
| tst.js:6:9:6:49 | queryParameter | tst.js:18:19:18:32 | queryParameter |
|
||||
| tst.js:6:26:6:49 | req.que ... rameter | tst.js:6:9:6:49 | queryParameter |
|
||||
| tst.js:6:26:6:49 | req.que ... rameter | tst.js:6:9:6:49 | queryParameter |
|
||||
| tst.js:6:26:6:49 | req.que ... rameter | tst.js:6:9:6:49 | queryParameter |
|
||||
| tst.js:6:26:6:49 | req.que ... rameter | tst.js:6:9:6:49 | queryParameter |
|
||||
| tst.js:18:19:18:32 | queryParameter | tst.js:21:24:21:26 | obj |
|
||||
| tst.js:18:19:18:32 | queryParameter | tst.js:21:24:21:26 | obj |
|
||||
| tst.js:21:24:21:26 | obj | tst.js:22:28:22:30 | obj |
|
||||
| tst.js:21:24:21:26 | obj | tst.js:22:28:22:30 | obj |
|
||||
| tst.js:21:24:21:26 | obj | tst.js:24:17:24:19 | obj |
|
||||
| tst.js:24:11:24:24 | str | tst.js:27:39:27:41 | str |
|
||||
| tst.js:24:17:24:19 | obj | tst.js:24:17:24:24 | obj + "" |
|
||||
| tst.js:24:17:24:24 | obj + "" | tst.js:24:11:24:24 | str |
|
||||
| tst.js:27:39:27:41 | str | tst.js:27:28:27:42 | JSON.parse(str) |
|
||||
| tst.js:27:39:27:41 | str | tst.js:27:28:27:42 | JSON.parse(str) |
|
||||
#select
|
||||
| tst.js:8:28:8:40 | bodyParameter | tst.js:5:25:5:32 | req.body | tst.js:8:28:8:40 | bodyParameter | Template object injection due to $@. | tst.js:5:25:5:32 | req.body | user-provided value |
|
||||
| tst.js:9:28:9:41 | queryParameter | tst.js:6:26:6:49 | req.que ... rameter | tst.js:9:28:9:41 | queryParameter | Template object injection due to $@. | tst.js:6:26:6:49 | req.que ... rameter | user-provided value |
|
||||
| tst.js:22:28:22:30 | obj | tst.js:6:26:6:49 | req.que ... rameter | tst.js:22:28:22:30 | obj | Template object injection due to $@. | tst.js:6:26:6:49 | req.que ... rameter | user-provided value |
|
||||
| tst.js:27:28:27:42 | JSON.parse(str) | tst.js:6:26:6:49 | req.que ... rameter | tst.js:27:28:27:42 | JSON.parse(str) | Template object injection due to $@. | tst.js:6:26:6:49 | req.que ... rameter | user-provided value |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-073/TemplateObjectInjection.ql
|
||||
28
javascript/ql/test/experimental/Security/CWE-073/tst.js
Normal file
28
javascript/ql/test/experimental/Security/CWE-073/tst.js
Normal file
@@ -0,0 +1,28 @@
|
||||
var app = require('express')();
|
||||
app.set('view engine', 'hbs');
|
||||
|
||||
app.post('/path', function(req, res) {
|
||||
var bodyParameter = req.body.bodyParameter;
|
||||
var queryParameter = req.query.queryParameter;
|
||||
|
||||
res.render('template', bodyParameter); // NOT OK
|
||||
res.render('template', queryParameter); // NOT OK
|
||||
|
||||
if (typeof bodyParameter === "string") {
|
||||
res.render('template', bodyParameter); // OK
|
||||
}
|
||||
res.render('template', queryParameter + ""); // OK
|
||||
|
||||
res.render('template', {profile: bodyParameter}); // OK
|
||||
|
||||
indirect(res, queryParameter);
|
||||
});
|
||||
|
||||
function indirect(res, obj) {
|
||||
res.render("template", obj); // NOT OK
|
||||
|
||||
const str = obj + "";
|
||||
res.render("template", str); // OK
|
||||
|
||||
res.render("template", JSON.parse(str)); // NOT OK
|
||||
}
|
||||
@@ -145,3 +145,8 @@ typeInferenceMismatch
|
||||
| tst.js:2:13:2:20 | source() | tst.js:45:10:45:24 | x.map(x2 => x2) |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:47:10:47:30 | Buffer. ... 'hex') |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:48:10:48:22 | new Buffer(x) |
|
||||
| xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text |
|
||||
| xml.js:12:17:12:24 | source() | xml.js:13:14:13:19 | result |
|
||||
| xml.js:23:18:23:25 | source() | xml.js:20:14:20:17 | attr |
|
||||
| xml.js:26:27:26:34 | source() | xml.js:26:10:26:39 | convert ... (), {}) |
|
||||
| xml.js:34:18:34:25 | source() | xml.js:31:18:31:21 | name |
|
||||
|
||||
37
javascript/ql/test/library-tests/TaintTracking/xml.js
Normal file
37
javascript/ql/test/library-tests/TaintTracking/xml.js
Normal file
@@ -0,0 +1,37 @@
|
||||
(function () {
|
||||
var Parser = require("node-expat").Parser
|
||||
var parser = new Parser();
|
||||
|
||||
parser.write(source());
|
||||
|
||||
parser.on("text", text => {
|
||||
sink(text); // NOT OK
|
||||
});
|
||||
|
||||
var parseString = require('xml2js').parseString;
|
||||
parseString(source(), function (err, result) {
|
||||
sink(result); // NOT OK
|
||||
});
|
||||
|
||||
var sax = require("sax");
|
||||
var parser = sax.parser(strict);
|
||||
|
||||
parser.onattribute = function (attr) {
|
||||
sink(attr); // NOT OK
|
||||
};
|
||||
|
||||
parser.write(source()).close();
|
||||
|
||||
var convert = require('xml-js');
|
||||
sink(convert.xml2json(source(), {})); // NOT OK
|
||||
|
||||
const htmlparser2 = require("htmlparser2");
|
||||
const parser = new htmlparser2.Parser({
|
||||
onopentag(name, attributes) {
|
||||
sink(name) // NOT OK
|
||||
}
|
||||
});
|
||||
parser.write(source());
|
||||
parser.end();
|
||||
|
||||
})();
|
||||
@@ -1,4 +1,10 @@
|
||||
test_ClientRequest
|
||||
| apollo.js:5:18:5:78 | new cre ... hql' }) |
|
||||
| apollo.js:10:1:10:54 | new Htt ... hql' }) |
|
||||
| apollo.js:14:16:14:69 | new Apo ... .com'}) |
|
||||
| apollo.js:17:1:17:34 | new Pre ... yurl"}) |
|
||||
| apollo.js:20:1:20:77 | createN ... phql'}) |
|
||||
| apollo.js:23:1:23:31 | new Web ... wsUri}) |
|
||||
| tst.js:11:5:11:16 | request(url) |
|
||||
| tst.js:13:5:13:20 | request.get(url) |
|
||||
| tst.js:15:5:15:23 | request.delete(url) |
|
||||
@@ -111,6 +117,12 @@ test_getHost
|
||||
| tst.js:93:5:93:35 | net.req ... host }) | tst.js:93:29:93:32 | host |
|
||||
| tst.js:219:5:219:41 | data.so ... Host"}) | tst.js:219:32:219:39 | "myHost" |
|
||||
test_getUrl
|
||||
| apollo.js:5:18:5:78 | new cre ... hql' }) | apollo.js:5:44:5:75 | 'https: ... raphql' |
|
||||
| apollo.js:10:1:10:54 | new Htt ... hql' }) | apollo.js:10:21:10:51 | 'http:/ ... raphql' |
|
||||
| apollo.js:14:16:14:69 | new Apo ... .com'}) | apollo.js:14:39:14:67 | 'https: ... le.com' |
|
||||
| apollo.js:17:1:17:34 | new Pre ... yurl"}) | apollo.js:17:26:17:32 | "myurl" |
|
||||
| apollo.js:20:1:20:77 | createN ... phql'}) | apollo.js:20:30:20:75 | 'https: ... raphql' |
|
||||
| apollo.js:23:1:23:31 | new Web ... wsUri}) | apollo.js:23:25:23:29 | wsUri |
|
||||
| tst.js:11:5:11:16 | request(url) | tst.js:11:13:11:15 | url |
|
||||
| tst.js:13:5:13:20 | request.get(url) | tst.js:13:17:13:19 | url |
|
||||
| tst.js:15:5:15:23 | request.delete(url) | tst.js:15:20:15:22 | url |
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ApolloClient } from 'apollo-client'
|
||||
import { HttpLink } from 'apollo-link-http'
|
||||
import { createHttpLink } from 'apollo-link-http'
|
||||
|
||||
const httpLink = new createHttpLink({ uri: 'https://api.github.com/graphql' }) // url 1
|
||||
|
||||
const ApolloClient = require('apollo-client-preset').ApolloClient;
|
||||
const HttpLink = require('apollo-link-http').HttpLink;
|
||||
|
||||
new HttpLink({ uri: 'http://localhost:8080/graphql' }); // url 2
|
||||
|
||||
import ApolloClient from 'apollo-boost'; // / 'apollo-client'
|
||||
|
||||
const client = new ApolloClient({uri: 'https://graphql.example.com'}); // url 3
|
||||
|
||||
let PresetHttpLink = require('apollo-client-preset').HttpLink;
|
||||
new PresetHttpLink({uri: "myurl"}); // url 4
|
||||
|
||||
import { createNetworkInterface } from 'apollo-client';
|
||||
createNetworkInterface({uri: 'https://web-go-demo.herokuapp.com/__/graphql'}) // url 5
|
||||
|
||||
const { WebSocketLink } = require('apollo-link-ws')
|
||||
new WebSocketLink({uri: wsUri}) // url 6
|
||||
@@ -0,0 +1,58 @@
|
||||
var obj = { a: source("a"), b: source("b1") };
|
||||
sink(obj["a"]); // NOT OK
|
||||
|
||||
const { Map, fromJS, List, OrderedMap, Record, merge, Stack, Set, OrderedSet } = require('immutable');
|
||||
|
||||
const map1 = Map(obj);
|
||||
|
||||
sink(map1.get("b")); // NOT OK
|
||||
|
||||
const map2 = map1.set('c', "safe");
|
||||
sink(map1.get("a")); // NOT OK
|
||||
sink(map2.get("a")); // NOT OK
|
||||
sink(map2.get("b")); // OK - but still flagged [INCONSISTENCY]
|
||||
|
||||
const map3 = map2.set("d", source("d"));
|
||||
sink(map1.get("d")); // OK
|
||||
sink(map3.get("d")); // NOT OK
|
||||
|
||||
|
||||
sink(map3.toJS()["a"]); // NOT OK
|
||||
|
||||
sink(fromJS({"e": source("e")}).get("e")); // NOT OK
|
||||
|
||||
const l1 = List([source(), "foobar"]);
|
||||
l1.forEach(x => sink(x)); // NOT OK
|
||||
|
||||
l1.map(x => "safe").forEach(x => sink(x)); // OK
|
||||
|
||||
List(["safe"]).map(x => source()).forEach(x => sink(x)); // NOT OK
|
||||
|
||||
List([source()]).map(x => x).filter(x => true).toList().forEach(x => sink(x)); // NOT OK
|
||||
|
||||
List(["safe"]).push(source()).forEach(x => sink(x)); // NOT OK
|
||||
|
||||
|
||||
const map4 = OrderedMap({}).set("f", source());
|
||||
sink(map4.get("f")); // NOT OK
|
||||
|
||||
const map5 = Record({a: source(), b: null, c: null})({b: source()});
|
||||
sink(map5.get("a")); // NOT OK
|
||||
sink(map5.get("b")); // NOT OK
|
||||
sink(map5.get("c")); // OK
|
||||
|
||||
const map6 = merge(Map({}), Record({a: source()})());
|
||||
sink(map6.get("a")); // NOT OK
|
||||
|
||||
const map7 = map6.merge(Map({b: source()}));
|
||||
sink(map7.get("b")); // NOT OK
|
||||
|
||||
Stack.of(source(), "foobar").forEach(x => sink(x)); // NOT OK
|
||||
|
||||
List.of(source()).filter(x => true).toList().forEach(x => sink(x)); // NOT OK
|
||||
|
||||
Set.of(source()).filter(x => true).toList().forEach(x => sink(x)); // NOT OK
|
||||
|
||||
Set([source()]).filter(x => true).toList().forEach(x => sink(x)); // NOT OK
|
||||
|
||||
OrderedSet([source()]).filter(x => true).toList().forEach(x => sink(x)); // NOT OK
|
||||
@@ -0,0 +1,22 @@
|
||||
| immutable.js:1:16:1:26 | source("a") | immutable.js:2:6:2:13 | obj["a"] |
|
||||
| immutable.js:1:16:1:26 | source("a") | immutable.js:11:6:11:18 | map1.get("a") |
|
||||
| immutable.js:1:16:1:26 | source("a") | immutable.js:12:6:12:18 | map2.get("a") |
|
||||
| immutable.js:1:16:1:26 | source("a") | immutable.js:20:6:20:21 | map3.toJS()["a"] |
|
||||
| immutable.js:1:32:1:43 | source("b1") | immutable.js:8:6:8:18 | map1.get("b") |
|
||||
| immutable.js:1:32:1:43 | source("b1") | immutable.js:13:6:13:18 | map2.get("b") |
|
||||
| immutable.js:15:28:15:38 | source("d") | immutable.js:17:6:17:18 | map3.get("d") |
|
||||
| immutable.js:22:19:22:29 | source("e") | immutable.js:22:6:22:40 | fromJS( ... et("e") |
|
||||
| immutable.js:24:18:24:25 | source() | immutable.js:25:22:25:22 | x |
|
||||
| immutable.js:29:25:29:32 | source() | immutable.js:29:53:29:53 | x |
|
||||
| immutable.js:31:7:31:14 | source() | immutable.js:31:75:31:75 | x |
|
||||
| immutable.js:33:21:33:28 | source() | immutable.js:33:49:33:49 | x |
|
||||
| immutable.js:36:38:36:45 | source() | immutable.js:37:6:37:18 | map4.get("f") |
|
||||
| immutable.js:39:25:39:32 | source() | immutable.js:40:6:40:18 | map5.get("a") |
|
||||
| immutable.js:39:58:39:65 | source() | immutable.js:41:6:41:18 | map5.get("b") |
|
||||
| immutable.js:44:40:44:47 | source() | immutable.js:45:6:45:18 | map6.get("a") |
|
||||
| immutable.js:47:33:47:40 | source() | immutable.js:48:6:48:18 | map7.get("b") |
|
||||
| immutable.js:50:10:50:17 | source() | immutable.js:50:48:50:48 | x |
|
||||
| immutable.js:52:9:52:16 | source() | immutable.js:52:64:52:64 | x |
|
||||
| immutable.js:54:8:54:15 | source() | immutable.js:54:63:54:63 | x |
|
||||
| immutable.js:56:6:56:13 | source() | immutable.js:56:62:56:62 | x |
|
||||
| immutable.js:58:13:58:20 | source() | immutable.js:58:69:58:69 | x |
|
||||
@@ -0,0 +1,18 @@
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.internal.StepSummary
|
||||
|
||||
class Config extends DataFlow::Configuration {
|
||||
Config() { this = "Config" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.(DataFlow::CallNode).getCalleeName() = "source"
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(DataFlow::CallNode call | call.getCalleeName() = "sink" | call.getAnArgument() = sink)
|
||||
}
|
||||
}
|
||||
|
||||
query predicate dataFlow(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(Config c).hasFlow(pred, succ)
|
||||
}
|
||||
@@ -351,3 +351,7 @@ var bad79 = /(a*)*b/;
|
||||
var bad80 = /(a+)*b/;
|
||||
var bad81 = /(a*)+b/;
|
||||
var bad82 = /(a+)+b/;
|
||||
|
||||
// GOOD
|
||||
var good40 = /(a|b)+/;
|
||||
var good41 = /(?:[\s;,"'<>(){}|[\]@=+*]|:(?![/\\]))+/;
|
||||
|
||||
@@ -88,6 +88,47 @@ nodes
|
||||
| execSeries.js:18:34:18:40 | req.url |
|
||||
| execSeries.js:19:12:19:16 | [cmd] |
|
||||
| execSeries.js:19:13:19:15 | cmd |
|
||||
| form-parsers.js:9:8:9:39 | "touch ... nalname |
|
||||
| form-parsers.js:9:8:9:39 | "touch ... nalname |
|
||||
| form-parsers.js:9:19:9:26 | req.file |
|
||||
| form-parsers.js:9:19:9:26 | req.file |
|
||||
| form-parsers.js:9:19:9:39 | req.fil ... nalname |
|
||||
| form-parsers.js:13:3:13:11 | req.files |
|
||||
| form-parsers.js:13:3:13:11 | req.files |
|
||||
| form-parsers.js:13:21:13:24 | file |
|
||||
| form-parsers.js:14:10:14:37 | "touch ... nalname |
|
||||
| form-parsers.js:14:10:14:37 | "touch ... nalname |
|
||||
| form-parsers.js:14:21:14:24 | file |
|
||||
| form-parsers.js:14:21:14:37 | file.originalname |
|
||||
| form-parsers.js:24:48:24:55 | filename |
|
||||
| form-parsers.js:24:48:24:55 | filename |
|
||||
| form-parsers.js:25:10:25:28 | "touch " + filename |
|
||||
| form-parsers.js:25:10:25:28 | "touch " + filename |
|
||||
| form-parsers.js:25:21:25:28 | filename |
|
||||
| form-parsers.js:35:25:35:30 | fields |
|
||||
| form-parsers.js:35:25:35:30 | fields |
|
||||
| form-parsers.js:36:10:36:31 | "touch ... ds.name |
|
||||
| form-parsers.js:36:10:36:31 | "touch ... ds.name |
|
||||
| form-parsers.js:36:21:36:26 | fields |
|
||||
| form-parsers.js:36:21:36:31 | fields.name |
|
||||
| form-parsers.js:40:26:40:31 | fields |
|
||||
| form-parsers.js:40:26:40:31 | fields |
|
||||
| form-parsers.js:41:10:41:31 | "touch ... ds.name |
|
||||
| form-parsers.js:41:10:41:31 | "touch ... ds.name |
|
||||
| form-parsers.js:41:21:41:26 | fields |
|
||||
| form-parsers.js:41:21:41:31 | fields.name |
|
||||
| form-parsers.js:52:34:52:39 | fields |
|
||||
| form-parsers.js:52:34:52:39 | fields |
|
||||
| form-parsers.js:53:10:53:31 | "touch ... ds.name |
|
||||
| form-parsers.js:53:10:53:31 | "touch ... ds.name |
|
||||
| form-parsers.js:53:21:53:26 | fields |
|
||||
| form-parsers.js:53:21:53:31 | fields.name |
|
||||
| form-parsers.js:58:30:58:33 | part |
|
||||
| form-parsers.js:58:30:58:33 | part |
|
||||
| form-parsers.js:59:10:59:33 | "touch ... ilename |
|
||||
| form-parsers.js:59:10:59:33 | "touch ... ilename |
|
||||
| form-parsers.js:59:21:59:24 | part |
|
||||
| form-parsers.js:59:21:59:33 | part.filename |
|
||||
| lib/subLib/index.js:7:32:7:35 | name |
|
||||
| lib/subLib/index.js:8:10:8:25 | "rm -rf " + name |
|
||||
| lib/subLib/index.js:8:10:8:25 | "rm -rf " + name |
|
||||
@@ -222,6 +263,40 @@ edges
|
||||
| execSeries.js:18:34:18:40 | req.url | execSeries.js:18:13:18:47 | require ... , true) |
|
||||
| execSeries.js:19:12:19:16 | [cmd] | execSeries.js:13:19:13:26 | commands |
|
||||
| execSeries.js:19:13:19:15 | cmd | execSeries.js:19:12:19:16 | [cmd] |
|
||||
| form-parsers.js:9:19:9:26 | req.file | form-parsers.js:9:19:9:39 | req.fil ... nalname |
|
||||
| form-parsers.js:9:19:9:26 | req.file | form-parsers.js:9:19:9:39 | req.fil ... nalname |
|
||||
| form-parsers.js:9:19:9:39 | req.fil ... nalname | form-parsers.js:9:8:9:39 | "touch ... nalname |
|
||||
| form-parsers.js:9:19:9:39 | req.fil ... nalname | form-parsers.js:9:8:9:39 | "touch ... nalname |
|
||||
| form-parsers.js:13:3:13:11 | req.files | form-parsers.js:13:21:13:24 | file |
|
||||
| form-parsers.js:13:3:13:11 | req.files | form-parsers.js:13:21:13:24 | file |
|
||||
| form-parsers.js:13:21:13:24 | file | form-parsers.js:14:21:14:24 | file |
|
||||
| form-parsers.js:14:21:14:24 | file | form-parsers.js:14:21:14:37 | file.originalname |
|
||||
| form-parsers.js:14:21:14:37 | file.originalname | form-parsers.js:14:10:14:37 | "touch ... nalname |
|
||||
| form-parsers.js:14:21:14:37 | file.originalname | form-parsers.js:14:10:14:37 | "touch ... nalname |
|
||||
| form-parsers.js:24:48:24:55 | filename | form-parsers.js:25:21:25:28 | filename |
|
||||
| form-parsers.js:24:48:24:55 | filename | form-parsers.js:25:21:25:28 | filename |
|
||||
| form-parsers.js:25:21:25:28 | filename | form-parsers.js:25:10:25:28 | "touch " + filename |
|
||||
| form-parsers.js:25:21:25:28 | filename | form-parsers.js:25:10:25:28 | "touch " + filename |
|
||||
| form-parsers.js:35:25:35:30 | fields | form-parsers.js:36:21:36:26 | fields |
|
||||
| form-parsers.js:35:25:35:30 | fields | form-parsers.js:36:21:36:26 | fields |
|
||||
| form-parsers.js:36:21:36:26 | fields | form-parsers.js:36:21:36:31 | fields.name |
|
||||
| form-parsers.js:36:21:36:31 | fields.name | form-parsers.js:36:10:36:31 | "touch ... ds.name |
|
||||
| form-parsers.js:36:21:36:31 | fields.name | form-parsers.js:36:10:36:31 | "touch ... ds.name |
|
||||
| form-parsers.js:40:26:40:31 | fields | form-parsers.js:41:21:41:26 | fields |
|
||||
| form-parsers.js:40:26:40:31 | fields | form-parsers.js:41:21:41:26 | fields |
|
||||
| form-parsers.js:41:21:41:26 | fields | form-parsers.js:41:21:41:31 | fields.name |
|
||||
| form-parsers.js:41:21:41:31 | fields.name | form-parsers.js:41:10:41:31 | "touch ... ds.name |
|
||||
| form-parsers.js:41:21:41:31 | fields.name | form-parsers.js:41:10:41:31 | "touch ... ds.name |
|
||||
| form-parsers.js:52:34:52:39 | fields | form-parsers.js:53:21:53:26 | fields |
|
||||
| form-parsers.js:52:34:52:39 | fields | form-parsers.js:53:21:53:26 | fields |
|
||||
| form-parsers.js:53:21:53:26 | fields | form-parsers.js:53:21:53:31 | fields.name |
|
||||
| form-parsers.js:53:21:53:31 | fields.name | form-parsers.js:53:10:53:31 | "touch ... ds.name |
|
||||
| form-parsers.js:53:21:53:31 | fields.name | form-parsers.js:53:10:53:31 | "touch ... ds.name |
|
||||
| form-parsers.js:58:30:58:33 | part | form-parsers.js:59:21:59:24 | part |
|
||||
| form-parsers.js:58:30:58:33 | part | form-parsers.js:59:21:59:24 | part |
|
||||
| form-parsers.js:59:21:59:24 | part | form-parsers.js:59:21:59:33 | part.filename |
|
||||
| form-parsers.js:59:21:59:33 | part.filename | form-parsers.js:59:10:59:33 | "touch ... ilename |
|
||||
| form-parsers.js:59:21:59:33 | part.filename | form-parsers.js:59:10:59:33 | "touch ... ilename |
|
||||
| lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name |
|
||||
| lib/subLib/index.js:8:22:8:25 | name | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name |
|
||||
| lib/subLib/index.js:8:22:8:25 | name | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name |
|
||||
@@ -293,6 +368,13 @@ edges
|
||||
| exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command depends on $@. | exec-sh2.js:14:25:14:31 | req.url | a user-provided value |
|
||||
| exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:44:15:50 | command | This command depends on $@. | exec-sh.js:19:25:19:31 | req.url | a user-provided value |
|
||||
| execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command depends on $@. | execSeries.js:18:34:18:40 | req.url | a user-provided value |
|
||||
| form-parsers.js:9:8:9:39 | "touch ... nalname | form-parsers.js:9:19:9:26 | req.file | form-parsers.js:9:8:9:39 | "touch ... nalname | This command depends on $@. | form-parsers.js:9:19:9:26 | req.file | a user-provided value |
|
||||
| form-parsers.js:14:10:14:37 | "touch ... nalname | form-parsers.js:13:3:13:11 | req.files | form-parsers.js:14:10:14:37 | "touch ... nalname | This command depends on $@. | form-parsers.js:13:3:13:11 | req.files | a user-provided value |
|
||||
| form-parsers.js:25:10:25:28 | "touch " + filename | form-parsers.js:24:48:24:55 | filename | form-parsers.js:25:10:25:28 | "touch " + filename | This command depends on $@. | form-parsers.js:24:48:24:55 | filename | a user-provided value |
|
||||
| form-parsers.js:36:10:36:31 | "touch ... ds.name | form-parsers.js:35:25:35:30 | fields | form-parsers.js:36:10:36:31 | "touch ... ds.name | This command depends on $@. | form-parsers.js:35:25:35:30 | fields | a user-provided value |
|
||||
| form-parsers.js:41:10:41:31 | "touch ... ds.name | form-parsers.js:40:26:40:31 | fields | form-parsers.js:41:10:41:31 | "touch ... ds.name | This command depends on $@. | form-parsers.js:40:26:40:31 | fields | a user-provided value |
|
||||
| form-parsers.js:53:10:53:31 | "touch ... ds.name | form-parsers.js:52:34:52:39 | fields | form-parsers.js:53:10:53:31 | "touch ... ds.name | This command depends on $@. | form-parsers.js:52:34:52:39 | fields | a user-provided value |
|
||||
| form-parsers.js:59:10:59:33 | "touch ... ilename | form-parsers.js:58:30:58:33 | part | form-parsers.js:59:10:59:33 | "touch ... ilename | This command depends on $@. | form-parsers.js:58:30:58:33 | part | a user-provided value |
|
||||
| lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | child_process-test.js:85:37:85:54 | req.query.fileName | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | This command depends on $@. | child_process-test.js:85:37:85:54 | req.query.fileName | a user-provided value |
|
||||
| other.js:7:33:7:35 | cmd | other.js:5:25:5:31 | req.url | other.js:7:33:7:35 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
|
||||
| other.js:8:28:8:30 | cmd | other.js:5:25:5:31 | req.url | other.js:8:28:8:30 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
var express = require('express');
|
||||
var multer = require('multer');
|
||||
var upload = multer({ dest: 'uploads/' });
|
||||
|
||||
var app = express();
|
||||
var exec = require("child_process").exec;
|
||||
|
||||
app.post('/profile', upload.single('avatar'), function (req, res, next) {
|
||||
exec("touch " + req.file.originalname); // NOT OK
|
||||
});
|
||||
|
||||
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
|
||||
req.files.forEach(file => {
|
||||
exec("touch " + file.originalname); // NOT OK
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
var http = require('http');
|
||||
var Busboy = require('busboy');
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
var busboy = new Busboy({ headers: req.headers });
|
||||
busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
|
||||
exec("touch " + filename); // NOT OK
|
||||
});
|
||||
req.pipe(busboy);
|
||||
}).listen(8000);
|
||||
|
||||
|
||||
const formidable = require('formidable');
|
||||
app.post('/api/upload', (req, res, next) => {
|
||||
let form = formidable({ multiples: true });
|
||||
|
||||
form.parse(req, (err, fields, files) => {
|
||||
exec("touch " + fields.name); // NOT OK
|
||||
});
|
||||
|
||||
let form2 = new formidable.IncomingForm();
|
||||
form2.parse(req, (err, fields, files) => {
|
||||
exec("touch " + fields.name); // NOT OK
|
||||
});
|
||||
});
|
||||
|
||||
var multiparty = require('multiparty');
|
||||
var http = require('http');
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
// parse a file upload
|
||||
var form = new multiparty.Form();
|
||||
|
||||
form.parse(req, function (err, fields, files) {
|
||||
exec("touch " + fields.name); // NOT OK
|
||||
});
|
||||
|
||||
|
||||
var form2 = new multiparty.Form();
|
||||
form2.on('part', function (part) { // / file / field
|
||||
exec("touch " + part.filename); // NOT OK
|
||||
});
|
||||
form2.parse(req);
|
||||
|
||||
}).listen(8080);
|
||||
@@ -7,6 +7,65 @@ nodes
|
||||
| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id |
|
||||
| ReflectedXss.js:17:31:17:39 | params.id |
|
||||
| ReflectedXss.js:17:31:17:39 | params.id |
|
||||
| ReflectedXss.js:22:12:22:19 | req.body |
|
||||
| ReflectedXss.js:22:12:22:19 | req.body |
|
||||
| ReflectedXss.js:22:12:22:19 | req.body |
|
||||
| ReflectedXss.js:23:12:23:27 | marked(req.body) |
|
||||
| ReflectedXss.js:23:12:23:27 | marked(req.body) |
|
||||
| ReflectedXss.js:23:19:23:26 | req.body |
|
||||
| ReflectedXss.js:23:19:23:26 | req.body |
|
||||
| ReflectedXss.js:29:12:29:19 | req.body |
|
||||
| ReflectedXss.js:29:12:29:19 | req.body |
|
||||
| ReflectedXss.js:29:12:29:19 | req.body |
|
||||
| ReflectedXss.js:30:7:33:4 | mytable |
|
||||
| ReflectedXss.js:30:17:33:4 | table([ ... y]\\n ]) |
|
||||
| ReflectedXss.js:30:23:33:3 | [\\n [ ... dy]\\n ] |
|
||||
| ReflectedXss.js:32:5:32:22 | ['body', req.body] |
|
||||
| ReflectedXss.js:32:14:32:21 | req.body |
|
||||
| ReflectedXss.js:32:14:32:21 | req.body |
|
||||
| ReflectedXss.js:34:12:34:18 | mytable |
|
||||
| ReflectedXss.js:34:12:34:18 | mytable |
|
||||
| ReflectedXss.js:41:12:41:19 | req.body |
|
||||
| ReflectedXss.js:41:12:41:19 | req.body |
|
||||
| ReflectedXss.js:41:12:41:19 | req.body |
|
||||
| ReflectedXss.js:42:12:42:39 | convert ... q.body) |
|
||||
| ReflectedXss.js:42:12:42:39 | convert ... q.body) |
|
||||
| ReflectedXss.js:42:31:42:38 | req.body |
|
||||
| ReflectedXss.js:42:31:42:38 | req.body |
|
||||
| ReflectedXss.js:56:12:56:19 | req.body |
|
||||
| ReflectedXss.js:56:12:56:19 | req.body |
|
||||
| ReflectedXss.js:56:12:56:19 | req.body |
|
||||
| ReflectedXss.js:64:14:64:21 | req.body |
|
||||
| ReflectedXss.js:64:14:64:21 | req.body |
|
||||
| ReflectedXss.js:64:39:64:42 | file |
|
||||
| ReflectedXss.js:65:16:65:19 | file |
|
||||
| ReflectedXss.js:65:16:65:19 | file |
|
||||
| ReflectedXss.js:68:12:68:41 | remark( ... q.body) |
|
||||
| ReflectedXss.js:68:12:68:52 | remark( ... tring() |
|
||||
| ReflectedXss.js:68:12:68:52 | remark( ... tring() |
|
||||
| ReflectedXss.js:68:33:68:40 | req.body |
|
||||
| ReflectedXss.js:68:33:68:40 | req.body |
|
||||
| ReflectedXss.js:72:12:72:56 | unified ... q.body) |
|
||||
| ReflectedXss.js:72:12:72:65 | unified ... oString |
|
||||
| ReflectedXss.js:72:12:72:65 | unified ... oString |
|
||||
| ReflectedXss.js:72:48:72:55 | req.body |
|
||||
| ReflectedXss.js:72:48:72:55 | req.body |
|
||||
| ReflectedXss.js:74:20:74:27 | req.body |
|
||||
| ReflectedXss.js:74:20:74:27 | req.body |
|
||||
| ReflectedXss.js:74:34:74:34 | f |
|
||||
| ReflectedXss.js:75:14:75:14 | f |
|
||||
| ReflectedXss.js:75:14:75:14 | f |
|
||||
| ReflectedXss.js:83:12:83:19 | req.body |
|
||||
| ReflectedXss.js:83:12:83:19 | req.body |
|
||||
| ReflectedXss.js:83:12:83:19 | req.body |
|
||||
| ReflectedXss.js:84:12:84:30 | snarkdown(req.body) |
|
||||
| ReflectedXss.js:84:12:84:30 | snarkdown(req.body) |
|
||||
| ReflectedXss.js:84:22:84:29 | req.body |
|
||||
| ReflectedXss.js:84:22:84:29 | req.body |
|
||||
| ReflectedXss.js:85:12:85:31 | snarkdown2(req.body) |
|
||||
| ReflectedXss.js:85:12:85:31 | snarkdown2(req.body) |
|
||||
| ReflectedXss.js:85:23:85:30 | req.body |
|
||||
| ReflectedXss.js:85:23:85:30 | req.body |
|
||||
| ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id |
|
||||
| ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id |
|
||||
| ReflectedXssContentTypes.js:10:24:10:36 | req.params.id |
|
||||
@@ -100,6 +159,50 @@ edges
|
||||
| ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id |
|
||||
| ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id |
|
||||
| ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id |
|
||||
| ReflectedXss.js:22:12:22:19 | req.body | ReflectedXss.js:22:12:22:19 | req.body |
|
||||
| ReflectedXss.js:23:19:23:26 | req.body | ReflectedXss.js:23:12:23:27 | marked(req.body) |
|
||||
| ReflectedXss.js:23:19:23:26 | req.body | ReflectedXss.js:23:12:23:27 | marked(req.body) |
|
||||
| ReflectedXss.js:23:19:23:26 | req.body | ReflectedXss.js:23:12:23:27 | marked(req.body) |
|
||||
| ReflectedXss.js:23:19:23:26 | req.body | ReflectedXss.js:23:12:23:27 | marked(req.body) |
|
||||
| ReflectedXss.js:29:12:29:19 | req.body | ReflectedXss.js:29:12:29:19 | req.body |
|
||||
| ReflectedXss.js:30:7:33:4 | mytable | ReflectedXss.js:34:12:34:18 | mytable |
|
||||
| ReflectedXss.js:30:7:33:4 | mytable | ReflectedXss.js:34:12:34:18 | mytable |
|
||||
| ReflectedXss.js:30:17:33:4 | table([ ... y]\\n ]) | ReflectedXss.js:30:7:33:4 | mytable |
|
||||
| ReflectedXss.js:30:23:33:3 | [\\n [ ... dy]\\n ] | ReflectedXss.js:30:17:33:4 | table([ ... y]\\n ]) |
|
||||
| ReflectedXss.js:32:5:32:22 | ['body', req.body] | ReflectedXss.js:30:23:33:3 | [\\n [ ... dy]\\n ] |
|
||||
| ReflectedXss.js:32:14:32:21 | req.body | ReflectedXss.js:32:5:32:22 | ['body', req.body] |
|
||||
| ReflectedXss.js:32:14:32:21 | req.body | ReflectedXss.js:32:5:32:22 | ['body', req.body] |
|
||||
| ReflectedXss.js:41:12:41:19 | req.body | ReflectedXss.js:41:12:41:19 | req.body |
|
||||
| ReflectedXss.js:42:31:42:38 | req.body | ReflectedXss.js:42:12:42:39 | convert ... q.body) |
|
||||
| ReflectedXss.js:42:31:42:38 | req.body | ReflectedXss.js:42:12:42:39 | convert ... q.body) |
|
||||
| ReflectedXss.js:42:31:42:38 | req.body | ReflectedXss.js:42:12:42:39 | convert ... q.body) |
|
||||
| ReflectedXss.js:42:31:42:38 | req.body | ReflectedXss.js:42:12:42:39 | convert ... q.body) |
|
||||
| ReflectedXss.js:56:12:56:19 | req.body | ReflectedXss.js:56:12:56:19 | req.body |
|
||||
| ReflectedXss.js:64:14:64:21 | req.body | ReflectedXss.js:64:39:64:42 | file |
|
||||
| ReflectedXss.js:64:14:64:21 | req.body | ReflectedXss.js:64:39:64:42 | file |
|
||||
| ReflectedXss.js:64:39:64:42 | file | ReflectedXss.js:65:16:65:19 | file |
|
||||
| ReflectedXss.js:64:39:64:42 | file | ReflectedXss.js:65:16:65:19 | file |
|
||||
| ReflectedXss.js:68:12:68:41 | remark( ... q.body) | ReflectedXss.js:68:12:68:52 | remark( ... tring() |
|
||||
| ReflectedXss.js:68:12:68:41 | remark( ... q.body) | ReflectedXss.js:68:12:68:52 | remark( ... tring() |
|
||||
| ReflectedXss.js:68:33:68:40 | req.body | ReflectedXss.js:68:12:68:41 | remark( ... q.body) |
|
||||
| ReflectedXss.js:68:33:68:40 | req.body | ReflectedXss.js:68:12:68:41 | remark( ... q.body) |
|
||||
| ReflectedXss.js:72:12:72:56 | unified ... q.body) | ReflectedXss.js:72:12:72:65 | unified ... oString |
|
||||
| ReflectedXss.js:72:12:72:56 | unified ... q.body) | ReflectedXss.js:72:12:72:65 | unified ... oString |
|
||||
| ReflectedXss.js:72:48:72:55 | req.body | ReflectedXss.js:72:12:72:56 | unified ... q.body) |
|
||||
| ReflectedXss.js:72:48:72:55 | req.body | ReflectedXss.js:72:12:72:56 | unified ... q.body) |
|
||||
| ReflectedXss.js:74:20:74:27 | req.body | ReflectedXss.js:74:34:74:34 | f |
|
||||
| ReflectedXss.js:74:20:74:27 | req.body | ReflectedXss.js:74:34:74:34 | f |
|
||||
| ReflectedXss.js:74:34:74:34 | f | ReflectedXss.js:75:14:75:14 | f |
|
||||
| ReflectedXss.js:74:34:74:34 | f | ReflectedXss.js:75:14:75:14 | f |
|
||||
| ReflectedXss.js:83:12:83:19 | req.body | ReflectedXss.js:83:12:83:19 | req.body |
|
||||
| ReflectedXss.js:84:22:84:29 | req.body | ReflectedXss.js:84:12:84:30 | snarkdown(req.body) |
|
||||
| ReflectedXss.js:84:22:84:29 | req.body | ReflectedXss.js:84:12:84:30 | snarkdown(req.body) |
|
||||
| ReflectedXss.js:84:22:84:29 | req.body | ReflectedXss.js:84:12:84:30 | snarkdown(req.body) |
|
||||
| ReflectedXss.js:84:22:84:29 | req.body | ReflectedXss.js:84:12:84:30 | snarkdown(req.body) |
|
||||
| ReflectedXss.js:85:23:85:30 | req.body | ReflectedXss.js:85:12:85:31 | snarkdown2(req.body) |
|
||||
| ReflectedXss.js:85:23:85:30 | req.body | ReflectedXss.js:85:12:85:31 | snarkdown2(req.body) |
|
||||
| ReflectedXss.js:85:23:85:30 | req.body | ReflectedXss.js:85:12:85:31 | snarkdown2(req.body) |
|
||||
| ReflectedXss.js:85:23:85:30 | req.body | ReflectedXss.js:85:12:85:31 | snarkdown2(req.body) |
|
||||
| ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id |
|
||||
| ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id |
|
||||
| ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id |
|
||||
@@ -178,6 +281,20 @@ edges
|
||||
#select
|
||||
| ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value |
|
||||
| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:17:31:17:39 | params.id | user-provided value |
|
||||
| ReflectedXss.js:22:12:22:19 | req.body | ReflectedXss.js:22:12:22:19 | req.body | ReflectedXss.js:22:12:22:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:22:12:22:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:23:12:23:27 | marked(req.body) | ReflectedXss.js:23:19:23:26 | req.body | ReflectedXss.js:23:12:23:27 | marked(req.body) | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:23:19:23:26 | req.body | user-provided value |
|
||||
| ReflectedXss.js:29:12:29:19 | req.body | ReflectedXss.js:29:12:29:19 | req.body | ReflectedXss.js:29:12:29:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:29:12:29:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:34:12:34:18 | mytable | ReflectedXss.js:32:14:32:21 | req.body | ReflectedXss.js:34:12:34:18 | mytable | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:32:14:32:21 | req.body | user-provided value |
|
||||
| ReflectedXss.js:41:12:41:19 | req.body | ReflectedXss.js:41:12:41:19 | req.body | ReflectedXss.js:41:12:41:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:41:12:41:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:42:12:42:39 | convert ... q.body) | ReflectedXss.js:42:31:42:38 | req.body | ReflectedXss.js:42:12:42:39 | convert ... q.body) | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:42:31:42:38 | req.body | user-provided value |
|
||||
| ReflectedXss.js:56:12:56:19 | req.body | ReflectedXss.js:56:12:56:19 | req.body | ReflectedXss.js:56:12:56:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:56:12:56:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:65:16:65:19 | file | ReflectedXss.js:64:14:64:21 | req.body | ReflectedXss.js:65:16:65:19 | file | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:64:14:64:21 | req.body | user-provided value |
|
||||
| ReflectedXss.js:68:12:68:52 | remark( ... tring() | ReflectedXss.js:68:33:68:40 | req.body | ReflectedXss.js:68:12:68:52 | remark( ... tring() | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:68:33:68:40 | req.body | user-provided value |
|
||||
| ReflectedXss.js:72:12:72:65 | unified ... oString | ReflectedXss.js:72:48:72:55 | req.body | ReflectedXss.js:72:12:72:65 | unified ... oString | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:72:48:72:55 | req.body | user-provided value |
|
||||
| ReflectedXss.js:75:14:75:14 | f | ReflectedXss.js:74:20:74:27 | req.body | ReflectedXss.js:75:14:75:14 | f | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:74:20:74:27 | req.body | user-provided value |
|
||||
| ReflectedXss.js:83:12:83:19 | req.body | ReflectedXss.js:83:12:83:19 | req.body | ReflectedXss.js:83:12:83:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:83:12:83:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:84:12:84:30 | snarkdown(req.body) | ReflectedXss.js:84:22:84:29 | req.body | ReflectedXss.js:84:12:84:30 | snarkdown(req.body) | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:84:22:84:29 | req.body | user-provided value |
|
||||
| ReflectedXss.js:85:12:85:31 | snarkdown2(req.body) | ReflectedXss.js:85:23:85:30 | req.body | ReflectedXss.js:85:12:85:31 | snarkdown2(req.body) | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:85:23:85:30 | req.body | user-provided value |
|
||||
| ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | user-provided value |
|
||||
| ReflectedXssContentTypes.js:20:14:20:36 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:20:24:20:36 | req.params.id | ReflectedXssContentTypes.js:20:14:20:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:20:24:20:36 | req.params.id | user-provided value |
|
||||
| ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | user-provided value |
|
||||
|
||||
@@ -16,3 +16,71 @@ app.get('/user/:id', function(req, res) {
|
||||
function moreBadStuff(params, res) {
|
||||
res.send("Unknown user: " + params.id); // NOT OK
|
||||
}
|
||||
|
||||
var marked = require("marked");
|
||||
app.get('/user/:id', function(req, res) {
|
||||
res.send(req.body); // NOT OK
|
||||
res.send(marked(req.body)); // NOT OK
|
||||
});
|
||||
|
||||
|
||||
var table = require('markdown-table')
|
||||
app.get('/user/:id', function(req, res) {
|
||||
res.send(req.body); // NOT OK
|
||||
var mytable = table([
|
||||
['Name', 'Content'],
|
||||
['body', req.body]
|
||||
]);
|
||||
res.send(mytable); // NOT OK
|
||||
});
|
||||
|
||||
var showdown = require('showdown');
|
||||
var converter = new showdown.Converter();
|
||||
|
||||
app.get('/user/:id', function(req, res) {
|
||||
res.send(req.body); // NOT OK
|
||||
res.send(converter.makeHtml(req.body)); // NOT OK
|
||||
});
|
||||
|
||||
var unified = require('unified');
|
||||
var markdown = require('remark-parse');
|
||||
var remark2rehype = require('remark-rehype');
|
||||
var doc = require('rehype-document');
|
||||
var format = require('rehype-format');
|
||||
var html = require('rehype-stringify');
|
||||
var remark = require("remark");
|
||||
var sanitize = require("rehype-sanitize");
|
||||
const { resetExtensions } = require('showdown');
|
||||
|
||||
app.get('/user/:id', function (req, res) {
|
||||
res.send(req.body); // NOT OK
|
||||
|
||||
unified()
|
||||
.use(markdown)
|
||||
.use(remark2rehype)
|
||||
.use(doc, { title: '👋🌍' })
|
||||
.use(format)
|
||||
.use(html)
|
||||
.process(req.body, function (err, file) {
|
||||
res.send(file); // NOT OK
|
||||
});
|
||||
|
||||
res.send(remark().processSync(req.body).toString()); // NOT OK
|
||||
|
||||
res.send(remark().use(sanitize).processSync(req.body).toString()); // OK
|
||||
|
||||
res.send(unified().use(markdown).processSync(req.body).toString); // NOT OK
|
||||
|
||||
remark().process(req.body, (e, f) => {
|
||||
res.send(f); // NOT OK
|
||||
})
|
||||
});
|
||||
|
||||
import snarkdown from 'snarkdown';
|
||||
var snarkdown2 = require("snarkdown");
|
||||
|
||||
app.get('/user/:id', function (req, res) {
|
||||
res.send(req.body); // NOT OK
|
||||
res.send(snarkdown(req.body)); // NOT OK
|
||||
res.send(snarkdown2(req.body)); // NOT OK
|
||||
});
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
| ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value |
|
||||
| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:17:31:17:39 | params.id | user-provided value |
|
||||
| ReflectedXss.js:22:12:22:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:22:12:22:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:23:12:23:27 | marked(req.body) | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:23:19:23:26 | req.body | user-provided value |
|
||||
| ReflectedXss.js:29:12:29:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:29:12:29:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:34:12:34:18 | mytable | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:32:14:32:21 | req.body | user-provided value |
|
||||
| ReflectedXss.js:41:12:41:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:41:12:41:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:42:12:42:39 | convert ... q.body) | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:42:31:42:38 | req.body | user-provided value |
|
||||
| ReflectedXss.js:56:12:56:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:56:12:56:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:65:16:65:19 | file | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:64:14:64:21 | req.body | user-provided value |
|
||||
| ReflectedXss.js:68:12:68:52 | remark( ... tring() | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:68:33:68:40 | req.body | user-provided value |
|
||||
| ReflectedXss.js:72:12:72:65 | unified ... oString | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:72:48:72:55 | req.body | user-provided value |
|
||||
| ReflectedXss.js:75:14:75:14 | f | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:74:20:74:27 | req.body | user-provided value |
|
||||
| ReflectedXss.js:83:12:83:19 | req.body | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:83:12:83:19 | req.body | user-provided value |
|
||||
| ReflectedXss.js:84:12:84:30 | snarkdown(req.body) | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:84:22:84:29 | req.body | user-provided value |
|
||||
| ReflectedXss.js:85:12:85:31 | snarkdown2(req.body) | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:85:23:85:30 | req.body | user-provided value |
|
||||
| ReflectedXssContentTypes.js:10:14:10:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:10:24:10:36 | req.params.id | user-provided value |
|
||||
| ReflectedXssContentTypes.js:20:14:20:36 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:20:24:20:36 | req.params.id | user-provided value |
|
||||
| ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | user-provided value |
|
||||
|
||||
@@ -1,4 +1,55 @@
|
||||
nodes
|
||||
| forms.js:8:23:8:28 | values |
|
||||
| forms.js:8:23:8:28 | values |
|
||||
| forms.js:9:31:9:36 | values |
|
||||
| forms.js:9:31:9:40 | values.foo |
|
||||
| forms.js:9:31:9:40 | values.foo |
|
||||
| forms.js:11:24:11:29 | values |
|
||||
| forms.js:11:24:11:29 | values |
|
||||
| forms.js:12:31:12:36 | values |
|
||||
| forms.js:12:31:12:40 | values.bar |
|
||||
| forms.js:12:31:12:40 | values.bar |
|
||||
| forms.js:24:15:24:20 | values |
|
||||
| forms.js:24:15:24:20 | values |
|
||||
| forms.js:25:23:25:28 | values |
|
||||
| forms.js:25:23:25:34 | values.email |
|
||||
| forms.js:25:23:25:34 | values.email |
|
||||
| forms.js:28:20:28:25 | values |
|
||||
| forms.js:28:20:28:25 | values |
|
||||
| forms.js:29:23:29:28 | values |
|
||||
| forms.js:29:23:29:34 | values.email |
|
||||
| forms.js:29:23:29:34 | values.email |
|
||||
| forms.js:34:11:34:53 | values |
|
||||
| forms.js:34:13:34:18 | values |
|
||||
| forms.js:34:13:34:18 | values |
|
||||
| forms.js:35:19:35:24 | values |
|
||||
| forms.js:35:19:35:30 | values.email |
|
||||
| forms.js:35:19:35:30 | values.email |
|
||||
| forms.js:44:21:44:26 | values |
|
||||
| forms.js:44:21:44:26 | values |
|
||||
| forms.js:45:21:45:26 | values |
|
||||
| forms.js:45:21:45:33 | values.stooge |
|
||||
| forms.js:45:21:45:33 | values.stooge |
|
||||
| forms.js:57:19:57:32 | e.target.value |
|
||||
| forms.js:57:19:57:32 | e.target.value |
|
||||
| forms.js:57:19:57:32 | e.target.value |
|
||||
| forms.js:71:21:71:24 | data |
|
||||
| forms.js:71:21:71:24 | data |
|
||||
| forms.js:72:19:72:22 | data |
|
||||
| forms.js:72:19:72:27 | data.name |
|
||||
| forms.js:72:19:72:27 | data.name |
|
||||
| forms.js:92:17:92:36 | values |
|
||||
| forms.js:92:26:92:36 | getValues() |
|
||||
| forms.js:92:26:92:36 | getValues() |
|
||||
| forms.js:93:25:93:30 | values |
|
||||
| forms.js:93:25:93:35 | values.name |
|
||||
| forms.js:93:25:93:35 | values.name |
|
||||
| forms.js:103:23:103:36 | e.target.value |
|
||||
| forms.js:103:23:103:36 | e.target.value |
|
||||
| forms.js:103:23:103:36 | e.target.value |
|
||||
| forms.js:107:23:107:36 | e.target.value |
|
||||
| forms.js:107:23:107:36 | e.target.value |
|
||||
| forms.js:107:23:107:36 | e.target.value |
|
||||
| xss-through-dom.js:2:16:2:34 | $("textarea").val() |
|
||||
| xss-through-dom.js:2:16:2:34 | $("textarea").val() |
|
||||
| xss-through-dom.js:2:16:2:34 | $("textarea").val() |
|
||||
@@ -50,6 +101,43 @@ nodes
|
||||
| xss-through-dom.js:79:4:79:34 | documen ... t.value |
|
||||
| xss-through-dom.js:79:4:79:34 | documen ... t.value |
|
||||
edges
|
||||
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
|
||||
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
|
||||
| forms.js:9:31:9:36 | values | forms.js:9:31:9:40 | values.foo |
|
||||
| forms.js:9:31:9:36 | values | forms.js:9:31:9:40 | values.foo |
|
||||
| forms.js:11:24:11:29 | values | forms.js:12:31:12:36 | values |
|
||||
| forms.js:11:24:11:29 | values | forms.js:12:31:12:36 | values |
|
||||
| forms.js:12:31:12:36 | values | forms.js:12:31:12:40 | values.bar |
|
||||
| forms.js:12:31:12:36 | values | forms.js:12:31:12:40 | values.bar |
|
||||
| forms.js:24:15:24:20 | values | forms.js:25:23:25:28 | values |
|
||||
| forms.js:24:15:24:20 | values | forms.js:25:23:25:28 | values |
|
||||
| forms.js:25:23:25:28 | values | forms.js:25:23:25:34 | values.email |
|
||||
| forms.js:25:23:25:28 | values | forms.js:25:23:25:34 | values.email |
|
||||
| forms.js:28:20:28:25 | values | forms.js:29:23:29:28 | values |
|
||||
| forms.js:28:20:28:25 | values | forms.js:29:23:29:28 | values |
|
||||
| forms.js:29:23:29:28 | values | forms.js:29:23:29:34 | values.email |
|
||||
| forms.js:29:23:29:28 | values | forms.js:29:23:29:34 | values.email |
|
||||
| forms.js:34:11:34:53 | values | forms.js:35:19:35:24 | values |
|
||||
| forms.js:34:13:34:18 | values | forms.js:34:11:34:53 | values |
|
||||
| forms.js:34:13:34:18 | values | forms.js:34:11:34:53 | values |
|
||||
| forms.js:35:19:35:24 | values | forms.js:35:19:35:30 | values.email |
|
||||
| forms.js:35:19:35:24 | values | forms.js:35:19:35:30 | values.email |
|
||||
| forms.js:44:21:44:26 | values | forms.js:45:21:45:26 | values |
|
||||
| forms.js:44:21:44:26 | values | forms.js:45:21:45:26 | values |
|
||||
| forms.js:45:21:45:26 | values | forms.js:45:21:45:33 | values.stooge |
|
||||
| forms.js:45:21:45:26 | values | forms.js:45:21:45:33 | values.stooge |
|
||||
| forms.js:57:19:57:32 | e.target.value | forms.js:57:19:57:32 | e.target.value |
|
||||
| forms.js:71:21:71:24 | data | forms.js:72:19:72:22 | data |
|
||||
| forms.js:71:21:71:24 | data | forms.js:72:19:72:22 | data |
|
||||
| forms.js:72:19:72:22 | data | forms.js:72:19:72:27 | data.name |
|
||||
| forms.js:72:19:72:22 | data | forms.js:72:19:72:27 | data.name |
|
||||
| forms.js:92:17:92:36 | values | forms.js:93:25:93:30 | values |
|
||||
| forms.js:92:26:92:36 | getValues() | forms.js:92:17:92:36 | values |
|
||||
| forms.js:92:26:92:36 | getValues() | forms.js:92:17:92:36 | values |
|
||||
| forms.js:93:25:93:30 | values | forms.js:93:25:93:35 | values.name |
|
||||
| forms.js:93:25:93:30 | values | forms.js:93:25:93:35 | values.name |
|
||||
| forms.js:103:23:103:36 | e.target.value | forms.js:103:23:103:36 | e.target.value |
|
||||
| forms.js:107:23:107:36 | e.target.value | forms.js:107:23:107:36 | e.target.value |
|
||||
| xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() |
|
||||
| xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() |
|
||||
| xss-through-dom.js:8:16:8:53 | $(".som ... arget") | xss-through-dom.js:8:16:8:53 | $(".som ... arget") |
|
||||
@@ -70,6 +158,17 @@ edges
|
||||
| xss-through-dom.js:73:20:73:41 | $("inpu ... 0).name | xss-through-dom.js:73:9:73:41 | selector |
|
||||
| xss-through-dom.js:79:4:79:34 | documen ... t.value | xss-through-dom.js:79:4:79:34 | documen ... t.value |
|
||||
#select
|
||||
| forms.js:9:31:9:40 | values.foo | forms.js:8:23:8:28 | values | forms.js:9:31:9:40 | values.foo | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:8:23:8:28 | values | DOM text |
|
||||
| forms.js:12:31:12:40 | values.bar | forms.js:11:24:11:29 | values | forms.js:12:31:12:40 | values.bar | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:11:24:11:29 | values | DOM text |
|
||||
| forms.js:25:23:25:34 | values.email | forms.js:24:15:24:20 | values | forms.js:25:23:25:34 | values.email | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:24:15:24:20 | values | DOM text |
|
||||
| forms.js:29:23:29:34 | values.email | forms.js:28:20:28:25 | values | forms.js:29:23:29:34 | values.email | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:28:20:28:25 | values | DOM text |
|
||||
| forms.js:35:19:35:30 | values.email | forms.js:34:13:34:18 | values | forms.js:35:19:35:30 | values.email | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:34:13:34:18 | values | DOM text |
|
||||
| forms.js:45:21:45:33 | values.stooge | forms.js:44:21:44:26 | values | forms.js:45:21:45:33 | values.stooge | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:44:21:44:26 | values | DOM text |
|
||||
| forms.js:57:19:57:32 | e.target.value | forms.js:57:19:57:32 | e.target.value | forms.js:57:19:57:32 | e.target.value | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:57:19:57:32 | e.target.value | DOM text |
|
||||
| forms.js:72:19:72:27 | data.name | forms.js:71:21:71:24 | data | forms.js:72:19:72:27 | data.name | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:71:21:71:24 | data | DOM text |
|
||||
| forms.js:93:25:93:35 | values.name | forms.js:92:26:92:36 | getValues() | forms.js:93:25:93:35 | values.name | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:92:26:92:36 | getValues() | DOM text |
|
||||
| forms.js:103:23:103:36 | e.target.value | forms.js:103:23:103:36 | e.target.value | forms.js:103:23:103:36 | e.target.value | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:103:23:103:36 | e.target.value | DOM text |
|
||||
| forms.js:107:23:107:36 | e.target.value | forms.js:107:23:107:36 | e.target.value | forms.js:107:23:107:36 | e.target.value | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:107:23:107:36 | e.target.value | DOM text |
|
||||
| xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:2:16:2:34 | $("textarea").val() | DOM text |
|
||||
| xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | DOM text |
|
||||
| xss-through-dom.js:8:16:8:53 | $(".som ... arget") | xss-through-dom.js:8:16:8:53 | $(".som ... arget") | xss-through-dom.js:8:16:8:53 | $(".som ... arget") | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:8:16:8:53 | $(".som ... arget") | DOM text |
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
import { Formik, withFormik, useFormikContext } from 'formik';
|
||||
|
||||
const FormikBasic = () => (
|
||||
<div>
|
||||
<Formik
|
||||
initialValues={{ email: '', password: '' }}
|
||||
validate={values => {
|
||||
$("#id").html(values.foo); // NOT OK
|
||||
}}
|
||||
onSubmit={(values, { setSubmitting }) => {
|
||||
$("#id").html(values.bar); // NOT OK
|
||||
}}
|
||||
>
|
||||
{(inputs) => (
|
||||
<form onSubmit={handleSubmit}></form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
|
||||
const FormikEnhanced = withFormik({
|
||||
mapPropsToValues: () => ({ name: '' }),
|
||||
validate: values => {
|
||||
$("#id").html(values.email); // NOT OK
|
||||
},
|
||||
|
||||
handleSubmit: (values, { setSubmitting }) => {
|
||||
$("#id").html(values.email); // NOT OK
|
||||
}
|
||||
})(MyForm);
|
||||
|
||||
(function () {
|
||||
const { values, submitForm } = useFormikContext();
|
||||
$("#id").html(values.email); // NOT OK
|
||||
|
||||
$("#id").html(submitForm.email); // OK
|
||||
})
|
||||
|
||||
import { Form } from 'react-final-form'
|
||||
|
||||
const App = () => (
|
||||
<Form
|
||||
onSubmit={async values => {
|
||||
$("#id").html(values.stooge); // NOT OK
|
||||
}}
|
||||
initialValues={{ stooge: 'larry', employed: false }}
|
||||
render={({ handleSubmit, form, submitting, pristine, values }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input type="text" name="stooge"></input>
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
function plainSubmit(e) {
|
||||
$("#id").html(e.target.value); // NOT OK
|
||||
}
|
||||
|
||||
const plainReact = () => (
|
||||
<form onSubmit={e => plainSubmit(e)}>
|
||||
<input type="text" value={this.state.value} onChange={this.handleChange} />
|
||||
<input type="submit" value="Submit" />
|
||||
</form>
|
||||
)
|
||||
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
function HookForm() {
|
||||
const { register, handleSubmit, errors } = useForm(); // initialize the hook
|
||||
const onSubmit = (data) => {
|
||||
$("#id").html(data.name); // NOT OK
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<input name="name" ref={register({ required: true })} />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function HookForm2() {
|
||||
const { register, getValues } = useForm();
|
||||
|
||||
return (
|
||||
<form>
|
||||
<input name="name" ref={register} />
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const values = getValues(); // { test: "test-input", test1: "test1-input" }
|
||||
$("#id").html(values.name); // NOT OK
|
||||
}}
|
||||
>
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function vanillaJS() {
|
||||
document.querySelector("form.myform").addEventListener("submit", e => {
|
||||
$("#id").html(e.target.value); // NOT OK
|
||||
});
|
||||
|
||||
document.querySelector("form.myform").onsubmit = function (e) {
|
||||
$("#id").html(e.target.value); // NOT OK
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user