This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-03
Channels
Hi everyone, I'm a beginner with biff and I'm trying to use DOMPurify in the UI of my Biff application - I just need to run DOMPurify.sanitize once on user input, I don't mind running it server side or client side. Is there any way to do this easily?
it is easy to do, in fact I'm using DOMPurify in my own app 🙂. I do it on the client side. You can add a JS file at E.g. resources/public/js/main.js
then include it in ui.clj
, then write a custom JS fn to do whatever you need. in my case I run it on render--I have a js fn that looks like so:
function renderPost() {
let element = $("#post-content");
let html = element.getAttribute('data-contents');
let options = {
USE_PROFILES: {
html: true,
svg: true,
},
ADD_ATTR: ["target"],
FORCE_BODY: true,
};
element.innerHTML = DOMPurify.sanitize(html, options);
...
}
then in Clojure I have a bit of code like [:div#post-content {:_ "init call renderPost()"}]
re: webjars, I learned about those about 30 seconds ago, but it should be straightforward to use with Biff. you could add some code to your project's on-save
function that copies the files from the webjars to your target/resources/public
directory.
or create some reitit handlers that return the files from the webjar at request time
Thank you for your answer. I ended up using the client-side solution and it works great!
>
let html = element.getAttribute('data-contents');
Do I understand it correctly?
1. you have an attribute called data-contents
inside of your div
2. This div somehow allows user's input
3. The init function runs once when the element is rendered
What happens when the user pastes some XSS snippet into the input field? Which part of these is the field?
You seem to be getting HTML of an attribute. So do you send non-pure HTML from back-end to the user to be sanitized there?
Then why not prevent saving this HTML into the database or sending it to back-end?1. yes--I accidentally omitted this from my :div
example above.
2. in my case the server takes content that's been previously inputed and renders it into the div, like [:div {:data-contents (get-html-from-s3 ...)}]
3. yes
in my case there's no input field because the unsafe content is being fetched/received via email, RSS, and bookmarked links. but if I were building something like a forum that allowed comments with arbitrary html, I'd follow a similar pattern--store the comments in the database as-is, then sanitize them on the client at render time with DOMPurify.
(let me know if that does/doesn't answer your questions at the end)
It clarifies your way of doing it but for me this is not good enough. Also you don't really pipe email content into database... it's still handled by your back-end code. Why not have a clean database? What if some administrator renders this in their DB tool?
well, yes, you can't receive emails without some sort of smtp server 🙂. is your point that since I have backend code that deals with receiving emails, why not put the sanitization at that part of the code? if you're concerned about untrusted content being accidentally rendered without sanitization, then by all means sanitize it at the point of ingestion. I haven't been concerned about this in my specific situation, and it's more convenient for me to use DOMPurify on the client since it's a JS lib, so that's why I sanitize at render time.
for completeness I should mention that I would only use DOMPurify in cases where you want to leave the original content as untouched as possible, which is needed for rendering email newsletters for example since they come with a ton of custom styling. if I was actually dealing with comments on a forum, I'd likely just use Jsoup to sanitize the html on the backend.
In my case I don't ever expect to accept HTML strings and I expect inputs of text that are later rendered only as text. So I don't know whether I need to do this sanitization (I probably should do it anyway). But maybe it's still a good idea anyway :thinking_face:
Also this: https://github.com/cure53/DOMPurify#what-about-legacy-browsers-like-internet-explorer
I'm building a single-page chatroom of sorts that will work as an HTML graffiti wall for users. I briefly considered filtering things client-side on user input, but I feel like that could be easy to bypass so I've kept it on the client side when things get rendered. As for why I'm not filtering things on the server, I don't want to run an instance of node just to filter content
https://htmx.org/docs/#rule-1-escape-all-user-content
> including removing attributes starting with hx-
and data-hx
> In my case I don't ever expect to accept HTML strings and I expect inputs of text that are later rendered only as text.
IMO sanitization would be overkill in this case; the main thing is to make sure the text is getting html-escaped on render, which Rum does by default.
> Also this:
> https://github.com/cure53/DOMPurify#what-about-legacy-browsers-like-internet-explorer
I hadn't noticed that--I'll probably modify my snippet above so it doesn't render at al if isSupported
is false.
> IMO sanitization would be overkill in this case
Yeah. I tested the inputs and outputs and it simply outputs as strings as expected. So as long as I don't directly put anything from DB then I should be good :thinking_face:
> I hadn't noticed that--I'll probably modify my snippet above so it doesn't render at al if isSupported
is false.
I think a better fix would be to check browser version. I think the "old" here means really too old :thinking_face:
^Alternatively, I can run it when I'm displaying the user input on everyone's screen
In case it's possible to use a webjar with biff, it has a webjar too