Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b920fba
Adds support for npm and xsjslib
mbaluda Aug 26, 2024
2e6d302
Merge branch 'main' into mbaluda/xsjslib
mbaluda Aug 26, 2024
f31c6ee
Add support for XSS sanitizer lib
mbaluda Aug 28, 2024
f70a4e2
Reapply "Replace location in external file"
mbaluda Aug 28, 2024
3859a18
modify expected file
mbaluda Aug 28, 2024
44f7acf
Format cds files
mbaluda Aug 28, 2024
8b0b5ac
Fix url
mbaluda Aug 28, 2024
5aef039
changed query @id
mbaluda Aug 28, 2024
1530e1d
expected
mbaluda Aug 29, 2024
90ceb4c
update qlpack
mbaluda Aug 29, 2024
9845134
try using hasLocationInfo
mbaluda Aug 29, 2024
6d62c81
Merge branch 'main' into mbaluda/xsjslib
mbaluda Sep 3, 2024
6f25abd
Merge branch 'main' into mbaluda/external-cds
mbaluda Sep 6, 2024
aee5bd2
Update code_scanning.yml
mbaluda Sep 6, 2024
6d1b531
index cds files
mbaluda Sep 6, 2024
3b4c070
fix expected file
mbaluda Sep 6, 2024
fe09676
Correct location in tests and code scanning
mbaluda Sep 16, 2024
e298c08
Update expected SARIF file
mbaluda Sep 16, 2024
1672d5b
modify alert location
mbaluda Sep 16, 2024
f0b07bc
standardized query ids
mbaluda Sep 16, 2024
f524389
update expected file
mbaluda Sep 16, 2024
614695d
fix tests
mbaluda Sep 16, 2024
c035863
fix location
mbaluda Sep 16, 2024
d3810c4
Update expected files
mbaluda Sep 16, 2024
61bc15e
Merge branch 'mbaluda/external-cds' into mbaluda/xsjslib
mbaluda Sep 17, 2024
62c0d0a
Deals with external .cds files
mbaluda Sep 19, 2024
43b4427
Fix error message
mbaluda Sep 19, 2024
dce19fd
Error message updated based on SAP feedback
mbaluda Sep 19, 2024
c47778d
Address review
mbaluda Sep 20, 2024
a769f99
Update to CodeQL v2.19.0
mbaluda Sep 23, 2024
ce599be
Deals with external .cds files
mbaluda Sep 19, 2024
73ac321
Address review
mbaluda Sep 20, 2024
2ffb055
Update to CodeQL v2.19.0
mbaluda Sep 23, 2024
fe26838
Update code_scanning.yml
mbaluda Sep 6, 2024
ddbfcc9
index cds files
mbaluda Sep 6, 2024
18896cb
update expected file
mbaluda Sep 16, 2024
a310fd0
Xsjslib modules
mbaluda Sep 23, 2024
4ed1079
Merge branch 'mbaluda/cds' into mbaluda/xsjslib
mbaluda Sep 23, 2024
7f58632
Update expected file
mbaluda Sep 23, 2024
bbec0c7
xss-secure sanitizers
mbaluda Sep 24, 2024
cc8990d
XSJSLibModules
mbaluda Sep 26, 2024
21509a8
remove duplicated test
mbaluda Sep 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion javascript/frameworks/xsjs/ext/xsjs.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,5 @@ extensions:
pack: codeql/javascript-all
extensible: summaryModel
data:
- [global, "Member[JSON].Member[parse]", "Argument[0]", "ReturnValue", taint]
- [global, "Member[JSON].Member[parse]", "Argument[0]", "ReturnValue", taint]
- ["@sap/xss-secure", "Member[encodeCSS,encodeHTML,encodeJS,encodeURL,encodeXML]", "Argument[0]", "ReturnValue", "taint"]
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import javascript
import DataFlow
import XSJSLibModules

