safer templating; drop domain delocalization
This commit is contained in:
parent
8de600b27e
commit
ddc8076e9d
|
@ -1,8 +1,8 @@
|
|||
package lila.app
|
||||
package templating
|
||||
|
||||
import play.twirl.api.Html
|
||||
import ornicar.scalalib.Zero
|
||||
import play.twirl.api.Html
|
||||
|
||||
import lila.user.{ User, UserContext }
|
||||
|
||||
|
@ -16,30 +16,33 @@ trait StringHelper { self: NumberHelper =>
|
|||
|
||||
val escapeHtml = lila.common.String.html.escape _
|
||||
|
||||
private val escapeHtmlUnsafe = lila.common.String.html.escapeUnsafe _
|
||||
|
||||
def shorten(text: String, length: Int, sep: String = "…"): Html = {
|
||||
val t = text.replace("\n", " ")
|
||||
if (t.size > (length + sep.size)) Html(escapeHtml(t take length).body ++ sep)
|
||||
if (t.size > (length + sep.size)) Html(escapeHtmlUnsafe(t take length) ++ sep)
|
||||
else escapeHtml(t)
|
||||
}
|
||||
|
||||
def shortenWithBr(text: String, length: Int) = Html {
|
||||
nl2br(escapeHtml(text).body.take(length)).replace("<br /><br />", "<br />")
|
||||
nl2brUnsafe(escapeHtmlUnsafe(text).take(length)).replace("<br /><br />", "<br />")
|
||||
}
|
||||
|
||||
def pluralize(s: String, n: Int) = s"$n $s${if (n > 1) "s" else ""}"
|
||||
|
||||
def autoLink(text: String): Html = Html(nl2br(addUserProfileLinks(addLinks(escapeHtml(text).body))))
|
||||
def autoLink(text: String): Html = nl2br(addUserProfileLinksUnsafe(addLinksUnsafe(escapeHtmlUnsafe(text))))
|
||||
|
||||
def nl2br(text: String) = text.replace("\r\n", "<br />").replace("\n", "<br />")
|
||||
private def nl2brUnsafe(text: String): String =
|
||||
text.replace("\r\n", "<br />").replace("\n", "<br />")
|
||||
|
||||
def nl2br(text: String) = Html(nl2brUnsafe(text))
|
||||
|
||||
private val markdownLinkRegex = """\[([^\[]+)\]\(([^\)]+)\)""".r
|
||||
|
||||
def markdownLinks(text: String) = Html {
|
||||
nl2br {
|
||||
markdownLinkRegex.replaceAllIn(escapeHtml(text).body, m => {
|
||||
s"""<a href="${m group 2}">${m group 1}</a>"""
|
||||
})
|
||||
}
|
||||
def markdownLinks(text: String): Html = nl2br {
|
||||
markdownLinkRegex.replaceAllIn(escapeHtmlUnsafe(text), m => {
|
||||
s"""<a href="${m group 2}">${m group 1}</a>"""
|
||||
})
|
||||
}
|
||||
|
||||
private val urlRegex = """(?i)\b((https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,6}/)((?:[^\s<>]+|\(([^\s<>]+|(\([^\s<>]+\)))*\))+(?:\(([^\s<>]+|(\([^\s<>]+\)))*\)|[^\s`!\[\]{};:'".,<>?«»“”‘’])))""".r
|
||||
|
@ -49,20 +52,25 @@ trait StringHelper { self: NumberHelper =>
|
|||
* @param text The text to regex match
|
||||
* @return The text as a HTML hyperlink
|
||||
*/
|
||||
def addUserProfileLinks(text: String) = User.atUsernameRegex.replaceAllIn(text, m => {
|
||||
val user = m group 1
|
||||
val url = s"//$netDomain/@/$user"
|
||||
def addUserProfileLinks(text: String) = Html(addUserProfileLinksUnsafe(text))
|
||||
|
||||
s"""<a href="$url">@$user</a>"""
|
||||
})
|
||||
private def addUserProfileLinksUnsafe(text: String): String =
|
||||
User.atUsernameRegex.replaceAllIn(text, m => {
|
||||
val user = m group 1
|
||||
val url = s"//$netDomain/@/$user"
|
||||
|
||||
def addLinks(text: String) = try {
|
||||
s"""<a href="$url">@$user</a>"""
|
||||
})
|
||||
|
||||
def addLinks(text: String) = Html(addLinksUnsafe(text))
|
||||
|
||||
private def addLinksUnsafe(text: String): String = try {
|
||||
urlRegex.replaceAllIn(text, m => {
|
||||
if (m.group(0) contains """) m.group(0)
|
||||
else if (m.group(2) == "http://" || m.group(2) == "https://") {
|
||||
if (s"${delocalize(m.group(3))}/" startsWith s"$netDomain/") {
|
||||
if (s"${m.group(3)}/" startsWith s"$netDomain/") {
|
||||
// internal
|
||||
val link = delocalize(m.group(3))
|
||||
val link = m.group(3)
|
||||
s"""<a rel="nofollow" href="//$link">$link</a>"""
|
||||
} else {
|
||||
// external
|
||||
|
@ -70,9 +78,9 @@ trait StringHelper { self: NumberHelper =>
|
|||
s"""<a rel="nofollow" href="$link" target="_blank">$link</a>"""
|
||||
}
|
||||
} else {
|
||||
if (s"${delocalize(m.group(2))}/" startsWith s"$netDomain/") {
|
||||
if (s"${m.group(2)}/" startsWith s"$netDomain/") {
|
||||
// internal
|
||||
val link = delocalize(m.group(1))
|
||||
val link = m.group(1)
|
||||
s"""<a rel="nofollow" href="//$link">$link</a>"""
|
||||
} else {
|
||||
// external
|
||||
|
@ -87,8 +95,6 @@ trait StringHelper { self: NumberHelper =>
|
|||
text
|
||||
}
|
||||
|
||||
private val delocalize = new lila.common.String.Delocalizer(netDomain)
|
||||
|
||||
def showNumber(n: Int): String = if (n > 0) s"+$n" else n.toString
|
||||
|
||||
implicit def lilaRichString(str: String) = new {
|
||||
|
|
|
@ -151,7 +151,7 @@ description = describeUser(u)).some) {
|
|||
<strong class="name">@name</strong>
|
||||
}
|
||||
@profile.nonEmptyBio.ifTrue(!u.troll || ctx.is(u)).map { bio =>
|
||||
<p class="bio">@Html(addUserProfileLinks(addLinks(shorten(bio, 400).body)))</p>
|
||||
<p class="bio">@addUserProfileLinks(addLinks(shorten(bio, 400).body).body)</p>
|
||||
}
|
||||
}
|
||||
<div class="stats">
|
||||
|
|
|
@ -58,7 +58,7 @@ final class ChatApi(
|
|||
def clear(chatId: ChatId) = coll.remove($id(chatId)).void
|
||||
|
||||
def system(chatId: ChatId, text: String) = {
|
||||
val line = UserLine(systemUserId, Writer delocalize text, troll = false, deleted = false)
|
||||
val line = UserLine(systemUserId, text, troll = false, deleted = false)
|
||||
pushLine(chatId, line) >>-
|
||||
lilaBus.publish(actorApi.ChatLine(chatId, line), channelOf(chatId)) inject line.some
|
||||
}
|
||||
|
@ -155,10 +155,9 @@ final class ChatApi(
|
|||
|
||||
import java.util.regex.Matcher.quoteReplacement
|
||||
|
||||
def preprocessUserInput(in: String) = multiline(noShouting(delocalize(noPrivateUrl(in))))
|
||||
def preprocessUserInput(in: String) = multiline(noShouting(noPrivateUrl(in)))
|
||||
|
||||
def cut(text: String) = Some(text.trim take 140) filter (_.nonEmpty)
|
||||
val delocalize = new lila.common.String.Delocalizer(netDomain)
|
||||
|
||||
private val domainRegex = netDomain.replace(".", """\.""")
|
||||
private val gameUrlRegex = (domainRegex + """\b/([\w]{8})[\w]{4}\b""").r
|
||||
|
|
|
@ -24,13 +24,6 @@ object String {
|
|||
}
|
||||
}
|
||||
|
||||
final class Delocalizer(netDomain: String) {
|
||||
|
||||
private val regex = ("""\w{2}\.""" + quoteReplacement(netDomain)).r
|
||||
|
||||
def apply(url: String) = regex.replaceAllIn(url, netDomain)
|
||||
}
|
||||
|
||||
def shorten(text: String, length: Int, sep: String = "…") = {
|
||||
val t = text.replace("\n", " ")
|
||||
if (t.size > (length + sep.size)) (t take length) ++ sep
|
||||
|
@ -53,7 +46,9 @@ object String {
|
|||
object html {
|
||||
|
||||
// from https://github.com/android/platform_frameworks_base/blob/d59921149bb5948ffbcb9a9e832e9ac1538e05a0/core/java/android/text/TextUtils.java#L1361
|
||||
def escape(s: String): Html = {
|
||||
def escape(s: String): Html = Html(escapeUnsafe(s))
|
||||
|
||||
def escapeUnsafe(s: String): String = {
|
||||
val sb = new StringBuilder
|
||||
var i = 0
|
||||
while (i < s.length) {
|
||||
|
@ -69,7 +64,7 @@ object String {
|
|||
}
|
||||
i += 1
|
||||
}
|
||||
Html(sb.toString)
|
||||
sb.toString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export default function(text: string, parseMoves: boolean): string {
|
||||
const escaped = escapeHtml(delocalize(text));
|
||||
const escaped = escapeHtml(text);
|
||||
const linked = autoLink(escaped);
|
||||
const plied = parseMoves && linked === escaped ? addPlies(linked) : linked;
|
||||
return plied;
|
||||
|
@ -17,12 +17,6 @@ function autoLink(html: string) {
|
|||
return html.replace(linkPattern, linkReplace);
|
||||
}
|
||||
|
||||
const delocalizePattern = /(^|[\s\n]|<[A-Za-z]*\/?>)\w{2}\.lichess\.org/gi;
|
||||
|
||||
function delocalize(html: string) {
|
||||
return html.replace(delocalizePattern, '$1lichess.org');
|
||||
}
|
||||
|
||||
function escapeHtml(html: string) {
|
||||
return html
|
||||
.replace(/&/g, "&")
|
||||
|
|
Loading…
Reference in a new issue