Merge branch 'main' into ts4

This commit is contained in:
Erik Krogh Kristensen
2020-08-28 10:27:49 +02:00
168 changed files with 5387 additions and 1294 deletions

View File

@@ -7,7 +7,6 @@
+ semmlecode-javascript-queries/Declarations/UniqueParameterNames.ql: /Correctness/Declarations
+ semmlecode-javascript-queries/Declarations/UniquePropertyNames.ql: /Correctness/Declarations
+ semmlecode-javascript-queries/Declarations/IneffectiveParameterType.ql: /Correctness/Declarations
+ semmlecode-javascript-queries/DOM/AmbiguousIdAttribute.ql: /Correctness/DOM
+ semmlecode-javascript-queries/DOM/ConflictingAttributes.ql: /Correctness/DOM
+ semmlecode-javascript-queries/DOM/MalformedIdAttribute.ql: /Correctness/DOM
+ semmlecode-javascript-queries/Expressions/ComparisonWithNaN.ql: /Correctness/Expressions

View File

@@ -35,6 +35,7 @@ import com.semmle.js.ast.jsx.JSXMemberExpression;
import com.semmle.js.ast.jsx.JSXNamespacedName;
import com.semmle.js.ast.jsx.JSXOpeningElement;
import com.semmle.js.ast.jsx.JSXSpreadAttribute;
import com.semmle.js.ast.jsx.JSXThisExpr;
import com.semmle.util.data.Either;
import java.util.ArrayList;
import java.util.List;
@@ -225,22 +226,26 @@ public class JSXParser extends Parser {
}
/** Parse next token as JSX identifier */
private JSXIdentifier jsx_parseIdentifier() {
private IJSXName jsx_parseIdentifier(boolean expectThisExpr) {
SourceLocation loc = new SourceLocation(this.startLoc);
String name = null;
if (this.type == jsxName) name = String.valueOf(this.value);
else if (this.type.keyword != null) name = this.type.keyword;
else this.unexpected();
this.next();
return this.finishNode(new JSXIdentifier(loc, name));
if (expectThisExpr && name.equals("this")) {
return this.finishNode(new JSXThisExpr(loc));
} else {
return this.finishNode(new JSXIdentifier(loc, name));
}
}
/** Parse namespaced identifier. */
private IJSXName jsx_parseNamespacedName() {
SourceLocation loc = new SourceLocation(this.startLoc);
JSXIdentifier namespace = this.jsx_parseIdentifier();
if (!((JSXOptions) options).allowNamespaces || !this.eat(colon)) return namespace;
return this.finishNode(new JSXNamespacedName(loc, namespace, this.jsx_parseIdentifier()));
IJSXName namespace = this.jsx_parseIdentifier(true);
if (namespace instanceof JSXThisExpr || (!((JSXOptions) options).allowNamespaces || !this.eat(colon))) return namespace;
return this.finishNode(new JSXNamespacedName(loc, (JSXIdentifier)namespace, (JSXIdentifier)this.jsx_parseIdentifier(false)));
}
/**
@@ -258,7 +263,7 @@ public class JSXParser extends Parser {
}
while (this.eat(dot)) {
SourceLocation loc = new SourceLocation(startPos);
node = this.finishNode(new JSXMemberExpression(loc, node, this.jsx_parseIdentifier()));
node = this.finishNode(new JSXMemberExpression(loc, node, (JSXIdentifier)this.jsx_parseIdentifier(false)));
}
return node;
}

View File

@@ -15,6 +15,7 @@ import com.semmle.js.ast.jsx.JSXMemberExpression;
import com.semmle.js.ast.jsx.JSXNamespacedName;
import com.semmle.js.ast.jsx.JSXOpeningElement;
import com.semmle.js.ast.jsx.JSXSpreadAttribute;
import com.semmle.js.ast.jsx.JSXThisExpr;
import com.semmle.ts.ast.ExportWholeDeclaration;
import com.semmle.ts.ast.ExternalModuleReference;
import com.semmle.ts.ast.ImportWholeDeclaration;
@@ -635,6 +636,11 @@ public class AST2JSON extends DefaultVisitor<Void, JsonElement> {
return result;
}
@Override
public JsonElement visit(JSXThisExpr nd, Void c) {
return this.mkNode(nd, "JSXThisExpr");
}
@Override
public JsonElement visit(JSXMemberExpression nd, Void c) {
JsonObject result = this.mkNode(nd);

View File

@@ -14,6 +14,7 @@ import com.semmle.js.ast.jsx.JSXMemberExpression;
import com.semmle.js.ast.jsx.JSXNamespacedName;
import com.semmle.js.ast.jsx.JSXOpeningElement;
import com.semmle.js.ast.jsx.JSXSpreadAttribute;
import com.semmle.js.ast.jsx.JSXThisExpr;
import com.semmle.ts.ast.ArrayTypeExpr;
import com.semmle.ts.ast.ConditionalTypeExpr;
import com.semmle.ts.ast.DecoratorList;
@@ -498,6 +499,11 @@ public class DefaultVisitor<C, R> implements Visitor<C, R> {
return visit((IJSXName) nd, c);
}
@Override
public R visit(JSXThisExpr nd, C c) {
return visit((IJSXName) nd, c);
}
@Override
public R visit(JSXMemberExpression nd, C c) {
return visit((IJSXName) nd, c);

View File

@@ -10,6 +10,7 @@ import com.semmle.js.ast.jsx.JSXMemberExpression;
import com.semmle.js.ast.jsx.JSXNamespacedName;
import com.semmle.js.ast.jsx.JSXOpeningElement;
import com.semmle.js.ast.jsx.JSXSpreadAttribute;
import com.semmle.js.ast.jsx.JSXThisExpr;
import com.semmle.ts.ast.ArrayTypeExpr;
import com.semmle.ts.ast.ConditionalTypeExpr;
import com.semmle.ts.ast.DecoratorList;
@@ -567,6 +568,11 @@ public class NodeCopier implements Visitor<Void, INode> {
return new JSXIdentifier(visit(nd.getLoc()), nd.getName());
}
@Override
public INode visit(JSXThisExpr nd, Void c) {
return new JSXThisExpr(visit(nd.getLoc()));
}
@Override
public INode visit(JSXMemberExpression nd, Void c) {
return new JSXMemberExpression(visit(nd.getLoc()), copy(nd.getObject()), copy(nd.getName()));

View File

@@ -10,6 +10,7 @@ import com.semmle.js.ast.jsx.JSXMemberExpression;
import com.semmle.js.ast.jsx.JSXNamespacedName;
import com.semmle.js.ast.jsx.JSXOpeningElement;
import com.semmle.js.ast.jsx.JSXSpreadAttribute;
import com.semmle.js.ast.jsx.JSXThisExpr;
import com.semmle.ts.ast.ArrayTypeExpr;
import com.semmle.ts.ast.ConditionalTypeExpr;
import com.semmle.ts.ast.DecoratorList;
@@ -200,6 +201,8 @@ public interface Visitor<C, R> {
public R visit(JSXIdentifier nd, C c);
public R visit(JSXThisExpr nd, C c);
public R visit(JSXMemberExpression nd, C c);
public R visit(JSXNamespacedName nd, C c);

View File

@@ -0,0 +1,21 @@
package com.semmle.js.ast.jsx;
import com.semmle.js.ast.ThisExpression;
import com.semmle.js.ast.SourceLocation;
import com.semmle.js.ast.Visitor;
public class JSXThisExpr extends ThisExpression implements IJSXName {
public JSXThisExpr(SourceLocation loc) {
super(loc);
}
@Override
public <C, R> R accept(Visitor<C, R> v, C c) {
return v.visit(this, c);
}
@Override
public String getQualifiedName() {
return "this";
}
}

View File

@@ -82,6 +82,7 @@ import com.semmle.js.ast.SwitchStatement;
import com.semmle.js.ast.TaggedTemplateExpression;
import com.semmle.js.ast.TemplateElement;
import com.semmle.js.ast.TemplateLiteral;
import com.semmle.js.ast.ThisExpression;
import com.semmle.js.ast.ThrowStatement;
import com.semmle.js.ast.TryStatement;
import com.semmle.js.ast.UnaryExpression;
@@ -105,6 +106,7 @@ import com.semmle.js.ast.jsx.JSXMemberExpression;
import com.semmle.js.ast.jsx.JSXNamespacedName;
import com.semmle.js.ast.jsx.JSXOpeningElement;
import com.semmle.js.ast.jsx.JSXSpreadAttribute;
import com.semmle.js.ast.jsx.JSXThisExpr;
import com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase;
import com.semmle.js.extractor.ExtractorConfig.Platform;
import com.semmle.js.extractor.ExtractorConfig.SourceType;
@@ -1645,6 +1647,11 @@ public class ASTExtractor {
return visit((Identifier) nd, c);
}
@Override
public Label visit(JSXThisExpr nd, Context c) {
return visit((ThisExpression) nd, c);
}
@Override
public Label visit(JSXMemberExpression nd, Context c) {
Label key = super.visit(nd, c);

View File

@@ -18,6 +18,7 @@ import com.semmle.js.ast.Literal;
import com.semmle.js.ast.LogicalExpression;
import com.semmle.js.ast.MemberExpression;
import com.semmle.js.ast.MetaProperty;
import com.semmle.js.ast.ThisExpression;
import com.semmle.js.ast.UnaryExpression;
import com.semmle.js.ast.UpdateExpression;
import com.semmle.js.ast.XMLAnyName;
@@ -28,6 +29,7 @@ import com.semmle.js.ast.XMLQualifiedIdentifier;
import com.semmle.js.ast.jsx.JSXIdentifier;
import com.semmle.js.ast.jsx.JSXMemberExpression;
import com.semmle.js.ast.jsx.JSXSpreadAttribute;
import com.semmle.js.ast.jsx.JSXThisExpr;
import com.semmle.js.extractor.ASTExtractor.IdContext;
import com.semmle.ts.ast.DecoratorList;
import com.semmle.ts.ast.ExpressionWithTypeArguments;
@@ -194,6 +196,11 @@ public class ExprKinds {
return visit((Identifier) nd, c);
}
@Override
public Integer visit(JSXThisExpr nd, Void c) {
return visit((ThisExpression) nd, c);
}
@Override
public Integer visit(LogicalExpression nd, Void q) {
return binOpKinds.get(nd.getOperator());

View File

@@ -1,7 +1,10 @@
package com.semmle.js.extractor;
import java.io.File;
import java.nio.file.Path;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.Optional;
import com.semmle.js.parser.TypeScriptParser;
@@ -23,6 +26,8 @@ public class ExtractorState {
private final ConcurrentHashMap<Path, FileSnippet> snippets = new ConcurrentHashMap<>();
private static final ConcurrentMap<File, Optional<String>> packageTypeCache = new ConcurrentHashMap<>();
public TypeScriptParser getTypeScriptParser() {
return typeScriptParser;
}
@@ -36,6 +41,15 @@ public class ExtractorState {
return snippets;
}
/**
* Returns a cache for the "type" field in `package.json` files.
*
* <p>The map is thread-safe and may be mutated by the caller.
*/
public ConcurrentMap<File, Optional<String>> getPackageTypeCache() {
return this.packageTypeCache;
}
/**
* Makes this semantically equivalent to a fresh state, but may internally retain shared resources
* that are expensive to reacquire.
@@ -43,5 +57,6 @@ public class ExtractorState {
public void reset() {
typeScriptParser.reset();
snippets.clear();
packageTypeCache.clear();
}
}

View File

@@ -119,7 +119,7 @@ public class FileExtractor {
JS(".js", ".jsx", ".mjs", ".cjs", ".es6", ".es") {
@Override
public IExtractor mkExtractor(ExtractorConfig config, ExtractorState state) {
return new ScriptExtractor(config);
return new ScriptExtractor(config, state);
}
@Override

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-08-20-2";
public static final String EXTRACTOR_VERSION = "2020-08-24";
public static final Pattern NEWLINE = Pattern.compile("\n");

View File

@@ -1,5 +1,16 @@
package com.semmle.js.extractor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.concurrent.ConcurrentMap;
import java.util.Optional;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.semmle.js.extractor.ExtractorConfig.Platform;
import com.semmle.js.extractor.ExtractorConfig.SourceType;
import com.semmle.js.parser.ParseError;
@@ -9,19 +20,24 @@ import com.semmle.util.trap.TrapWriter.Label;
/** Extract a stand-alone JavaScript script. */
public class ScriptExtractor implements IExtractor {
private ExtractorConfig config;
private ConcurrentMap<File, Optional<String>> packageTypeCache;
public ScriptExtractor(ExtractorConfig config) {
public ScriptExtractor(ExtractorConfig config, ExtractorState state) {
this.config = config;
this.packageTypeCache = state.getPackageTypeCache();
}
/** True if files with the given extension should always be treated as modules. */
private boolean isAlwaysModule(String extension) {
return extension.equals(".mjs") || extension.equals(".es6") || extension.equals(".es");
/** True if files with the given extension and type (from package.json) should always be treated as ES2015 modules. */
private boolean isAlwaysModule(String extension, String packageType) {
if (extension.equals(".mjs") || extension.equals(".es6") || extension.equals(".es")) {
return true;
}
return "module".equals(packageType) && extension.equals(".js");
}
/** True if files with the given extension should always be treated as CommonJS modules. */
private boolean isAlwaysCommonJSModule(String extension) {
return extension.equals(".cjs");
/** True if files with the given extension and type (from package.json) should always be treated as CommonJS modules. */
private boolean isAlwaysCommonJSModule(String extension, String packageType) {
return extension.equals(".cjs") || (extension.equals(".js") && "commonjs".equals(packageType));
}
@Override
@@ -49,13 +65,16 @@ public class ScriptExtractor implements IExtractor {
locationManager.setStart(2, 1);
}
// Some file extensions are interpreted as modules by default.
String packageType = getPackageType(locationManager.getSourceFile().getParentFile());
String extension = locationManager.getSourceFileExtension();
// Some files are interpreted as modules by default.
if (config.getSourceType() == SourceType.AUTO) {
if (isAlwaysModule(locationManager.getSourceFileExtension())) {
if (isAlwaysModule(extension, packageType)) {
config = config.withSourceType(SourceType.MODULE);
}
if (isAlwaysCommonJSModule(locationManager.getSourceFileExtension())) {
config = config.withSourceType(SourceType.COMMONJS_MODULE);
if (isAlwaysCommonJSModule(extension, packageType)) {
config = config.withSourceType(SourceType.COMMONJS_MODULE).withPlatform(Platform.NODE);
}
}
@@ -78,4 +97,40 @@ public class ScriptExtractor implements IExtractor {
return loc;
}
/**
* A minimal model of `package.json` files that can be used to read the "type" field.
*/
private static class PackageJSON {
String type;
}
/**
* Returns the "type" field from the nearest `package.json` file (searching up the file hierarchy).
*/
private String getPackageType(File folder) {
if (folder == null || !folder.isDirectory()) {
return null;
}
if (packageTypeCache.containsKey(folder)) {
return packageTypeCache.get(folder).orElse(null);
}
File file = new File(folder, "package.json");
if (file.isDirectory()) {
return null;
}
if (!file.exists()) {
String result = getPackageType(folder.getParentFile());
packageTypeCache.put(folder, Optional.ofNullable(result));
return result;
}
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
String result = new Gson().fromJson(reader, PackageJSON.class).type;
packageTypeCache.put(folder, Optional.ofNullable(result));
return result;
} catch (IOException | JsonSyntaxException e) {
return null;
}
}
}

View File

@@ -111,6 +111,7 @@ import com.semmle.js.ast.jsx.JSXIdentifier;
import com.semmle.js.ast.jsx.JSXMemberExpression;
import com.semmle.js.ast.jsx.JSXOpeningElement;
import com.semmle.js.ast.jsx.JSXSpreadAttribute;
import com.semmle.js.ast.jsx.JSXThisExpr;
import com.semmle.js.parser.JSParser.Result;
import com.semmle.ts.ast.ArrayTypeExpr;
import com.semmle.ts.ast.ConditionalTypeExpr;
@@ -2377,7 +2378,7 @@ public class TypeScriptASTConverter {
convertJSXName(me.getObject()),
(JSXIdentifier) convertJSXName(me.getProperty()));
}
if (e instanceof ThisExpression) return new JSXIdentifier(e.getLoc(), "this");
if (e instanceof ThisExpression) return new JSXThisExpr(e.getLoc());
return (IJSXName) e;
}

View File

@@ -60,82 +60,89 @@ toplevels(#20001,0)
#20020=@"loc,{#10000},1,1,2,0"
locations_default(#20020,#10000,1,1,2,0)
hasLocation(#20001,#20020)
#20021=@"module;{#10000},1,1"
scopes(#20021,3)
scopenodes(#20001,#20021)
scopenesting(#20021,#20000)
#20022=@"var;{require};{#20021}"
variables(#20022,"require",#20021)
#20023=@"var;{module};{#20021}"
variables(#20023,"module",#20021)
#20024=@"var;{exports};{#20021}"
variables(#20024,"exports",#20021)
#20025=@"var;{__filename};{#20021}"
variables(#20025,"__filename",#20021)
#20026=@"var;{__dirname};{#20021}"
variables(#20026,"__dirname",#20021)
#20027=@"var;{arguments};{#20021}"
variables(#20027,"arguments",#20021)
#20021=@"var;{global};{#20000}"
variables(#20021,"global",#20000)
#20022=@"var;{process};{#20000}"
variables(#20022,"process",#20000)
#20023=@"var;{console};{#20000}"
variables(#20023,"console",#20000)
#20024=@"var;{Buffer};{#20000}"
variables(#20024,"Buffer",#20000)
#20025=@"module;{#10000},1,1"
scopes(#20025,3)
scopenodes(#20001,#20025)
scopenesting(#20025,#20000)
#20026=@"var;{require};{#20025}"
variables(#20026,"require",#20025)
#20027=@"var;{module};{#20025}"
variables(#20027,"module",#20025)
#20028=@"var;{exports};{#20025}"
variables(#20028,"exports",#20025)
#20029=@"var;{__filename};{#20025}"
variables(#20029,"__filename",#20025)
#20030=@"var;{__dirname};{#20025}"
variables(#20030,"__dirname",#20025)
#20031=@"var;{arguments};{#20025}"
variables(#20031,"arguments",#20025)
isModule(#20001)
#20028=*
stmts(#20028,2,#20001,0,"console ... onJS"");")
hasLocation(#20028,#20003)
stmtContainers(#20028,#20001)
#20029=*
exprs(#20029,13,#20028,0,"console ... monJS"")")
#20030=@"loc,{#10000},1,1,1,29"
locations_default(#20030,#10000,1,1,1,29)
hasLocation(#20029,#20030)
enclosingStmt(#20029,#20028)
exprContainers(#20029,#20001)
#20031=*
exprs(#20031,14,#20029,-1,"console.log")
#20032=@"loc,{#10000},1,1,1,11"
locations_default(#20032,#10000,1,1,1,11)
hasLocation(#20031,#20032)
enclosingStmt(#20031,#20028)
exprContainers(#20031,#20001)
#20032=*
stmts(#20032,2,#20001,0,"console ... onJS"");")
hasLocation(#20032,#20003)
stmtContainers(#20032,#20001)
#20033=*
exprs(#20033,79,#20031,0,"console")
hasLocation(#20033,#20005)
enclosingStmt(#20033,#20028)
exprs(#20033,13,#20032,0,"console ... monJS"")")
#20034=@"loc,{#10000},1,1,1,29"
locations_default(#20034,#10000,1,1,1,29)
hasLocation(#20033,#20034)
enclosingStmt(#20033,#20032)
exprContainers(#20033,#20001)
literals("console","console",#20033)
#20034=@"var;{console};{#20000}"
variables(#20034,"console",#20000)
bind(#20033,#20034)
#20035=*
exprs(#20035,0,#20031,1,"log")
hasLocation(#20035,#20009)
enclosingStmt(#20035,#20028)
exprs(#20035,14,#20033,-1,"console.log")
#20036=@"loc,{#10000},1,1,1,11"
locations_default(#20036,#10000,1,1,1,11)
hasLocation(#20035,#20036)
enclosingStmt(#20035,#20032)
exprContainers(#20035,#20001)
literals("log","log",#20035)
#20036=*
exprs(#20036,4,#20029,0,"""Hello CommonJS""")
hasLocation(#20036,#20013)
enclosingStmt(#20036,#20028)
exprContainers(#20036,#20001)
literals("Hello CommonJS","""Hello CommonJS""",#20036)
#20037=*
regexpterm(#20037,14,#20036,0,"Hello CommonJS")
#20038=@"loc,{#10000},1,14,1,27"
locations_default(#20038,#10000,1,14,1,27)
hasLocation(#20037,#20038)
regexpConstValue(#20037,"Hello CommonJS")
exprs(#20037,79,#20035,0,"console")
hasLocation(#20037,#20005)
enclosingStmt(#20037,#20032)
exprContainers(#20037,#20001)
literals("console","console",#20037)
bind(#20037,#20023)
#20038=*
exprs(#20038,0,#20035,1,"log")
hasLocation(#20038,#20009)
enclosingStmt(#20038,#20032)
exprContainers(#20038,#20001)
literals("log","log",#20038)
#20039=*
entry_cfg_node(#20039,#20001)
#20040=@"loc,{#10000},1,1,1,0"
locations_default(#20040,#10000,1,1,1,0)
hasLocation(#20039,#20040)
#20041=*
exit_cfg_node(#20041,#20001)
hasLocation(#20041,#20019)
successor(#20028,#20033)
successor(#20036,#20029)
successor(#20035,#20031)
successor(#20033,#20035)
successor(#20031,#20036)
successor(#20029,#20041)
successor(#20039,#20028)
exprs(#20039,4,#20033,0,"""Hello CommonJS""")
hasLocation(#20039,#20013)
enclosingStmt(#20039,#20032)
exprContainers(#20039,#20001)
literals("Hello CommonJS","""Hello CommonJS""",#20039)
#20040=*
regexpterm(#20040,14,#20039,0,"Hello CommonJS")
#20041=@"loc,{#10000},1,14,1,27"
locations_default(#20041,#10000,1,14,1,27)
hasLocation(#20040,#20041)
regexpConstValue(#20040,"Hello CommonJS")
#20042=*
entry_cfg_node(#20042,#20001)
#20043=@"loc,{#10000},1,1,1,0"
locations_default(#20043,#10000,1,1,1,0)
hasLocation(#20042,#20043)
#20044=*
exit_cfg_node(#20044,#20001)
hasLocation(#20044,#20019)
successor(#20032,#20037)
successor(#20039,#20033)
successor(#20038,#20035)
successor(#20037,#20038)
successor(#20035,#20039)
successor(#20033,#20044)
successor(#20042,#20032)
isNodejs(#20001)
numlines(#10000,1,1,0)
filetype(#10000,"javascript")

View File

@@ -0,0 +1,3 @@
// I'm invalid JSON
{
"type": "foo"

View File

@@ -0,0 +1,15 @@
#10000=@"/package.json;sourcefile"
files(#10000,"/package.json","package","json",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=*
json_errors(#20000,"Error: Unexpected token")
#20001=@"loc,{#10000},3,1,3,1"
locations_default(#20001,#10000,3,1,3,1)
hasLocation(#20000,#20001)
numlines(#10000,3,0,0)
filetype(#10000,"json")

View File

@@ -0,0 +1,28 @@
#10000=@"/tst.js;sourcefile"
files(#10000,"/tst.js","tst","js",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=@"script;{#10000},1,1"
numlines(#20001,0,0,0)
#20002=*
tokeninfo(#20002,0,#20001,0,"")
#20003=@"loc,{#10000},1,1,1,0"
locations_default(#20003,#10000,1,1,1,0)
hasLocation(#20002,#20003)
toplevels(#20001,0)
hasLocation(#20001,#20003)
#20004=*
entry_cfg_node(#20004,#20001)
hasLocation(#20004,#20003)
#20005=*
exit_cfg_node(#20005,#20001)
hasLocation(#20005,#20003)
successor(#20004,#20005)
numlines(#10000,0,0,0)
filetype(#10000,"javascript")

View File

@@ -0,0 +1,3 @@
{
"type": 123
}

View File

@@ -0,0 +1,22 @@
#10000=@"/package.json;sourcefile"
files(#10000,"/package.json","package","json",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=*
json(#20000,5,#10000,0,"{\n ""type"": 123\n}")
#20001=@"loc,{#10000},1,1,3,1"
locations_default(#20001,#10000,1,1,3,1)
json_locations(#20000,#20001)
#20002=*
json(#20002,2,#20000,0,"123")
#20003=@"loc,{#10000},2,11,2,13"
locations_default(#20003,#10000,2,11,2,13)
json_locations(#20002,#20003)
json_literals("123","123",#20002)
json_properties(#20000,"type",#20002)
numlines(#10000,3,0,0)
filetype(#10000,"json")

View File

@@ -0,0 +1,28 @@
#10000=@"/tst2.js;sourcefile"
files(#10000,"/tst2.js","tst2","js",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=@"script;{#10000},1,1"
numlines(#20001,0,0,0)
#20002=*
tokeninfo(#20002,0,#20001,0,"")
#20003=@"loc,{#10000},1,1,1,0"
locations_default(#20003,#10000,1,1,1,0)
hasLocation(#20002,#20003)
toplevels(#20001,0)
hasLocation(#20001,#20003)
#20004=*
entry_cfg_node(#20004,#20001)
hasLocation(#20004,#20003)
#20005=*
exit_cfg_node(#20005,#20001)
hasLocation(#20005,#20003)
successor(#20004,#20005)
numlines(#10000,0,0,0)
filetype(#10000,"javascript")

View File

@@ -301,94 +301,90 @@ hasLocation(#20099,#20100)
enclosingStmt(#20099,#20077)
exprContainers(#20099,#20001)
#20101=*
exprs(#20101,79,#20099,0,"this")
exprs(#20101,6,#20099,0,"this")
hasLocation(#20101,#20039)
enclosingStmt(#20101,#20077)
exprContainers(#20101,#20001)
literals("this","this",#20101)
#20102=@"var;{this};{#20000}"
variables(#20102,"this",#20000)
bind(#20101,#20102)
#20102=*
exprs(#20102,0,#20099,1,"props")
hasLocation(#20102,#20043)
enclosingStmt(#20102,#20077)
exprContainers(#20102,#20001)
literals("props","props",#20102)
#20103=*
exprs(#20103,0,#20099,1,"props")
hasLocation(#20103,#20043)
exprs(#20103,0,#20097,1,"icon")
hasLocation(#20103,#20047)
enclosingStmt(#20103,#20077)
exprContainers(#20103,#20001)
literals("props","props",#20103)
literals("icon","icon",#20103)
#20104=*
exprs(#20104,0,#20097,1,"icon")
hasLocation(#20104,#20047)
exprs(#20104,4,#20079,-4,"")
#20105=@"loc,{#10000},3,3,3,2"
locations_default(#20105,#10000,3,3,3,2)
hasLocation(#20104,#20105)
enclosingStmt(#20104,#20077)
exprContainers(#20104,#20001)
literals("icon","icon",#20104)
#20105=*
exprs(#20105,4,#20079,-4,"")
#20106=@"loc,{#10000},3,3,3,2"
locations_default(#20106,#10000,3,3,3,2)
hasLocation(#20105,#20106)
enclosingStmt(#20105,#20077)
exprContainers(#20105,#20001)
literals("
","",#20105)
#20107=*
regexpterm(#20107,14,#20105,0,"
","",#20104)
#20106=*
regexpterm(#20106,14,#20104,0,"
")
#20108=@"loc,{#10000},3,4,3,6"
locations_default(#20108,#10000,3,4,3,6)
hasLocation(#20107,#20108)
regexpConstValue(#20107,"
#20107=@"loc,{#10000},3,4,3,6"
locations_default(#20107,#10000,3,4,3,6)
hasLocation(#20106,#20107)
regexpConstValue(#20106,"
")
#20109=*
exprs(#20109,89,#20079,-5,"<name-with-dashes/>")
#20110=@"loc,{#10000},3,3,3,21"
locations_default(#20110,#10000,3,3,3,21)
hasLocation(#20109,#20110)
enclosingStmt(#20109,#20077)
exprContainers(#20109,#20001)
#20111=*
exprs(#20111,0,#20109,-1,"name-with-dashes")
#20112=@"loc,{#10000},3,4,3,19"
locations_default(#20112,#10000,3,4,3,19)
hasLocation(#20111,#20112)
enclosingStmt(#20111,#20077)
exprContainers(#20111,#20001)
literals("name-with-dashes","name-with-dashes",#20111)
#20113=*
exprs(#20113,4,#20079,-6,"")
#20114=@"loc,{#10000},4,1,4,0"
locations_default(#20114,#10000,4,1,4,0)
hasLocation(#20113,#20114)
enclosingStmt(#20113,#20077)
exprContainers(#20113,#20001)
#20108=*
exprs(#20108,89,#20079,-5,"<name-with-dashes/>")
#20109=@"loc,{#10000},3,3,3,21"
locations_default(#20109,#10000,3,3,3,21)
hasLocation(#20108,#20109)
enclosingStmt(#20108,#20077)
exprContainers(#20108,#20001)
#20110=*
exprs(#20110,0,#20108,-1,"name-with-dashes")
#20111=@"loc,{#10000},3,4,3,19"
locations_default(#20111,#10000,3,4,3,19)
hasLocation(#20110,#20111)
enclosingStmt(#20110,#20077)
exprContainers(#20110,#20001)
literals("name-with-dashes","name-with-dashes",#20110)
#20112=*
exprs(#20112,4,#20079,-6,"")
#20113=@"loc,{#10000},4,1,4,0"
locations_default(#20113,#10000,4,1,4,0)
hasLocation(#20112,#20113)
enclosingStmt(#20112,#20077)
exprContainers(#20112,#20001)
literals("
","",#20113)
#20115=*
regexpterm(#20115,14,#20113,0,"
","",#20112)
#20114=*
regexpterm(#20114,14,#20112,0,"
")
#20116=@"loc,{#10000},4,2,4,2"
locations_default(#20116,#10000,4,2,4,2)
hasLocation(#20115,#20116)
regexpConstValue(#20115,"
#20115=@"loc,{#10000},4,2,4,2"
locations_default(#20115,#10000,4,2,4,2)
hasLocation(#20114,#20115)
regexpConstValue(#20114,"
")
#20117=*
entry_cfg_node(#20117,#20001)
#20118=@"loc,{#10000},1,1,1,0"
locations_default(#20118,#10000,1,1,1,0)
hasLocation(#20117,#20118)
#20119=*
exit_cfg_node(#20119,#20001)
hasLocation(#20119,#20075)
#20116=*
entry_cfg_node(#20116,#20001)
#20117=@"loc,{#10000},1,1,1,0"
locations_default(#20117,#10000,1,1,1,0)
hasLocation(#20116,#20117)
#20118=*
exit_cfg_node(#20118,#20001)
hasLocation(#20118,#20075)
successor(#20077,#20080)
successor(#20113,#20079)
successor(#20111,#20109)
successor(#20109,#20113)
successor(#20105,#20111)
successor(#20104,#20097)
successor(#20103,#20099)
successor(#20101,#20103)
successor(#20099,#20104)
successor(#20112,#20079)
successor(#20110,#20108)
successor(#20108,#20112)
successor(#20104,#20110)
successor(#20103,#20097)
successor(#20102,#20099)
successor(#20101,#20102)
successor(#20099,#20103)
successor(#20097,#20095)
successor(#20095,#20105)
successor(#20095,#20104)
successor(#20091,#20101)
successor(#20089,#20086)
successor(#20088,#20089)
@@ -397,7 +393,7 @@ successor(#20084,#20081)
successor(#20083,#20084)
successor(#20081,#20088)
successor(#20080,#20083)
successor(#20079,#20119)
successor(#20117,#20077)
successor(#20079,#20118)
successor(#20116,#20077)
numlines(#10000,4,4,0)
filetype(#10000,"typescript")

View File

@@ -8,7 +8,7 @@
* @id js/duplicate-html-id
* @tags maintainability
* correctness
* @precision high
* @precision low
*/
import javascript

View File

@@ -6,7 +6,7 @@
<overview>
<p>
Sanitizing untrusted input for HTML meta-characters is an important
Sanitizing untrusted input for HTML meta-characters is a common
technique for preventing cross-site scripting attacks. Usually, this
is done by escaping <code>&lt;</code>, <code>&gt;</code>,
<code>&amp;</code> and <code>&quot;</code>. However, the context in which
@@ -38,6 +38,14 @@
</p>
<p>
An even safer alternative is to design the application
so that sanitization is not needed, for instance by using HTML
templates that are explicit about the values they treat as HTML.
</p>
</recommendation>
<example>

View File

@@ -5,7 +5,7 @@
<overview>
<p>
Sanitizing untrusted input is an important technique for preventing injection attacks such as
Sanitizing untrusted input is a common technique for preventing injection attacks such as
SQL injection or cross-site scripting. Usually, this is done by escaping meta-characters such
as quotes in a domain-specific way so that they are treated as normal characters.
</p>
@@ -31,6 +31,14 @@ still have undesirable effects, such as badly rendered or confusing output.
Use a (well-tested) sanitization library if at all possible. These libraries are much more
likely to handle corner cases correctly than a custom implementation.
</p>
<p>
An even safer alternative is to design the application so that sanitization is not
needed, for instance by using prepared statements for SQL queries.
</p>
<p>
Otherwise, make sure to use a regular expression with the <code>g</code> flag to ensure that
all occurrences are replaced, and remember to escape backslashes if applicable.

View File

@@ -6,8 +6,8 @@
<overview>
<p>
Sanitizing untrusted input for HTML meta-characters is an
important technique for preventing cross-site scripting attacks. But
Sanitizing untrusted input for HTML meta-characters is a
common technique for preventing cross-site scripting attacks. But
even a sanitized input can be dangerous to use if it is modified
further before a browser treats it as HTML.
@@ -28,6 +28,15 @@
them as HTML.
</p>
<p>
An even safer alternative is to design the application
so that sanitization is not needed, for instance by using HTML
templates that are explicit about the values they treat as HTML.
</p>
</recommendation>
<example>

View File

@@ -4,7 +4,7 @@
<overview>
<p>
Sanitizing untrusted HTTP request parameters is an important
Sanitizing untrusted HTTP request parameters is a common
technique for preventing injection attacks such as SQL injection or
path traversal. This is sometimes done by checking if the request
parameters contain blacklisted substrings.
@@ -35,6 +35,15 @@
is user-controlled.
</p>
<p>
An even safer alternative is to design the application so that sanitization is not
needed, for instance by using prepared statements for SQL queries.
</p>
</recommendation>
<example>

View File

@@ -0,0 +1,26 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Failing to set the 'secure' flag on a cookie can cause it to be sent in cleartext.
This makes it easier for an attacker to intercept.</p>
</overview>
<recommendation>
<p>Always set the <code>secure</code> flag to `true` on a cookie before adding it
to an HTTP response (if the default value is `false`).</p>
</recommendation>
<references>
<li>Production Best Practices: Security:<a href="https://expressjs.com/en/advanced/best-practice-security.html#use-cookies-securely">Use cookies securely</a>.</li>
<li>NodeJS security cheat sheet:<a href="https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html#set-cookie-flags-appropriately">Set cookie flags appropriately</a>.</li>
<li>express-session:<a href="https://github.com/expressjs/session#cookiesecure">cookie.secure</a>.</li>
<li>cookie-session:<a href="https://github.com/expressjs/cookie-session#cookie-options">Cookie Options</a>.</li>
<li><a href="https://expressjs.com/en/api.html#res.cookie">express response.cookie</a>.</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie">Set-Cookie</a>.</li>
<li><a href="https://github.com/js-cookie/js-cookie">js-cookie</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,18 @@
/**
* @name Failure to set secure cookies
* @description Insecure cookies may be sent in cleartext, which makes them vulnerable to
* interception.
* @kind problem
* @problem.severity error
* @precision high
* @id js/insecure-cookie
* @tags security
* external/cwe/cwe-614
*/
import javascript
import InsecureCookie::Cookie
from Cookie cookie
where not cookie.isSecure()
select cookie, "Cookie is added to response without the 'secure' flag being set to true"

View File

@@ -0,0 +1,146 @@
/**
* Provides classes for reasoning about cookies added to response without the 'secure' flag being set.
* A cookie without the 'secure' flag being set can be intercepted and read by a malicious user.
*/
import javascript
module Cookie {
/**
* `secure` property of the cookie options.
*/
string flag() { result = "secure" }
/**
* Abstract class to represent different cases of insecure cookie settings.
*/
abstract class Cookie extends DataFlow::Node {
/**
* Gets the name of the middleware/library used to set the cookie.
*/
abstract string getKind();
/**
* Gets the options used to set this cookie, if any.
*/
abstract DataFlow::Node getCookieOptionsArgument();
/**
* Holds if this cookie is secure.
*/
abstract predicate isSecure();
}
/**
* A cookie set using the `express` module `cookie-session` (https://github.com/expressjs/cookie-session).
*/
class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance, Cookie {
override string getKind() { result = "cookie-session" }
override DataFlow::SourceNode getCookieOptionsArgument() { result = this.getOption("cookie") }
private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
override predicate isSecure() {
// The flag `secure` is set to `false` by default for HTTP, `true` by default for HTTPS (https://github.com/expressjs/cookie-session#cookie-options).
// A cookie is secure if the `secure` flag is not explicitly set to `false`.
not getCookieFlagValue(flag()).mayHaveBooleanValue(false)
}
}
/**
* A cookie set using the `express` module `express-session` (https://github.com/expressjs/session).
*/
class InsecureExpressSessionCookie extends ExpressLibraries::ExpressSession::MiddlewareInstance,
Cookie {
override string getKind() { result = "express-session" }
override DataFlow::SourceNode getCookieOptionsArgument() { result = this.getOption("cookie") }
private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
override predicate isSecure() {
// The flag `secure` is not set by default (https://github.com/expressjs/session#Cookieecure).
// The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
// A cookie is secure if there are the cookie options with the `secure` flag set to `true` or to `auto`.
getCookieFlagValue(flag()).mayHaveBooleanValue(true) or
getCookieFlagValue(flag()).mayHaveStringValue("auto")
}
}
/**
* A cookie set using `response.cookie` from `express` module (https://expressjs.com/en/api.html#res.cookie).
*/
class InsecureExpressCookieResponse extends Cookie, DataFlow::MethodCallNode {
InsecureExpressCookieResponse() { this.calls(any(Express::ResponseExpr r).flow(), "cookie") }
override string getKind() { result = "response.cookie" }
override DataFlow::SourceNode getCookieOptionsArgument() {
result = this.getLastArgument().getALocalSource()
}
private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
getCookieFlagValue(flag()).mayHaveBooleanValue(true)
}
}
/**
* A cookie set using `Set-Cookie` header of an `HTTP` response (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).
*/
class InsecureSetCookieHeader extends Cookie {
InsecureSetCookieHeader() {
this.asExpr() = any(HTTP::SetCookieHeader setCookie).getHeaderArgument()
}
override string getKind() { result = "set-cookie header" }
override DataFlow::Node getCookieOptionsArgument() {
result.asExpr() = this.asExpr().(ArrayExpr).getAnElement()
}
override predicate isSecure() {
// A cookie is secure if the 'secure' flag is specified in the cookie definition.
exists(string s |
getCookieOptionsArgument().mayHaveStringValue(s) and
s.regexpMatch("(.*;)?\\s*secure.*")
)
}
}
/**
* A cookie set using `js-cookie` library (https://github.com/js-cookie/js-cookie).
*/
class InsecureJsCookie extends Cookie {
InsecureJsCookie() {
this =
[DataFlow::globalVarRef("Cookie"),
DataFlow::globalVarRef("Cookie").getAMemberCall("noConflict"),
DataFlow::moduleImport("js-cookie")].getAMemberCall("set")
}
override string getKind() { result = "js-cookie" }
override DataFlow::SourceNode getCookieOptionsArgument() {
result = this.(DataFlow::CallNode).getAnArgument().getALocalSource()
}
DataFlow::Node getCookieFlagValue(string flag) {
result = this.getCookieOptionsArgument().getAPropertyWrite(flag).getRhs()
}
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
getCookieFlagValue(flag()).mayHaveBooleanValue(true)
}
}
}

View File

@@ -181,6 +181,7 @@ class JSXQualifiedName extends Expr, @jsxqualifiedname {
class JSXName extends Expr {
JSXName() {
this instanceof Identifier or
this instanceof ThisExpr or
this.(DotExpr).getBase() instanceof JSXName or
this instanceof JSXQualifiedName
}
@@ -198,6 +199,9 @@ class JSXName extends Expr {
exists(JSXQualifiedName qual | qual = this |
result = qual.getNamespace().getName() + ":" + qual.getName().getName()
)
or
this instanceof ThisExpr and
result = "this"
}
}

View File

@@ -162,6 +162,17 @@ private predicate isRequire(DataFlow::Node nd) {
not nd.getFile().getExtension() = "mjs"
or
isRequire(nd.getAPredecessor())
or
// `import { createRequire } from 'module';` support.
// specialized to ES2015 modules to avoid recursion in the `DataFlow::moduleImport()` predicate.
exists(ImportDeclaration imp | imp.getImportedPath().getValue() = "module" |
nd =
imp
.getImportedModuleNode()
.(DataFlow::SourceNode)
.getAPropertyRead("createRequire")
.getACall()
)
}
/**

View File

@@ -0,0 +1 @@
console.log(".mjs inside a `type:\"commonjs\" is still a ES2015 module`");

View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

View File

@@ -0,0 +1 @@
console.log("I'm empty! The containing package.json determines the type.");

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@@ -0,0 +1 @@
console.log(".cjs inside a `type:\"module\" is still a CommonJS module`");

View File

@@ -0,0 +1 @@
console.log();

View File

@@ -0,0 +1 @@
console.log("I'm empty! The containing package.json determines the type.");

View File

@@ -1,5 +1,10 @@
| commonjs.cjs:1:1:3:16 | <toplevel> | node |
| commonjsPackage/innermjs.mjs:1:1:1:74 | <toplevel> | es2015 |
| commonjsPackage/tst.js:1:1:1:75 | <toplevel> | node |
| import.js:1:1:5:2 | <toplevel> | es2015 |
| mjs.mjs:1:1:1:32 | <toplevel> | es2015 |
| modulePackage/subdir/innercjs.cjs:1:1:1:74 | <toplevel> | node |
| modulePackage/subdir/subfile.js:1:1:1:14 | <toplevel> | es2015 |
| modulePackage/tst.js:1:1:1:75 | <toplevel> | es2015 |
| require.js:1:1:7:1 | <toplevel> | node |
| script.js:1:1:1:35 | <toplevel> | non-module |

View File

@@ -15,6 +15,7 @@
| g.js:1:43:1:61 | require("electron") |
| index.js:1:12:1:26 | require('path') |
| index.js:2:1:2:41 | require ... b.js")) |
| mjs-files/createRequire.mjs:4:26:4:49 | require ... erver') |
| mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') |
| mjs-files/require-from-js.js:2:12:2:39 | require ... me.js') |
| mjs-files/require-from-js.js:3:12:3:40 | require ... e.mjs') |

View File

@@ -0,0 +1,4 @@
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const { ApolloServer } = require('apollo-server');

View File

@@ -0,0 +1,17 @@
import semmle.javascript.frameworks.React
query predicate test_JSXname(JSXElement element, JSXName jsxname, string name, string type) {
name = jsxname.getValue() and
(
jsxname instanceof Identifier and type = "Identifier"
or
jsxname instanceof ThisExpr and type = "thisExpr"
or
jsxname.(DotExpr).getBase() instanceof JSXName and type = "dot"
or
jsxname instanceof JSXQualifiedName and type = "qualifiedName"
) and
element.getNameExpr() = jsxname
}
query ThisExpr test_JSXName_this(JSXElement element) { result.getParentExpr+() = element }

View File

@@ -35,6 +35,7 @@ test_ReactComponent_getInstanceMethod
| thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | someInstanceMethod | thisAccesses.js:26:25:28:5 | functio ... ;\\n } |
| thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | render | thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} |
| thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | render | thisAccesses.js:39:13:44:5 | functio ... ;\\n } |
| thisAccesses.js:54:1:63:1 | class C ... }\\n} | render | thisAccesses.js:59:11:62:5 | () {\\n ... ;\\n } |
| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | render | thisAccesses_importedMappers.js:5:13:14:5 | functio ... ;\\n } |
test_react
| es5.js:1:13:1:17 | React |
@@ -70,6 +71,7 @@ test_react
| thisAccesses.js:38:1:38:5 | React |
| thisAccesses.js:40:9:40:13 | React |
| thisAccesses.js:47:18:47:22 | React |
| thisAccesses.js:54:18:54:22 | React |
| thisAccesses_importedMappers.js:1:8:1:12 | React |
| thisAccesses_importedMappers.js:4:1:4:5 | React |
| thisAccesses_importedMappers.js:6:9:6:13 | React |
@@ -165,6 +167,10 @@ test_ReactComponent_ref
| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:48:17:48:16 | this |
| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:49:9:49:12 | this |
| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:50:9:50:12 | this |
| thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:55:16:55:15 | this |
| thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:59:11:59:10 | this |
| thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:60:20:60:23 | this |
| thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:61:20:61:23 | this |
| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} |
| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:5:13:5:12 | this |
| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:6:38:6:37 | this |
@@ -248,6 +254,7 @@ test_ReactComponent
| thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} |
| thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} |
| thisAccesses.js:47:1:52:1 | class C ... }\\n} |
| thisAccesses.js:54:1:63:1 | class C ... }\\n} |
| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} |
test_ReactComponent_getAPropRead
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | name | es5.js:4:24:4:38 | this.props.name |
@@ -258,3 +265,30 @@ test_ReactComponent_getAPropRead
| preact.js:1:1:7:1 | class H ... }\\n} | name | preact.js:3:9:3:18 | props.name |
| probably-a-component.js:1:1:6:1 | class H ... }\\n} | name | probably-a-component.js:3:9:3:23 | this.props.name |
| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | name | statePropertyWrites.js:38:24:38:38 | this.props.name |
test_JSXname
| es5.js:4:12:4:45 | <div>He ... }</div> | es5.js:4:13:4:15 | div | div | Identifier |
| es5.js:20:12:20:44 | <h1>Hel ... e}</h1> | es5.js:20:13:20:14 | h1 | h1 | Identifier |
| es6.js:3:12:3:45 | <div>He ... }</div> | es6.js:3:13:3:15 | div | div | Identifier |
| exportedComponent.jsx:2:12:2:46 | <div st ... lor}}/> | exportedComponent.jsx:2:13:2:15 | div | div | Identifier |
| importedComponent.jsx:4:12:4:39 | <MyComp ... olor}/> | importedComponent.jsx:4:13:4:23 | MyComponent | MyComponent | Identifier |
| plainfn.js:2:10:2:38 | <div>He ... }</div> | plainfn.js:2:11:2:13 | div | div | Identifier |
| preact.js:5:16:5:21 | <div/> | preact.js:5:17:5:19 | div | div | Identifier |
| probably-a-component.js:4:16:4:21 | <div/> | probably-a-component.js:4:17:4:19 | div | div | Identifier |
| props.js:7:6:7:37 | <C prop ... JSX"}/> | props.js:7:7:7:7 | C | C | Identifier |
| props.js:19:6:19:37 | <C prop ... JSX"}/> | props.js:19:7:19:7 | C | C | Identifier |
| props.js:27:16:27:21 | <div/> | props.js:27:17:27:19 | div | div | Identifier |
| props.js:32:6:32:37 | <C prop ... JSX"}/> | props.js:32:7:32:7 | C | C | Identifier |
| statePropertyWrites.js:38:12:38:45 | <div>He ... }</div> | statePropertyWrites.js:38:13:38:15 | div | div | Identifier |
| thisAccesses.js:23:16:23:21 | <div/> | thisAccesses.js:23:17:23:19 | div | div | Identifier |
| thisAccesses.js:35:12:35:17 | <div/> | thisAccesses.js:35:13:35:15 | div | div | Identifier |
| thisAccesses.js:43:16:43:21 | <div/> | thisAccesses.js:43:17:43:19 | div | div | Identifier |
| thisAccesses.js:60:19:60:41 | <this.n ... s.name> | thisAccesses.js:60:20:60:28 | this.name | this.name | dot |
| thisAccesses.js:61:19:61:41 | <this.t ... s.this> | thisAccesses.js:61:20:61:28 | this.this | this.this | dot |
| thisAccesses_importedMappers.js:13:16:13:21 | <div/> | thisAccesses_importedMappers.js:13:17:13:19 | div | div | Identifier |
test_JSXName_this
| es5.js:4:12:4:45 | <div>He ... }</div> | es5.js:4:24:4:27 | this |
| es5.js:20:12:20:44 | <h1>Hel ... e}</h1> | es5.js:20:24:20:27 | this |
| es6.js:3:12:3:45 | <div>He ... }</div> | es6.js:3:24:3:27 | this |
| statePropertyWrites.js:38:12:38:45 | <div>He ... }</div> | statePropertyWrites.js:38:24:38:27 | this |
| thisAccesses.js:60:19:60:41 | <this.n ... s.name> | thisAccesses.js:60:20:60:23 | this |
| thisAccesses.js:61:19:61:41 | <this.t ... s.this> | thisAccesses.js:61:20:61:23 | this |

View File

@@ -8,3 +8,4 @@ import ReactComponent_getADirectPropsSource
import ReactComponent_getACandidatePropsValue
import ReactComponent
import ReactComponent_getAPropRead
import ReactName

View File

@@ -50,3 +50,14 @@ class C2 extends React.Component {
this.state = y;
}
}
class C3 extends React.Component {
constructor() {
}
render() {
var foo = <this.name></this.name>;
var bar = <this.this></this.this>;
}
}

View File

@@ -0,0 +1,9 @@
| test_cookie-session.js:18:9:28:2 | session ... }\\n}) | Cookie is added to response without the 'secure' flag being set to true |
| test_express-session.js:5:9:8:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
| test_express-session.js:10:9:13:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
| test_express-session.js:15:9:18:2 | session ... T OK\\n}) | Cookie is added to response without the 'secure' flag being set to true |
| test_express-session.js:25:9:25:21 | session(sess) | Cookie is added to response without the 'secure' flag being set to true |
| test_httpserver.js:7:37:7:73 | ["type= ... cript"] | Cookie is added to response without the 'secure' flag being set to true |
| test_jscookie.js:2:1:2:48 | js_cook ... alse }) | Cookie is added to response without the 'secure' flag being set to true |
| test_responseCookie.js:5:5:10:10 | res.coo ... }) | Cookie is added to response without the 'secure' flag being set to true |
| test_responseCookie.js:20:5:20:40 | res.coo ... ptions) | Cookie is added to response without the 'secure' flag being set to true |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-614/InsecureCookie.ql

View File

@@ -0,0 +1,28 @@
const express = require('express')
const app = express()
const session = require('cookie-session')
const expiryDate = new Date(Date.now() + 60 * 60 * 1000)
app.use(session({
name: 'session',
keys: ['key1', 'key2'],
cookie: {
secure: true, // OK
httpOnly: true,
domain: 'example.com',
path: 'foo/bar',
expires: expiryDate
}
}))
app.use(session({
name: 'session',
keys: ['key1', 'key2'],
cookie: {
secure: false, // NOT OK
httpOnly: true,
domain: 'example.com',
path: 'foo/bar',
expires: expiryDate
}
}))

View File

@@ -0,0 +1,33 @@
const express = require('express')
const app = express()
const session = require('express-session')
app.use(session({
secret: 'secret',
cookie: { secure: false } // NOT OK
}))
app.use(session({
secret: 'secret'
// NOT OK
}))
app.use(session({
secret: 'secret',
cookie: {} // NOT OK
}))
const sess = {
secret: 'secret',
cookie: { secure: false } // NOT OK
}
app.use(session(sess))
app.set('trust proxy', 1)
app.use(session({
secret: 'secret',
cookie: { secure: true } // OK
}))

View File

@@ -0,0 +1,22 @@
const http = require('http');
function test1() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
// NOT OK
res.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
}
function test2() {
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
// OK
res.setHeader("Set-Cookie", ["type=ninja; Secure", "language=javascript; secure"]);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
}

View File

@@ -0,0 +1,3 @@
const js_cookie = require('js-cookie')
js_cookie.set('key', 'value', { secure: false }); // NOT OK
js_cookie.set('key', 'value', { secure: true }); // OK

View File

@@ -0,0 +1,33 @@
const express = require('express')
const app = express()
app.get('/a', function (req, res, next) {
res.cookie('name', 'value',
{
maxAge: 9000000000,
httpOnly: true,
secure: false // NOT OK
});
res.end('ok')
})
app.get('/b', function (req, res, next) {
let options = {
maxAge: 9000000000,
httpOnly: true,
secure: false // NOT OK
}
res.cookie('name', 'value', options);
res.end('ok')
})
app.get('/c', function (req, res, next) {
res.cookie('name', 'value',
{
maxAge: 9000000000,
httpOnly: true,
secure: true // OK
});
res.end('ok')
})