Skip to content

Commit e171123

Browse files
committed
Add initial query for CWE-942
1 parent 67a0112 commit e171123

File tree

8 files changed

+231
-0
lines changed

8 files changed

+231
-0
lines changed

javascript/ql/lib/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import semmle.javascript.frameworks.ActionsLib
7171
import semmle.javascript.frameworks.Angular2
7272
import semmle.javascript.frameworks.AngularJS
7373
import semmle.javascript.frameworks.Anser
74+
import semmle.javascript.frameworks.ApolloGraphQL
7475
import semmle.javascript.frameworks.AsyncPackage
7576
import semmle.javascript.frameworks.AWS
7677
import semmle.javascript.frameworks.Azure
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Provides classes for working with Apollo GraphQL connectors.
3+
*/
4+
5+
import javascript
6+
7+
/** Provides classes modeling concepts of Apollo GraphQL. */
8+
module ApolloGraphQL {
9+
/** A string-valued expression that is interpreted as a Apollo GraphQL query. */
10+
abstract class GraphQLString extends DataFlow::Node { }
11+
12+
/** A string-valued expression that is interpreted as a Apollo GraphQL query. */
13+
abstract class ApolloGraphQLServer extends DataFlow::Node { }
14+
}
15+
16+
/**
17+
* Provides classes modeling the apollo packages [@apollo/server](https://npmjs.com/package/@apollo/server`)
18+
*/
19+
private module Apollo {
20+
/** Get an instanceof of `Apollo` */
21+
private API::Node apollo() {
22+
result =
23+
API::moduleImport([
24+
"@apollo/server", "apollo/server", "@apollo/apollo-server-express",
25+
"@apollo/apollo-server-core", "apollo-server", "apollo-server-express"
26+
]).getMember("ApolloServer")
27+
}
28+
29+
/** Get an instanceof of `gql` */
30+
private API::Node gql() {
31+
result =
32+
API::moduleImport([
33+
"@apollo/server", "apollo/server", "@apollo/apollo-server-express",
34+
"@apollo/apollo-server-core", "apollo-server", "apollo-server-express"
35+
]).getMember("gql")
36+
}
37+
38+
/** A string that is interpreted as a GraphQL query by a `octokit` package. */
39+
private class ApolloGraphQLString extends GraphQL::GraphQLString {
40+
ApolloGraphQLString() { this = gql().getACall() }
41+
}
42+
43+
/** A string that is interpreted as a GraphQL query by a `graphql` package. */
44+
private class ApolloServer extends ApolloGraphQL::ApolloGraphQLServer {
45+
ApolloServer() {
46+
this = apollo().getAnInstantiation()
47+
// or this = apollo().getAnInstantiation().getOptionArgument(0, "cors")
48+
}
49+
50+
predicate isPermissive() {
51+
this.(DataFlow::NewNode)
52+
.getOptionArgument(0, "cors")
53+
.getALocalSource()
54+
.getAPropertyWrite("origin")
55+
.getRhs()
56+
.mayHaveBooleanValue(true)
57+
}
58+
}
59+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for reasoning about
3+
* overly permissive CORS configurations, as well as
4+
* extension points for adding your own.
5+
*/
6+
7+
import javascript
8+
9+
module CorsPermissiveConfiguration {
10+
/**
11+
* A data flow source for permissive CORS configuration.
12+
*/
13+
abstract class Source extends DataFlow::Node { }
14+
15+
/**
16+
* A data flow sink for permissive CORS configuration.
17+
*/
18+
abstract class Sink extends DataFlow::Node { }
19+
20+
/**
21+
* A sanitizer for permissive CORS configuration.
22+
*/
23+
abstract class Sanitizer extends DataFlow::Node { }
24+
25+
/** A source of remote user input, considered as a flow source for CORS misconfiguration. */
26+
class RemoteFlowSourceAsSource extends Source instanceof RemoteFlowSource {
27+
RemoteFlowSourceAsSource() { not this instanceof ClientSideRemoteFlowSource }
28+
}
29+
30+
/** true and null are considered bad values */
31+
class BadValues extends Source instanceof DataFlow::Node {
32+
BadValues() { this.mayHaveBooleanValue(true) or this.asExpr() instanceof NullLiteral }
33+
}
34+
35+
/**
36+
* The value of cors origin when initializing the application.
37+
*/
38+
class CorsApolloServer extends Sink, DataFlow::ValueNode {
39+
CorsApolloServer() {
40+
exists(ApolloGraphQL::ApolloGraphQLServer agql |
41+
this =
42+
agql.(DataFlow::NewNode)
43+
.getOptionArgument(0, "cors")
44+
.getALocalSource()
45+
.getAPropertyWrite("origin")
46+
.getRhs()
47+
)
48+
}
49+
}
50+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Provides a dataflow taint tracking configuration for reasoning
3+
* about overly permissive CORS configurations.
4+
*
5+
* Note, for performance reasons: only import this file if
6+
* `CorsPermissiveConfiguration::Configuration` is needed,
7+
* otherwise `CorsPermissiveConfigurationCustomizations` should
8+
* be imported instead.
9+
*/
10+
11+
import javascript
12+
import CorsPermissiveConfigurationCustomizations::CorsPermissiveConfiguration
13+
14+
/**
15+
* A data flow configuration for overly permissive CORS configuration.
16+
*/
17+
class Configuration extends TaintTracking::Configuration {
18+
Configuration() { this = "CorsPermissiveConfiguration" }
19+
20+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
21+
22+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
23+
24+
override predicate isSanitizer(DataFlow::Node node) {
25+
super.isSanitizer(node) or
26+
node instanceof Sanitizer
27+
}
28+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name overly CORS configuration
3+
* @description Misconfiguration of CORS HTTP headers allows CSRF attacks.
4+
* @kind path-problem
5+
* @problem.severity error
6+
* @security-severity 7.5
7+
* @precision high
8+
* @id js/cors-misconfiguration
9+
* @tags security
10+
* external/cwe/cwe-942
11+
*/
12+
13+
import javascript
14+
import semmle.javascript.security.dataflow.CorsPermissiveConfigurationQuery
15+
import DataFlow::PathGraph
16+
17+
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
18+
where cfg.hasFlowPath(source, sink)
19+
select sink.getNode(), source, sink, "$@ misconfiguration due to a $@.", sink.getNode(),
20+
"CORS Origin", source.getNode(), "too permissive or user controlled value"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
nodes
2+
| tst.js:8:9:8:59 | user_origin |
3+
| tst.js:8:23:8:46 | url.par ... , true) |
4+
| tst.js:8:23:8:52 | url.par ... ).query |
5+
| tst.js:8:23:8:59 | url.par ... .origin |
6+
| tst.js:8:33:8:39 | req.url |
7+
| tst.js:8:33:8:39 | req.url |
8+
| tst.js:8:42:8:45 | true |
9+
| tst.js:8:42:8:45 | true |
10+
| tst.js:11:25:11:28 | true |
11+
| tst.js:11:25:11:28 | true |
12+
| tst.js:11:25:11:28 | true |
13+
| tst.js:16:25:16:28 | true |
14+
| tst.js:16:25:16:28 | true |
15+
| tst.js:16:25:16:28 | true |
16+
| tst.js:26:25:26:28 | null |
17+
| tst.js:26:25:26:28 | null |
18+
| tst.js:26:25:26:28 | null |
19+
| tst.js:31:25:31:35 | user_origin |
20+
| tst.js:31:25:31:35 | user_origin |
21+
edges
22+
| tst.js:8:9:8:59 | user_origin | tst.js:31:25:31:35 | user_origin |
23+
| tst.js:8:9:8:59 | user_origin | tst.js:31:25:31:35 | user_origin |
24+
| tst.js:8:23:8:46 | url.par ... , true) | tst.js:8:23:8:52 | url.par ... ).query |
25+
| tst.js:8:23:8:52 | url.par ... ).query | tst.js:8:23:8:59 | url.par ... .origin |
26+
| tst.js:8:23:8:59 | url.par ... .origin | tst.js:8:9:8:59 | user_origin |
27+
| tst.js:8:33:8:39 | req.url | tst.js:8:23:8:46 | url.par ... , true) |
28+
| tst.js:8:33:8:39 | req.url | tst.js:8:23:8:46 | url.par ... , true) |
29+
| tst.js:8:42:8:45 | true | tst.js:8:23:8:46 | url.par ... , true) |
30+
| tst.js:8:42:8:45 | true | tst.js:8:23:8:46 | url.par ... , true) |
31+
| tst.js:11:25:11:28 | true | tst.js:11:25:11:28 | true |
32+
| tst.js:16:25:16:28 | true | tst.js:16:25:16:28 | true |
33+
| tst.js:26:25:26:28 | null | tst.js:26:25:26:28 | null |
34+
#select
35+
| tst.js:11:25:11:28 | true | tst.js:11:25:11:28 | true | tst.js:11:25:11:28 | true | $@ misconfiguration due to a $@. | tst.js:11:25:11:28 | true | CORS Origin | tst.js:11:25:11:28 | true | too permissive or user controlled value |
36+
| tst.js:16:25:16:28 | true | tst.js:16:25:16:28 | true | tst.js:16:25:16:28 | true | $@ misconfiguration due to a $@. | tst.js:16:25:16:28 | true | CORS Origin | tst.js:16:25:16:28 | true | too permissive or user controlled value |
37+
| tst.js:26:25:26:28 | null | tst.js:26:25:26:28 | null | tst.js:26:25:26:28 | null | $@ misconfiguration due to a $@. | tst.js:26:25:26:28 | null | CORS Origin | tst.js:26:25:26:28 | null | too permissive or user controlled value |
38+
| tst.js:31:25:31:35 | user_origin | tst.js:8:33:8:39 | req.url | tst.js:31:25:31:35 | user_origin | $@ misconfiguration due to a $@. | tst.js:31:25:31:35 | user_origin | CORS Origin | tst.js:8:33:8:39 | req.url | too permissive or user controlled value |
39+
| tst.js:31:25:31:35 | user_origin | tst.js:8:42:8:45 | true | tst.js:31:25:31:35 | user_origin | $@ misconfiguration due to a $@. | tst.js:31:25:31:35 | user_origin | CORS Origin | tst.js:8:42:8:45 | true | too permissive or user controlled value |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE-942/CorsPermissiveConfiguration.ql
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { ApolloServer } from 'apollo-server';
2+
var https = require('https'),
3+
url = require('url');
4+
5+
var server = https.createServer(function () { });
6+
7+
server.on('request', function (req, res) {
8+
let user_origin = url.parse(req.url, true).query.origin;
9+
// BAD: attacker can choose the value of origin
10+
const server_1 = new ApolloServer({
11+
cors: { origin: true }
12+
});
13+
14+
// BAD: CORS too permissive
15+
const server_2 = new ApolloServer({
16+
cors: { origin: true }
17+
});
18+
19+
// GOOD: restrictive CORS
20+
const server_3 = new ApolloServer({
21+
cors: false
22+
});
23+
24+
// BAD: CORS too permissive
25+
const server_4 = new ApolloServer({
26+
cors: { origin: null }
27+
});
28+
29+
// BAD: CORS is controlled by user
30+
const server_5 = new ApolloServer({
31+
cors: { origin: user_origin }
32+
});
33+
});

0 commit comments

Comments
 (0)