Skip to content

Commit 20ff4c4

Browse files
committed
Ruby: Model ActiveRecord::Relation#touch_all
1 parent 7dfab37 commit 20ff4c4

File tree

3 files changed

+28
-1
lines changed

3 files changed

+28
-1
lines changed

ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ private module Persistence {
359359
}
360360

361361
/**
362-
* Holds if `call` has a keyword argument of with value `value`.
362+
* Holds if `call` has a keyword argument with value `value`.
363363
*/
364364
private predicate keywordArgumentWithValue(DataFlow::CallNode call, DataFlow::ExprNode value) {
365365
exists(ExprNodes::PairCfgNode pair | pair = call.getArgument(_).asExpr() |
@@ -412,6 +412,28 @@ private module Persistence {
412412
}
413413
}
414414

415+
/**
416+
* A call to `ActiveRecord::Relation#touch_all`, which updates the `updated_at`
417+
* attribute on all records in the relation, setting it to the current time or
418+
* the time specified. If passed additional attribute names, they will also be
419+
* updated with the time.
420+
* Examples:
421+
* ```rb
422+
* Person.all.touch_all
423+
* Person.where(name: "David").touch_all
424+
* Person.all.touch_all(:created_at)
425+
* Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
426+
* ```
427+
*/
428+
private class TouchAllCall extends DataFlow::CallNode, PersistentWriteAccess::Range {
429+
TouchAllCall() {
430+
exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and
431+
this.getMethodName() = "touch_all"
432+
}
433+
434+
override DataFlow::Node getValue() { result = this.getKeywordArgument("time") }
435+
}
436+
415437
/** A call to e.g. `User.insert_all([{name: "foo"}, {name: "bar"}])` */
416438
private class InsertAllLikeCall extends DataFlow::CallNode, PersistentWriteAccess::Range {
417439
private ExprNodes::ArrayLiteralCfgNode arr;

ruby/ql/test/library-tests/concepts/PersistentWriteAccess.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
| app/controllers/users_controller.rb:20:7:20:57 | call to update_attributes | app/controllers/users_controller.rb:20:49:20:55 | call to get_uid |
1515
| app/controllers/users_controller.rb:23:7:23:42 | call to update_attribute | app/controllers/users_controller.rb:23:37:23:41 | "U13" |
1616
| app/controllers/users_controller.rb:26:19:26:23 | ... = ... | app/controllers/users_controller.rb:26:19:26:23 | "U14" |
17+
| app/controllers/users_controller.rb:31:7:31:32 | call to touch_all | app/controllers/users_controller.rb:31:28:31:31 | call to time |
1718
| app/models/user.rb:4:5:4:28 | call to update | app/models/user.rb:4:23:4:27 | "U15" |
1819
| app/models/user.rb:5:5:5:23 | call to update | app/models/user.rb:5:18:5:22 | "U16" |
1920
| app/models/user.rb:6:5:6:56 | call to update_attributes | app/models/user.rb:6:35:6:39 | "U17" |

ruby/ql/test/library-tests/concepts/app/controllers/users_controller.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ def create_or_modify
2525
# AssignAttributeCall
2626
user.name = "U14"
2727
user.save
28+
29+
# TouchAllCall
30+
User.touch_all
31+
User.touch_all(time: time)
2832
end
2933

3034
def get_uid

0 commit comments

Comments
 (0)