Skip to content

Commit

Permalink
Add mass assignment query
Browse files Browse the repository at this point in the history
  • Loading branch information
joefarebrother committed Mar 20, 2024
1 parent 4177c38 commit a9f49ce
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 0 deletions.
100 changes: 100 additions & 0 deletions ruby/ql/lib/codeql/ruby/security/MassAssignmentQuery.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Provides a taint tracking configuration for reasoning about insecure mass assignment.
*/

private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.TaintTracking
private import codeql.ruby.dataflow.RemoteFlowSources

/** Provides default sources and sinks for the mass assignment query. */
module MassAssignment {
/**
* A data flow source for user input used for mass assignment.
*/
abstract class Source extends DataFlow::Node { }

/**
* A data flow sink for user input used for mass assignment.
*/
abstract class Sink extends DataFlow::Node { }

/**
* A call that permits arbitrary parameters to be used for mass assignment.
*/
abstract class MassPermit extends DataFlow::Node {
/** Gets the argument for the parameters to be permitted */
abstract DataFlow::Node getParamsArgument();

/** Gets the result node of the permitted parameters. */
abstract DataFlow::Node getPermittedParamsResult();
}

private class RemoteSource extends Source instanceof RemoteFlowSource { }

private class CreateSink extends Sink {
CreateSink() {
this.asExpr().getExpr().(MethodCall).getMethodName() = ["create", "new", "update"]
}
}

private class PermitCall extends MassPermit instanceof DataFlow::CallNode {
PermitCall() {
this.asExpr().getExpr().(MethodCall).getMethodName() = ["permit!", "to_unsafe_h"]
}

override DataFlow::Node getParamsArgument() { result = this.(DataFlow::CallNode).getReceiver() }

override DataFlow::Node getPermittedParamsResult() { result = this }
}
}

private module FlowState {
private newtype TState =
TUnpermitted() or
TPermitted()

/** A flow state used to distinguish whether arbitrary user parameters have been permitted to be used for mass assignment. */
class State extends TState {
string toString() {
this = TUnpermitted() and result = "unpermitted"
or
this = TPermitted() and result = "permitted"
}
}

/** A flow state used for user parameters for which arbitrary parameters have not been permitted to use for mass assignment. */
class Unpermitted extends State, TUnpermitted { }

/** A flow state used for user parameters for which arbitrary parameters have been permitted to use for mass assignment. */
class Permitted extends State, TPermitted { }
}

/** A flow configuration for reasoning about insecure mass assignment. */
private module Config implements DataFlow::StateConfigSig {
class FlowState = FlowState::State;

predicate isSource(DataFlow::Node node, FlowState state) {
node instanceof MassAssignment::Source and
state instanceof FlowState::Unpermitted
}

predicate isSink(DataFlow::Node node, FlowState state) {
node instanceof MassAssignment::Sink and
state instanceof FlowState::Permitted
}

predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
exists(MassAssignment::MassPermit permit |
node1 = permit.getParamsArgument() and
state1 instanceof FlowState::Unpermitted and
node2 = permit.getPermittedParamsResult() and
state2 instanceof FlowState::Permitted
)
}
}

/** Taint tracking for reasoning about user input used for mass assignment. */
module MassAssignmentFlow = TaintTracking::GlobalWithState<Config>;
18 changes: 18 additions & 0 deletions ruby/ql/src/queries/security/cwe-915/MassAssignment.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @name Insecure Mass Assignment
* @description Using mass assignment with user-controlled keys allows unintended parameters to be set.
* @kind path-problem
* @problem.severity error
* @security-severity 7.5
* @precision high
* @id ruby/insecure-mass-assignment
* @tags security
* external/cwe/cwe-915
*/

import codeql.ruby.security.MassAssignmentQuery
import MassAssignmentFlow::PathGraph

from MassAssignmentFlow::PathNode source, MassAssignmentFlow::PathNode sink
where MassAssignmentFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "mass assignment"

0 comments on commit a9f49ce

Please sign in to comment.