Yet another SSRF query for Javascript#6714
Conversation
|
Please auto-format your (QL) code. |
| exists(DataFlow::Node falseNode | | ||
| this.getAPredecessor+() = falseNode and | ||
| falseNode.asExpr().(BooleanLiteral).mayHaveBooleanValue(false) | ||
| ) and |
There was a problem hiding this comment.
You should be able to simplify this to this.getAPredecessor+().asExpr().(BooleanLiteral).mayHaveBooleanValue(false), I think.
asgerf
left a comment
There was a problem hiding this comment.
👍 Thanks for the submission. It's very valuable to get feedback on our queries like this, and in time we'll most likely incorporate the new sanitizers into the standard library.
For now, please see my inline comment regarding the heuristic that was supposedly removed. I'd like to get clarity on what happened here and that the query works as described.
| override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) { | ||
| sanitizingPrefixEdge(source, sink) | ||
| } |
There was a problem hiding this comment.
In the related issue, you wrote this:
The original query has a heuristic where this case request(http://example.com/${base}${tainted}`);` is assumed OK to avoid false positives. We removed such heuristic. Our query flags this same case as SSRF.
However, the sanitizingPrefixEdge predicate actually includes the heuristic you meant to remove.
I've included a suggestion below which makes the query behave as you described (removing the heuristic):
| override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) { | |
| sanitizingPrefixEdge(source, sink) | |
| } | |
| private predicate hasSanitizingSubstring(DataFlow::Node nd) { | |
| nd.getStringValue().regexpMatch(".*[?#].*") | |
| or | |
| hasSanitizingSubstring(StringConcatenation::getAnOperand(nd)) | |
| or | |
| hasSanitizingSubstring(nd.getAPredecessor()) | |
| } | |
| private predicate strictSanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sink) { | |
| exists(DataFlow::Node operator, int n | | |
| StringConcatenation::taintStep(source, sink, operator, n) and | |
| hasSanitizingSubstring(StringConcatenation::getOperand(operator, [0 .. n - 1])) | |
| ) | |
| } | |
| override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) { | |
| strictSanitizingPrefixEdge(source, sink) | |
| } |
There was a problem hiding this comment.
You're right. Sorry, I mixed it up with a similar query for Go (we did a more significant change there and I though we'd done that for Javascript too). Now I remember, we tried to remove that heuristic but couldn't make it work.
We're testing your suggestion now :)
|
Is it normal for "Checks (Github Actions)" to last so long? It's been stuck there for a month now, and I don't know how to relaunch it. |
|
Hi again @valeria-meli, Sorry for the long wait. It took longer than expected to assess the precision of the query. I've started the tests (for external contributions the tests need to be triggered by staff) and will proceed with a merge decision shortly. |
|
@valeria-meli could you run the auto-formatter one more time? That's the only failing check. Otherwise LGTM. |
This query finds SSRFs (Server Side Request Forgeries). This is intended to be an alternative to the current Request Forgery query.
Why a new query?
We use codeql queries at Mercado Libre to block pull requests. We need them to be very precise.
We recommend input validation, not URL escaping, when fixing a SSRF. Some NGINx based routing tend to decode before routing, making our URL escape useless. But input validation always works.
So we adapted the original query to this blocking context and to accept different forms of input validation.