JS: add MembershipTests.qll#3415
Conversation
There was a problem hiding this comment.
Cool! Very comprehensive. 👏
It seems like a lot of code to save 9 lines of code 😅 but I guess time will tell if it pays off.
I couldn't help poke at the tuple counts a bit to see what might explain the speed-up. It seems barrier guards now rely slightly less on magic so maybe that's it? My previous attempts at getting rid of that magic cascaded into other misoptimizations so kudos for wobbling the optimizer in the right direction 😄.
Do we still need the ConstantComparison sanitizer guard or is it completely generalized by MembershipTest? (For retaining backwards compatibility, it should suffice to just remove its AdditionalSanitizerGuard base class while deprecating it).
| /** | ||
| * A test for whether a candidate is a member of an array. | ||
| */ | ||
| class ArrayMembershipTest extends OrdinaryInclusionTest { |
There was a problem hiding this comment.
Either make this private or expose array via a predicate.
Did you mean for every class here to be public? It's a lot to add to the global scope brought in by import javascript.
There was a problem hiding this comment.
I have made most classes private, and wrapped them all in MembershipTestImplementations.
| class SwitchCaseMembershipTest extends MembershipTest::Range, DataFlow::ValueNode { | ||
| SwitchStmt switch; | ||
|
|
||
| SwitchCaseMembershipTest() { this = switch.getACase().getExpr().flow() } |
There was a problem hiding this comment.
Hm, I think we're missing a specification of what happens with the result of the check. From the description of MembershipTest I'd expect this to be the result of the check (i.e. a boolean) but here, this refers to the case expression.
There was a problem hiding this comment.
That is an excellent point. It does not look like we have any nodes for the implicit comparison that is performed at the case, so the choice of this here is arbitrary.
To avoid feature creep, I have removed this class from the PR.
Looking forward: do we lean towards add a new synthetic data flow node for the implicit case comparisons, or towards complicating MembershipTest with an optional getResultNode (this won't solve the problem of where the test occurs)?
| or | ||
| exists(EqualityTest eq, Expr null | | ||
| eq.flow() = this and | ||
| polarity = eq.getPolarity() and |
There was a problem hiding this comment.
Shouldn't there be a booleanNot here? (Equal to null implies falsy)
I think it will, we are moving towards spotting blacklist usage in general, and the added lines are not just a refactoring:
Yeah, I got lucky with the wobble. We'll know more when nightly/security/dpm finishes tomorrow.
It is completely generalized. |
| */ | ||
| class ArrayMembershipTest extends OrdinaryInclusionTest { | ||
| DataFlow::ArrayCreationNode array; | ||
| module MembershipTestImplementations { |
There was a problem hiding this comment.
I'm a little sad to see yet another naming convention emerge here.
This is public API (it's used by a query) so it shouldn't be half-hidden behind a name like "implementations". Looking at the use-case in the prototype pollution query, it doesn't strike me as an implementation detail that there are different kinds of membership tests, so I think we should just merge it with the MembershipTest module itself.
There was a problem hiding this comment.
Good point. I have merged the two modules.
|
The dpm evaluation is more neutral, so it looks like the rebase, 7139422, or solar flares unwobbled the optimizer. |
|
Hm, likely a fluke then but maybe run a smoke-test to verify if addressing the last few commits caused it. What's up with |
|
I accidentally aborted extraction of that project earlier, so dist-compare
simply skipped the analysis of the project during the rerun.
… |
Will do.
Two test-FPs that have disappeared due to the following pattern where we wrongly classify something as a whitelist check. newNumber = prefix + Math.floor(Math.random() * 10000); // TAINTED
if (currentList.indexOf(newNumber) === -1) { // SANITIZED (wrongly)
currentList.push(newNumber);We could fiddle (later) with the membership tests to ensure that this non-duplication pattern is supported, but that shouldn't matter in general for taint analysis. |
|
My worry is that the following is now being seen as a sanitizer in the true case, which it clearly shouldn't be: if (currentList.indexOf(newNumber) === -1)From your comment it's not clear to me why this isn't considered a problem. It's a bug that was introduced by this PR. My theory is now that the above is three kinds of membership tests at once:
As a result, the call to I remember writing a comment about this in my original review of the two equality membership classes and I'm quite frustrated to find that it has simply disappeared. The problem is that when classes have overlapping |
|
Coming back at this, maybe a less invasive change is to explicitly exclude equality tests that are also |
|
How about changing the class structure such that the candidate is used in the charpred? That ought to be functional. class MembershipTestCandidate ... {
DataFlow::Node getTest() { ... }
boolean getTestPolarity(){ ... }
DataFlow::Node getATestMemberNode() { ... }
string getATestMemberString() { ... }
} |
|
That might work. Technically we could even bring back the |
d733d24 to
3cc6ae4
Compare
Done. The changes are mainly textual and then ther required candidate/test swapping.
Done. A new evaluation is required. |
|
Evaluation on nightly/security has excellent performance. |
…chemeCheck.js Co-authored-by: Asger F <asgerf@github.com>
|
After this is merged we could look at a generalization to support the sanitizer guard in https://github.com/github/codeql-javascript-CVE-coverage/issues/714. if (typeof type !== 'undefined' && RRecordTypes.indexOf(type) === -1) {
console.error('Invalid rrtype:', type);
return null;
} |
|
Can get a thumbs up on the conflict resolution? |
Addresses https://github.com/github/codeql-javascript-team/issues/93
Adds
MembershipTest, as a generalization ofInclusionTest. I am open to merging it all into the existingInclusionTest, but given the array- and string-focused docstrings onInclusionTest, I would rather avoid breakage and simply have a slightly ambigous pair of class names.The new class improves our taint sanitizers and simplifies
js/incomplete-url-scheme-check. The most controversial part of this PR is that equality tests are now treated as sanitizers regardless of whether they have a constant side or not.The sanitizer improvement apparently triggers a performance boost. I am re-running the latest version (with a fix for the missing prototype-pollution results).
I have built this on top of #3391, so if this is reviewed early, the last commit should to be reviewed instead of the entire diff.