Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS: Improve handling of spread arguments and rest parameters [shared data flow branch] #17213

Merged
merged 37 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4cdaccd
JS: Add InlineFlowTest
asgerf Aug 7, 2024
5d77c33
Test case for spread and rest args/params
asgerf Aug 8, 2024
6c7d745
JS: Add nodes for static/dynamic argument/parameter arrays
asgerf Aug 9, 2024
a72f795
JS: Add corresponding argument positions
asgerf Aug 9, 2024
623dbda
Do not pass regular positional args into the rest parameter
asgerf Aug 12, 2024
fa7ad03
JS: Add store/load steps for the new argument arrays
asgerf Aug 9, 2024
ed33a6e
JS: Add explicit model of .join()
asgerf Aug 12, 2024
bbb1c8c
Remove old arguments-array position
asgerf Aug 12, 2024
60c3d07
Update DataFlowImplConsistency.qll
asgerf Aug 12, 2024
c04f0be
Update DataFlowConsistency.expected
asgerf Aug 12, 2024
5c7e623
JS: Add some tests for missing handling of dynamic args in flow summa…
asgerf Aug 12, 2024
53a2a66
Add new nodes to early stage
asgerf Aug 12, 2024
acdc896
JS: Support for dynamic args to flow summaries
asgerf Aug 12, 2024
6a08313
JS: Hide some nodes
asgerf Aug 12, 2024
079a622
JS: Add tests showing missing taint flow
asgerf Aug 14, 2024
895cb87
JS: Add taint into dynamic argument array
asgerf Aug 14, 2024
5084d02
Update tests.expected
asgerf Aug 14, 2024
ac1dd18
JS: Remove taint step from array element to whole array
asgerf Aug 14, 2024
34e6864
JS: Note issue with .apply() calls
asgerf Aug 14, 2024
4389b5c
JS: Fix issue for .apply() calls
asgerf Aug 14, 2024
4e7bd9d
JS: Update Arrays test now that array elements do not taint the whole…
asgerf Aug 19, 2024
df42e7c
JS: Add test showing lack of implicit reads for ArrayElement
asgerf Aug 19, 2024
371f7ef
JS: Add implicit taint read of array elements
asgerf Aug 19, 2024
aa8bd33
JS: Add a few more tests
asgerf Aug 22, 2024
3e196f8
JS: Update Promises/flow2 test
asgerf Aug 23, 2024
2e2181b
JS: Update test output that only affects nodes/edges/subpaths
asgerf Aug 27, 2024
837a8be
JS: Update test output and add related TODO in 'markdown-table' model
asgerf Aug 27, 2024
a2d53c2
JS: Update test output and add related TODO in model of 'async'
asgerf Aug 26, 2024
cb5dbb9
JS: Update test to reflect implicit read flow has been fixed
asgerf Aug 27, 2024
f65879e
JS: Update a test that no longer fails
asgerf Aug 27, 2024
65a36b0
JS: Add regression test for argument position confusion
asgerf Aug 29, 2024
4568967
JS: Do not use legacy taint steps in TaintedUrlSuffix
asgerf Aug 29, 2024
92bb4b3
JS: Address some comments from hvitved
asgerf Sep 5, 2024
379c7ef
JS: Add test to show lack of unknown array element being propagated
asgerf Sep 5, 2024
a9a8351
JS: Fix one case of missing handling of unknown array index
asgerf Sep 5, 2024
1da68aa
JS: Benign test output change
asgerf Sep 5, 2024
fb9732a
JS: Add another test and TODO about an issue with constant array indices
asgerf Sep 5, 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
5 changes: 2 additions & 3 deletions javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1780,8 +1780,7 @@ module DataFlow {
)
}

