Skip to content
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

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

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 Feb 28, 2022
@alexrford alexrford requested a review from as a code owner Feb 28, 2022
hmac
hmac previously approved these changes Mar 7, 2022
Copy link
Contributor

@hmac hmac left a comment

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

@hmac hmac Mar 7, 2022

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

@alexrford alexrford Mar 9, 2022

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

@hmac hmac Mar 7, 2022

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

@alexrford alexrford Mar 9, 2022

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

@hmac hmac Mar 7, 2022

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

@alexrford alexrford commented Mar 9, 2022

@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 Mar 9, 2022
hmac
hmac approved these changes Mar 10, 2022
Copy link
Contributor

@hmac hmac left a comment

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

@alexrford alexrford commented Mar 10, 2022

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
31 checks passed
@alexrford alexrford deleted the alexrford/ruby/orm-write-access branch Mar 10, 2022
@alexrford alexrford restored the alexrford/ruby/orm-write-access branch Mar 10, 2022
@alexrford alexrford deleted the alexrford/ruby/orm-write-access branch Mar 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

2 participants