/**
* The root XSJS namespace, accessed as a dollar sign (`$`) symbol.
*/
class XSJSDollarNamespace extends GlobalVarRefNode {
XSJSDollarNamespace() {
this = globalVarRef("$") and
this.getFile().getExtension() = "xsjs"
class XSJSDollarNode extends DataFlow::SourceNode {
XSJSDollarNode() {
this.accessesGlobal("$") and
this.getFile().getExtension() = ["xsjs", "xsjslib"]
}
}

/**
* `TypeModel` for `XSJSDollarNamespace`.
* `TypeModel` for `XSJSDollarNode`.
*/
class XSJSDollarTypeModel extends ModelInput::TypeModel {
override DataFlow::Node getASource(string type) {
type = "XsjsDollar" and
result = any(XSJSDollarNamespace dollar)
result = any(XSJSDollarNode dollar)
}

/**
Expand All @@ -37,9 +38,9 @@ class XSJSRequestOrResponse extends SourceNode instanceof PropRef {
XSJSRequestOrResponse() {
fieldName = ["request", "response"] and
(
exists(XSJSDollarNamespace dollar | this = dollar.getAPropertyReference(fieldName))
exists(XSJSDollarNode dollar | this = dollar.getAPropertyReference(fieldName))
or
exists(XSJSDollarNamespace dollar |
exists(XSJSDollarNode dollar |
this =
dollar
.getAPropertyReference(fieldName)
Expand Down Expand Up @@ -171,7 +172,7 @@ class XSJSDatabaseConnectionReference extends MethodCallNode {
string subNamespace;

XSJSDatabaseConnectionReference() {
exists(XSJSDollarNamespace dollar |
exists(XSJSDollarNode dollar |
this.getMethodName() = "getConnection" and
this.getReceiver().getALocalSource() = dollar.getAPropertyReference(subNamespace)
)
Expand Down Expand Up @@ -211,7 +212,7 @@ class XSJSHDBConnectionReference extends XSJSDatabaseConnectionReference {

class XSJSUtilNamespace extends SourceNode instanceof PropRef {
XSJSUtilNamespace() {
exists(XSJSDollarNamespace dollar | this = dollar.getAPropertyReference("util"))
exists(XSJSDollarNode dollar | this = dollar.getAPropertyReference("util"))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/** Provides classes for working with XSJSLib modules. */

import javascript

/**
* An XSJS module.
*/
class XSJSModule extends Module {
XSJSModule() { this.getFile().getExtension() = ["xsjs", "xsjslib"] }

override XSJSImportExpr getAnImport() { result.getTopLevel() = this }

/** Gets a module from which this module imports. */
override XSJSModule getAnImportedModule() { result = this.getAnImport().getImportedModule() }

override DataFlow::ValueNode getAnExportedValue(string name) {
result.getFile() = this.getFile() and
exists(FunctionDeclStmt fds | fds = result.getAstNode() |
fds.getParent() instanceof TopLevel and
name = fds.getName()
)
}
}

/**
* An import declaration.
*
* Example:
*
* ```
* $.import("module.xsjslib");
* ```
*/
class XSJSImportExpr extends CallExpr, Import {
XSJSImportExpr() {
this.getReceiver().(GlobalVarAccess).getName() = "$" and
this.getFile().getExtension() = ["xsjs", "xsjslib"] and
this.getCalleeName() = "import" and
this.getNumArgument() = 1
}

override Module getEnclosingModule() { result = this.getTopLevel() }

override XSJSLiteralImportPath getImportedPath() { result = this.getArgument(0) }

override DataFlow::Node getImportedModuleNode() { result = DataFlow::valueNode(this) }
}

/** A literal path expression appearing in an `import` declaration. */
private class XSJSLiteralImportPath extends PathExpr, ConstantString {
XSJSLiteralImportPath() { exists(XSJSImportExpr req | this = req.getArgument(0)) }

override Folder getAdditionalSearchRoot(int priority) {
priority = 0 and
result = this.getFile().getParentContainer()
}

override string getValue() { result = this.getStringValue() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,14 @@ class Configuration extends TaintTracking::Configuration {
)
)
}

override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node)
or
node instanceof DomBasedXss::Sanitizer
or
node =
DataFlow::moduleMember("@sap/xss-secure",
["encodeCSS", "encodeHTML", "encodeJS", "encodeURL", "encodeXML"]).getACall().getArgument(0)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
| npm.xsjs:3:14:3:71 | crypto. ... 1024 }) |
| npm.xsjs:5:15:5:72 | crypto. ... 4096 }) |
4 changes: 4 additions & 0 deletions javascript/frameworks/xsjs/test/models/modules/npm/npm.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import javascript

