Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files
... and 2 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
|
I saw that files types are handled differently for |
|
Should I completely remove type |
We can make sure to set |
Daverball
left a comment
There was a problem hiding this comment.
Looks good overall, but there's a couple of details we should iron out.
src/onegov/form/fields.py
Outdated
|
|
||
| upload_field_class: type[UploadField] = UploadField | ||
| upload_widget: Widget[UploadField] = UploadWidget() | ||
| validators = [WhitelistedMimeType()] |
It's probably fine to remove it for now. There may however be the rare false positive for any files that cannot be identified correctly by libmagic. Generally pdfs, zips and any other binary file formats can end up as |
| if not any(isinstance(validator, WhitelistedMimeType) | ||
| for validator in validators | ||
| ): | ||
| validators.append(validator) |
There was a problem hiding this comment.
Make sure this logic still holds up when field dependencies are in play, I have a feeling it does not, since it will wrap everything in a conditional validator, so you probably need to detect that case, so you don't add an unconditional and potentially redundant mimetype validator.
There was a problem hiding this comment.
It might be easier and more robust to store the mimetypes in an extra attribute and to override the post_validate hook, since validation_stopped is one of the parameters, which would be True if the dependency condition is unfulfilled.
So you can do something like the following in post_validate:
if not validation_stopped:
WhitelistedMimeType(self.mimetypes)(form, self)There was a problem hiding this comment.
Actually nevermind, this has the same problem, StrictOptional only stops validation if there is no data. Although technically there should be no data, when the field is hidden, so it might not matter in the end.
There was a problem hiding this comment.
I think it would still be a bit cleaner to move the validator into post_validate like in my code suggestion above, instead of trying to modify the given validator chain. If someone passes in a WhitelistValidator we can emit a warning or raise an exception to use the mimetypes parameter instead.
Co-authored-by: David Salvisberg <david.salvisberg@seantis.ch>
| var bar = $('<div class="progress"><span class="meter" style="width: 0%"></span></div>') | ||
| .attr('data-filename', file.name) | ||
| .prependTo(progress); | ||
| var bar = $( |
Daverball
left a comment
There was a problem hiding this comment.
We're in a pretty good spot now. But we should clean up and refactor things a bit.
If mimetypes is an argument to upload fields, we no longer need to pass around instances of WhitelistedMimeType everywhere, we should use the new parameter and avoid inserting a validator object, instead let's rely on the post_validate method of Field, which we can override in UploadField (we don't need to override it in UploadMultipleField, but we should add an explicit mimetypes parameter, so we can easily access mimetypes from UploadMultipleField.mimetypes in addition to each subfield, this means this parameter should both be assigned to self.mimetypes and passed to the self.upload_field_class call.
| if not any(isinstance(validator, WhitelistedMimeType) | ||
| for validator in validators | ||
| ): | ||
| validators.append(validator) |
There was a problem hiding this comment.
I think it would still be a bit cleaner to move the validator into post_validate like in my code suggestion above, instead of trying to modify the given validator chain. If someone passes in a WhitelistValidator we can emit a warning or raise an exception to use the mimetypes parameter instead.
| id=id, | ||
| default=default, | ||
| widget=widget, # type:ignore[arg-type] | ||
| validators=[*(validators or ())], |
There was a problem hiding this comment.
You're just looking for the validator in the wrong place in that test. The validator is on each field in the field list, not the field list itself. So I would get rid of this again and simplify the validators, otherwise the validator will run twice for each file. Once you move the validation from a validator in validators to post_validate you no longer will have to check for the presence of that validator anyways, since it's baked into the field itself.
| def assert_whitelisted_mimetype_validator( | ||
| field: UploadField | UploadMultipleField | ||
| ) -> None: | ||
|
|
||
| validator = find_validator(field, WhitelistedMimeType) | ||
| assert validator | ||
| assert validator.whitelist == WhitelistedMimeType.whitelist # type:ignore[attr-defined] | ||
|
|
||
|
|
||
| def find_validator( | ||
| field: Field | FileField, | ||
| cls: type | ||
| ) -> Validator[Any, Any] | None: | ||
| return next((v for v in field.validators if isinstance(v, cls)), None) |
There was a problem hiding this comment.
We can significantly simplify this when the whitelist is stored on the field (in fact you can probably get rid of this function and just replace it with the assertion):
| def assert_whitelisted_mimetype_validator( | |
| field: UploadField | UploadMultipleField | |
| ) -> None: | |
| validator = find_validator(field, WhitelistedMimeType) | |
| assert validator | |
| assert validator.whitelist == WhitelistedMimeType.whitelist # type:ignore[attr-defined] | |
| def find_validator( | |
| field: Field | FileField, | |
| cls: type | |
| ) -> Validator[Any, Any] | None: | |
| return next((v for v in field.validators if isinstance(v, cls)), None) | |
| def assert_whitelisted_mimetype_validator( | |
| field: UploadField | UploadMultipleField | |
| ) -> None: | |
| assert field.whitelist == WhitelistedMimeType.whitelist |
| validator = find_validator(form['file'], WhitelistedMimeType) | ||
| assert validator | ||
| assert validator.whitelist == { # type:ignore[attr-defined] | ||
| 'application/msword', 'application/pdf' | ||
| } |
There was a problem hiding this comment.
| validator = find_validator(form['file'], WhitelistedMimeType) | |
| assert validator | |
| assert validator.whitelist == { # type:ignore[attr-defined] | |
| 'application/msword', 'application/pdf' | |
| } | |
| assert form['file'].whitelist == { # type:ignore[attr-defined] | |
| 'application/msword', 'application/pdf' | |
| } |
| validator = find_validator(form['file'], WhitelistedMimeType) | ||
| assert validator | ||
| assert validator.whitelist == WhitelistedMimeType.whitelist # type:ignore[attr-defined] |
There was a problem hiding this comment.
| validator = find_validator(form['file'], WhitelistedMimeType) | |
| assert validator | |
| assert validator.whitelist == WhitelistedMimeType.whitelist # type:ignore[attr-defined] | |
| assert form['file'].whitelist == WhitelistedMimeType.whitelist # type:ignore[attr-defined] |
| validator = find_validator(form['files'], WhitelistedMimeType) | ||
| assert validator | ||
| assert validator.whitelist == { # type:ignore[attr-defined] | ||
| 'application/msword', 'application/pdf' | ||
| } |
There was a problem hiding this comment.
| validator = find_validator(form['files'], WhitelistedMimeType) | |
| assert validator | |
| assert validator.whitelist == { # type:ignore[attr-defined] | |
| 'application/msword', 'application/pdf' | |
| } | |
| assert form['files'].whitelist == { # type:ignore[attr-defined] | |
| 'application/msword', 'application/pdf' | |
| } |
| validator = find_validator(form['my_files'], WhitelistedMimeType) | ||
| assert validator | ||
| assert validator.whitelist == WhitelistedMimeType.whitelist # type:ignore[attr-defined] |
There was a problem hiding this comment.
| validator = find_validator(form['my_files'], WhitelistedMimeType) | |
| assert validator | |
| assert validator.whitelist == WhitelistedMimeType.whitelist # type:ignore[attr-defined] | |
| assert form['my_files'].whitelist == WhitelistedMimeType.whitelist # type:ignore[attr-defined] |
…st, it's good enough if it works for each field in the list. Co-authored-by: David Salvisberg <david.salvisberg@seantis.ch>
…ub.com:OneGov/onegov-cloud into feature/ogc-2738-pentest-arbitrary-file-upload

Org: Ensure mime type validator on file upload fields in form code
TYPE: Feature
LINK: ogc-2738