mirror of
https://github.com/github/codeql.git
synced 2026-05-05 13:45:19 +02:00
Merge branch 'main' into exportObj
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added the `Selection` api as a DOM text source in the `js/xss-through-dom` query.
|
||||
@@ -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.
|
||||
14
javascript/ql/lib/change-notes/released/0.1.2.md
Normal file
14
javascript/ql/lib/change-notes/released/0.1.2.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.1.1
|
||||
lastReleaseVersion: 0.1.2
|
||||
|
||||
@@ -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
|
||||
|
||||
252
javascript/ql/lib/semmle/javascript/Actions.qll
Normal file
252
javascript/ql/lib/semmle/javascript/Actions.qll
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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") }
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) => { ... }
|
||||
|
||||
@@ -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))) }
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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"]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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])
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user