JS: Fix handling of constant array index reads, and fix the fallout#17412
Merged
asgerf merged 25 commits intoSep 18, 2024
Conversation
For ContentSet it is ambiguous whether asArrayIndex() should get a singleton content set, or the KnownArrayElement content set. The user will now have to choose between asSingleton().asArrayIndex() or ContentSet::arrayElementKnown.
OptionalStep[foo] and OptionalBarrier[foo] contribute steps/barriers that are not active by default, but can be opted into by specific queries or for specific flow states. (Will be used in the following commits)
This summary uses the notion of optional steps/barriers so it becomes configurable whether there is flow into the zero'th array element. Also makes sure we handle the second-argument version of split().
Array.prototype.shift only returns the first array element. The mutation of Argument[this] is not yet modelled, and is better handled when we have use-use flow.
TaintedUrlSuffix is currently only used in TaintTracking configs meaning it is already propagated by taint steps. The inclusion of these taint steps here however meant that implicit reads could appear prior to any of these steps. This was is problematic for PropRead steps as an expression like x[0] could spuriously read from array element 1 via the path: x [element 1] x [empty access path] (after implicit read) x[0] (taint step through PropRead)
A URL of form https://example.com?evil#bar will contain '?evil' after splitting out the '#' suffix, and vice versa.
Preserving tainted-url-suffix into array element 0 seemed like a good idea, but didn't work out so well.
5f90820 to
1df69ec
Compare
erik-krogh
approved these changes
Sep 17, 2024
erik-krogh
left a comment
Contributor
There was a problem hiding this comment.
Really nice work again 👍
| input = "Argument[this].ArrayElement[0]" and | ||
| output = "ReturnValue" | ||
| or | ||
| // ArrayElement[0] is not automatically converted to a taint step, so add it manually |
Contributor
There was a problem hiding this comment.
I don't think this comment is placed at the right place?
Or you meant Argument[this] instead of ArrayElement[0]?
Contributor
Author
There was a problem hiding this comment.
It refers to the ArrayElement[0] in the summary above here.
ArrayElement is automatically converted to a taint step, but ArrayElement[0] is not, which is why we need this extra summary.
Contributor
There was a problem hiding this comment.
Oh, now I get it.
Maybe a reword like:
// The above doesn't add a taint-step from the array itself to the return value, so we add that manually.
erik-krogh
approved these changes
Sep 17, 2024
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Previously, when reading an array element at a known index, we did not read values stored at an unknown index. We now properly read from unknown array indices.
However, this interacted poorly with how we handled
split('#')andsplit('?')calls inTaintedUrlSuffix. In this case we want to block flow into the first element of the resulting array, while also transitioning from thetainted-url-suffixstate to thetaintstate. Instead of the various hacks we've employed in the past, this is now modelled with the help of flow summaries.Optional steps: Flow summaries for flow state and query-specific steps
Flow summaries cannot be query-specific and don't know anything about flow states (because those are query-specific), but I've adopted a solution where a flow summary can contribute so-called optional steps and barriers identified by a name, which queries can then opt into using their name.
So we use this summary for
split('#')andsplit('?'):And we then use those steps in our queries (here simplifying the indirection through the
TaintedUrlSuffix.qlllibrary)The barrier is used to disable the default behaviour of the split summary (which taints the whole array), and the step then enables flow into the array element at index 1.
At the moment this is hidden in
DataFlowPrivatebut we could consider exposing the feature more widely in the future.Evaluation
Evaluation looks good, it fixes 8 FPs and has neutral perf. Since that's based on a slightly older version of the PR, a new run is also underway.