Skip to content

Commit 0f45a53

Browse files
Add mass assignment query
1 parent 1785086 commit 0f45a53

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
exists(DataFlow::CallNode create |
38+
create.asExpr().getExpr().(MethodCall).getMethodName() = ["create", "new", "update"] and
39+
this = create.getArgument(0)
40+
)
41+
}
42+
}
43+
44+
private class PermitCall extends MassPermit instanceof DataFlow::CallNode {
45+
PermitCall() { this.asExpr().getExpr().(MethodCall).getMethodName() = "permit!" }
46+
47+
override DataFlow::Node getParamsArgument() { result = this.(DataFlow::CallNode).getReceiver() }
48+
49+
override DataFlow::Node getPermittedParamsResult() { result = this }
50+
}
51+
}
52+
53+
private module FlowState {
54+
private newtype TState =
55+
TUnpermitted() or
56+
TPermitted()
57+
58+
/** A flow state used to distinguish whether arbitrary user parameters have been permitted to be used for mass assignment. */
59+
class State extends TState {
60+
string toString() {
61+
this = TUnpermitted() and result = "unpermitted"
62+
or
63+
this = TPermitted() and result = "permitted"
64+
}
65+
}
66+
67+
/** A flow state used for user parameters for which arbitrary parameters have not been permitted to use for mass assignment. */
68+
class Unpermitted extends State, TUnpermitted { }
69+
70+
/** A flow state used for user parameters for which arbitrary parameters have been permitted to use for mass assignment. */
71+
class Permitted extends State, TPermitted { }
72+
}
73+
74+
/** A flow configuration for reasoning about insecure mass assignment. */
75+
private module Config implements DataFlow::StateConfigSig {
76+
class FlowState = FlowState::State;
77+
78+
predicate isSource(DataFlow::Node node, FlowState state) {
79+
node instanceof MassAssignment::Source and
80+
state instanceof FlowState::Unpermitted
81+
// or
82+
// node = any(MassAssignment::MassPermit p).getPermittedParamsResult() and
83+
// state instanceof FlowState::Permitted
84+
}
85+
86+
predicate isSink(DataFlow::Node node, FlowState state) {
87+
node instanceof MassAssignment::Sink and
88+
state instanceof FlowState::Permitted
89+
}
90+
91+
predicate isAdditionalFlowStep(
92+
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
93+
) {
94+
exists(MassAssignment::MassPermit permit |
95+
node1 = permit.getParamsArgument() and
96+
state1 instanceof FlowState::Unpermitted and
97+
node2 = permit.getPermittedParamsResult() and
98+
state2 instanceof FlowState::Permitted
99+
)
100+
}
101+
}
102+
103+
/** Taint tracking for reasoning about user input used for mass assignment. */
104+
module MassAssignmentFlow = TaintTracking::GlobalWithState<Config>;
Lines changed: 18 additions & 0 deletions
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)