Skip to content

Ruby: Add OrmWriteAccess concept to model writes to a DB using an ORM #8271

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

Merged
merged 14 commits into from
Mar 10, 2022

Conversation

alexrford
Copy link
Contributor

@alexrford alexrford commented Feb 28, 2022

This is implemented for ActiveRecord::Persistence methods - in particular those that include a field name and value as part of the method call. This notably doesn't include save or destroy and similar methods.

The immediate intention for this change is to support a plaintext storage query including database sinks.

The main limitation of the modelling here is that it assumes that these methods are called with literal arguments - e.g. new_name = "foo"; user.update({name: new_name}) would be picked up since the argument is a hash literal, but new_attrs = {name: "foo"}; user.update(new_attrs) would not as the argument is a variable read.

On a separate note, assignments such as user.name = "foo", where user is an ActiveRecord model object, are included as DB writes here. This is not strictly true, as the write only occurs if the object is subsequently persisted (e.g. with a save call). It seemed worth pretending that these were in fact writes to avoid cases where the flow from the assignment to the actual write may be too complex for us to track, in which case we would miss potential writes. The tradeoff of FPs where there is an assignment without a subsequent write seems worthwhile to me since this seems like it would be an uncommon thing to do, but I may not have considered all use cases.

See https://apidock.com/rails/v6.1.3.1/ActiveRecord/Persistence/ and https://apidock.com/rails/v6.1.3.1/ActiveRecord/Persistence/ClassMethods/ for reference.

@alexrford alexrford added the Ruby label Feb 28, 2022
@alexrford alexrford marked this pull request as ready for review February 28, 2022 08:34
@alexrford alexrford requested a review from a team as a code owner February 28, 2022 08:34
hmac
hmac previously approved these changes Mar 7, 2022
Copy link
Contributor

@hmac hmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Just a few minor comments.

* Extend this class to refine existing API models. If you want to model new APIs,
* extend `OrmWriteAccess::Range` instead.
*/
class OrmWriteAccess extends DataFlow::Node instanceof OrmWriteAccess::Range {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth thinking about whether this class would be useful when shared with Python/JS. JS already has a PersistentWriteAccess class that models cookie and web storage writes - I wonder if that would also cover this case, or if we need a more specific ORM concept?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that PersistentWriteAccess is fine here. I'd initially thought that having the fieldName available would be important for implementing rb/clear-text-storage-sensitive-information, but in practice I haven't found this necessary.

I've added PersistentWriteAccess as a Range pattern class for the Ruby version of this - the JS version of it is a normal abstract class.

|
keyExpr.getConstantValue().isStringOrSymbol(result) and
// avoid vacuous matches where the key is not a string or not a symbol
not result = "" and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a bug in getConstantValue - the only expressions that should have an empty string constant value are :"" and "". We should probably open an issue for this, to come back and fix later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 opened an issue for this - I tracked it as far as https://github.com/github/codeql/blob/main/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll#L836-L846 returning the empty string for non-empty symbol literal expr nodes, but I'm not sure on what the right fix is.

@@ -0,0 +1,2 @@
class User < ApplicationRecord
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have some calls in this class to test we identify them as well, since that's quite common. E.g.

class User
  def make_admin!
    update!(is_admin: true)
  end
end

@alexrford
Copy link
Contributor Author

@hmac thanks for the review. As part of switching to use PersistentWriteAccess, I was able to drop the code in ActiveRecord::Persistence that dealt with tracking the field name. We can always add this back later as a separate predicate if it's useful, but it doesn't seem to be required for now.

@alexrford alexrford requested a review from hmac March 9, 2022 22:45
Copy link
Contributor

@hmac hmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! There's a few QL4QL alerts that we might want to fix (I think maybe a new check just got merged?). Otherwise 👍

@alexrford
Copy link
Contributor Author

There's a few QL4QL alerts that we might want to fix

👍 I'll address these in a followup PR.

@alexrford alexrford merged commit 19c7f7b into main Mar 10, 2022
@alexrford alexrford deleted the alexrford/ruby/orm-write-access branch March 10, 2022 17:35
@alexrford alexrford restored the alexrford/ruby/orm-write-access branch March 10, 2022 17:35
@alexrford alexrford deleted the alexrford/ruby/orm-write-access branch March 10, 2022 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants