farmerbb
09/12/2023, 8:59 PMElement.innerHTML()
?
https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTMLian.shaun.thomas
09/13/2023, 5:42 AM@OptIn(ComposeWebInternalApi::class)
@Composable
fun HtmlText(rawHtml: String) {
val element = document.createElement("span")
element.innerHTML = rawHtml
ComposeNode<DomNodeWrapper, DomApplier>(
factory = { DomNodeWrapper(element) },
update = {
set(rawHtml) { value -> (node as Element).innerHTML = value }
},
)
}
The general goal here was to allow properly displaying localized strings that have embedded markup such as inline bold or italicized text.David Herman
09/13/2023, 7:55 AMattrs.ref { ... }
method
Here's another way to write the above code, which is both fewer lines and does not require internal API access:
@Composable
fun HtmlText(rawHtml: String) {
Span(attrs = {
ref {
element -> element.innerHTML = rawHTML
onDispose {}
}
}
}
David Herman
09/13/2023, 7:57 AMinnerHTML
as a last resort. @CLOVIS has more experience with the anti-pattern than I do, but it's code that at best needs to be audited carefully, because you can end up introducing unexpected security issues in your site when using it.CLOVIS
09/13/2023, 8:53 AMinnerHtml
is the most common entry point for an attacker. If you use it on your app, it's almost guaranteed that's where an attack will come from, because it's one of the only ways an attacker can remotely inject code without physical access. So you must audit all usages to be 100% it's impossible for an attacker to control the source of data.
...but the only real way to be 100% sure the attacker cannot impact the source of data, is if the data is entirely produced by the app without any network access, and in that case, using regular components is just simplerCLOVIS
09/13/2023, 8:54 AMdangerouslySetInnerHtml
so it's easy to CTRL+F for, and I've seen multiple apps fail a pentest just because it was present in the codebase.ian.shaun.thomas
09/13/2023, 6:11 PMWelcome, <b>Ian</b>
in a simple string sure that's easy to work around breaking it up but in more complex templates this isn't easy to work around without actually parsing the html. Fwiw as well, these strings are pre packaged as part of a localization flow so im not very worried about security risks and it's an app for "Smart" TVs so it isn't necessarily prone to the same attack vectors as a web browser.CLOVIS
09/13/2023, 8:40 PMian.shaun.thomas
09/13/2023, 8:41 PMDavid Herman
09/13/2023, 8:42 PMtranslations.json
, translations.fr.json
), download that on page startup and parse them into a structure that maps keys to values.CLOVIS
09/13/2023, 8:42 PM**
for bold and __
for italic. I'm not sure how much formatting you need, but I feel like that should be enough for almost all use-cases.David Herman
09/13/2023, 8:44 PMDavid Herman
09/13/2023, 8:45 PMDavid Herman
09/13/2023, 8:46 PMian.shaun.thomas
09/13/2023, 8:49 PM{
"TopLevelId": {
"Hello": "World"
}
"AnotherPage": {
....
}
}
Using gradle I combine all the possible keys, spit out warnings for locale files missing keys and back fill them the base english translations. Finally rewrite that as a js file that is dynamically imported based on the user locale if one exists and also always import english. Lastly, I use poet to generate files like this which provide key mappings + a localization function to walk the user locale lookup with a redundant backup to fall back to english for each localization if somehow that didn't happen properly in the gradle task:
public class R {
public class TopLevelId {
public companion object {
public inline val Hello: String
get() = "TopLevelId.Hello".localized()
}
}
}
locales["en"] = {"TopLevelId":{"Hello":"World"}}
locales["es-ES"] = {"TopLevelId":{"Hello":"Mundo"}}
ian.shaun.thomas
09/13/2023, 8:49 PMCurious how well Compose HTML performs on a Smart TV, if there's anything you can share.bad
David Herman
09/13/2023, 8:50 PMian.shaun.thomas
09/13/2023, 8:50 PMDavid Herman
09/13/2023, 8:52 PMian.shaun.thomas
09/13/2023, 8:55 PMCLOVIS
09/13/2023, 9:04 PMian.shaun.thomas
09/13/2023, 9:34 PMbuildConfig
like most people already use.