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

Validate the user invitation HTML templates #4835

Merged
merged 9 commits into from
Oct 31, 2024
Merged

Conversation

rdimitrov
Copy link
Member

@rdimitrov rdimitrov commented Oct 28, 2024

Summary

The following PR:

  • Adds validation for the user invitation HTML templates against HTML and JS injection
  • Adds email validation for the user invitation feature based on RFC 5322
  • Adds unit tests

Change Type

Mark the type of change your PR introduces:

  • Bug fix (resolves an issue without affecting existing features)
  • Feature (adds new functionality without breaking changes)
  • Breaking change (may impact existing functionalities or require documentation updates)
  • Documentation (updates or additions to documentation)
  • Refactoring or test improvements (no bug fixes or new functionality)

Testing

Outline how the changes were tested, including steps to reproduce and any relevant configurations.
Attach screenshots if helpful.

Review Checklist:

  • Reviewed my own code for quality and clarity.
  • Added comments to complex or tricky code sections.
  • Updated any affected documentation.
  • Included tests that validate the fix or feature.
  • Checked that related changes are merged.

@rdimitrov rdimitrov requested a review from a team as a code owner October 28, 2024 09:42
@rdimitrov rdimitrov self-assigned this Oct 28, 2024
@rdimitrov rdimitrov force-pushed the validate-invite-html branch 3 times, most recently from 0594a87 to 062e537 Compare October 28, 2024 10:50
// getEmailSubject returns the subject for the email based on the message payload
func getEmailSubject(project string) string {
return fmt.Sprintf("You have been invited to join the %s organization in Minder", project)
}

// isValidField checks if the string is empty or contains HTML or JavaScript injection
func isValidField(str string) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is also covered by html/template -- do we need both?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to add it because html/template was not erroring if it finds html in the template input, instead it escapes it and so we end up with things like <script>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment that we want to detect and error, rather than escape?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment 👍

internal/email/email.go Outdated Show resolved Hide resolved
@coveralls
Copy link

coveralls commented Oct 28, 2024

Pull Request Test Coverage Report for Build 11603399089

Details

  • 67 of 89 (75.28%) changed or added relevant lines in 2 files are covered.
  • 2 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.009%) to 55.216%

Changes Missing Coverage Covered Lines Changed/Added Lines %
internal/invites/service.go 0 2 0.0%
internal/email/email.go 67 87 77.01%
Files with Coverage Reduction New Missed Lines %
internal/email/email.go 2 75.0%
Totals Coverage Status
Change from base Build 11600690174: 0.009%
Covered Lines: 15462
Relevant Lines: 28003

💛 - Coveralls

Copy link
Member

@evankanderson evankanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just got a chance to review this on computer rather than on phone, and I've got a few more comments.

internal/email/email.go Outdated Show resolved Hide resolved
internal/email/email.go Outdated Show resolved Hide resolved
internal/email/email.go Outdated Show resolved Hide resolved
internal/email/email.go Outdated Show resolved Hide resolved
internal/email/email.go Show resolved Hide resolved
// getEmailSubject returns the subject for the email based on the message payload
func getEmailSubject(project string) string {
return fmt.Sprintf("You have been invited to join the %s organization in Minder", project)
}

// isValidField checks if the string is empty or contains HTML or JavaScript injection
func isValidField(str string) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment that we want to detect and error, rather than escape?

}

// Check for JavaScript content separately
escapedJS := template.JSEscaper(str)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like JSEscapeString should be simpler / faster here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that any potential JS code would have been wrapped into HTML tags and so this is rather obsolete/redundant or it's something else?

internal/email/email.go Outdated Show resolved Hide resolved
internal/email/email_test.go Outdated Show resolved Hide resolved

// isEmail is used to validate if the email is a valid email address according to RFC 5322
func isEmail(emailString string) bool {
_, err := mail.ParseAddress(emailString)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be using only the address part for the rest of the function? I'm not sure we've thought about "Joe Bob <[email protected]>" and "Oh gosh, I really dislike you <[email protected]>" in the rest of our code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(If so, we could just return string or "")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, but do we want to support such kind of inputs? For now at least I assume it's much cleaner to fail if the input is not strictly an email.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is that mail.ParseAddress will accept "An unpleasant joke from recent politics <[email protected]>", and the Name field will be set to "An unpleasant joke from recent politics".

(And I was suggesting we should return just the parsed.Address part of the ParseAddress result.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(And, I'm okay removing this bit of code in favor of #4840 )

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to remove it in favour of the validation checks we'll introduce 👍

@rdimitrov rdimitrov force-pushed the validate-invite-html branch 3 times, most recently from b9869c6 to 91f14ac Compare October 29, 2024 23:51
Copy link
Member

@evankanderson evankanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits (which you can ignore) aligning with what I think of as idiomatic go, and one bit on isEmail that we probably do want to fix.

Comment on lines 82 to 83
err = data.Validate()
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
err = data.Validate()
if err != nil {
if err := data.Validate(); err != nil {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied 👍

return nil, err
}
// Create the HTML and text bodies
strBodyHTML, err := data.GetEmailBodyHTML(ctx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, these are really nice as methods on the data struct!

internal/email/email.go Outdated Show resolved Hide resolved
internal/email/email_test.go Outdated Show resolved Hide resolved

tests := []struct {
input bodyData
expectedErr bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need expectedErr separately from expectedErrMsg != ""?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 👍 Removed the obsolete bool for expected error 👍


// isEmail is used to validate if the email is a valid email address according to RFC 5322
func isEmail(emailString string) bool {
_, err := mail.ParseAddress(emailString)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is that mail.ParseAddress will accept "An unpleasant joke from recent politics <[email protected]>", and the Name field will be set to "An unpleasant joke from recent politics".

(And I was suggesting we should return just the parsed.Address part of the ParseAddress result.

@rdimitrov rdimitrov force-pushed the validate-invite-html branch from 91f14ac to 2e3ae2f Compare October 30, 2024 23:40
@rdimitrov rdimitrov merged commit df4a1ea into main Oct 31, 2024
25 checks passed
@rdimitrov rdimitrov deleted the validate-invite-html branch October 31, 2024 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants