Merge remote-tracking branch 'upstream/master' into MoarJQuery

This commit is contained in:
Erik Krogh Kristensen
2020-04-23 14:10:53 +02:00
33 changed files with 762 additions and 124 deletions

View File

@@ -1,39 +1,126 @@
# Code of Conduct
## Our Pledge
This code of conduct outlines expectations for participation in the Semmle open source community, including any open source repositories on GitHub.com, as well as steps for reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all.
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
People violating this code of conduct may be banned from the community.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
Our community strives to:
* Be friendly and patient: Remember you might not be communicating in someone elses primary spoken or programming language, and others may not have your level of understanding.
* Be welcoming: Our community welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
* Be respectful: We are a world-wide community of professionals, and we conduct ourselves professionally. Disagreement is no excuse for poor behavior and poor manners. Disrespectful and unacceptable behavior includes, but is not limited to:
* Violent threats or language.
* Discriminatory or derogatory jokes and language.
* Posting sexually explicit or violent material.
* Posting, or threatening to post, peoples personally identifying information (“doxing”).
* Insults, especially those using discriminatory terms or slurs.
* Behavior that could be perceived as sexual attention.
* Advocating for or encouraging any of the above behaviors.
* Understand disagreements: Disagreements, both social and technical, are useful learning opportunities. Seek to understand others viewpoints and resolve differences constructively.
## Our Standards
This code is not exhaustive or complete. It serves to capture our common understanding of a productive, collaborative environment. We expect the code to be followed in spirit as much as in the letter.
Examples of behavior that contributes to a positive environment for our
community include:
# Scope
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
This code of conduct applies to all repositories and communities for Semmle open source projects, regardless of whether or not the repository explicitly calls out its use of this code. The code also applies in public spaces when an individual is representing the Semmle open source community. Examples include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
# Reporting Code of Conduct Issues
We encourage members of the community to resolve issues on their own whenever possible. This builds a broader and deeper understanding and ultimately a healthier interaction. In the event that an issue cannot be resolved locally, please feel free to report your concerns by contacting code-of-conduct@semmle.com.
In your report please include:
* Your contact information.
* Names (real, usernames or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well.
* Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public chat log), please include a link or attachment.
* Any additional information that may be helpful.
## Enforcement Responsibilities
All reports will be reviewed by a multi-person team and will result in a response that is deemed necessary and appropriate to the circumstances. Where additional perspectives are needed, the team may seek insight from others with relevant expertise or experience. The confidentiality of the person reporting the incident will be kept at all times. Involved parties are never part of the review team.
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the review team may take any action they deem appropriate, including a permanent ban from the community.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
*This text is licensed under the [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) license. It is based on a template established by the [TODO Group](http://todogroup.org/) and variants thereof used by numerous other large communities (e.g., [Microsoft](https://microsoft.github.io/codeofconduct/), [Facebook](https://code.fb.com/codeofconduct/), [Yahoo](https://yahoo.github.io/codeofconduct), [Twitter](https://github.com/twitter/code-of-conduct), [GitHub](https://blog.github.com/2015-07-20-adopting-the-open-code-of-conduct/)) and the Scope section from the [Contributor Covenant version 1.4](http://contributor-covenant.org/version/1/4/).*
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
opensource@github.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -3,6 +3,7 @@
## General improvements
* Support for the following frameworks and libraries has been improved:
- [jGrowl](https://github.com/stanlemon/jGrowl)
- [jQuery](https://jquery.com/)
## New queries

View File

@@ -12,12 +12,12 @@ abstract class Assertion extends Locatable {
}
/**
* A libc assert, as defined in assert.h. A macro with the head
* "assert(expr)" that expands to a conditional expression which
* may terminate the program.
* A libc assert, as defined in assert.h. A macro with a head
* that matches the prefix "assert(", and expands to a conditional
* expression which may terminate the program.
*/
class LibcAssert extends MacroInvocation, Assertion {
LibcAssert() { this.getMacro().getHead() = "assert(expr)" }
LibcAssert() { this.getMacro().getHead().matches("assert(%") }
override Expr getAsserted() {
exists(ConditionalExpr ce | this.getAGeneratedElement() = ce | result = ce.getCondition())

View File

@@ -145,8 +145,6 @@ class HexLiteral extends Literal {
/**
* A C/C++ aggregate literal.
*
* For example:
*/
class AggregateLiteral extends Expr, @aggregateliteral {
override string getCanonicalQLClass() { result = "AggregateLiteral" }

View File

@@ -0,0 +1,325 @@
/**
* Provides classes and predicates for discovering points of interest
* in an unknown code base.
*
* To use this module, subclass the
* `ActivePoI` class, override *one* of its `is` predicates, and use
* `alertQuery` as a `@kind problem` query . This will present
* the desired points of interest as alerts that are easily browsable
* in a codeql IDE. By itself, this is no different from an ordinary
* query, but the strength of this module lies in its extensibility
* and standard library:
*
* - points of interest can be added, removed and mixed seamlessly
* - this module comes with a collection of standard points of interest (see `StandardPoIs`)
*
* A global configuration for the points of interest (see
* `PoIConfiguration`) can be used to easily manage multiple points of
* interests, and to restrict the points of interest to specific
* corners of the code base.
*
* Below is an example use of this module that will produce an alert
* for each route handler and route handler setup in a file named
* "server-core.js". The route setup alerts will contain a link to its
* associated route handler.
*
* ```
* /**
* * @kind problem
* *\/
*
* import PoI
*
* class Configuration extends PoIConfiguration {
* Configuration() { this = "Configuration" }
*
* override predicate shown(DataFlow::Node n) { n.getFile().getBaseName() = "server-core.js" }
* }
*
* class RouteHandlerPoI extends ActivePoI {
* RouteHandlerPoI() { this = "RouteHandlerPoI" }
* override predicate is(DataFlow::Node l0) { l0 instanceof Express::RouteHandler }
* }
*
* class RouteSetupAndRouteHandlerPoI extends ActivePoI {
* RouteSetupAndRouteHandlerPoI() { this = "RouteSetupAndRouteHandlerPoI" }
*
* override predicate is(DataFlow::Node l0, DataFlow::Node l1, string t1) {
* l0.asExpr().(Express::RouteSetup).getARouteHandler() = l1 and t1 = "routehandler"
* }
* }
*
* query predicate problems = alertQuery/6;
* ```
*/
import javascript
private import DataFlow
private import filters.ClassifyFiles
private import semmle.javascript.RestrictedLocations
/**
* Provides often used points of interest.
*
* Note that these points of interest should not extend
* `ActivePoI`, and that they can be enabled on
* demand like this:
*
* ```
* class MyPoI extends ServerRelatedPoI, ActivePoI {}
* ```
*/
private module StandardPoIs {
/**
* An unpromoted route setup candidate.
*/
class UnpromotedRouteSetupPoI extends PoI {
UnpromotedRouteSetupPoI() { this = "UnpromotedRouteSetupPoI" }
override predicate is(Node l0) {
l0 instanceof HTTP::RouteSetupCandidate and not l0.asExpr() instanceof HTTP::RouteSetup
}
}
/**
* An unpromoted route handler candidate.
*/
class UnpromotedRouteHandlerPoI extends PoI {
UnpromotedRouteHandlerPoI() { this = "UnpromotedRouteHandlerPoI" }
override predicate is(Node l0) {
l0 instanceof HTTP::RouteHandlerCandidate and not l0 instanceof HTTP::RouteHandler
}
}
/**
* An unpromoted route handler candidate, with explanatory data flow information.
*/
class UnpromotedRouteHandlerWithFlowPoI extends PoI {
UnpromotedRouteHandlerWithFlowPoI() { this = "UnpromotedRouteHandlerWithFlowPoI" }
private DataFlow::SourceNode track(HTTP::RouteHandlerCandidate cand, DataFlow::TypeTracker t) {
t.start() and
result = cand
or
exists(DataFlow::TypeTracker t2 | result = track(cand, t2).track(t2, t))
}
override predicate is(Node l0, Node l1, string t1) {
l0 instanceof HTTP::RouteHandlerCandidate and
not l0 instanceof HTTP::RouteHandler and
l1 = track(l0, TypeTracker::end()) and
(if l1 = l0 then t1 = "ends here" else t1 = "starts/ends here")
}
}
/**
* A callee that is unknown.
*/
class UnknownCalleePoI extends PoI {
UnknownCalleePoI() { this = "UnknownCalleePoI" }
override predicate is(Node l0) {
exists(InvokeNode invk | l0 = invk.getCalleeNode() and not exists(invk.getACallee()))
}
}
/**
* A source of remote flow.
*/
class RemoteFlowSourcePoI extends PoI {
RemoteFlowSourcePoI() { this = "RemoteFlowSourcePoI" }
override predicate is(Node l0) { l0 instanceof RemoteFlowSource }
}
/**
* A "source" for any active configuration.
*/
class SourcePoI extends PoI {
SourcePoI() { this = "SourcePoI" }
override predicate is(Node l0) {
exists(Configuration cfg | cfg.isSource(l0) or cfg.isSource(l0, _))
}
}
/**
* A "sink" for any active configuration.
*/
class SinkPoI extends PoI {
SinkPoI() { this = "SinkPoI" }
override predicate is(Node l0) {
exists(Configuration cfg | cfg.isSink(l0) or cfg.isSink(l0, _))
}
}
/**
* A "barrier" for any active configuration.
*/
class BarrierPoI extends PoI {
BarrierPoI() { this = "BarrierPoI" }
override predicate is(Node l0) {
exists(Configuration cfg |
cfg.isBarrier(l0) or
cfg.isBarrierEdge(l0, _) or
cfg.isBarrierEdge(l0, _, _) or
cfg.isLabeledBarrier(l0, _)
)
}
}
/**
* Provides groups of often used points of interest.
*/
module StandardPoIGroups {
/**
* A server-related point of interest.
*/
class ServerRelatedPoI extends PoI {
ServerRelatedPoI() {
this instanceof UnpromotedRouteSetupPoI or
this instanceof UnpromotedRouteHandlerPoI or
this instanceof UnpromotedRouteHandlerWithFlowPoI
}
}
/**
* A configuration-related point of interest.
*/
class DataFlowConfigurationPoI extends PoI {
DataFlowConfigurationPoI() {
this instanceof SourcePoI or
this instanceof SinkPoI
}
}
}
import StandardPoIGroups
}
import StandardPoIs
/**
* A tagging interface for a custom point of interest that should be
* enabled in the absence of an explicit
* `PoIConfiguration::enabled/1`.
*/
abstract class ActivePoI extends PoI {
bindingset[this]
ActivePoI() { any() }
}
private module PoIConfigDefaults {
predicate enabled(PoI poi) { poi instanceof ActivePoI }
predicate shown(Node n) { not classify(n.getFile(), _) }
}
/**
* A configuration for the points of interest to display.
*/
abstract class PoIConfiguration extends string {
bindingset[this]
PoIConfiguration() { any() }
/**
* Holds if the points of interest from `poi` should be shown.
*/
predicate enabled(PoI poi) { PoIConfigDefaults::enabled(poi) }
/**
* Holds if the points of interest `n` should be shown.
*/
predicate shown(Node n) { PoIConfigDefaults::shown(n) }
}
/**
* A class of points of interest.
*
* Note that only one of the `is/1`, `is/3`, `is/5` methods should
* be overridden, as two overrides will degrade the alert UI
* slightly.
*/
abstract class PoI extends string {
bindingset[this]
PoI() { any() }
/**
* Holds if `l0` is a point of interest.
*/
predicate is(Node l0) { none() }
/**
* Holds if `l0` is a point of interest, with `l1` as an auxiliary location described by `t1`.
*/
predicate is(Node l0, Node l1, string t1) { none() }
/**
* Holds if `l0` is a point of interest, with `l1` and `l2` as auxiliary locations described by `t1` and `t2`.
*/
predicate is(Node l0, Node l1, string t1, Node l2, string t2) { none() }
/**
* Gets the message format for the point of interest.
*/
string getFormat() {
is(_) and result = ""
or
is(_, _, _) and result = "$@"
or
is(_, _, _, _, _) and result = "$@ $@"
}
}
/**
* An alert query for a point of interest.
*
* Should be used as:
*
* ```
* query predicate problems = alertQuery/6;
* ```
*
* Or alternatively:
*
* ```
* from Locatable l1line, string msg, Node l2, string s2, Node l3, string s3
* where alertQuery(l1line, msg, l2, s2, l3, s3)
* select l1line, msg, l2, s2, l3, s3
* ```
*
* Note that some points of interest do not have auxiliary
* locations, so `l2`,`l3`, `s2`, `s3` may have placeholder values.
*/
predicate alertQuery(Locatable l1line, string msg, Node l2, string s2, Node l3, string s3) {
exists(PoI poi, Node l1, string m |
l1.getAstNode().(FirstLineOf) = l1line and
(
not exists(PoIConfiguration cfg) and
PoIConfigDefaults::enabled(poi) and
PoIConfigDefaults::shown(l1)
or
exists(PoIConfiguration cfg |
cfg.enabled(poi) and
cfg.shown(l1)
)
) and
m = poi.getFormat() and
if m = "" then msg = poi else msg = poi + ": " + m
|
poi.is(l1) and
l1 = l2 and
s2 = "irrelevant" and
l1 = l3 and
s3 = "irrelevant"
or
poi.is(l1, l2, s2) and
l1 = l3 and
s3 = "irrelevant"
or
poi.is(l1, l2, s2, l3, s3)
)
}

View File

@@ -7,85 +7,8 @@
* @id js/file-classifier
*/
import semmle.javascript.GeneratedCode
import semmle.javascript.frameworks.Testing
import semmle.javascript.frameworks.Templating
import semmle.javascript.dependencies.FrameworkLibraries
/**
* Holds if `e` may be caused by parsing a template file as plain HTML or JavaScript.
*
* We use two heuristics: check for the presence of a known template delimiter preceding
* the error on the same line, and check whether the file name contains `template` or
* `templates`.
*/
predicate maybeCausedByTemplate(JSParseError e) {
exists(File f | f = e.getFile() |
e.getLine().indexOf(Templating::getADelimiter()) <= e.getLocation().getStartColumn()
or
f.getAbsolutePath().regexpMatch("(?i).*\\btemplates?\\b.*")
)
}
/**
* Holds if `e` is an expression in the form `o.p1.p2.p3....pn`.
*/
private predicate isNestedDotExpr(DotExpr e) {
e.getBase() instanceof VarAccess or
isNestedDotExpr(e.getBase())
}
/**
* Holds if `tl` only contains variable declarations and field reads.
*/
private predicate looksLikeExterns(TopLevel tl) {
forex(Stmt s | s.getParent() = tl |
exists(VarDeclStmt vds | vds = s |
forall(VariableDeclarator vd | vd = vds.getADecl() | not exists(vd.getInit()))
)
or
isNestedDotExpr(s.(ExprStmt).getExpr())
)
}
/**
* Holds if `f` is classified as belonging to `category`.
*
* There are currently four categories:
* - `"generated"`: `f` contains generated or minified code;
* - `"test"`: `f` contains test code;
* - `"externs"`: `f` contains externs declarations;
* - `"library"`: `f` contains library code;
* - `"template"`: `f` contains template code.
*/
predicate classify(File f, string category) {
isGenerated(f.getATopLevel()) and category = "generated"
or
(
exists(Test t | t.getFile() = f)
or
exists(string stemExt | stemExt = "test" or stemExt = "spec" |
f = getTestFile(any(File orig), stemExt)
)
or
f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*")
) and
category = "test"
or
(f.getATopLevel().isExterns() or looksLikeExterns(f.getATopLevel())) and
category = "externs"
or
f.getATopLevel() instanceof FrameworkLibraryInstance and category = "library"
or
exists(JSParseError err | maybeCausedByTemplate(err) |
f = err.getFile() and category = "template"
)
or
// Polymer templates
exists(HTML::Element elt | elt.getName() = "template" |
f = elt.getFile() and category = "template"
)
}
import javascript
import ClassifyFiles
from File f, string category
where classify(f, category)

View File

@@ -0,0 +1,85 @@
/**
* Provides classes and predicates for classifying files as containing
* generated code, test code, externs declarations, library code or
* template code.
*/
import semmle.javascript.GeneratedCode
import semmle.javascript.frameworks.Testing
import semmle.javascript.frameworks.Templating
import semmle.javascript.dependencies.FrameworkLibraries
/**
* Holds if `e` may be caused by parsing a template file as plain HTML or JavaScript.
*
* We use two heuristics: check for the presence of a known template delimiter preceding
* the error on the same line, and check whether the file name contains `template` or
* `templates`.
*/
predicate maybeCausedByTemplate(JSParseError e) {
exists(File f | f = e.getFile() |
e.getLine().indexOf(Templating::getADelimiter()) <= e.getLocation().getStartColumn()
or
f.getAbsolutePath().regexpMatch("(?i).*\\btemplates?\\b.*")
)
}
/**
* Holds if `e` is an expression in the form `o.p1.p2.p3....pn`.
*/
private predicate isNestedDotExpr(DotExpr e) {
e.getBase() instanceof VarAccess or
isNestedDotExpr(e.getBase())
}
/**
* Holds if `tl` only contains variable declarations and field reads.
*/
private predicate looksLikeExterns(TopLevel tl) {
forex(Stmt s | s.getParent() = tl |
exists(VarDeclStmt vds | vds = s |
forall(VariableDeclarator vd | vd = vds.getADecl() | not exists(vd.getInit()))
)
or
isNestedDotExpr(s.(ExprStmt).getExpr())
)
}
/**
* Holds if `f` is classified as belonging to `category`.
*
* There are currently four categories:
* - `"generated"`: `f` contains generated or minified code;
* - `"test"`: `f` contains test code;
* - `"externs"`: `f` contains externs declarations;
* - `"library"`: `f` contains library code;
* - `"template"`: `f` contains template code.
*/
predicate classify(File f, string category) {
isGenerated(f.getATopLevel()) and category = "generated"
or
(
exists(Test t | t.getFile() = f)
or
exists(string stemExt | stemExt = "test" or stemExt = "spec" |
f = getTestFile(any(File orig), stemExt)
)
or
f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*")
) and
category = "test"
or
(f.getATopLevel().isExterns() or looksLikeExterns(f.getATopLevel())) and
category = "externs"
or
f.getATopLevel() instanceof FrameworkLibraryInstance and category = "library"
or
exists(JSParseError err | maybeCausedByTemplate(err) |
f = err.getFile() and category = "template"
)
or
// Polymer templates
exists(HTML::Element elt | elt.getName() = "template" |
f = elt.getFile() and category = "template"
)
}

View File

@@ -99,6 +99,8 @@ module DomBasedXss {
this = any(Typeahead::TypeaheadSuggestionFunction f).getAReturn()
or
this = any(Handlebars::SafeString s).getAnArgument()
or
this = any(JQuery::MethodCall call | call.getMethodName() = "jGrowl").getArgument(0)
}
}

View File

@@ -0,0 +1,4 @@
| tst.js:16:15:16:25 | req.query.x | SourcePoI | tst.js:16:15:16:25 | req.query.x | irrelevant | tst.js:16:15:16:25 | req.query.x | irrelevant |
| tst.js:17:11:17:21 | req.query.x | SinkPoI | tst.js:17:11:17:21 | req.query.x | irrelevant | tst.js:17:11:17:21 | req.query.x | irrelevant |
| tst.js:17:11:17:21 | req.query.x | SourcePoI | tst.js:17:11:17:21 | req.query.x | irrelevant | tst.js:17:11:17:21 | req.query.x | irrelevant |
| tst.js:18:12:18:22 | req.query.x | SourcePoI | tst.js:18:12:18:22 | req.query.x | irrelevant | tst.js:18:12:18:22 | req.query.x | irrelevant |

View File

@@ -0,0 +1,13 @@
/**
* @kind problem
*/
import javascript
import experimental.poi.PoI
import semmle.javascript.security.dataflow.CommandInjection
import semmle.javascript.security.dataflow.IndirectCommandInjection
import semmle.javascript.security.dataflow.ShellCommandInjectionFromEnvironment
class MyDataFlowConfigurationPoIs extends DataFlowConfigurationPoI, ActivePoI { }
query predicate problems = alertQuery/6;

View File

@@ -0,0 +1,3 @@
| tst.js:6:1:6:16 | (req, res) => 42 | UnpromotedRouteHandlerPoI | tst.js:6:1:6:16 | (req, res) => 42 | irrelevant | tst.js:6:1:6:16 | (req, res) => 42 | irrelevant |
| tst.js:6:1:6:16 | (req, res) => 42 | UnpromotedRouteHandlerWithFlowPoI: $@ | tst.js:6:1:6:16 | (req, res) => 42 | ends here | tst.js:6:1:6:16 | (req, res) => 42 | irrelevant |
| tst.js:13:1:13:36 | otherAp ... h", rh) | UnpromotedRouteSetupPoI | tst.js:13:1:13:36 | otherAp ... h", rh) | irrelevant | tst.js:13:1:13:36 | otherAp ... h", rh) | irrelevant |

View File

@@ -0,0 +1,10 @@
/**
* @kind problem
*/
import javascript
import experimental.poi.PoI
class MyServerRelatedPoIs extends ServerRelatedPoI, ActivePoI { }
query predicate problems = alertQuery/6;

View File

@@ -0,0 +1,3 @@
| tst.js:6:1:6:16 | (req, res) => 42 | UnpromotedRouteHandlerPoI | tst.js:6:1:6:16 | (req, res) => 42 | irrelevant | tst.js:6:1:6:16 | (req, res) => 42 | irrelevant |
| tst.js:6:1:6:16 | (req, res) => 42 | UnpromotedRouteHandlerWithFlowPoI: $@ | tst.js:6:1:6:16 | (req, res) => 42 | ends here | tst.js:6:1:6:16 | (req, res) => 42 | irrelevant |
| tst.js:13:1:13:36 | otherAp ... h", rh) | UnpromotedRouteSetupPoI | tst.js:13:1:13:36 | otherAp ... h", rh) | irrelevant | tst.js:13:1:13:36 | otherAp ... h", rh) | irrelevant |

View File

@@ -0,0 +1,10 @@
/**
* @kind problem
*/
import javascript
import experimental.poi.PoI
class MyServerRelatedPoI extends ServerRelatedPoI, ActivePoI { }
query predicate problems = alertQuery/6;

View File

@@ -0,0 +1,6 @@
| tst.js:1:23:1:31 | "express" | SinkPoI | tst.js:1:23:1:31 | "express" | irrelevant | tst.js:1:23:1:31 | "express" | irrelevant |
| tst.js:2:16:2:19 | "fs" | SinkPoI | tst.js:2:16:2:19 | "fs" | irrelevant | tst.js:2:16:2:19 | "fs" | irrelevant |
| tst.js:3:16:3:30 | "child_process" | SinkPoI | tst.js:3:16:3:30 | "child_process" | irrelevant | tst.js:3:16:3:30 | "child_process" | irrelevant |
| tst.js:16:15:16:25 | req.query.x | SourcePoI | tst.js:16:15:16:25 | req.query.x | irrelevant | tst.js:16:15:16:25 | req.query.x | irrelevant |
| tst.js:17:11:17:21 | req.query.x | SourcePoI | tst.js:17:11:17:21 | req.query.x | irrelevant | tst.js:17:11:17:21 | req.query.x | irrelevant |
| tst.js:18:12:18:22 | req.query.x | SourcePoI | tst.js:18:12:18:22 | req.query.x | irrelevant | tst.js:18:12:18:22 | req.query.x | irrelevant |

View File

@@ -0,0 +1,11 @@
/**
* @kind problem
*/
import javascript
import experimental.poi.PoI
import semmle.javascript.security.dataflow.TaintedPath
class MyDataflowRelatedPoIs extends DataFlowConfigurationPoI, ActivePoI { }
query predicate problems = alertQuery/6;

View File

@@ -0,0 +1,9 @@
| tst.js:8:1:8:44 | app.get ... es) {}) | RouteSetupAndRouterAndRouteHandlerPoI: $@ $@ | tst.js:4:11:4:19 | express() | router | tst.js:8:23:8:43 | functio ... res) {} | routehandler |
| tst.js:8:23:8:43 | functio ... res) {} | RouteHandlerAndSetupPoI: $@ | tst.js:8:1:8:44 | app.get ... es) {}) | setup | tst.js:8:23:8:43 | functio ... res) {} | irrelevant |
| tst.js:8:23:8:43 | functio ... res) {} | RouteHandlerPoI | tst.js:8:23:8:43 | functio ... res) {} | irrelevant | tst.js:8:23:8:43 | functio ... res) {} | irrelevant |
| tst.js:10:10:10:30 | functio ... res) {} | RouteHandlerAndSetupPoI: $@ | tst.js:11:1:11:31 | app.get ... h", rh) | setup | tst.js:10:10:10:30 | functio ... res) {} | irrelevant |
| tst.js:10:10:10:30 | functio ... res) {} | RouteHandlerPoI | tst.js:10:10:10:30 | functio ... res) {} | irrelevant | tst.js:10:10:10:30 | functio ... res) {} | irrelevant |
| tst.js:11:1:11:31 | app.get ... h", rh) | RouteSetupAndRouterAndRouteHandlerPoI: $@ $@ | tst.js:4:11:4:19 | express() | router | tst.js:10:10:10:30 | functio ... res) {} | routehandler |
| tst.js:15:1:19:2 | app.get ... .x);\\n}) | RouteSetupAndRouterAndRouteHandlerPoI: $@ $@ | tst.js:4:11:4:19 | express() | router | tst.js:15:23:19:1 | functio ... y.x);\\n} | routehandler |
| tst.js:15:23:19:1 | functio ... y.x);\\n} | RouteHandlerAndSetupPoI: $@ | tst.js:15:1:19:2 | app.get ... .x);\\n}) | setup | tst.js:15:23:19:1 | functio ... y.x);\\n} | irrelevant |
| tst.js:15:23:19:1 | functio ... y.x);\\n} | RouteHandlerPoI | tst.js:15:23:19:1 | functio ... y.x);\\n} | irrelevant | tst.js:15:23:19:1 | functio ... y.x);\\n} | irrelevant |

View File

@@ -0,0 +1,34 @@
/**
* @kind problem
*/
import javascript
import experimental.poi.PoI
import DataFlow
class RouteHandlerPoI extends ActivePoI {
RouteHandlerPoI() { this = "RouteHandlerPoI" }
override predicate is(Node l0) { l0 instanceof Express::RouteHandler }
}
class RouteHandlerAndSetupPoI extends ActivePoI {
RouteHandlerAndSetupPoI() { this = "RouteHandlerAndSetupPoI" }
override predicate is(Node l0, Node l1, string t1) {
l1.asExpr().(Express::RouteSetup).getARouteHandler() = l0 and t1 = "setup"
}
}
class RouteSetupAndRouterAndRouteHandlerPoI extends ActivePoI {
RouteSetupAndRouterAndRouteHandlerPoI() { this = "RouteSetupAndRouterAndRouteHandlerPoI" }
override predicate is(Node l0, Node l1, string t1, Node l2, string t2) {
l0.asExpr().(Express::RouteSetup).getRouter().flow() = l1 and
t1 = "router" and
l0.asExpr().(Express::RouteSetup).getARouteHandler() = l2 and
t2 = "routehandler"
}
}
query predicate problems = alertQuery/6;

View File

@@ -0,0 +1 @@
| tst.js:6:1:6:16 | (req, res) => 42 | UnpromotedRouteHandlerPoI | tst.js:6:1:6:16 | (req, res) => 42 | irrelevant | tst.js:6:1:6:16 | (req, res) => 42 | irrelevant |

View File

@@ -0,0 +1,10 @@
/**
* @kind problem
*/
import javascript
import experimental.poi.PoI
class MyUnpromotedRouteHandlerPoIs extends UnpromotedRouteHandlerPoI, ActivePoI { }
query predicate problems = alertQuery/6;

View File

@@ -0,0 +1,4 @@
| tst.js:16:15:16:25 | req.query.x | SourcePoI | tst.js:16:15:16:25 | req.query.x | irrelevant | tst.js:16:15:16:25 | req.query.x | irrelevant |
| tst.js:17:11:17:21 | req.query.x | SourcePoI | tst.js:17:11:17:21 | req.query.x | irrelevant | tst.js:17:11:17:21 | req.query.x | irrelevant |
| tst.js:18:12:18:22 | req.query.x | SinkPoI | tst.js:18:12:18:22 | req.query.x | irrelevant | tst.js:18:12:18:22 | req.query.x | irrelevant |
| tst.js:18:12:18:22 | req.query.x | SourcePoI | tst.js:18:12:18:22 | req.query.x | irrelevant | tst.js:18:12:18:22 | req.query.x | irrelevant |

View File

@@ -0,0 +1,14 @@
/**
* @kind problem
*/
import javascript
import experimental.poi.PoI
import semmle.javascript.security.dataflow.ReflectedXss
import semmle.javascript.security.dataflow.StoredXss
import semmle.javascript.security.dataflow.DomBasedXss
import semmle.javascript.security.dataflow.ExceptionXss
class MyDataFlowConfigurationPoIs extends DataFlowConfigurationPoI, ActivePoI { }
query predicate problems = alertQuery/6;