/** A load step from a reflective parameter node to each parameter. */
private class ReflectiveParamsStep extends PreCallGraphStep {
private class ReflectiveParamsStep extends LegacyPreCallGraphStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f, int i |
f.getFunction() = params.getFunction() and
Expand All @@ -1793,7 +1792,7 @@ module DataFlow {
}

/** A taint step from the reflective parameters node to any parameter. */
private class ReflectiveParamsTaintStep extends TaintTracking::SharedTaintStep {
private class ReflectiveParamsTaintStep extends TaintTracking::LegacyTaintStep {
override predicate step(DataFlow::Node obj, DataFlow::Node element) {
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f |
f.getFunction() = params.getFunction() and
Expand Down
12 changes: 8 additions & 4 deletions javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,14 @@ module TaintTracking {
)
}

private class HeapLegacyTaintStep extends LegacyTaintStep {
override predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) {
// arrays with tainted elements are tainted (in old data flow)
succ.(DataFlow::ArrayCreationNode).getAnElement() = pred and
not any(PromiseAllCreation call).getArrayNode() = succ
}
}

/**
* A taint propagating data flow edge through object or array elements and
* promises.
Expand All @@ -274,10 +282,6 @@ module TaintTracking {
// spreading a tainted value into an array literal gives a tainted array
succ.(DataFlow::ArrayCreationNode).getASpreadArgument() = pred
or
// arrays with tainted elements and objects with tainted property names are tainted
succ.(DataFlow::ArrayCreationNode).getAnElement() = pred and
not any(PromiseAllCreation call).getArrayNode() = succ
or
// reading from a tainted object yields a tainted result
succ.(DataFlow::PropRead).getBase() = pred and
not (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,10 @@ module Public {
Content asSingleton() { this = MkSingletonContent(result) }

/** Gets the property name to be accessed. */
PropertyName asPropertyName() { result = this.asSingleton().asPropertyName() }
PropertyName asPropertyName() {
// TODO: array indices should be mapped to a ContentSet that also reads from UnknownArrayElement
result = this.asSingleton().asPropertyName()
}

/** Gets the array index to be accessed. */
int asArrayIndex() { result = this.asSingleton().asArrayIndex() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ private module ConsistencyConfig implements InputSig<Location, JSDataFlow> {
or
n instanceof FlowSummaryIntermediateAwaitStoreNode
or
n instanceof FlowSummaryDynamicParameterArrayNode
or
n instanceof GenericSynthesizedNode
or
n = DataFlow::globalAccessPathRootPseudoNode()
Expand All @@ -37,6 +39,16 @@ private module ConsistencyConfig implements InputSig<Location, JSDataFlow> {
isAmbientNode(call.asOrdinaryCall()) or
isAmbientNode(call.asAccessorCall())
}

predicate argHasPostUpdateExclude(ArgumentNode node) {
// Side-effects directly on these can't propagate back to the caller, and for longer access paths it's too imprecise
node instanceof TStaticArgumentArrayNode or
node instanceof TDynamicArgumentArrayNode
}

predicate reverseReadExclude(DataFlow::Node node) {
node instanceof FlowSummaryDynamicParameterArrayNode
}
}

module Consistency = MakeConsistency<Location, JSDataFlow, JSTaintFlow, ConsistencyConfig>;
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ private import semmle.javascript.dataflow.internal.VariableCapture as VariableCa

cached
private module Cached {
private Content dynamicArgumentsContent() {
result.asArrayIndex() = [0 .. 10]
or
result.isUnknownArrayElement()
}

/**
* The raw data type underlying `DataFlow::Node`.
*/
Expand All @@ -33,6 +39,25 @@ private module Cached {
} or
TThisNode(StmtContainer f) { f.(Function).getThisBinder() = f or f instanceof TopLevel } or
TFunctionSelfReferenceNode(Function f) or
TStaticArgumentArrayNode(InvokeExpr node) or
TDynamicArgumentArrayNode(InvokeExpr node) { node.isSpreadArgument(_) } or
TStaticParameterArrayNode(Function f) {
f.getAParameter().isRestParameter() or f.usesArgumentsObject()
} or
TDynamicParameterArrayNode(Function f) or
/** Data about to be stored in the rest parameter object. Needed for shifting array indices. */
TRestParameterStoreNode(Function f, Content storeContent) {
f.getRestParameter().getIndex() > 0 and
storeContent = dynamicArgumentsContent()
} or
/** Data about to be stored in the dynamic argument array of an invocation. Needed for shifting array indices. */
TDynamicArgumentStoreNode(InvokeExpr invoke, Content storeContent) {
invoke.isSpreadArgument(_) and
storeContent = dynamicArgumentsContent()
} or
TApplyCallTaintNode(MethodCallExpr node) {
node.getMethodName() = "apply" and exists(node.getArgument(1))
} or
TDestructuredModuleImportNode(ImportDeclaration decl) {
exists(decl.getASpecifier().getImportedName())
} or
Expand All @@ -43,7 +68,7 @@ private module Cached {
TExceptionalInvocationReturnNode(InvokeExpr e) or
TGlobalAccessPathRoot() or
TTemplatePlaceholderTag(Templating::TemplatePlaceholderTag tag) or
TReflectiveParametersNode(Function f) or
TReflectiveParametersNode(Function f) { f.usesArgumentsObject() } or
TExprPostUpdateNode(AST::ValueNode e) {
e = any(InvokeExpr invoke).getAnArgument() or
e = any(PropAccess access).getBase() or
Expand All @@ -58,6 +83,7 @@ private module Cached {
TConstructorThisArgumentNode(InvokeExpr e) { e instanceof NewExpr or e instanceof SuperCall } or
TConstructorThisPostUpdate(Constructor ctor) or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
TFlowSummaryDynamicParameterArrayNode(FlowSummaryImpl::Public::SummarizedCallable callable) or
TFlowSummaryIntermediateAwaitStoreNode(FlowSummaryImpl::Private::SummaryNode sn) {
// NOTE: This dependency goes through the 'Steps' module whose instantiation depends on the call graph,
// but the specific predicate we're referering to does not use that information.
Expand Down Expand Up @@ -96,7 +122,8 @@ private class TEarlyStageNode =
TFunctionSelfReferenceNode or TDestructuredModuleImportNode or THtmlAttributeNode or
TFunctionReturnNode or TExceptionalFunctionReturnNode or TExceptionalInvocationReturnNode or
TGlobalAccessPathRoot or TTemplatePlaceholderTag or TReflectiveParametersNode or
TExprPostUpdateNode or TConstructorThisArgumentNode;
TExprPostUpdateNode or TConstructorThisArgumentNode or TStaticArgumentArrayNode or
TDynamicArgumentArrayNode or TStaticParameterArrayNode or TDynamicParameterArrayNode;

/**
* A data-flow node that is not a flow summary node.
Expand Down
Loading
Loading