mirror of
https://github.com/github/codeql.git
synced 2025-12-18 01:33:15 +01:00
Merge branch 'aegilops/js/insecure-helmet-middleware' of https://github.com/aegilops/codeql into aegilops/js/insecure-helmet-middleware
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
|
||||
load("@semmle_code//:dist.bzl", "dist")
|
||||
load("@semmle_code//buildutils-internal:zipmerge.bzl", "zipmerge")
|
||||
load("//misc/bazel:pkg.bzl", "codeql_pack")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
@@ -23,26 +22,25 @@ pkg_files(
|
||||
strip_prefix = None,
|
||||
)
|
||||
|
||||
dist(
|
||||
name = "javascript-extractor-pack",
|
||||
# We have to use a zip of the typescript parser wrapper, as it's generated by a genrule
|
||||
# and we don't know a list of its output files.
|
||||
codeql_pack(
|
||||
name = "javascript",
|
||||
srcs = [
|
||||
":dbscheme-group",
|
||||
"//javascript/downgrades",
|
||||
"//javascript/externs",
|
||||
"//javascript/extractor:tools-extractor",
|
||||
"@semmle_code//language-packs/javascript:resources",
|
||||
"//javascript/resources",
|
||||
],
|
||||
prefix = "javascript",
|
||||
visibility = ["//visibility:public"],
|
||||
zips = {"//javascript/extractor/lib/typescript": "tools"},
|
||||
)
|
||||
|
||||
# We have to zipmerge in the typescript parser wrapper, as it's generated by a genrule
|
||||
# and we don't know a list of its output files. Therefore, we sidestep the
|
||||
# rules_pkg tooling here, and generate the zip for the language pack manually.
|
||||
zipmerge(
|
||||
name = "javascript",
|
||||
srcs = [
|
||||
":javascript-extractor-pack.zip",
|
||||
"//javascript/extractor/lib/typescript",
|
||||
],
|
||||
out = "javascript.zip",
|
||||
# TODO copy for internal repository backward compatibility
|
||||
genrule(
|
||||
name = "javascript.zip",
|
||||
srcs = [":javascript-generic-zip"],
|
||||
outs = ["javascript.zip"],
|
||||
cmd = "cp $< $@",
|
||||
)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
load("@semmle_code//:dist.bzl", "pack_zip")
|
||||
load("//misc/bazel:pkg.bzl", "codeql_pkg_files", "strip_prefix")
|
||||
|
||||
pack_zip(
|
||||
codeql_pkg_files(
|
||||
name = "downgrades",
|
||||
srcs = glob(
|
||||
["**/*"],
|
||||
exclude = ["BUILD.bazel"],
|
||||
),
|
||||
prefix = "downgrades",
|
||||
visibility = ["//visibility:public"],
|
||||
strip_prefix = strip_prefix.from_pkg(),
|
||||
visibility = ["//javascript:__pkg__"],
|
||||
)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
load("@semmle_code//:dist.bzl", "pack_zip")
|
||||
load("//misc/bazel:pkg.bzl", "codeql_pkg_files", "strip_prefix")
|
||||
|
||||
pack_zip(
|
||||
codeql_pkg_files(
|
||||
name = "externs",
|
||||
srcs = glob(
|
||||
["**/*"],
|
||||
exclude = ["BUILD.bazel"],
|
||||
),
|
||||
prefix = "tools/data/externs",
|
||||
visibility = ["//visibility:public"],
|
||||
strip_prefix = strip_prefix.from_pkg(),
|
||||
visibility = ["//javascript:__pkg__"],
|
||||
)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
load("@semmle_code//:common.bzl", "on_windows")
|
||||
|
||||
# Builds a zip file of the compiled typscript-parser-wrapper and its dependencies.
|
||||
genrule(
|
||||
name = "typescript",
|
||||
@@ -33,19 +31,16 @@ genrule(
|
||||
# Install again with only runtime deps
|
||||
"$$NPM install --prod",
|
||||
"mv node_modules build/",
|
||||
"mkdir -p javascript/tools/typescript-parser-wrapper",
|
||||
"mv build/* javascript/tools/typescript-parser-wrapper",
|
||||
"mkdir -p typescript-parser-wrapper",
|
||||
"mv build/* typescript-parser-wrapper",
|
||||
"OUT=$$BAZEL_ROOT/$@",
|
||||
"case $$OSTYPE in",
|
||||
" cygwin|msys|win32) OUT=$$(cygpath -w $$OUT);;",
|
||||
"esac",
|
||||
"",
|
||||
]) + on_windows(
|
||||
" && ".join([
|
||||
"$$BAZEL_ROOT/$(execpath @bazel_tools//tools/zip:zipper) cC $$(cygpath -w $$BAZEL_ROOT/$@) $$(find javascript -name '*' -print)",
|
||||
"rm -rf $$TEMP",
|
||||
]),
|
||||
" && ".join([
|
||||
"$$BAZEL_ROOT/$(execpath @bazel_tools//tools/zip:zipper) cC $$BAZEL_ROOT/$@ $$(find javascript -name '*' -print)",
|
||||
"rm -rf $$TEMP",
|
||||
]),
|
||||
),
|
||||
"$$BAZEL_ROOT/$(execpath @bazel_tools//tools/zip:zipper) cC $$OUT $$(find typescript-parser-wrapper -name '*' -print)",
|
||||
"rm -rf $$TEMP",
|
||||
]),
|
||||
tools = [
|
||||
"@bazel_tools//tools/zip:zipper",
|
||||
"@nodejs//:node_bin",
|
||||
|
||||
@@ -159,6 +159,7 @@ import com.semmle.util.trap.TrapWriter;
|
||||
* <li>Files with base name "package.json" or "tsconfig.json", and files whose base name
|
||||
* is of the form "codeql-javascript-*.json".
|
||||
* <li>JavaScript, JSON or YAML files whose base name starts with ".eslintrc".
|
||||
* <li>JSON files whose base name is ".xsaccess".
|
||||
* <li>All extension-less files.
|
||||
* </ul>
|
||||
*
|
||||
@@ -393,9 +394,10 @@ public class AutoBuild {
|
||||
for (FileType filetype : defaultExtract)
|
||||
for (String extension : filetype.getExtensions()) patterns.add("**/*" + extension);
|
||||
|
||||
// include .eslintrc files, package.json files, tsconfig.json files, and
|
||||
// codeql-javascript-*.json files
|
||||
// include .eslintrc files, .xsaccess files, package.json files,
|
||||
// tsconfig.json files, and codeql-javascript-*.json files
|
||||
patterns.add("**/.eslintrc*");
|
||||
patterns.add("**/.xsaccess");
|
||||
patterns.add("**/package.json");
|
||||
patterns.add("**/tsconfig*.json");
|
||||
patterns.add("**/codeql-javascript-*.json");
|
||||
@@ -735,6 +737,7 @@ public class AutoBuild {
|
||||
.collect(Collectors.toList());
|
||||
|
||||
filesToExtract = filesToExtract.stream()
|
||||
.filter(p -> !isFileTooLarge(p))
|
||||
.sorted(PATH_ORDERING)
|
||||
.collect(Collectors.toCollection(() -> new LinkedHashSet<>()));
|
||||
|
||||
@@ -1010,6 +1013,15 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
|
||||
return config;
|
||||
}
|
||||
|
||||
private boolean isFileTooLarge(Path f) {
|
||||
long fileSize = f.toFile().length();
|
||||
if (fileSize > 1_000_000L * this.maximumFileSizeInMegabytes) {
|
||||
warn("Skipping " + f + " because it is too large (" + StringUtil.printFloat(fileSize / 1_000_000.0) + " MB). The limit is " + this.maximumFileSizeInMegabytes + " MB.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<Path> extractTypeScript(
|
||||
Set<Path> files,
|
||||
Set<Path> extractedFiles,
|
||||
@@ -1051,9 +1063,10 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
|
||||
// compiler can parse them for us.
|
||||
continue;
|
||||
}
|
||||
if (!extractedFiles.contains(sourcePath)) {
|
||||
typeScriptFiles.add(sourcePath);
|
||||
if (extractedFiles.contains(sourcePath)) {
|
||||
continue;
|
||||
}
|
||||
typeScriptFiles.add(sourcePath);
|
||||
}
|
||||
typeScriptFiles.sort(PATH_ORDERING);
|
||||
extractTypeScriptFiles(typeScriptFiles, extractedFiles, extractors);
|
||||
@@ -1236,11 +1249,6 @@ protected DependencyInstallationResult preparePackagesAndDependencies(Set<Path>
|
||||
warn("Skipping " + file + ", which does not exist.");
|
||||
return;
|
||||
}
|
||||
long fileSize = f.length();
|
||||
if (fileSize > 1_000_000L * this.maximumFileSizeInMegabytes) {
|
||||
warn("Skipping " + file + " because it is too large (" + StringUtil.printFloat(fileSize / 1_000_000.0) + " MB). The limit is " + this.maximumFileSizeInMegabytes + " MB.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
long start = logBeginProcess("Extracting " + file);
|
||||
|
||||
@@ -184,8 +184,8 @@ public class FileExtractor {
|
||||
if (super.contains(f, lcExt, config)) return true;
|
||||
|
||||
// detect JSON-encoded configuration files whose name starts with `.` and ends with `rc`
|
||||
// (e.g., `.eslintrc` or `.babelrc`)
|
||||
if (f.isFile() && f.getName().matches("\\..*rc")) {
|
||||
// (e.g., `.eslintrc` or `.babelrc`) as well as `.xsaccess` files
|
||||
if (f.isFile() && f.getName().matches("\\..*rc|\\.xsaccess")) {
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
|
||||
// check whether the first two non-empty lines look like the start of a JSON object
|
||||
// (two lines because the opening brace is usually on a line by itself)
|
||||
|
||||
@@ -50,7 +50,7 @@ public class AllTests {
|
||||
entry = zis.getNextEntry();
|
||||
}
|
||||
}
|
||||
Path tsWrapper = tempDir.resolve("javascript/tools/typescript-parser-wrapper/main.js");
|
||||
Path tsWrapper = tempDir.resolve("typescript-parser-wrapper/main.js");
|
||||
if (!Files.exists(tsWrapper)) {
|
||||
throw new RuntimeException("Could not find ts-wrapper at " + tsWrapper);
|
||||
}
|
||||
|
||||
3
javascript/extractor/tests/xsaccess/input/.xsaccess
Normal file
3
javascript/extractor/tests/xsaccess/input/.xsaccess
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"exposed": true // Expose data via http
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#10000=@"/.xsaccess;sourcefile"
|
||||
files(#10000,"/.xsaccess")
|
||||
#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 ""ex ... http\n}")
|
||||
#20001=@"loc,{#10000},1,1,3,1"
|
||||
locations_default(#20001,#10000,1,1,3,1)
|
||||
json_locations(#20000,#20001)
|
||||
#20002=*
|
||||
json(#20002,1,#20000,0,"true")
|
||||
#20003=@"loc,{#10000},2,14,2,17"
|
||||
locations_default(#20003,#10000,2,14,2,17)
|
||||
json_locations(#20002,#20003)
|
||||
json_literals("true","true",#20002)
|
||||
json_properties(#20000,"exposed",#20002)
|
||||
numlines(#10000,3,0,0)
|
||||
filetype(#10000,"json")
|
||||
1
javascript/ql/integration-tests/legacy
Normal file
1
javascript/ql/integration-tests/legacy
Normal file
@@ -0,0 +1 @@
|
||||
These tests are still run with the legacy test runner
|
||||
@@ -1,3 +1,17 @@
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Additional heuristics for a new sensitive data classification for private information (e.g. credit card numbers) have been added to the shared `SensitiveDataHeuristics.qll` library. This may result in additional results for queries that use sensitive data such as `js/clear-text-storage-sensitive-data` and `js/clear-text-logging`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed a bug where very large TypeScript files would cause database creation to crash. Large files over 10MB were already excluded from analysis, but the file size check was not applied to TypeScript files.
|
||||
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Additional heuristics for a new sensitive data classification for private information (e.g. credit card numbers) have been added to the shared `SensitiveDataHeuristics.qll` library. This may result in additional results for queries that use sensitive data such as `js/clear-text-storage-sensitive-data` and `js/clear-text-logging`.
|
||||
13
javascript/ql/lib/change-notes/released/1.0.0.md
Normal file
13
javascript/ql/lib/change-notes/released/1.0.0.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Additional heuristics for a new sensitive data classification for private information (e.g. credit card numbers) have been added to the shared `SensitiveDataHeuristics.qll` library. This may result in additional results for queries that use sensitive data such as `js/clear-text-storage-sensitive-data` and `js/clear-text-logging`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed a bug where very large TypeScript files would cause database creation to crash. Large files over 10MB were already excluded from analysis, but the file size check was not applied to TypeScript files.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.1
|
||||
lastReleaseVersion: 1.0.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-all
|
||||
version: 0.9.2-dev
|
||||
version: 1.0.1-dev
|
||||
groups: javascript
|
||||
dbscheme: semmlecode.javascript.dbscheme
|
||||
extractor: javascript
|
||||
|
||||
@@ -33,4 +33,10 @@ newtype TNode =
|
||||
TExceptionalInvocationReturnNode(InvokeExpr e) or
|
||||
TGlobalAccessPathRoot() or
|
||||
TTemplatePlaceholderTag(Templating::TemplatePlaceholderTag tag) or
|
||||
TReflectiveParametersNode(Function f)
|
||||
TReflectiveParametersNode(Function f) or
|
||||
TForbiddenRecursionGuard() {
|
||||
none() and
|
||||
// We want to prune irrelevant models before materialising data flow nodes, so types contributed
|
||||
// directly from CodeQL must expose their pruning info without depending on data flow nodes.
|
||||
(any(ModelInput::TypeModel tm).isTypeUsed("") implies any())
|
||||
}
|
||||
|
||||
@@ -168,9 +168,20 @@ module ModelInput {
|
||||
* A unit class for adding additional type model rows from CodeQL models.
|
||||
*/
|
||||
class TypeModel extends Unit {
|
||||
/**
|
||||
* Holds if any of the other predicates in this class might have a result
|
||||
* for the given `type`.
|
||||
*
|
||||
* The implementation of this predicate should not depend on `DataFlow::Node`.
|
||||
*/
|
||||
bindingset[type]
|
||||
predicate isTypeUsed(string type) { none() }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node that is a source of the given `type`.
|
||||
*
|
||||
* Note that `type` should also be included in `isTypeUsed`.
|
||||
*
|
||||
* This must not depend on API graphs, but ensures that an API node is generated for
|
||||
* the source.
|
||||
*/
|
||||
@@ -180,6 +191,8 @@ module ModelInput {
|
||||
* Gets a data-flow node that is a sink of the given `type`,
|
||||
* usually because it is an argument passed to a parameter of that type.
|
||||
*
|
||||
* Note that `type` should also be included in `isTypeUsed`.
|
||||
*
|
||||
* This must not depend on API graphs, but ensures that an API node is generated for
|
||||
* the sink.
|
||||
*/
|
||||
@@ -188,6 +201,8 @@ module ModelInput {
|
||||
/**
|
||||
* Gets an API node that is a source or sink of the given `type`.
|
||||
*
|
||||
* Note that `type` should also be included in `isTypeUsed`.
|
||||
*
|
||||
* Unlike `getASource` and `getASink`, this may depend on API graphs.
|
||||
*/
|
||||
API::Node getAnApiNode(string type) { none() }
|
||||
@@ -354,6 +369,28 @@ private predicate typeVariableModel(string name, string path) {
|
||||
Extensions::typeVariableModel(name, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given extension tuple `madId` should pretty-print as `model`.
|
||||
*
|
||||
* This predicate should only be used in tests.
|
||||
*/
|
||||
predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
exists(string type, string path, string kind |
|
||||
Extensions::sourceModel(type, path, kind, madId) and
|
||||
model = "Source: " + type + "; " + path + "; " + kind
|
||||
)
|
||||
or
|
||||
exists(string type, string path, string kind |
|
||||
Extensions::sinkModel(type, path, kind, madId) and
|
||||
model = "Sink: " + type + "; " + path + "; " + kind
|
||||
)
|
||||
or
|
||||
exists(string type, string path, string input, string output, string kind |
|
||||
Extensions::summaryModel(type, path, input, output, kind, madId) and
|
||||
model = "Summary: " + type + "; " + path + "; " + input + "; " + output + "; " + kind
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if rows involving `type` might be relevant for the analysis of this database.
|
||||
*/
|
||||
@@ -367,6 +404,8 @@ predicate isRelevantType(string type) {
|
||||
(
|
||||
Specific::isTypeUsed(type)
|
||||
or
|
||||
any(TypeModel model).isTypeUsed(type)
|
||||
or
|
||||
exists(TestAllModels t)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
|
||||
## 0.8.16
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
5
javascript/ql/src/change-notes/released/1.0.0.md
Normal file
5
javascript/ql/src/change-notes/released/1.0.0.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 1.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.8.16
|
||||
lastReleaseVersion: 1.0.0
|
||||
|
||||
211
javascript/ql/src/experimental/semmle/javascript/Execa.qll
Normal file
211
javascript/ql/src/experimental/semmle/javascript/Execa.qll
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Models the `execa` library in terms of `FileSystemAccess` and `SystemCommandExecution`.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Provide model for [Execa](https://github.com/sindresorhus/execa) package
|
||||
*/
|
||||
module Execa {
|
||||
/**
|
||||
* The Execa input file read and output file write
|
||||
*/
|
||||
class ExecaFileSystemAccess extends FileSystemReadAccess, DataFlow::Node {
|
||||
API::Node execaArg;
|
||||
boolean isPipedToFile;
|
||||
|
||||
ExecaFileSystemAccess() {
|
||||
(
|
||||
execaArg = API::moduleImport("execa").getMember("$").getParameter(0) and
|
||||
isPipedToFile = false
|
||||
or
|
||||
execaArg =
|
||||
API::moduleImport("execa")
|
||||
.getMember(["execa", "execaCommand", "execaCommandSync", "execaSync"])
|
||||
.getParameter([0, 1, 2]) and
|
||||
isPipedToFile = false
|
||||
or
|
||||
execaArg =
|
||||
API::moduleImport("execa")
|
||||
.getMember(["execa", "execaCommand", "execaCommandSync", "execaSync"])
|
||||
.getReturn()
|
||||
.getMember(["pipeStdout", "pipeAll", "pipeStderr"])
|
||||
.getParameter(0) and
|
||||
isPipedToFile = true
|
||||
) and
|
||||
this = execaArg.asSink()
|
||||
}
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = execaArg.getMember("inputFile").asSink() and isPipedToFile = false
|
||||
or
|
||||
result = execaArg.asSink() and isPipedToFile = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execa.execa` or `execa.execaSync`
|
||||
*/
|
||||
class ExecaCall extends API::CallNode {
|
||||
boolean isSync;
|
||||
|
||||
ExecaCall() {
|
||||
this = API::moduleImport("execa").getMember("execa").getACall() and
|
||||
isSync = false
|
||||
or
|
||||
this = API::moduleImport("execa").getMember("execaSync").getACall() and
|
||||
isSync = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The system command execution nodes for `execa.execa` or `execa.execaSync` functions
|
||||
*/
|
||||
class ExecaExec extends SystemCommandExecution, ExecaCall {
|
||||
ExecaExec() { isSync = [false, true] }
|
||||
|
||||
override DataFlow::Node getACommandArgument() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
// if shell: true then first and second args are sinks
|
||||
// options can be third argument
|
||||
arg = [this.getArgument(0), this.getParameter(1).getUnknownMember().asSink()] and
|
||||
isExecaShellEnable(this.getParameter(2))
|
||||
or
|
||||
// options can be second argument
|
||||
arg = this.getArgument(0) and
|
||||
isExecaShellEnable(this.getParameter(1))
|
||||
}
|
||||
|
||||
override DataFlow::Node getArgumentList() {
|
||||
// execa(cmd, [arg]);
|
||||
exists(DataFlow::Node arg | arg = this.getArgument(1) |
|
||||
// if it is a object then it is a option argument not command argument
|
||||
result = arg and not arg.asExpr() instanceof ObjectExpr
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSync() { isSync = true }
|
||||
|
||||
override DataFlow::Node getOptionsArg() {
|
||||
result = this.getLastArgument() and result.asExpr() instanceof ObjectExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execa.$` or `execa.$.sync` or `execa.$({})` or `execa.$.sync({})` tag functions
|
||||
*/
|
||||
private class ExecaScriptCall extends API::CallNode {
|
||||
boolean isSync;
|
||||
|
||||
ExecaScriptCall() {
|
||||
exists(API::Node script |
|
||||
script =
|
||||
[
|
||||
API::moduleImport("execa").getMember("$"),
|
||||
API::moduleImport("execa").getMember("$").getReturn()
|
||||
]
|
||||
|
|
||||
this = script.getACall() and
|
||||
isSync = false
|
||||
or
|
||||
this = script.getMember("sync").getACall() and
|
||||
isSync = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The system command execution nodes for `execa.$` or `execa.$.sync` tag functions
|
||||
*/
|
||||
class ExecaScript extends SystemCommandExecution, ExecaScriptCall {
|
||||
ExecaScript() { isSync = [false, true] }
|
||||
|
||||
override DataFlow::Node getACommandArgument() {
|
||||
result = this.getParameter(1).asSink() and
|
||||
not isTaggedTemplateFirstChildAnElement(this.getParameter(1).asSink().asExpr().getParent())
|
||||
}
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
isExecaShellEnable(this.getParameter(0)) and
|
||||
arg = this.getAParameter().asSink()
|
||||
}
|
||||
|
||||
override DataFlow::Node getArgumentList() {
|
||||
result = this.getParameter(any(int i | i >= 1)).asSink() and
|
||||
isTaggedTemplateFirstChildAnElement(this.getParameter(1).asSink().asExpr().getParent())
|
||||
or
|
||||
result = this.getParameter(any(int i | i >= 2)).asSink() and
|
||||
not isTaggedTemplateFirstChildAnElement(this.getParameter(1).asSink().asExpr().getParent())
|
||||
}
|
||||
|
||||
override DataFlow::Node getOptionsArg() { result = this.getParameter(0).asSink() }
|
||||
|
||||
override predicate isSync() { isSync = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execa.execaCommandSync` or `execa.execaCommand`
|
||||
*/
|
||||
private class ExecaCommandCall extends API::CallNode {
|
||||
boolean isSync;
|
||||
|
||||
ExecaCommandCall() {
|
||||
this = API::moduleImport("execa").getMember("execaCommandSync").getACall() and
|
||||
isSync = true
|
||||
or
|
||||
this = API::moduleImport("execa").getMember("execaCommand").getACall() and
|
||||
isSync = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The system command execution nodes for `execa.execaCommand` or `execa.execaCommandSync` functions
|
||||
*/
|
||||
class ExecaCommandExec extends SystemCommandExecution, ExecaCommandCall {
|
||||
ExecaCommandExec() { isSync = [false, true] }
|
||||
|
||||
override DataFlow::Node getACommandArgument() {
|
||||
result = this.(DataFlow::CallNode).getArgument(0)
|
||||
}
|
||||
|
||||
override DataFlow::Node getArgumentList() {
|
||||
// execaCommand(`${cmd} ${arg}`);
|
||||
result.asExpr() = this.getParameter(0).asSink().asExpr().getAChildExpr() and
|
||||
not result.asExpr() = this.getArgument(0).asExpr().getChildExpr(0)
|
||||
}
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
// execaCommandSync(`${cmd} ${arg}`, {shell: true})
|
||||
arg.asExpr() = this.getArgument(0).asExpr().getAChildExpr+() and
|
||||
isExecaShellEnable(this.getParameter(1))
|
||||
or
|
||||
// there is only one argument that is constructed in previous nodes,
|
||||
// it makes sanitizing really hard to select whether it is vulnerable to argument injection or not
|
||||
arg = this.getParameter(0).asSink() and
|
||||
not exists(this.getArgument(0).asExpr().getChildExpr(1))
|
||||
}
|
||||
|
||||
override predicate isSync() { isSync = true }
|
||||
|
||||
override DataFlow::Node getOptionsArg() {
|
||||
result = this.getLastArgument() and result.asExpr() instanceof ObjectExpr
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a TemplateLiteral and check if first child is a template element */
|
||||
private predicate isTaggedTemplateFirstChildAnElement(TemplateLiteral templateLit) {
|
||||
exists(templateLit.getChildExpr(0).(TemplateElement))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds whether Execa has shell enabled options or not, get Parameter responsible for options
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate isExecaShellEnable(API::Node n) {
|
||||
n.getMember("shell").asSink().asExpr().(BooleanLiteral).getValue() = "true"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-queries
|
||||
version: 0.8.17-dev
|
||||
version: 1.0.1-dev
|
||||
groups:
|
||||
- javascript
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
passingPositiveTests
|
||||
| PASSED | CommandInjection | tests.js:11:46:11:70 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:12:43:12:67 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:13:63:13:87 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:14:62:14:86 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:15:60:15:84 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:17:45:17:69 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:18:42:18:66 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:19:62:19:86 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:20:63:20:87 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:21:60:21:84 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:23:43:23:67 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:24:40:24:64 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:25:40:25:64 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:26:60:26:84 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:28:41:28:65 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:29:58:29:82 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:31:51:31:75 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:32:68:32:92 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:34:49:34:73 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:35:66:35:90 | // test ... jection |
|
||||
failingPositiveTests
|
||||
@@ -0,0 +1,36 @@
|
||||
import { execa, execaSync, execaCommand, execaCommandSync, $ } from 'execa';
|
||||
import http from 'node:http'
|
||||
import url from 'url'
|
||||
|
||||
http.createServer(async function (req, res) {
|
||||
let cmd = url.parse(req.url, true).query["cmd"][0];
|
||||
let arg1 = url.parse(req.url, true).query["arg1"];
|
||||
let arg2 = url.parse(req.url, true).query["arg2"];
|
||||
let arg3 = url.parse(req.url, true).query["arg3"];
|
||||
|
||||
await $`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
await $`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$({ shell: false }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$({ shell: true }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$({ shell: false }).sync`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
|
||||
$.sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$.sync`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
await $({ shell: true })`${cmd} ${arg1} ${arg2} ${arg3}` // test: CommandInjection
|
||||
await $({ shell: false })`${cmd} ${arg1} ${arg2} ${arg3}` // test: CommandInjection
|
||||
await $({ shell: false })`ssh ${arg1} ${arg2} ${arg3}` // test: CommandInjection
|
||||
|
||||
await execa(cmd, [arg1, arg2, arg3]); // test: CommandInjection
|
||||
await execa(cmd, { shell: true }); // test: CommandInjection
|
||||
await execa(cmd, { shell: true }); // test: CommandInjection
|
||||
await execa(cmd, [arg1, arg2, arg3], { shell: true }); // test: CommandInjection
|
||||
|
||||
execaSync(cmd, [arg1, arg2, arg3]); // test: CommandInjection
|
||||
execaSync(cmd, [arg1, arg2, arg3], { shell: true }); // test: CommandInjection
|
||||
|
||||
await execaCommand(cmd + arg1 + arg2 + arg3); // test: CommandInjection
|
||||
await execaCommand(cmd + arg1 + arg2 + arg3, { shell: true }); // test: CommandInjection
|
||||
|
||||
execaCommandSync(cmd + arg1 + arg2 + arg3); // test: CommandInjection
|
||||
execaCommandSync(cmd + arg1 + arg2 + arg3, { shell: true }); // test: CommandInjection
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import javascript
|
||||
|
||||
class InlineTest extends LineComment {
|
||||
string tests;
|
||||
|
||||
InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) }
|
||||
|
||||
string getPositiveTest() {
|
||||
result = tests.trim().splitAt(",").trim() and not result.matches("!%")
|
||||
}
|
||||
|
||||
predicate hasPositiveTest(string test) { test = this.getPositiveTest() }
|
||||
|
||||
predicate inNode(DataFlow::Node n) {
|
||||
this.getLocation().getFile() = n.getFile() and
|
||||
this.getLocation().getStartLine() = n.getStartLine()
|
||||
}
|
||||
}
|
||||
|
||||
import experimental.semmle.javascript.Execa
|
||||
|
||||
query predicate passingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "PASSED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "CommandInjection" and
|
||||
exists(SystemCommandExecution n |
|
||||
t.inNode(n.getArgumentList()) or t.inNode(n.getACommandArgument())
|
||||
)
|
||||
}
|
||||
|
||||
query predicate failingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "FAILED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "CommandInjection" and
|
||||
not exists(SystemCommandExecution n |
|
||||
t.inNode(n.getArgumentList()) or t.inNode(n.getACommandArgument())
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
passingPositiveTests
|
||||
| PASSED | PathInjection | tests.js:9:43:9:64 | // test ... jection |
|
||||
| PASSED | PathInjection | tests.js:12:50:12:71 | // test ... jection |
|
||||
| PASSED | PathInjection | tests.js:15:61:15:82 | // test ... jection |
|
||||
| PASSED | PathInjection | tests.js:18:73:18:94 | // test ... jection |
|
||||
failingPositiveTests
|
||||
19
javascript/ql/test/experimental/Execa/PathInjection/tests.js
Normal file
19
javascript/ql/test/experimental/Execa/PathInjection/tests.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { execa, $ } from 'execa';
|
||||
import http from 'node:http'
|
||||
import url from 'url'
|
||||
|
||||
http.createServer(async function (req, res) {
|
||||
let filePath = url.parse(req.url, true).query["filePath"][0];
|
||||
|
||||
// Piping to stdin from a file
|
||||
await $({ inputFile: filePath })`cat` // test: PathInjection
|
||||
|
||||
// Piping to stdin from a file
|
||||
await execa('cat', { inputFile: filePath }); // test: PathInjection
|
||||
|
||||
// Piping Stdout to file
|
||||
await execa('echo', ['example3']).pipeStdout(filePath); // test: PathInjection
|
||||
|
||||
// Piping all of command output to file
|
||||
await execa('echo', ['example4'], { all: true }).pipeAll(filePath); // test: PathInjection
|
||||
});
|
||||
34
javascript/ql/test/experimental/Execa/PathInjection/tests.ql
Normal file
34
javascript/ql/test/experimental/Execa/PathInjection/tests.ql
Normal file
@@ -0,0 +1,34 @@
|
||||
import javascript
|
||||
|
||||
class InlineTest extends LineComment {
|
||||
string tests;
|
||||
|
||||
InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) }
|
||||
|
||||
string getPositiveTest() {
|
||||
result = tests.trim().splitAt(",").trim() and not result.matches("!%")
|
||||
}
|
||||
|
||||
predicate hasPositiveTest(string test) { test = this.getPositiveTest() }
|
||||
|
||||
predicate inNode(DataFlow::Node n) {
|
||||
this.getLocation().getFile() = n.getFile() and
|
||||
this.getLocation().getStartLine() = n.getStartLine()
|
||||
}
|
||||
}
|
||||
|
||||
import experimental.semmle.javascript.Execa
|
||||
|
||||
query predicate passingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "PASSED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "PathInjection" and
|
||||
exists(FileSystemReadAccess n | t.inNode(n.getAPathArgument()))
|
||||
}
|
||||
|
||||
query predicate failingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "FAILED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "PathInjection" and
|
||||
not exists(FileSystemReadAccess n | t.inNode(n.getAPathArgument()))
|
||||
}
|
||||
@@ -79,6 +79,7 @@ taintFlow
|
||||
| test.js:269:10:269:31 | this.ba ... ource() | test.js:269:10:269:31 | this.ba ... ource() |
|
||||
| test.js:272:6:272:40 | new MyS ... ource() | test.js:272:6:272:40 | new MyS ... ource() |
|
||||
| test.js:274:6:274:39 | testlib ... eName() | test.js:274:6:274:39 | testlib ... eName() |
|
||||
| test.js:277:8:277:31 | "danger ... .danger | test.js:277:8:277:31 | "danger ... .danger |
|
||||
isSink
|
||||
| test.js:54:18:54:25 | source() | test-sink |
|
||||
| test.js:55:22:55:29 | source() | test-sink |
|
||||
|
||||
@@ -11,6 +11,7 @@ extensions:
|
||||
- ['testlib', 'Member[ParamDecoratorSource].DecoratedParameter', 'test-source']
|
||||
- ['testlib', 'Member[getSource].ReturnValue', 'test-source']
|
||||
- ['(testlib)', 'Member[parenthesizedPackageName].ReturnValue', 'test-source']
|
||||
- ['danger-constant', 'Member[danger]', 'test-source']
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/javascript-all
|
||||
|
||||
@@ -272,3 +272,9 @@ class MySubclass2 extends MySubclass {
|
||||
sink(new MySubclass2().baseclassSource()); // NOT OK
|
||||
|
||||
sink(testlib.parenthesizedPackageName()); // NOT OK
|
||||
|
||||
function dangerConstant() {
|
||||
sink("danger-constant".danger); // NOT OK
|
||||
sink("danger-constant".safe); // OK
|
||||
sink("danger-constant"); // OK
|
||||
}
|
||||
|
||||
@@ -2,6 +2,15 @@ import javascript
|
||||
import testUtilities.ConsistencyChecking
|
||||
import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||
|
||||
class TypeModelFromCodeQL extends ModelInput::TypeModel {
|
||||
override predicate isTypeUsed(string type) { type = "danger-constant" }
|
||||
|
||||
override DataFlow::Node getASource(string type) {
|
||||
type = "danger-constant" and
|
||||
result.getStringValue() = "danger-constant"
|
||||
}
|
||||
}
|
||||
|
||||
class BasicTaintTracking extends TaintTracking::Configuration {
|
||||
BasicTaintTracking() { this = "BasicTaintTracking" }
|
||||
|
||||
|
||||
15
javascript/resources/BUILD.bazel
Normal file
15
javascript/resources/BUILD.bazel
Normal file
@@ -0,0 +1,15 @@
|
||||
load("//misc/bazel:pkg.bzl", "codeql_pkg_files")
|
||||
|
||||
codeql_pkg_files(
|
||||
name = "resources",
|
||||
srcs = glob(
|
||||
["**/*"],
|
||||
exclude = [
|
||||
"tools/*.sh",
|
||||
"BUILD.bazel",
|
||||
],
|
||||
),
|
||||
exes = glob(["tools/*.sh"]),
|
||||
strip_prefix = "",
|
||||
visibility = ["//javascript:__pkg__"],
|
||||
)
|
||||
94
javascript/resources/codeql-extractor.yml
Normal file
94
javascript/resources/codeql-extractor.yml
Normal file
@@ -0,0 +1,94 @@
|
||||
name: "javascript"
|
||||
aliases:
|
||||
- javascript-typescript
|
||||
- typescript
|
||||
display_name: "JavaScript/TypeScript"
|
||||
version: 1.22.1
|
||||
column_kind: "utf16"
|
||||
unicode_newlines: true
|
||||
build_modes:
|
||||
- none
|
||||
file_coverage_languages:
|
||||
- name: javascript
|
||||
display_name: JavaScript
|
||||
scc_languages:
|
||||
- JavaScript
|
||||
- name: typescript
|
||||
display_name: TypeScript
|
||||
scc_languages:
|
||||
- TypeScript
|
||||
- TypeScript Typings
|
||||
github_api_languages:
|
||||
- JavaScript
|
||||
- TypeScript
|
||||
scc_languages:
|
||||
- JavaScript
|
||||
- TypeScript
|
||||
- TypeScript Typings
|
||||
file_types:
|
||||
- name: javascript
|
||||
display_name: JavaScript
|
||||
extensions:
|
||||
- .js
|
||||
- .jsx
|
||||
- name: ecmascript
|
||||
display_name: ECMAScript
|
||||
extensions:
|
||||
- .es
|
||||
- .es6
|
||||
- .mjs
|
||||
- name: typescript
|
||||
display_name: TypeScript
|
||||
extensions:
|
||||
- .ts
|
||||
- .tsx
|
||||
- name: html
|
||||
display_name: HTML
|
||||
extensions:
|
||||
- .html
|
||||
- .htm
|
||||
- .xhtm
|
||||
- .xhtml
|
||||
- name: vue
|
||||
display_name: Vue.js component
|
||||
extensions:
|
||||
- .vue
|
||||
- name: data
|
||||
display_name: Data or configuration files
|
||||
extensions:
|
||||
- .json
|
||||
- .yml
|
||||
- .yaml
|
||||
- .raml
|
||||
legacy_qltest_extraction: true
|
||||
options:
|
||||
trap:
|
||||
title: TRAP options
|
||||
description: Options about how the extractor handles TRAP files
|
||||
type: object
|
||||
visibility: 3
|
||||
properties:
|
||||
cache:
|
||||
title: TRAP cache options
|
||||
description: Options about how the extractor handles its TRAP cache
|
||||
type: object
|
||||
properties:
|
||||
dir:
|
||||
title: TRAP cache directory
|
||||
description: The directory of the TRAP cache to use
|
||||
type: string
|
||||
bound:
|
||||
title: TRAP cache bound
|
||||
description: A soft limit (in MB) on the size of the TRAP cache
|
||||
type: string
|
||||
pattern: "[0-9]+"
|
||||
write:
|
||||
title: TRAP cache writeable
|
||||
description: Whether to write to the TRAP cache as well as reading it
|
||||
type: string
|
||||
pattern: "(true|TRUE|false|FALSE)"
|
||||
skip_types:
|
||||
title: Skip type extraction for TypeScript
|
||||
description: Whether to skip the extraction of types in a TypeScript application
|
||||
type: string
|
||||
pattern: "^(false|true)$"
|
||||
30
javascript/resources/tools/autobuild.cmd
Normal file
30
javascript/resources/tools/autobuild.cmd
Normal file
@@ -0,0 +1,30 @@
|
||||
@echo off
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
|
||||
set jvm_args=-Xss16m
|
||||
|
||||
rem If CODEQL_RAM is set, use half for Java and half for TS.
|
||||
if NOT [%CODEQL_RAM%] == [] (
|
||||
set /a "half_ram=CODEQL_RAM/2"
|
||||
set LGTM_TYPESCRIPT_RAM=%half_ram%
|
||||
set jvm_args=!jvm_args! -Xmx!half_ram!m
|
||||
)
|
||||
|
||||
rem If CODEQL_THREADS is set, propagate via LGTM_THREADS.
|
||||
if NOT [%CODEQL_THREADS%] == [] (
|
||||
set LGTM_THREADS=%CODEQL_THREADS%
|
||||
)
|
||||
|
||||
rem The JS autobuilder expects to find typescript modules under SEMMLE_DIST/tools.
|
||||
rem They are included in the pack, but we need to set SEMMLE_DIST appropriately.
|
||||
set SEMMLE_DIST=%CODEQL_EXTRACTOR_JAVASCRIPT_ROOT%
|
||||
|
||||
rem The JS autobuilder expects LGTM_SRC to be set to the source root.
|
||||
set LGTM_SRC=%CD%
|
||||
|
||||
type NUL && "%CODEQL_JAVA_HOME%\bin\java.exe" %jvm_args% ^
|
||||
-cp "%CODEQL_EXTRACTOR_JAVASCRIPT_ROOT%\tools\extractor-javascript.jar" ^
|
||||
com.semmle.js.extractor.AutoBuild
|
||||
exit /b %ERRORLEVEL%
|
||||
|
||||
ENDLOCAL
|
||||
29
javascript/resources/tools/autobuild.sh
Executable file
29
javascript/resources/tools/autobuild.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
jvm_args=-Xss16m
|
||||
|
||||
# If CODEQL_RAM is set, use half for Java and half for TS.
|
||||
if [ -n "${CODEQL_RAM:-}" ] ; then
|
||||
half_ram="$(( CODEQL_RAM / 2 ))"
|
||||
LGTM_TYPESCRIPT_RAM="$half_ram"
|
||||
export LGTM_TYPESCRIPT_RAM
|
||||
jvm_args="$jvm_args -Xmx${half_ram}m"
|
||||
fi
|
||||
|
||||
# If CODEQL_THREADS is set, propagate via LGTM_THREADS.
|
||||
if [ -n "${CODEQL_THREADS:-}" ] ; then
|
||||
LGTM_THREADS="$CODEQL_THREADS"
|
||||
export LGTM_THREADS
|
||||
fi
|
||||
|
||||
# The JS autobuilder expects to find typescript modules under SEMMLE_DIST/tools.
|
||||
# They are included in the pack, but we need to set SEMMLE_DIST appropriately.
|
||||
# We want to word-split $jvm_args, so disable the shellcheck warning.
|
||||
# shellcheck disable=SC2086
|
||||
env SEMMLE_DIST="$CODEQL_EXTRACTOR_JAVASCRIPT_ROOT" \
|
||||
LGTM_SRC="$(pwd)" \
|
||||
"${CODEQL_JAVA_HOME}/bin/java" $jvm_args \
|
||||
-cp "$CODEQL_EXTRACTOR_JAVASCRIPT_ROOT/tools/extractor-javascript.jar" \
|
||||
com.semmle.js.extractor.AutoBuild
|
||||
8
javascript/resources/tools/baseline-config.json
Normal file
8
javascript/resources/tools/baseline-config.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"paths-ignore": [
|
||||
"**/node_modules/**",
|
||||
"**/bower_components/**",
|
||||
"**/*.min.js",
|
||||
"**/*-min.js"
|
||||
]
|
||||
}
|
||||
2
javascript/resources/tools/configure-baseline.cmd
Normal file
2
javascript/resources/tools/configure-baseline.cmd
Normal file
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
type "%CODEQL_EXTRACTOR_JAVASCRIPT_ROOT%\tools\baseline-config.json"
|
||||
3
javascript/resources/tools/configure-baseline.sh
Executable file
3
javascript/resources/tools/configure-baseline.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
cat "$CODEQL_EXTRACTOR_JAVASCRIPT_ROOT/tools/baseline-config.json"
|
||||
4
javascript/resources/tools/index-dir.sh
Executable file
4
javascript/resources/tools/index-dir.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Not implemented." 1>&2
|
||||
exit 1
|
||||
Reference in New Issue
Block a user