from CryptographicKeyCreation crypto
select crypto
5 changes: 5 additions & 0 deletions javascript/frameworks/xsjs/test/models/modules/npm/npm.xsjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const crypto = $.require("crypto");

const bad1 = crypto.generateKeyPairSync("rsa", { modulusLength: 1024 }); // NOT OK

const good1 = crypto.generateKeyPairSync("rsa", { modulusLength: 4096 }); // OK
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
| xsjslib.xsjs:1:1:4:0 | <toplevel> |
| xsjslib.xsjslib:1:1:15:2 | <toplevel> |
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import javascript
import advanced_security.javascript.frameworks.xsjs.XSJSLibModules

select any(Module m)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var injection1=$.import("xsjslib.xsjslib");

injection1.test1(requestParameters);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* True positive case: Sink being `$.db.Connection.prepareStatement(query, arguments...)`,
* the API in the older namespace. The remote value is concatenated raw into the query.
*/
function test1(requestParameters) {
let someParameterValue1 = JSON.parse(requestParameters.get("someParameter1"));
let someParameterValue2 = JSON.parse(requestParameters.get("someParameter2"));
let query = "INSERT INTO " + someParameterValue1 + ".ENTITY (COL1) VALUES (" + someParameterValue2 + ")";

let dbConnection = $.db.getConnection();
let preparedStatement = dbConnection.prepareStatement(query);
preparedStatement.executeUpdate();
dbConnection.commit();
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
nodes
| lib/injection1.xsjslib:6:7:6:79 | someParameterValue1 |
| lib/injection1.xsjslib:6:29:6:79 | JSON.pa ... ter1")) |
| lib/injection1.xsjslib:6:40:6:78 | request ... eter1") |
| lib/injection1.xsjslib:6:40:6:78 | request ... eter1") |
| lib/injection1.xsjslib:7:7:7:79 | someParameterValue2 |
| lib/injection1.xsjslib:7:29:7:79 | JSON.pa ... ter2")) |
| lib/injection1.xsjslib:7:40:7:78 | request ... eter2") |
| lib/injection1.xsjslib:7:40:7:78 | request ... eter2") |
| lib/injection1.xsjslib:8:7:8:106 | query |
| lib/injection1.xsjslib:8:15:8:106 | "INSERT ... 2 + ")" |
| lib/injection1.xsjslib:8:32:8:50 | someParameterValue1 |
| lib/injection1.xsjslib:8:82:8:100 | someParameterValue2 |
| lib/injection1.xsjslib:11:57:11:61 | query |
| lib/injection1.xsjslib:11:57:11:61 | query |
edges
| lib/injection1.xsjslib:6:7:6:79 | someParameterValue1 | lib/injection1.xsjslib:8:32:8:50 | someParameterValue1 |
| lib/injection1.xsjslib:6:29:6:79 | JSON.pa ... ter1")) | lib/injection1.xsjslib:6:7:6:79 | someParameterValue1 |
| lib/injection1.xsjslib:6:40:6:78 | request ... eter1") | lib/injection1.xsjslib:6:29:6:79 | JSON.pa ... ter1")) |
| lib/injection1.xsjslib:6:40:6:78 | request ... eter1") | lib/injection1.xsjslib:6:29:6:79 | JSON.pa ... ter1")) |
| lib/injection1.xsjslib:7:7:7:79 | someParameterValue2 | lib/injection1.xsjslib:8:82:8:100 | someParameterValue2 |
| lib/injection1.xsjslib:7:29:7:79 | JSON.pa ... ter2")) | lib/injection1.xsjslib:7:7:7:79 | someParameterValue2 |
| lib/injection1.xsjslib:7:40:7:78 | request ... eter2") | lib/injection1.xsjslib:7:29:7:79 | JSON.pa ... ter2")) |
| lib/injection1.xsjslib:7:40:7:78 | request ... eter2") | lib/injection1.xsjslib:7:29:7:79 | JSON.pa ... ter2")) |
| lib/injection1.xsjslib:8:7:8:106 | query | lib/injection1.xsjslib:11:57:11:61 | query |
| lib/injection1.xsjslib:8:7:8:106 | query | lib/injection1.xsjslib:11:57:11:61 | query |
| lib/injection1.xsjslib:8:15:8:106 | "INSERT ... 2 + ")" | lib/injection1.xsjslib:8:7:8:106 | query |
| lib/injection1.xsjslib:8:32:8:50 | someParameterValue1 | lib/injection1.xsjslib:8:15:8:106 | "INSERT ... 2 + ")" |
| lib/injection1.xsjslib:8:82:8:100 | someParameterValue2 | lib/injection1.xsjslib:8:15:8:106 | "INSERT ... 2 + ")" |
#select
| lib/injection1.xsjslib:11:57:11:61 | query | lib/injection1.xsjslib:6:40:6:78 | request ... eter1") | lib/injection1.xsjslib:11:57:11:61 | query | This query depends on a $@. | lib/injection1.xsjslib:6:40:6:78 | request ... eter1") | user-provided value |
| lib/injection1.xsjslib:11:57:11:61 | query | lib/injection1.xsjslib:7:40:7:78 | request ... eter2") | lib/injection1.xsjslib:11:57:11:61 | query | This query depends on a $@. | lib/injection1.xsjslib:7:40:7:78 | request ... eter2") | user-provided value |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
XSJSSqlInjection/XSJSSqlInjection.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
$.import("lib/injection1.xsjslib");
let t1=$.import("lib/injection2.xsjslib");
$.import("lib/injection3.xsjslib");
$.import("lib/injection4.xsjslib");

var requestParameters = $.request.parameters;

test1(requestParameters);
t1.test1(requestParameters);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* True positive case: Sink being `$.db.Connection.prepareStatement(query, arguments...)`,
* the API in the older namespace. The remote value is concatenated raw into the query.
*/
function test1(requestParameters) {
let someParameterValue1 = JSON.parse(requestParameters.get("someParameter1"));
let someParameterValue2 = JSON.parse(requestParameters.get("someParameter2"));
let query = "INSERT INTO " + someParameterValue1 + ".ENTITY (COL1) VALUES (" + someParameterValue2 + ")";

let dbConnection = $.db.getConnection();
let preparedStatement = dbConnection.prepareStatement(query);
preparedStatement.executeUpdate();
dbConnection.commit();
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,16 @@ function test3(requestParameters) {
test1(requestParameters);
test2(requestParameters);
test3(requestParameters);

/**
* False positive case: the value is sanitized
*/
var xssSecure = $.require('@sap/xss-secure');
function test4(requestParameters) {
let someParameterValue4 = requestParameters.get("someParameter4");
$.response.contentType = "text/html";
$.response.setBody(requestParameterHandler(xssSecure.encodeHTML(someParameterValue4)));

Check failure

Code scanning / CodeQL

XSJS Reflected XSS High test

Reflected XSS vulnerability due to user-provided value.
$.response.status = $.net.http.OK;
}

test4(requestParameters);
Loading