-
Notifications
You must be signed in to change notification settings - Fork 4
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
feat(security): restrict email sender #2719
base: staging
Are you sure you want to change the base?
Conversation
@@ -39,6 +39,9 @@ gem "redis", "~> 4.0" | |||
# Use Active Model has_secure_password | |||
# gem 'bcrypt', '~> 3.1.7' | |||
|
|||
# Ensure uploaded file are secure | |||
gem "mimemagic", "~> 0.4" |
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.
Pour info cette Gem dépend du package système shared-mime-info
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.
Merci @Michaelvilleneuve 👍 , globalement ça m'a l'air cool et le mécanisme a l'air ok, je t'ai juste laissé quelques remarques.
En l'état actuel les fichiers invalides sont simplement retirés de la liste.
Je sais pas si c'est hors scope mais vu que ça demanderait plus de travail que l'estimation actuelle je préfère déjà proposer cette PR.
Pour moi c'est ok, faut juste confirmer avec le mécanisme entier avec l'équipe produit.
<%= form_for(:email, url: organisations_user_added_notifications_path, method: :post) do |f| %> | ||
<%= form_for(:email, url: department_organisation_user_added_notifications_path(department_id: current_department.id, organisation_id: organisation.id), method: :post) do |f| %> | ||
<%= f.hidden_field :user_id, value: user.id %> | ||
<%= f.hidden_field :source, value: local_assigns[:source] %> |
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.
Pourquoi est-ce qu'on ne passe pas toujours une source
à ce formulaire ?
Ce serait plus clair, là ça ne se voit pas que quand on en met pas que ça veut dire qu'on sur la page show
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.
Oui c'est pas faux, mon idée était d'en mettre une quand elle diffère de la source originale qui est l'affectation directe. Mais je suis d'accord ça sera plus clair si on en met systématiquement une
<%= f.text_area :content, readonly: true, class: "form-control", cols: 15, rows: 4, value: user_added_notification_content(source: local_assigns[:source], user:, organisation:) %> | ||
</div> | ||
<div class="mb-3"> | ||
<h5 class="text-dark-blue h4-as-labels">Ajouter un message personnalisé</h5> | ||
<p>Ce message apparaitra après le message ci-dessus</p> | ||
<%= f.text_area :custom_content, class: "form-control", cols: 15, rows: 6, placeholder: "Votre message personnalisé." %> |
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.
nitpick: il y a qq problèmes d'indentations ici
} | ||
|
||
.form-control[readonly] { | ||
background-color: white; |
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.
ça risque de causer des soucis si on le met sur toute l'appli, on pourrait pas définir la couleur au niveau de l'input ?
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.
Alors j'avais vérifié il n'y aucun autre endroit impacté dans l'application et ce venait venait overrider un code déjà existant au global :
Je me suis dit que le readonly ne méritait pas le même traitement qu'un disable donc j'ai overridé car ce gris était violent.
À la limite je vais confirmer l'ensemble visuellement + l'input custom avec l'équipe produit et je mettrai le code spécifiquement au niveau de l'input s'ils souhaitent poursuivre avec visuel 👍
end | ||
|
||
def sanitize | ||
return nil unless @uploaded_file.respond_to?(:original_filename) |
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.
dans quel cas est-ce qu'on peut tomber dans ce cas ?
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.
C'était au cas où quelqu'un manipule la requête et essai d'envoyer d'autres choses que des ActionDispatch::Http::UploadedFile
en paramètre attachments
du formulaire.
Théoriquement l'appli aurait crash sur les lignes suivantes mais là elle se contente du comportement classique, enlever le fichier des attachments.
C'est peut-être superflu, let me know
filename = @uploaded_file.original_filename | ||
extension = File.extname(filename).downcase | ||
mime_type = @uploaded_file.content_type | ||
file_size = @uploaded_file.size |
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.
gros nitpick: on pourrait extraire dans des méthodes d'instance privées si on le voulait vu que les méthodes qui les utilisent sont privées mais ça marche comme ça aussi
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.
C'est vrai que ce n'est pas forcément la responsabilité de cette méthode que d'avoir cette connaissance. Je fais le refacto 👍
resources :organisations, only: [] do | ||
resources :user_added_notifications, only: [:create], controller: 'organisations/user_added_notifications' | ||
end |
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.
il y a une raison particulière de changer les routes ?
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.
Oui, j'aurais du expliquer dans la description de la PR, en fait désormais vu qu'on ne passe plus le contenu en paramètre direct, on a besoin de savoir de reconstituer le message en backend, donc on doit savoir à quelle orga est destinée le message, on a donc besoin des paramètres suivants :
- department_id -> Car on veut s'assurer que l'agent vient bien du même département que l'orga à qui il a ajouté l'usager
- organisation_id -> Car on veut pouvoir récupérer le destinaire non pas via param mais via l'orga directement
- user_id -> Il nous faut préciser le nom et prénom de l'usager dans le contenu du mail et sachant qu'il n'est plus passé en paramètre
Ces 3 éléments font que cette route fait désormais plus de sens scopé à l'intérieur du département et de l'orga à laquelle est destiné l'email, que génériquement à la racine.
".csv" => "text/csv" | ||
}.freeze | ||
|
||
def self.sanitize(uploaded_files) |
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.
nitpick: pour être iso avec la méthode d'instance sanitize
ce serait bien de renommer cette méthode (par ex sanitize_all
) pour montrer qu'elle prend plusieurs arguments
".jpg" => "image/jpeg", | ||
".jpeg" => "image/jpeg", | ||
".png" => "image/png", | ||
".pdf" => "application/pdf", | ||
".xlsx" => ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/zip"], | ||
".csv" => "text/csv" |
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.
nitpick: j'ai l'impression que l'implémentation marche tel quel mais pour plus de cohérence est-ce qu'on ne devrait pas avoir que des array en valeur ?
def user_attachements | ||
(email_params[:attachments] || []).compact_blank | ||
def custom_content | ||
ActionView::Base.full_sanitizer.sanitize(email_params[:custom_content]) |
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.
Moi ça m'a l'air ok le fait de pouvoir rajouter un élément custom de la façon dont tu le proposes mais est-ce que t'as validé le principe et le wording avec l'équipe produit ?
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.
Non en effet, je leur en touche un mot pour confirmer ça et voir s'ils veulent modifier le visuel (à cause de la notion de read-only pour les autres champs qui peut être confusante)
J'ai mis à jour le visuel suite aux discussions avec Samantha : ![]() |
Cette PR permet d'améliorer la sécurité liée à l'envoi du mail d'information lors de l'ajout d'un usager dans une autre organisation du département.
Voici les changements fonctionnels apportés :
Il n'est plus possible de changer le destinataire du mail
Désormais le destinaire est figé, il reste affiché en read only dans la modale pour information mais n'est plus modifiable par l'usager.
J'ai aussi enlevé la sélection car dans tous les cas nous n'utilisions que le mail de l'organisation.
Aussi nous n'utilisons plus les params envoyé pour déduire l'email, on va le chercher depuis l'organisation en elle même.
Il n'est plus possible d'éditer le contenu principal
Le contenu principal du mail devient lui aussi figé afin de garantir que l'information principale (le transfert de l'usager) reste le texte principal affiché dans le mail.
Il devient possible d'ajouter un message personnalisé
J'ajoute la possibilité d'ajouter un message custom, celui-ci est désormais restreint à l'intérieur d'un bloc blockquote afin de le séparer visuellement et il clairement affiché comme étant un message personnalisé de l'organisation.
On ajoute de la vérification sur les pièces jointes
Toutes les pièces jointes ajoutées sont désormais scannées :
Envoi de mail depuis la fiche usager
Envoi de mail depuis la création d'orientation
Visuel du mail avec message custom
Visuel du mail depuis la création d'orientations
Amélioration potentielle
En l'état actuel les fichiers invalides sont simplement retirés de la liste.
Je sais pas si c'est hors scope mais vu que ça demanderait plus de travail que l'estimation actuelle je préfère déjà proposer cette PR.
Mais je pense qu'on devrait aussi revoir le workflow pour permettre plus de gestion d'erreurs et ainsi notifier l'utilisateur que certains fichiers sont invalides et les raisons associées pour qu'il puisse revoir sa liste.
En l'état dès que l'on appuie sur envoyer la modal est dismissed donc ça rend l'affichage simple des erreurs inutiles puisque l'usager n'a pas moyen d'éditer son contenu, faudra donc revoir ça aussi.
Fix #2696