challenge 一部のHTMLタグを通すフィルタ

ユーザが入力した文字列から、一部のタグだけを許可して他をエスケープするコードを書いてください。要件は次のようになります。
  • 通すタグはAとBRとSTRONGのみ。大文字小文字は区別しない。
  • それ以外のタグとして意味を持ちうる文字列は<を&lt;に変換することで無効化する(削除するのではない。>は変換してもしなくてもよい)
  • Aタグのhrefとname以外の属性は削除する。BRやSTRONGの属性はすべて削除する。

このお題はperezvonさんの提案を元にしています。ありがとうございました。 ただ、いきなりだと難しいかと思ったので、肝の部分以外を先に出題しました。このお題は続編で徐々に難しくなっていきます。

追記:属性に<や>が含まれてしまうケースに漏れのある解答が多いようなのでテストケースを追加します。
これは「この出力なら十分」という意味です。この出力の通りでなければいけないという意味ではありません。

<script foo="<script>alert('bar')</script>">alert('foo')</script>
&lt;script foo="&lt;script&gt;alert('bar')&lt;/script&gt;"&gt;alert('foo')&lt;/script&gt;


<script foo="<a href='link'>link</a>">alert('foo')</script>
&lt;script foo="&lt;a href='link'&gt;link&lt;/a&gt;"&gt;alert('foo')&lt;/script&gt;

<a href='www.g>oogle.com'>link</a>

<a href="./www.g%3Eoogle.com">link</a>

Posted feedbacks - Scala

ライブラリを使ったら面白くないので、自分で書いてみた。思ったよりすっきりかけた気がする。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.util.regex._
class ExtendedString(self:String) {
  def gsub(reg:Pattern, f:(Matcher)=>String):String = {
    val result = new StringBuffer
    val m = reg.matcher(self)
    while(m.find) m.appendReplacement(result, f(m))
    m.appendTail(result)
    result.toString
  }
  def gsub(reg:String, f:(Matcher)=>String):String = gsub(Pattern.compile(reg), f)
}
implicit def string2ext(self:String) = new ExtendedString(self);


object htmlEscape{
  lazy val tagRegex = Pattern.compile(
    """(</?)([^"'< >]*)([^"'<>]*(?:"[^"]*"[^"'<>]*|'[^']*'[^"'<>]*)*)((?:>|(?=<)|$(?!\n)))"""
  )
  lazy val attrRegex = Pattern.compile(
    """[\s'"](\w+)\s*=\s*([^\s'">]+|'[^']+'|\"[^"]+")"""
  , Pattern.DOTALL | Pattern.CASE_INSENSITIVE)
  lazy val tagAllowed = Set("a", "br", "strong")
  lazy val attrAllowed = Map("a" -> Set("href", "name"))

  def apply(html:String) = {
    html.gsub(tagRegex, (m:Matcher) => {
      val tag = m.group(2).toLowerCase.replace("/","")
      (if(tagAllowed.contains(tag)){
        val attrs = m.group(3).gsub(attrRegex, (m2:Matcher) => {
          if(attrAllowed.getOrElse(tag, Set[String]()).contains(m2.group(1).toLowerCase)) {
            m2.group(0)
          }else {
            ""
          }
        })
        List(m.group(1),m.group(2), attrs, m.group(4))
      }else {
        List(m.group(1).replace("<", "&lt;"), m.group(2), m.group(3), m.group(4))
      }).mkString("")
    })
  }
}
println(htmlEscape("""<a href='www.google.com'>link</a> <blink>and</blink> <strong onClick='alert("NG")'>click<br/>me!</strong>"""))

Index

Feed

Other

Link

Pathtraq

loading...