Params and json coercions rescue from exceptions


#1

Looking at dry-types source code, I have been very surprised to see that json and params coercions rescue from ArgumentError, TypeError and, for dates and times, RangeError exceptions, passing through everything they don’t know how to coerce.

I guess that the rationale behind it is that params and json are user provided input, so they can be anything and we should let the application deal with what to do. But I have some objections:

  • It is true that params are always user provided input, but it is not the case for json, which, for example, could be some automatically generated output data from a datastore.
  • It leads to extremely unsafe code in two ways:
    • It doesn’t do any parameter sanitation at all.
    • It gives you with a value that can effectively be anything, so you have to ask what it is in the style of #is_a?(String). I would expect dry-types to give me some type safety in this regard.

In my case I’m working with params and I would like to let the application crash if the user gives me something I don’t expect, because it is a sign that he is trying to tamper in some way. However, I still see the benefit in letting the application deal with such errors. For this reason, I think that a better approach would be to be safe by default but still let the application a way to deal with the errors wrapping the argument, type or range error in a custom dry-types exception with some fields like the value given and the coercion that was tried to be performed.

What do you think?


#2

Don’t think is a good idea to crash, but you can use strict types https://dry-rb.org/gems/dry-types/strict/ if you really want to.

Also consider using dry-validation to validate sanitize user input.


#3

Thanks for your response @alexandru-calinoiu

Well, I can’t use strict types because I need coercion, because they are params and everything arrives a a string. In some cases I could use simple coercibles types (Types::Coercible...), but they are not as comprehensive as param types, as they only use Kernel coerce methods. But, anyway, I think that the policy of hiding exceptions is very dangerous and it would be better to wrap in a custom exception.

In fact, I’m using dry-validation with the types extension, which is used to pre-process input.


#4

We could make them raise and in places where we want safe coercions we could use Types::Safe which is designed to rescue from exceptions. In fact, when I think about it now, this seems to be the way to go. Originally I implemented coercions that rescue from exceptions because they were supposed to return original input when something goes wrong so that it can be validated by ie schemas. Later on, I added Types::Safe but I’ve never used it for individual types that are used as member types in hashes because corresponding coercion methods already rescue.


#5

Happy to hear that. However, I don’t think Types::Safe would be a good namespace there. It would be safe in the sense that it would not raise any exception, but it would be an unsafe type (you could end up with any type) and also unsafe in the sense that no sanitation would take place. What about Types::NonStrict or Types::Lax?


#6

Actually, that’s a good point. We should come up with a better name. Looking into other languages for inspiration is probably a good idea.


#7

Yes, I think it is a good idea.

I haven’t used it, but I think that in haskell it would be something like CoerceI, which is a lifted coercion which can be whether a proper coercion or the identity coercion:

data CoercionI
CoercionI represents a lifted ordinary Coercion, in that it can represent either one of:

1. A proper Coercion

2. The identity coercion

Constructors
IdCo	
ACo Coercion	

Here it is a link for an old version of the docs in GHC. I can’t find it in newer versions nor a reason why it has been moved or deleted. If we need more help from here we can always ask the haskell cafe mailing list, they are always extremely helpful and kind. Maybe in dry-types we could name them Types::WithIdentity or Types::WithSelf.

Also, from an operational point of view, we could name them Types::Try or Types::TryJust.


#8

I thought about Types::Try too, especially that error-catching method is already called Type#try.


#9

I think that either one could be quite good. Maybe I prefer WithSelf (or similar) because it is more declarative than Try, which is quite imperative.


#10

Putting aside the never easy naming decision, would then be considered a PR with:

  • Changing Types::Params to raise
  • Changing Types::JSON to raise
  • Introducing Types::Try::Params and Types::Try::JSON, which won’t raise

?