-
-
Notifications
You must be signed in to change notification settings - Fork 885
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
Fixes #2900 - Checks slur regex to see if it is too permissive #3146
Fixes #2900 - Checks slur regex to see if it is too permissive #3146
Conversation
…e along with small validation organization
@dessalines @Nutomic - It looks like my build is failing since I'm using features from Would you be OK with bumping to the |
crates/api_crud/src/site/create.rs
Outdated
let icon = diesel_option_overwrite_to_url(&data.icon)?; | ||
let banner = diesel_option_overwrite_to_url(&data.banner)?; | ||
// Check that the slur regex compiles, and returns the regex if valid... | ||
let slur_regex = build_and_check_regex(&local_site.slur_filter_regex.as_deref())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect.
crates/api_crud/src/site/create.rs
Outdated
|
||
if let Some(Some(desc)) = &description { | ||
if let Some(desc) = &data.description { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing that scares me about this change, is that the diesel_option_overwrite function has a special case: it considers sending empty string as a set the DB item to null
. In which case, this check shouldn't be run.
By removing that, it now runs these checks on empty strings, which might fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see this working as (I believe) expected here: https://github.com/LemmyNet/lemmy/pull/3146/files#diff-d9124d8135ec751589f580dd3a84eba50f3ddb2bd27bd2cfb7a436f6af9e9c54R276
crates/api_crud/src/site/update.rs
Outdated
|
||
if let Some(desc) = &data.description { | ||
site_description_length_check(desc)?; | ||
check_slurs_opt(&data.description, &slur_regex)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same concern as above, and this also should probably be changed to if let Some(Some(...
.
I hope it won't fail on empty strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had started moving the validation portions into a function; based on this and your previous comments I'll just go ahead and do that! It'll let me write more targeted unit tests and show things should continue to work as expected 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see this working as (I believe) expected here: https://github.com/LemmyNet/lemmy/pull/3146/files#diff-fcc9fa5b2820776716027bd57d2b8c83dfd11b52b3274a1cb341e5845aa1feebR370
// may match against any string text. To keep it simple, we'll match the regex | ||
// against an innocuous string - a single number - which should help catch a regex | ||
// that accidentally matches against all strings. | ||
if regex.is_match("1") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be problematic if there is ever a community that rejects numbers; however, I feel like this would work for the majority of cases. Not sure if there's a better way to check that wont get complicated quickly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems fine, a slur filter which matches a single character is almost certainly wrong.
|
||
/// Checks the site name length, the limit as defined in the DB. | ||
pub fn site_name_length_check(name: &str) -> LemmyResult<()> { | ||
min_max_length_check( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that the site name is required in the database, so I added a check to also validate a minimum site name length. Otherwise the changes are just moved from when it lived in site_utils.rs
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, thanks.
crates/api_crud/src/site/update.rs
Outdated
check_slurs_opt(&edit_site.description, &slur_regex)?; | ||
} | ||
|
||
if let Some(listing_type) = &edit_site.default_post_listing_type { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this check also be done when creating a site?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its probably a good idea, for consistency, although lemmy-ui doesn't show nearly as many fields when creating the site, as for updating it.
crates/api_crud/src/site/update.rs
Outdated
let enabled_federation_with_private_instance = edit_site.federation_enabled == Some(true) | ||
&& edit_site.private_instance.unwrap_or(private_instance); | ||
|
||
if enabled_private_instance_with_federation || enabled_federation_with_private_instance { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here - should this check also be done when creating a site?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a good idea for consistency.
crates/api_crud/src/site/update.rs
Outdated
federation_enabled: bool, | ||
private_instance: bool, | ||
edit_site: &EditSite, | ||
) -> LemmyResult<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following comments are questions I have, that I can address in a subsequent PR:
Do we need a check to make sure that the site does exist (opposite of the local_site.site_setup
in create)? Not immediately clear to me if this would also work as a way to create the initial site.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really necessary IMO, because it never sets that site_setup field in the form builder.
crates/api_crud/src/site/create.rs
Outdated
} | ||
} | ||
|
||
let invalid_payloads = [( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't add cases for functions from validation.rs
because the unit tests in that file should cover those cases. Let me know if you think otherwise, and I can add more test cases here!
crates/api_crud/src/site/create.rs
Outdated
} | ||
|
||
is_valid_body_field(&data.sidebar)?; | ||
validate_create_payload(local_site.site_setup, local_site.slur_filter_regex, data)?; | ||
|
||
let application_question = diesel_option_overwrite(&data.application_question); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to move this into the validation fn in the next PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good
@dessalines - Thank you for the initial feedback! I think this is ready to review again at your leisure. For additional proof, I've added some screenshots and a video of this working in the MR description. I also created a PR for the translations at LemmyNet/lemmy-translations#65. |
9181f8f
to
33eff7e
Compare
crates/db_views/src/comment_view.rs
Outdated
@@ -600,21 +606,22 @@ mod tests { | |||
|
|||
let read_comment_views_no_person = CommentQuery::builder() | |||
.pool(pool) | |||
.sort(Some(CommentSortType::Hot)) | |||
.sort(Some(CommentSortType::Old)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the main change required to get the tests to not be flaky on both main
and this branch. It seems like the hot ranking doesn't always update in time for the sort order Hot
to pick the first comment on the post.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep that's perfectly fine,we should be using New or Old for sorting for the tests, to be predictable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great, and thank you for adding all these tests too!
crates/api_crud/src/site/create.rs
Outdated
} | ||
|
||
is_valid_body_field(&data.sidebar)?; | ||
validate_create_payload(local_site.site_setup, local_site.slur_filter_regex, data)?; | ||
|
||
let application_question = diesel_option_overwrite(&data.application_question); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good
crates/api_crud/src/site/update.rs
Outdated
federation_enabled: bool, | ||
private_instance: bool, | ||
edit_site: &EditSite, | ||
) -> LemmyResult<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really necessary IMO, because it never sets that site_setup field in the form builder.
crates/api_crud/src/site/update.rs
Outdated
check_slurs_opt(&edit_site.description, &slur_regex)?; | ||
} | ||
|
||
if let Some(listing_type) = &edit_site.default_post_listing_type { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its probably a good idea, for consistency, although lemmy-ui doesn't show nearly as many fields when creating the site, as for updating it.
crates/api_crud/src/site/update.rs
Outdated
let enabled_federation_with_private_instance = edit_site.federation_enabled == Some(true) | ||
&& edit_site.private_instance.unwrap_or(private_instance); | ||
|
||
if enabled_private_instance_with_federation || enabled_federation_with_private_instance { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a good idea for consistency.
crates/db_views/src/comment_view.rs
Outdated
@@ -600,21 +606,22 @@ mod tests { | |||
|
|||
let read_comment_views_no_person = CommentQuery::builder() | |||
.pool(pool) | |||
.sort(Some(CommentSortType::Hot)) | |||
.sort(Some(CommentSortType::Old)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep that's perfectly fine,we should be using New or Old for sorting for the tests, to be predictable.
|
||
/// Checks the site name length, the limit as defined in the DB. | ||
pub fn site_name_length_check(name: &str) -> LemmyResult<()> { | ||
min_max_length_check( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, thanks.
@dessalines I did (what I think is) the last step and updated the submodules 🎉 Let me know if there's anything else I need to do! Thank you! |
} | ||
} | ||
}, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it pretty confusing to loop through tests like this. Would be much clearer to have a helper function and call that explicitly with each set of params.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Nutomic Trying to understand your feedback here - are you saying you'd rather have a test per case, and call
match validate_update_payload(local_site_slur_filter_regex.clone(), false, true, edit_site)
{
Ok(_) => {
panic!(
"Got Ok, but validation should have failed with error: {} for invalid_payloads.nth({})",
expected_err, idx
)
}
Err(error) => {
assert!(
error.message.eq(&Some(String::from(expected_err))),
"Got Err {:?}, but should have failed with message: {} for invalid_payloads.nth({})",
error.message,
expected_err,
idx
)
}
}
as the helper function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that you write it out, it doesnt seem to make much difference. Anyway the errors are being logged so it should be fine. Just resolve the conflicts and we can merge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had started cleaning up the validation some more in anticipation of putting up a second PR, but at this point I just pushed it all up. I also updated the tests so that there's now a reason included in the tuples, which should make it easier to understand each case and not need to make a separate test for each one.
WHAT
permissive_regex
)invalid_regex
)site_name_length_overflow
site_name_required
lemmy-translations
PR here: LemmyNet/lemmy-translations#65PROOF
Updating site name
OLD - Attempting to clear site name
(Your site now has a name of empty string, which seems not great).
NEW - Attempting to clear site name
OLD - Site name is too long
(Your change had no effect, which is confusing)
NEW - Site name is too long
Updating the site with different regex patterns
Screen.Recording.2023-06-17.at.3.39.04.PM.mov