Merge pull request #19211 from asgerf/js/pp-unit-tests

Support post-procesed inline expectations for query predicates in unit tests
This commit is contained in:
Asger F
2025-04-03 16:00:18 +02:00
committed by GitHub
5 changed files with 68 additions and 33 deletions

View File

@@ -31,14 +31,14 @@ sensitiveAction
| tst.js:23:1:23:25 | require ... .exit() |
| tst.js:24:1:24:21 | global. ... .exit() |
sensitiveExpr
| tst.js:1:1:1:8 | password |
| tst.js:2:1:2:8 | PassWord |
| tst.js:3:1:3:21 | myPassw ... eartext |
| tst.js:4:1:4:10 | x.password |
| tst.js:5:1:5:11 | getPassword |
| tst.js:5:1:5:13 | getPassword() |
| tst.js:6:1:6:13 | x.getPassword |
| tst.js:6:1:6:15 | x.getPassword() |
| tst.js:7:1:7:15 | get("password") |
| tst.js:8:1:8:17 | x.get("password") |
| tst.js:21:1:21:6 | secret |
| tst.js:1:1:1:8 | password | password |
| tst.js:2:1:2:8 | PassWord | password |
| tst.js:3:1:3:21 | myPassw ... eartext | password |
| tst.js:4:1:4:10 | x.password | password |
| tst.js:5:1:5:11 | getPassword | password |
| tst.js:5:1:5:13 | getPassword() | password |
| tst.js:6:1:6:13 | x.getPassword | password |
| tst.js:6:1:6:15 | x.getPassword() | password |
| tst.js:7:1:7:15 | get("password") | password |
| tst.js:8:1:8:17 | x.get("password") | password |
| tst.js:21:1:21:6 | secret | secret |

View File

@@ -20,4 +20,4 @@ query predicate processTermination(NodeJSLib::ProcessTermination term) { any() }
query predicate sensitiveAction(SensitiveAction ac) { any() }
query predicate sensitiveExpr(SensitiveNode e) { any() }
query predicate sensitiveExpr(SensitiveNode e, string kind) { kind = e.getClassification() }

View File

@@ -0,0 +1,2 @@
query: tests.ql
postprocess: utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,11 +1,11 @@
password;
PassWord;
myPasswordInCleartext;
x.password;
getPassword();
x.getPassword();
get("password");
x.get("password");
password; // $ cleartextPasswordExpr sensitiveExpr=password
PassWord; // $ cleartextPasswordExpr sensitiveExpr=password
myPasswordInCleartext; // $ cleartextPasswordExpr sensitiveExpr=password
x.password; // $ cleartextPasswordExpr sensitiveExpr=password
getPassword(); // $ cleartextPasswordExpr sensitiveExpr=password
x.getPassword(); // $ cleartextPasswordExpr sensitiveExpr=password
get("password"); // $ cleartextPasswordExpr sensitiveExpr=password
x.get("password"); // $ cleartextPasswordExpr sensitiveExpr=password
hashed_password;
password_hashed;
@@ -15,13 +15,13 @@ hashedPassword;
var exit = require('exit');
var e = process.exit;
e();
exit();
e(); // $ processTermination sensitiveAction
exit(); // $ processTermination sensitiveAction
secret;
secret; // $ sensitiveExpr=secret
require("process").exit();
global.process.exit();
require("process").exit(); // $ processTermination sensitiveAction
global.process.exit(); // $ processTermination sensitiveAction
get("https://example.com/news?password=true")
get("https://username:password@example.com")

View File

@@ -858,17 +858,26 @@ module TestPostProcessing {
bindingset[result]
string getARelevantTag() { any() }
predicate tagMatches = PathProblemSourceTestInput::tagMatches/2;
bindingset[expectedTag, actualTag]
predicate tagMatches(string expectedTag, string actualTag) {
PathProblemSourceTestInput::tagMatches(expectedTag, actualTag)
or
not exists(getQueryKind()) and
expectedTag = actualTag
}
bindingset[expectedTag]
predicate tagIsOptional(string expectedTag) {
// ignore irrelevant tags
not expectedTag.regexpMatch(getTagRegex())
or
// ignore tags annotated with a query ID that does not match the current query ID
exists(string queryId |
queryId = expectedTag.regexpCapture(getTagRegex(), 3) and
queryId != getQueryId()
exists(getQueryKind()) and
(
// ignore irrelevant tags
not expectedTag.regexpMatch(getTagRegex())
or
// ignore tags annotated with a query ID that does not match the current query ID
exists(string queryId |
queryId = expectedTag.regexpCapture(getTagRegex(), 3) and
queryId != getQueryId()
)
)
}
@@ -911,6 +920,28 @@ module TestPostProcessing {
not hasPathProblemSink(row, location, _, _)
}
/**
* Holds if a custom query predicate implies `tag=value` at the given `location`.
*
* Such query predicates are only allowed in kind-less queries, usually in the form
* of a `.ql` file in a test folder, with a same-named `.qlref` file to enable
* post-processing for that test.
*/
private predicate hasCustomQueryPredicateResult(
int row, TestLocation location, string element, string tag, string value
) {
not exists(getQueryKind()) and
queryResults(tag, row, 0, location.getRelativeUrl()) and
queryResults(tag, row, 1, element) and
(
queryResults(tag, row, 2, value) and
not queryResults(tag, row, 3, _) // ignore if arity is greater than expected
or
not queryResults(tag, row, 2, _) and
value = "" // allow value-less expectations for unary predicates
)
}
/**
* Gets the expected value for result row `row`, if any. This value must
* match the value at the corresponding path-problem source (if it is
@@ -939,6 +970,8 @@ module TestPostProcessing {
or
value = getValue(row)
)
or
hasCustomQueryPredicateResult(_, location, element, tag, value)
}
}