View File

@@ -0,0 +1,19 @@
var express = require("express"),
fs = require("fs"),
cp = require("child_process");
var app = express();
(req, res) => 42;
app.get("/some/path", function(req, res) {});
let rh = function(req, res) {};
app.get("/some/other/path", rh);
otherApp.get("/some/other/path", rh);
app.get("/some/path", function(req, res) {
fs.readFile(req.query.x);
cp.exec(req.query.x);
res.send(req.query.x);
});

View File

@@ -341,6 +341,12 @@ nodes
| tst.js:347:20:347:36 | document.location |
| tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:354:7:354:39 | target |
| tst.js:354:16:354:32 | document.location |
| tst.js:354:16:354:32 | document.location |
| tst.js:354:16:354:39 | documen ... .search |
| tst.js:355:12:355:17 | target |
| tst.js:355:12:355:17 | target |
| typeahead.js:20:13:20:45 | target |
| typeahead.js:20:22:20:38 | document.location |
| typeahead.js:20:22:20:38 | document.location |
@@ -659,6 +665,11 @@ edges
| tst.js:347:20:347:36 | document.location | tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:347:20:347:36 | document.location | tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:347:20:347:36 | document.location | tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:354:7:354:39 | target | tst.js:355:12:355:17 | target |
| tst.js:354:7:354:39 | target | tst.js:355:12:355:17 | target |
| tst.js:354:16:354:32 | document.location | tst.js:354:16:354:39 | documen ... .search |
| tst.js:354:16:354:32 | document.location | tst.js:354:16:354:39 | documen ... .search |
| tst.js:354:16:354:39 | documen ... .search | tst.js:354:7:354:39 | target |
| typeahead.js:20:13:20:45 | target | typeahead.js:21:12:21:17 | target |
| typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search |
| typeahead.js:20:22:20:38 | document.location | typeahead.js:20:22:20:45 | documen ... .search |
@@ -760,6 +771,7 @@ edges
| tst.js:319:35:319:42 | location | tst.js:319:35:319:42 | location | tst.js:319:35:319:42 | location | Cross-site scripting vulnerability due to $@. | tst.js:319:35:319:42 | location | user-provided value |
| tst.js:336:18:336:35 | params.get('name') | tst.js:330:18:330:34 | document.location | tst.js:336:18:336:35 | params.get('name') | Cross-site scripting vulnerability due to $@. | tst.js:330:18:330:34 | document.location | user-provided value |
| tst.js:349:5:349:30 | getUrl( ... ring(1) | tst.js:347:20:347:36 | document.location | tst.js:349:5:349:30 | getUrl( ... ring(1) | Cross-site scripting vulnerability due to $@. | tst.js:347:20:347:36 | document.location | user-provided value |
| tst.js:355:12:355:17 | target | tst.js:354:16:354:32 | document.location | tst.js:355:12:355:17 | target | Cross-site scripting vulnerability due to $@. | tst.js:354:16:354:32 | document.location | user-provided value |
| typeahead.js:25:18:25:20 | val | typeahead.js:20:22:20:38 | document.location | typeahead.js:25:18:25:20 | val | Cross-site scripting vulnerability due to $@. | typeahead.js:20:22:20:38 | document.location | user-provided value |
| v-html.vue:2:8:2:23 | v-html=tainted | v-html.vue:6:42:6:58 | document.location | v-html.vue:2:8:2:23 | v-html=tainted | Cross-site scripting vulnerability due to $@. | v-html.vue:6:42:6:58 | document.location | user-provided value |
| winjs.js:3:43:3:49 | tainted | winjs.js:2:17:2:33 | document.location | winjs.js:3:43:3:49 | tainted | Cross-site scripting vulnerability due to $@. | winjs.js:2:17:2:33 | document.location | user-provided value |

View File

@@ -38,6 +38,9 @@ nodes
| xss-through-dom.js:64:30:64:40 | valMethod() |
| xss-through-dom.js:64:30:64:40 | valMethod() |
| xss-through-dom.js:64:30:64:40 | valMethod() |
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name |
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name |
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name |
edges
| xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() |
| xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() |
@@ -52,6 +55,7 @@ edges
| xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") |
| xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") |
| xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() |
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name |
#select
| xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | xss-through-dom.js:2:16:2:34 | $("textarea").val() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:2:16:2:34 | $("textarea").val() | DOM text |
| xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:4:16:4:40 | $(".som ... .text() | DOM text |
@@ -66,3 +70,4 @@ edges
| xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:57:30:57:67 | $("inpu ... "name") | DOM text |
| xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") | xss-through-dom.js:61:30:61:69 | $(docum ... value") | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:61:30:61:69 | $(docum ... value") | DOM text |
| xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() | xss-through-dom.js:64:30:64:40 | valMethod() | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:64:30:64:40 | valMethod() | DOM text |
| xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | Cross-site scripting vulnerability due to $@. | xss-through-dom.js:71:11:71:32 | $("inpu ... 0).name | DOM text |

View File

@@ -341,6 +341,12 @@ nodes
| tst.js:347:20:347:36 | document.location |
| tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:354:7:354:39 | target |
| tst.js:354:16:354:32 | document.location |
| tst.js:354:16:354:32 | document.location |
| tst.js:354:16:354:39 | documen ... .search |
| tst.js:355:12:355:17 | target |
| tst.js:355:12:355:17 | target |
| typeahead.js:9:28:9:30 | loc |
| typeahead.js:9:28:9:30 | loc |
| typeahead.js:10:16:10:18 | loc |
@@ -663,6 +669,11 @@ edges
| tst.js:347:20:347:36 | document.location | tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:347:20:347:36 | document.location | tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:347:20:347:36 | document.location | tst.js:349:5:349:30 | getUrl( ... ring(1) |
| tst.js:354:7:354:39 | target | tst.js:355:12:355:17 | target |
| tst.js:354:7:354:39 | target | tst.js:355:12:355:17 | target |
| tst.js:354:16:354:32 | document.location | tst.js:354:16:354:39 | documen ... .search |
| tst.js:354:16:354:32 | document.location | tst.js:354:16:354:39 | documen ... .search |
| tst.js:354:16:354:39 | documen ... .search | tst.js:354:7:354:39 | target |
| typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc |
| typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc |
| typeahead.js:9:28:9:30 | loc | typeahead.js:10:16:10:18 | loc |

View File

@@ -348,4 +348,10 @@ function hash() {
}
$(getUrl().hash.substring(1)); // NOT OK
}
}
function growl() {
var target = document.location.search
$.jGrowl(target); // NOT OK
}

View File

@@ -67,4 +67,6 @@
if(myValue.property) {
$("#id").get(0).innerHTML = myValue; // OK.
}
$.jGrowl($("input").get(0).name); // NOT OK.
})();

View File

@@ -1,10 +1,10 @@
import python
from NameNode name, CallNode call, string debug
from ControlFlowNode arg, CallNode call, string debug
where
call.getAnArg() = name and
call.getAnArg() = arg and
call.getFunction().(NameNode).getId() = "check" and
if exists(name.pointsTo())
then debug = name.pointsTo().toString()
if exists(arg.pointsTo())
then debug = arg.pointsTo().toString()
else debug = "<MISSING pointsTo()>"
select name, debug
select arg, debug

View File

@@ -1,10 +1,10 @@
import python
from NameNode name, CallNode call, string debug
from ControlFlowNode arg, CallNode call, string debug
where
call.getAnArg() = name and
call.getAnArg() = arg and
call.getFunction().(NameNode).getId() = "check" and
if exists(name.pointsTo())
then debug = name.pointsTo().toString()
if exists(arg.pointsTo())
then debug = arg.pointsTo().toString()
else debug = "<MISSING pointsTo()>"
select name, debug
select arg, debug

View File

@@ -0,0 +1,2 @@
| test.py:10:11:10:14 | ControlFlowNode for open | <MISSING pointsTo()> |
| test.py:14:11:14:14 | ControlFlowNode for open | Builtin-function open |

View File

@@ -0,0 +1,10 @@
import python
from ControlFlowNode arg, CallNode call, string debug
where
call.getAnArg() = arg and
call.getFunction().(NameNode).getId() = "check" and
if exists(arg.pointsTo())
then debug = arg.pointsTo().toString()
else debug = "<MISSING pointsTo()>"
select arg, debug

View File

@@ -0,0 +1,18 @@
# Points-to information seems to be missing if our analysis thinks the enclosing function
# is never called. However, as illustrated by the code below, it's easy to fool our
# analysis :(
# This was inspired by a problem in real code, where our analysis doesn't have any
# points-to information about the `open` call in
# https://google-gruyere.appspot.com/code/gruyere.py on line 227
def _func_not_called(filename, mode='rb'):
check(open)
return open(filename, mode)
def _func_called(filename, mode='rb'):
check(open)
return open(filename, mode)
globals()['_func_not_called']('test.txt')
_func_called('test.txt')