Merge branch 'main' into exportObj

This commit is contained in:
Erik Krogh Kristensen
2022-05-23 14:18:31 +02:00
738 changed files with 39983 additions and 11121 deletions

View File

@@ -1,3 +1,18 @@
## 0.1.2
### Deprecated APIs
* The `ReflectedXss`, `StoredXss`, `XssThroughDom`, and `ExceptionXss` modules from `Xss.qll` have been deprecated.
Use the `Customizations.qll` file belonging to the query instead.
### Minor Analysis Improvements
* The [cash](https://github.com/fabiospampinato/cash) library is now modelled as an alias for JQuery.
Sinks and sources from cash should now be handled by all XSS queries.
* Added the `Selection` api as a DOM text source in the `js/xss-through-dom` query.
* The security queries now recognize drag and drop data as a source, enabling the queries to flag additional alerts.
* The security queries now recognize ClipboardEvent function parameters as a source, enabling the queries to flag additional alerts.
## 0.1.1
## 0.1.0

View File

@@ -1,5 +0,0 @@
---
category: minorAnalysis
---
* The security queries now recognize drag and drop data as a source, enabling the queries to flag additional alerts.
* The security queries now recognize ClipboardEvent function parameters as a source, enabling the queries to flag additional alerts.

View File

@@ -1,5 +0,0 @@
---
category: deprecated
---
* The `ReflectedXss`, `StoredXss`, `XssThroughDom`, and `ExceptionXss` modules from `Xss.qll` have been deprecated.
Use the `Customizations.qll` file belonging to the query instead.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added the `Selection` api as a DOM text source in the `js/xss-through-dom` query.

View File

@@ -1,5 +0,0 @@
---
category: minorAnalysis
---
* The [cash](https://github.com/fabiospampinato/cash) library is now modelled as an alias for JQuery.
Sinks and sources from cash should now be handled by all XSS queries.

View File

@@ -0,0 +1,14 @@
## 0.1.2
### Deprecated APIs
* The `ReflectedXss`, `StoredXss`, `XssThroughDom`, and `ExceptionXss` modules from `Xss.qll` have been deprecated.
Use the `Customizations.qll` file belonging to the query instead.
### Minor Analysis Improvements
* The [cash](https://github.com/fabiospampinato/cash) library is now modelled as an alias for JQuery.
Sinks and sources from cash should now be handled by all XSS queries.
* Added the `Selection` api as a DOM text source in the `js/xss-through-dom` query.
* The security queries now recognize drag and drop data as a source, enabling the queries to flag additional alerts.
* The security queries now recognize ClipboardEvent function parameters as a source, enabling the queries to flag additional alerts.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.1.1
lastReleaseVersion: 0.1.2

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-all
version: 0.1.2-dev
version: 0.1.3-dev
groups: javascript
dbscheme: semmlecode.javascript.dbscheme
extractor: javascript

View File

@@ -0,0 +1,252 @@
/**
* Libraries for modeling GitHub Actions workflow files written in YAML.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
*/
import javascript
/**
* Libraries for modeling GitHub Actions workflow files written in YAML.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
*/
module Actions {
/** A YAML node in a GitHub Actions workflow file. */
private class Node extends YAMLNode {
Node() {
this.getLocation()
.getFile()
.getRelativePath()
.regexpMatch("(^|.*/)\\.github/workflows/.*\\.yml$")
}
}
/**
* An Actions workflow. This is a mapping at the top level of an Actions YAML workflow file.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions.
*/
class Workflow extends Node, YAMLDocument, YAMLMapping {
/** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */
YAMLMapping getJobs() { result = this.lookup("jobs") }
/** Gets the name of the workflow file. */
string getFileName() { result = this.getFile().getBaseName() }
/** Gets the `on:` in this workflow. */
On getOn() { result = this.lookup("on") }
/** Gets the job within this workflow with the given job ID. */
Job getJob(string jobId) { result.getWorkflow() = this and result.getId() = jobId }
}
/**
* An Actions On trigger within a workflow.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#on.
*/
class On extends YAMLNode, YAMLMappingLikeNode {
Workflow workflow;
On() { workflow.lookup("on") = this }
/** Gets the workflow that this trigger is in. */
Workflow getWorkflow() { result = workflow }
}
/**
* An Actions job within a workflow.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobs.
*/
class Job extends YAMLNode, YAMLMapping {
string jobId;
Workflow workflow;
Job() { this = workflow.getJobs().lookup(jobId) }
/**
* Gets the ID of this job, as a string.
* This is the job's key within the `jobs` mapping.
*/
string getId() { result = jobId }
/**
* Gets the ID of this job, as a YAML scalar node.
* This is the job's key within the `jobs` mapping.
*/
YAMLString getIdNode() { workflow.getJobs().maps(result, this) }
/** Gets the human-readable name of this job, if any, as a string. */
string getName() { result = this.getNameNode().getValue() }
/** Gets the human-readable name of this job, if any, as a YAML scalar node. */
YAMLString getNameNode() { result = this.lookup("name") }
/** Gets the step at the given index within this job. */
Step getStep(int index) { result.getJob() = this and result.getIndex() = index }
/** Gets the sequence of `steps` within this job. */
YAMLSequence getSteps() { result = this.lookup("steps") }
/** Gets the workflow this job belongs to. */
Workflow getWorkflow() { result = workflow }
/** Gets the value of the `if` field in this job, if any. */
JobIf getIf() { result.getJob() = this }
}
/**
* An `if` within a job.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif.
*/
class JobIf extends YAMLNode, YAMLScalar {
Job job;
JobIf() { job.lookup("if") = this }
/** Gets the step this field belongs to. */
Job getJob() { result = job }
}
/**
* A step within an Actions job.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idsteps.
*/
class Step extends YAMLNode, YAMLMapping {
int index;
Job job;
Step() { this = job.getSteps().getElement(index) }
/** Gets the 0-based position of this step within the sequence of `steps`. */
int getIndex() { result = index }
/** Gets the job this step belongs to. */
Job getJob() { result = job }
/** Gets the value of the `uses` field in this step, if any. */
Uses getUses() { result.getStep() = this }
/** Gets the value of the `run` field in this step, if any. */
Run getRun() { result.getStep() = this }
/** Gets the value of the `if` field in this step, if any. */
StepIf getIf() { result.getStep() = this }
}
/**
* An `if` within a step.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsif.
*/
class StepIf extends YAMLNode, YAMLScalar {
Step step;
StepIf() { step.lookup("if") = this }
/** Gets the step this field belongs to. */
Step getStep() { result = step }
}
/**
* Gets a regular expression that parses an `owner/repo@version` reference within a `uses` field in an Actions job step.
* The capture groups are:
* 1: The owner of the repository where the Action comes from, e.g. `actions` in `actions/checkout@v2`
* 2: The name of the repository where the Action comes from, e.g. `checkout` in `actions/checkout@v2`.
* 3: The version reference used when checking out the Action, e.g. `v2` in `actions/checkout@v2`.
*/
private string usesParser() { result = "([^/]+)/([^/@]+)@(.+)" }
/**
* A `uses` field within an Actions job step, which references an action as a reusable unit of code.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsuses.
*
* For example:
* ```
* uses: actions/checkout@v2
* ```
*
* Does not handle local repository references, e.g. `.github/actions/action-name`.
*/
class Uses extends YAMLNode, YAMLScalar {
Step step;
Uses() { step.lookup("uses") = this }
/** Gets the step this field belongs to. */
Step getStep() { result = step }
/** Gets the owner and name of the repository where the Action comes from, e.g. `actions/checkout` in `actions/checkout@v2`. */
string getGitHubRepository() {
result =
this.getValue().regexpCapture(usesParser(), 1) + "/" +
this.getValue().regexpCapture(usesParser(), 2)
}
/** Gets the version reference used when checking out the Action, e.g. `v2` in `actions/checkout@v2`. */
string getVersion() { result = this.getValue().regexpCapture(usesParser(), 3) }
}
/**
* A `with` field within an Actions job step, which references an action as a reusable unit of code.
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepswith.
*
* For example:
* ```
* with:
* arg1: 1
* arg2: abc
* ```
*/
class With extends YAMLNode, YAMLMapping {
Step step;
With() { step.lookup("with") = this }
/** Gets the step this field belongs to. */
Step getStep() { result = step }
}
/**
* A `ref:` field within an Actions `with:` specific to `actions/checkout` action.
*
* For example:
* ```
* uses: actions/checkout@v2
* with:
* ref: ${{ github.event.pull_request.head.sha }}
* ```
*/
class Ref extends YAMLNode, YAMLString {
With with;
Ref() { with.lookup("ref") = this }
/** Gets the `with` field this field belongs to. */
With getWith() { result = with }
}
/**
* A `run` field within an Actions job step, which runs command-line programs using an operating system shell.
* See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsrun.
*/
class Run extends YAMLNode, YAMLString {
Step step;
Run() { step.lookup("run") = this }
/** Gets the step that executes this `run` command. */
Step getStep() { result = step }
/**
* Holds if `${{ e }}` is a GitHub Actions expression evaluated within this `run` command.
* See https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions.
* Only finds simple expressions like `${{ github.event.comment.body }}`, where the expression contains only alphanumeric characters, underscores, dots, or dashes.
* Does not identify more complicated expressions like `${{ fromJSON(env.time) }}`, or ${{ format('{{Hello {0}!}}', github.event.head_commit.author.name) }}
*/
string getASimpleReferenceExpression() {
// We use `regexpFind` to obtain *all* matches of `${{...}}`,
// not just the last (greedy match) or first (reluctant match).
result =
this.getValue()
.regexpFind("\\$\\{\\{\\s*[A-Za-z0-9_\\.\\-]+\\s*\\}\\}", _, _)
.regexpCapture("\\$\\{\\{\\s*([A-Za-z0-9_\\.\\-]+)\\s*\\}\\}", 1)
}
}
}

View File

@@ -68,7 +68,7 @@ private predicate hasDefaultExport(ES2015Module mod) {
* Holds if `mod` contains both named and `default` exports.
*
* This is used to determine whether a default-import of the module should be reinterpreted
* as a namespace-import, to accomodate the non-standard behavior implemented by some compilers.
* as a namespace-import, to accommodate the non-standard behavior implemented by some compilers.
*/
private predicate hasBothNamedAndDefaultExports(ES2015Module mod) {
hasNamedExports(mod) and
@@ -615,7 +615,7 @@ class ReExportDefaultSpecifier extends ExportDefaultSpecifier {
}
/**
* A namespace export specifier, that is `*` or `* as x` occuring in an export declaration.
* A namespace export specifier, that is `*` or `* as x` occurring in an export declaration.
*
* Examples:
*

View File

@@ -2286,9 +2286,7 @@ class ComprehensionExpr extends @comprehension_expr, Expr {
/** Holds if this is a legacy postfix comprehension expression. */
predicate isPostfix() {
exists(Token tk | tk = this.getFirstToken().getNextToken() |
not tk.getValue().regexpMatch("if|for")
)
exists(Token tk | tk = this.getFirstToken().getNextToken() | not tk.getValue() = ["if", "for"])
}
override string getAPrimaryQlClass() { result = "ComprehensionExpr" }
@@ -2904,7 +2902,7 @@ class ImportMetaExpr extends @import_meta_expr, Expr {
* let data2 = {{{ user_data2 }}};
* ```
*
* Note that templating placeholders occuring inside strings literals are not parsed,
* Note that templating placeholders occurring inside strings literals are not parsed,
* and are simply seen as being part of the string literal.
* For example, following snippet does not contain any `GeneratedCodeExpr` nodes:
* ```js

View File

@@ -420,7 +420,7 @@ module AccessPath {
*/
module DominatingPaths {
/**
* A classification of acccess paths into reads and writes.
* A classification of access paths into reads and writes.
*/
private newtype AccessPathKind =
AccessPathRead() or

View File

@@ -96,7 +96,7 @@ File resolveMainModule(PackageJson pkg, int priority) {
or
result = tryExtensions(main.resolve(), "index", priority)
or
not exists(main.resolve()) and
not main.resolve() instanceof File and
exists(int n | n = main.getNumComponent() |
result = tryExtensions(main.resolveUpTo(n - 1), getStem(main.getComponent(n - 1)), priority)
)

View File

@@ -193,7 +193,7 @@ private module PrintJavaScript {
/**
* Gets the `i`th child of `element`.
* Can be overriden in subclasses to get more specific behavior for `getChild()`.
* Can be overridden in subclasses to get more specific behavior for `getChild()`.
*/
AstNode getChildNode(int childIndex) { result = getLocationSortedChild(element, childIndex) }
}

View File

@@ -1309,7 +1309,7 @@ module RegExp {
}
/**
* Holds if `term` can match any occurence of `char` within a string (not taking into account
* Holds if `term` can match any occurrence of `char` within a string (not taking into account
* the context in which `term` appears).
*
* This predicate is under-approximate and never considers sequences to guarantee a match.

View File

@@ -226,7 +226,7 @@ class ArgumentsVariable extends Variable {
*/
class VarRef extends @varref, Identifier, BindingPattern, LexicalRef {
/** Gets the variable this identifier refers to. */
override Variable getVariable() { none() } // Overriden in VarAccess and VarDecl
override Variable getVariable() { none() } // Overridden in VarAccess and VarDecl
override string getName() { result = Identifier.super.getName() }

View File

@@ -441,3 +441,74 @@ class YAMLParseError extends @yaml_error, Error {
override string toString() { result = this.getMessage() }
}
/**
* A YAML node that may contain sub-nodes that can be identified by a name.
* I.e. a mapping, sequence, or scalar.
*
* Is used in e.g. GithHub Actions, which is quite flexible in parsing YAML.
*
* For example:
* ```
* on: pull_request
* ```
* and
* ```
* on: [pull_request]
* ```
* and
* ```
* on:
* pull_request:
* ```
*
* are equivalent.
*/
class YAMLMappingLikeNode extends YAMLNode {
YAMLMappingLikeNode() {
this instanceof YAMLMapping
or
this instanceof YAMLSequence
or
this instanceof YAMLScalar
}
/** Gets sub-name identified by `name`. */
YAMLNode getNode(string name) {
exists(YAMLMapping mapping |
mapping = this and
result = mapping.lookup(name)
)
or
exists(YAMLSequence sequence, YAMLNode node |
sequence = this and
sequence.getAChildNode() = node and
node.eval().toString() = name and
result = node
)
or
exists(YAMLScalar scalar |
scalar = this and
scalar.getValue() = name and
result = scalar
)
}
/** Gets the number of elements in this mapping or sequence. */
int getElementCount() {
exists(YAMLMapping mapping |
mapping = this and
result = mapping.getNumChild() / 2
)
or
exists(YAMLSequence sequence |
sequence = this and
result = sequence.getNumChild()
)
or
exists(YAMLScalar scalar |
scalar = this and
result = 1
)
}
}

View File

@@ -330,8 +330,6 @@ module ClientRequest {
* A model of a URL request made using `require("needle")(...)`.
*/
class PromisedNeedleRequest extends ClientRequest::Range {
DataFlow::Node url;
PromisedNeedleRequest() { this = DataFlow::moduleImport("needle").getACall() }
override DataFlow::Node getUrl() { result = this.getArgument(1) }

View File

@@ -683,8 +683,6 @@ private module ExpressJwt {
*/
private module NodeRsa {
private class CreateKey extends CryptographicKeyCreation, API::InvokeNode {
CryptographicAlgorithm algorithm;
CreateKey() {
this = API::moduleImport("node-rsa").getAnInstantiation()
or

View File

@@ -413,7 +413,7 @@ module Fastify {
/**
* A call to `rep.view('file', { ... })`, seen as a template instantiation.
*
* Assumes the presense of a plugin that provides the `view` method, such as the `point-of-view` plugin.
* Assumes the presence of a plugin that provides the `view` method, such as the `point-of-view` plugin.
*/
private class ViewCall extends Templating::TemplateInstantiation::Range, DataFlow::CallNode {
ViewCall() { this = any(ReplySource rep).ref().getAMethodCall("view") }

View File

@@ -5,7 +5,7 @@
import javascript
/**
* Provides classes and predicates modeling the `jwt-decode` libary.
* Provides classes and predicates modeling the `jwt-decode` library.
*/
private module JwtDecode {
/**
@@ -23,7 +23,7 @@ private module JwtDecode {
}
/**
* Provides classes and predicates modeling the `jsonwebtoken` libary.
* Provides classes and predicates modeling the `jsonwebtoken` library.
*/
private module JsonWebToken {
/**

View File

@@ -74,7 +74,7 @@ module Koa {
* Gets a reference to a request parameter defined by this route handler.
*/
DataFlow::Node getARequestParameterAccess() {
none() // overriden in subclasses.
none() // overridden in subclasses.
}
/**

View File

@@ -4,7 +4,7 @@
import javascript
/** Provices classes for modelling NoSQL query sinks. */
/** Provides classes for modeling NoSQL query sinks. */
module NoSql {
/** An expression that is interpreted as a NoSQL query. */
abstract class Query extends Expr {

View File

@@ -951,7 +951,7 @@ module Redux {
)
}
/** Gets the block to execute when `case` matches sucessfully. */
/** Gets the block to execute when `case` matches successfully. */
private BasicBlock getCaseBlock(SwitchCase case) {
result = case.getBodyStmt(0).getBasicBlock()
or

View File

@@ -45,7 +45,7 @@ private DataFlow::Node pipeOutput(DataFlow::CallNode pipe) {
/**
* Holds if `pipe` acts as the identity function for success values.
*
* We currently lack a data-flow node to represent its input/ouput so it must
* We currently lack a data-flow node to represent its input/output so it must
* be special-cased.
*/
private predicate isIdentityPipe(DataFlow::CallNode pipe) {

View File

@@ -227,7 +227,7 @@ module Vuex {
result = getAMappedAccess(getMapHelperForCommitKind(kind), name).getParameter(0).getARhs()
}
/** Gets a node that refers the payload of a comitted mutation with the given `name.` */
/** Gets a node that refers the payload of a committed mutation with the given `name.` */
private DataFlow::Node committedPayloadSucc(string kind, string name) {
// mutations: {
// name: (state, payload) => { ... }

View File

@@ -167,9 +167,16 @@ class AccessPathToken extends string {
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
pragma[nomagic]
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument() { result = this.getArgument(_) }
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument(string name) { result = this.getArgument(name, _) }
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
}

View File

@@ -59,7 +59,7 @@
* A `(package,type)` pair may refer to a static type or a synthetic type name used internally in the model.
* Synthetic type names can be used to reuse intermediate sub-paths, when there are multiple ways to access the same
* element.
* See `ModelsAsData.qll` for the langauge-specific interpretation of packages and static type names.
* See `ModelsAsData.qll` for the language-specific interpretation of packages and static type names.
*
* By convention, if one wants to avoid clashes with static types from the package, the type name
* should be prefixed with a tilde character (`~`). For example, `(foo, ~Bar)` can be used to indicate that
@@ -396,7 +396,7 @@ predicate isValidTokenNameInIdentifyingAccessPath(string name) {
}
/**
* Holds if `name` is a valid name for an access path token with no arguments, occuring
* Holds if `name` is a valid name for an access path token with no arguments, occurring
* in an identifying access path.
*/
bindingset[name]

View File

@@ -233,7 +233,7 @@ predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) {
}
/**
* Holds if `name` is a valid name for an access path token with no arguments, occuring
* Holds if `name` is a valid name for an access path token with no arguments, occurring
* in an identifying access path.
*/
predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) {

View File

@@ -307,7 +307,7 @@ private module JQueryClientRequest {
}
/**
* Gets a node refering to the response contained in an `jqXHR` object.
* Gets a node referring to the response contained in an `jqXHR` object.
*/
private DataFlow::SourceNode getAResponseNodeFromAnXHRObject(DataFlow::SourceNode obj) {
result =

View File

@@ -19,7 +19,7 @@ private predicate xUnitDetected() {
private predicate possiblyAttribute(Expr e, string name) {
exists(Identifier id | id = e or id = e.(CallExpr).getCallee() |
name = id.getName() and
name.regexpMatch("Async|Data|Fact|Fixture|Import|ImportJson|Skip|Trait")
name = ["Async", "Data", "Fact", "Fixture", "Import", "ImportJson", "Skip", "Trait"]
)
}

View File

@@ -28,14 +28,14 @@ private module RegexpMatching {
* but if `ignorePrefix` is true, it will only match "foo".
*/
predicate test(string str, boolean ignorePrefix) {
none() // maybe overriden in subclasses
none() // maybe overridden in subclasses
}
/**
* Same as `test(..)`, but where the `fillsCaptureGroup` afterwards tells which capture groups were filled by the given string.
*/
predicate testWithGroups(string str, boolean ignorePrefix) {
none() // maybe overriden in subclasses
none() // maybe overridden in subclasses
}
/**

View File

@@ -52,7 +52,7 @@ module CodeInjection {
}
/**
* A template tag occuring in JS code, viewed as a code injection sink.
* A template tag occurring in JS code, viewed as a code injection sink.
*/
class TemplateTagInScriptSink extends Sink {
TemplateTagInScriptSink() {

View File

@@ -132,7 +132,7 @@ module IndirectCommandInjection {
}
/**
* An array of command line arguments (`argv`) parsed by the `yargs` libary.
* An array of command line arguments (`argv`) parsed by the `yargs` library.
*/
class YargsArgv extends Source {
YargsArgv() {

View File

@@ -0,0 +1,102 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
* insecure temporary file creation, as well as
* extension points for adding your own.
*/
import javascript
/**
* Classes and predicates for reasoning about insecure temporary file creation.
*/
module InsecureTemporaryFile {
/**
* A data flow source for insecure temporary file creation.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for insecure temporary file creation.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for random insecure temporary file creation.
*/
abstract class Sanitizer extends DataFlow::Node { }
/** A call that opens a file with a given path. */
class OpenFileCall extends DataFlow::CallNode {
string methodName;
OpenFileCall() {
methodName =
[
"open", "openSync", "writeFile", "writeFileSync", "writeJson", "writeJSON",
"writeJsonSync", "writeJSONSync", "outputJson", "outputJSON", "outputJsonSync",
"outputJSONSync", "outputFile", "outputFileSync"
] and
this = NodeJSLib::FS::moduleMember(methodName).getACall()
}
DataFlow::Node getPath() { result = this.getArgument(0) }
DataFlow::Node getMode() {
methodName = ["open", "openSync"] and
result = this.getArgument(2)
or
not methodName = ["open", "openSync"] and
result = this.getOptionArgument(2, "mode")
}
}
/** Holds if the `mode` ensure no access to other users. */
bindingset[mode]
private predicate isSecureMode(int mode) {
// the lowest 6 bits should be 0.
// E.g. `0o600` is secure (each digit in a octal number is 3 bits)
mode.bitAnd(1) = 0 and
mode.bitAnd(2) = 0 and
mode.bitAnd(4) = 0 and
mode.bitAnd(8) = 0 and
mode.bitAnd(16) = 0 and
mode.bitAnd(32) = 0
}
/** The path in a call that opens a file without specifying a secure `mode`. Seen as a sink for insecure temporary file creation. */
class InsecureFileOpen extends Sink {
InsecureFileOpen() {
exists(OpenFileCall call |
not exists(call.getMode())
or
exists(int mode | mode = call.getMode().getIntValue() | not isSecureMode(mode))
|
this = call.getPath()
)
}
}
/** A string that references the global tmp dir. Seen as a source for insecure temporary file creation. */
class OSTempDir extends Source {
OSTempDir() {
this = DataFlow::moduleImport("os").getAMemberCall("tmpdir")
or
this.getStringValue().matches("/tmp/%")
}
}
/** A non-first leaf in a string-concatenation. Seen as a sanitizer for insecure temporary file creation. */
class NonFirstStringConcatLeaf extends Sanitizer {
NonFirstStringConcatLeaf() {
exists(StringOps::ConcatenationRoot root |
this = root.getALeaf() and
not this = root.getFirstLeaf()
)
or
exists(DataFlow::CallNode join |
join = DataFlow::moduleMember("path", "join").getACall() and
this = join.getArgument([1 .. join.getNumArgument() - 1])
)
}
}
}

View File

@@ -0,0 +1,27 @@
/**
* Provides a taint tracking configuration for reasoning about insecure temporary
* file creation.
*
* Note, for performance reasons: only import this file if
* `InsecureTemporaryFile::Configuration` is needed, otherwise
* `InsecureTemporaryFileCustomizations` should be imported instead.
*/
import javascript
import InsecureTemporaryFileCustomizations::InsecureTemporaryFile
/**
* A taint-tracking configuration for reasoning about insecure temporary file creation.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "InsecureTemporaryFile" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer
}
}

View File

@@ -1,6 +1,6 @@
/**
* Provides a taint-tracking configuration for reasoning about code
* constructed from libary input vulnerabilities.
* constructed from library input vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `UnsafeCodeConstruction::Configuration` is needed, otherwise

View File

@@ -1,6 +1,6 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about code
* constructed from libary input vulnerabilities, as well as extension points for
* constructed from library input vulnerabilities, as well as extension points for
* adding your own.
*/

View File

@@ -45,7 +45,7 @@ module UnsafeHtmlConstruction {
/**
* Gets the kind of vulnerability to report in the alert message.
*
* Defaults to `Cross-site scripting`, but may be overriden for sinks
* Defaults to `Cross-site scripting`, but may be overridden for sinks
* that do not allow script injection, but injection of other undesirable HTML elements.
*/
abstract string getVulnerabilityKind();

View File

@@ -14,7 +14,7 @@ module Shared {
/**
* Gets the kind of vulnerability to report in the alert message.
*
* Defaults to `Cross-site scripting`, but may be overriden for sinks
* Defaults to `Cross-site scripting`, but may be overridden for sinks
* that do not allow script injection, but injection of other undesirable HTML elements.
*/
string getVulnerabilityKind() { result = "Cross-site scripting" }

View File

@@ -51,7 +51,7 @@
* either a single character, a set of characters represented by a
* character class, or the set of all characters.
* * The product automaton is constructed lazily, starting with pair states
* `(q, q)` where `q` is a fork, and proceding along an over-approximate
* `(q, q)` where `q` is a fork, and proceeding along an over-approximate
* step relation.
* * The over-approximate step relation allows transitions along pairs of
* abstract input symbols where the symbols have overlap in the characters they accept.

View File

@@ -610,16 +610,23 @@ State after(RegExpTerm t) {
or
exists(RegExpGroup grp | t = grp.getAChild() | result = after(grp))
or
exists(EffectivelyStar star | t = star.getAChild() | result = before(star))
exists(EffectivelyStar star | t = star.getAChild() |
not isPossessive(star) and
result = before(star)
)
or
exists(EffectivelyPlus plus | t = plus.getAChild() |
result = before(plus) or
not isPossessive(plus) and
result = before(plus)
or
result = after(plus)
)
or
exists(EffectivelyQuestion opt | t = opt.getAChild() | result = after(opt))
or
exists(RegExpRoot root | t = root | result = AcceptAnySuffix(root))
exists(RegExpRoot root | t = root |
if matchesAnySuffix(root) then result = AcceptAnySuffix(root) else result = Accept(root)
)
}
/**
@@ -690,7 +697,7 @@ predicate delta(State q1, EdgeLabel lbl, State q2) {
lbl = Epsilon() and q2 = Accept(root)
)
or
exists(RegExpRoot root | q1 = Match(root, 0) | lbl = Any() and q2 = q1)
exists(RegExpRoot root | q1 = Match(root, 0) | matchesAnyPrefix(root) and lbl = Any() and q2 = q1)
or
exists(RegExpDollar dollar | q1 = before(dollar) |
lbl = Epsilon() and q2 = Accept(getRoot(dollar))

View File

@@ -12,6 +12,24 @@ predicate isEscapeClass(RegExpTerm term, string clazz) {
exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz)
}
/**
* Holds if `term` is a possessive quantifier.
* As javascript's regexes do not support possessive quantifiers, this never holds, but is used by the shared library.
*/
predicate isPossessive(RegExpQuantifier term) { none() }
/**
* Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against.
* Not yet implemented for Javascript.
*/
predicate matchesAnyPrefix(RegExpTerm term) { any() }
/**
* Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against.
* Not yet implemented for Javascript.
*/
predicate matchesAnySuffix(RegExpTerm term) { any() }
/**
* Holds if the regular expression should not be considered.
*