Skip to content

Commit a9f49ce

Browse files
Add mass assignment query
1 parent 4177c38 commit a9f49ce

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* Provides a taint tracking configuration for reasoning about insecure mass assignment.
3+
*/
4+
5+
private import codeql.ruby.AST
6+
private import codeql.ruby.DataFlow
7+
private import codeql.ruby.TaintTracking
8+
private import codeql.ruby.dataflow.RemoteFlowSources
9+
10+
/** Provides default sources and sinks for the mass assignment query. */
11+
module MassAssignment {
12+
/**
13+
* A data flow source for user input used for mass assignment.
14+
*/
15+
abstract class Source extends DataFlow::Node { }
16+
17+
/**
18+
* A data flow sink for user input used for mass assignment.
19+
*/
20+
abstract class Sink extends DataFlow::Node { }
21+
22+
/**
23+
* A call that permits arbitrary parameters to be used for mass assignment.
24+
*/
25+
abstract class MassPermit extends DataFlow::Node {
26+
/** Gets the argument for the parameters to be permitted */
27+
abstract DataFlow::Node getParamsArgument();
28+
29+
/** Gets the result node of the permitted parameters. */
30+
abstract DataFlow::Node getPermittedParamsResult();
31+
}
32+
33+
private class RemoteSource extends Source instanceof RemoteFlowSource { }
34+
35+
private class CreateSink extends Sink {
36+
CreateSink() {
37+
this.asExpr().getExpr().(MethodCall).getMethodName() = ["create", "new", "update"]
38+
}
39+
}
40+
41+
private class PermitCall extends MassPermit instanceof DataFlow::CallNode {
42+
PermitCall() {
43+
this.asExpr().getExpr().(MethodCall).getMethodName() = ["permit!", "to_unsafe_h"]
44+
}
45+
46+
override DataFlow::Node getParamsArgument() { result = this.(DataFlow::CallNode).getReceiver() }
47+
48+
override DataFlow::Node getPermittedParamsResult() { result = this }
49+
}
50+
}
51+
52+
private module FlowState {
53+
private newtype TState =
54+
TUnpermitted() or
55+
TPermitted()
56+
57+
/** A flow state used to distinguish whether arbitrary user parameters have been permitted to be used for mass assignment. */
58+
class State extends TState {
59+
string toString() {
60+
this = TUnpermitted() and result = "unpermitted"
61+
or
62+
this = TPermitted() and result = "permitted"
63+
}
64+
}
65+
66+
/** A flow state used for user parameters for which arbitrary parameters have not been permitted to use for mass assignment. */
67+
class Unpermitted extends State, TUnpermitted { }
68+
69+
/** A flow state used for user parameters for which arbitrary parameters have been permitted to use for mass assignment. */
70+
class Permitted extends State, TPermitted { }
71+
}
72+
73+
/** A flow configuration for reasoning about insecure mass assignment. */
74+
private module Config implements DataFlow::StateConfigSig {
75+
class FlowState = FlowState::State;
76+
77+
predicate isSource(DataFlow::Node node, FlowState state) {
78+
node instanceof MassAssignment::Source and
79+
state instanceof FlowState::Unpermitted
80+
}
81+
82+
predicate isSink(DataFlow::Node node, FlowState state) {
83+
node instanceof MassAssignment::Sink and
84+
state instanceof FlowState::Permitted
85+
}
86+
87+
predicate isAdditionalFlowStep(
88+
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
89+
) {
90+
exists(MassAssignment::MassPermit permit |
91+
node1 = permit.getParamsArgument() and
92+
state1 instanceof FlowState::Unpermitted and
93+
node2 = permit.getPermittedParamsResult() and
94+
state2 instanceof FlowState::Permitted
95+
)
96+
}
97+
}
98+
99+
/** Taint tracking for reasoning about user input used for mass assignment. */
100+
module MassAssignmentFlow = TaintTracking::GlobalWithState<Config>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Insecure Mass Assignment
3+
* @description Using mass assignment with user-controlled keys allows unintended parameters to be set.
4+
* @kind path-problem
5+
* @problem.severity error
6+
* @security-severity 7.5
7+
* @precision high
8+
* @id ruby/insecure-mass-assignment
9+
* @tags security
10+
* external/cwe/cwe-915
11+
*/
12+
13+
import codeql.ruby.security.MassAssignmentQuery
14+
import MassAssignmentFlow::PathGraph
15+
16+
from MassAssignmentFlow::PathNode source, MassAssignmentFlow::PathNode sink
17+
where MassAssignmentFlow::flowPath(source, sink)
18+
select sink.getNode(), source, sink, "mass assignment"

0 commit comments

Comments
 (0)