Merge commit 'f3814c6468' into js/redux-less

This commit is contained in:
Pavel Avgustinov
2021-03-05 11:53:52 +00:00
776 changed files with 31750 additions and 7884 deletions

View 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)

View 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)

View File

@@ -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)

View 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).

View 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)

View 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)

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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).

View File

@@ -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,

View File

@@ -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()));

View File

@@ -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");

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -13,7 +13,7 @@ import java.util.List;
* class StringList extends List&lt;string&gt; {}
* </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;

View File

@@ -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;

View File

@@ -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&lt;number&gt;</tt> */
/** An instantiation of a named type, such as <code>Array&lt;number&gt;</code> */
public class GenericTypeExpr extends TypeExpression {
private final ITypeExpression typeName; // Always Identifier or MemberExpression
private final List<ITypeExpression> typeArguments;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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 {}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -5,8 +5,8 @@ import com.semmle.js.ast.Visitor;
import java.util.List;
/**
* An intersection type such as <tt>T&amp;S</tt>, denoting the intersection of type <tt>T</tt> and
* type <tt>S</tt>.
* An intersection type such as <code>T&amp;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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>
* &lt;T&gt; E</code>.
*/
public boolean isAsExpression() {

View File

@@ -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_;

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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.
*/

View File

@@ -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);

View File

@@ -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.
*

View File

@@ -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;

View File

@@ -0,0 +1,5 @@
<html>
<script>
{"Hello": 123}
</script>
</html>

View File

@@ -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")

View File

@@ -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"

View File

@@ -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)
});

View File

@@ -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})
});

View File

@@ -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

View File

@@ -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))
}

View File

@@ -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`.
*/

View File

@@ -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))
}

View File

@@ -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() }
}
}
}

View File

@@ -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

View File

@@ -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" }
}

View 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
}
}
}

View 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)
}
}

View File

@@ -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()
}
}
}

View File

@@ -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)
}
}

View File

@@ -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
}
}
}

View File

@@ -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()
)
}
}
}
}

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-073/TemplateObjectInjection.ql

View 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
}

View File

@@ -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 |

View 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();
})();

View File

@@ -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 |

View File

@@ -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

View File

@@ -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

View File

@@ -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 |

View File

@@ -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)
}

View File

@@ -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;,"'<>(){}|[\]@=+*]|:(?![/\\]))+/;

View File

@@ -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 |

View File

@@ -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);

View File

@@ -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 |

View File

@@ -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
});

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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
}
}