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

Validator added every time a step is submitted #42

Closed
joefitter opened this issue May 13, 2016 · 21 comments
Closed

Validator added every time a step is submitted #42

joefitter opened this issue May 13, 2016 · 21 comments
Labels

Comments

@joefitter
Copy link
Contributor

joefitter commented May 13, 2016

https://github.com/UKHomeOffice/passports-form-controller/blob/master/lib/validation/index.js#L62-L67

This appears to be an issue with any step that contains a field with options (radio, typeahead etc). An additional validator is added to the list of validators (created if empty) to check the value is in the list of options. The problem being that it doesn't check if the validator has already been added, and mutates the original array (reference to original stored, not cloned).

More worryingly is that this is persisted for the life of the instance, and across all users, each user that completes that step causes another identical validator to be added to the validators config, and this is persisted server-wide causing a potentially large memory leak.

@gxxm has been working on a fix, but probably worth pointing out to @easternbloc @JoeChapman @daniel-ac-martin and @gavboulton as will likely be affecting live systems.

This likely wasn't picked up before as the same validator is added many times, so the reduce below just calls the validator function over and over again yielding the same result (this should be replaced with _.any for efficiency), @gxxm noticed it when altering the options dynamically based on the results of a postcode lookup - multiple validators are added with different options and validation always fails

@joefitter joefitter added the bug label May 13, 2016
@joefitter joefitter changed the title Validator added every time a step is visited Validator added every time a step is submitted May 13, 2016
@daniel-ac-martin
Copy link
Contributor

Nice catch guys!
I doubt this will be the only place where we have this bug.
In this case it is easy to fix as we can just test whether the validator already exists.
But, there is a wider question of how can we patch the validators whilst limiting the scope of the patch to a single request. Ultimately, I think we should be performing a deep copy of the fields object (which starts its life as configuration, which we tend to think of as immutable, but isn't really "because JavaScript") on every request. Then we can patch it on a pre-request basis.

@joefitter
Copy link
Contributor Author

yeah a deep clone would fix this case but as you said this may not be the only place the bug occurs. We should be ideally making config immutable - could use something like https://facebook.github.io/immutable-js/ or https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze. Obviously @gxxm's current method of setting options on the fly would need rethinking slightly, could perhaps unfreeze, amend the re-freeze?

@joefitter
Copy link
Contributor Author

actually there isn't a way to unfreeze but still think we should consider locking config down

@JoeChapman
Copy link

JoeChapman commented May 13, 2016

+1 for making config immutable, unfortunately Object.freeze means that in most cases, in strict mode, trying to modify a "frozen" object will throw a TypeError so we might need to change more than just how we create the config.

@easternbloc
Copy link
Collaborator

Nice spot! More debug around this might have made it visible earlier. It's something I'm keen to add more to our modules UKHomeOffice-attic/hof#90

@joefitter
Copy link
Contributor Author

Yes, it will need some thought but will hopefully pinpoint any other places in the code where we are directly modifying config

@daniel-ac-martin
Copy link
Contributor

@joefitter: A deep copy of fields on each request should fix all the related bugs as well shouldn't it?

Obviously code should still check that a validator doesn't already exist when adding it to the list.

@joefitter
Copy link
Contributor Author

@daniel-ac-martin yes it would but it wouldn't stop us from unknowingly altering the fields object again somewhere further down the line, or steps, or any other config we may wish to pass. Object.freeze should be used on any config objects to ensure this doesn't happen in future, but a deep copy on each request should fix the current issue

@daniel-ac-martin
Copy link
Contributor

In case it isn't obvious, I'd like to say that I think this bug should have quite a high priority (though I'm biased). - We do need to patch the validators on a per request basis and this bug can potentially allow a malicious user to render the forms un-completable for other users. (My new date-controller suffers from this but cannot really be fixed until it is fixed here.)

Of course, I also think this will be a pain to fix. Anyone feeling energetic?

@JoeChapman
Copy link

You start Dan.

@daniel-ac-martin
Copy link
Contributor

@JoeChapman: Well I'm sure I will at some point, but I don't really get any time in my working day to do HOF stuff. (Been mainly writing Scala recently.) So really me being able to do this all hinges on me having no life. ;-)
Just wanted to put this out there in case someone has the time to work on fixing miscellaneous bugs in the HMPO form stack.

oliverweighell pushed a commit that referenced this issue Jun 16, 2016
This is an initial fix for issue #42. Rather than adding an option validator (to check that the selected option is one of the defined set of options) on each form submission, add it once on start instead.

- Moves the equality validator on 'options' outside of the function return
- This means that the validator is added to the field on start up, not on each submission of the form.
- We can avoid the overhead of checking if the validator has already been added on each submission.
- Unit test added for the updated code, and others cleaned up slightly.
oliverweighell pushed a commit that referenced this issue Jun 16, 2016
This is an initial fix for issue #42. For 'options' fields the form controller adds a validator to check that the chosen option is one of the defined set of options for the field. Rather than adding this validator on each form submission, this change adds it once on start up.

- Moves the equality validator on 'options' outside of the function return
- Unit test added for the updated code, and others cleaned up slightly.
@oliverweighell
Copy link
Contributor

Thanks @joefitter and @gxxm for flagging this!

The Passport renewals service has now gone public, so keen to plug the memory leak as an initial step - #47

@oliverweighell
Copy link
Contributor

oliverweighell commented Jun 16, 2016

Cheers @daniel-ac-martin for merging.

In terms of the wider discussion, I agree about immutable config. However, not so convinced about patching validators per request (quite possibly due to not really knowing the specifics of what you are doing).

What if validators (formatters, etc...) could instead ask for the data they need? Rough example: a1ebdec

Is that viable, and would it give enough flexibility to satisfy the needs of, for example, the date controller?

@daniel-ac-martin
Copy link
Contributor

@oli-weighell2: Yes I've been coming around to that point of view as well. It does mean that we will need something in the fields config to indicate that a field should be treated as an input-date mixin. (Support for the input-date mixin is the reason I thought we would need this.)

I'd favour introducing a new concept (processors?) rather than expanding the scope of the validators and formatters. - They are quite neat little things as thing stand.

I have a few ideas, but they will require PRs on the HMPO stack rather than simply being a controller that can sit in its own module.

@daniel-ac-martin
Copy link
Contributor

Updated HMPO/hmpo-form-wizard#35 to discuss.

@daniel-ac-martin
Copy link
Contributor

Incidentally, deep copy is a PITA in JavaScript in any case.

@joefitter
Copy link
Contributor Author

@joefitter
Copy link
Contributor Author

however I dont think underscore has an equivalent

@easternbloc
Copy link
Collaborator

It does not. We should move all the things to lodash imho

@daniel-ac-martin
Copy link
Contributor

@easternbloc I started to do just that in a branch. Stuff broke and I ran out of time. :-(
(Though I think the hmpo modules might use an old version of underscore which might be the cause of the breakage.)

@HughePaul
Copy link
Contributor

fixed by deep cloning options a while back

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants