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 - awk

まあ普通こういう処理はawkではしませんが・・・

% awk -f filter.awk
<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>

なお、タグの途中に改行が含まれるケースには対応していません。
 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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
BEGIN {
	ok_tag["A"] = ok_tag["BR"] = ok_tag["STRONG"] = 1
}

/<[A-Za-z]+/ {
	delete attributes
	s = $0
	while (match(s,/<[A-Za-z]+/)) {
		if (RSTART > 1) print substr(s,1,RSTART-1)
		tag = substr(s,RSTART+1,RLENGTH-1)
		s = substr(s,RSTART+RLENGTH)
		if (s ~ /^>/) {
			s = substr(s,2)
		} else {
			while (s != "") {
				gsub(/^[ \t\r\n]*/,"",s)
				if (match(s,/^[A-Za-z]+=/)) {
					attribute = substr(s,1,RLENGTH-1)
					s = substr(s,RLENGTH+1)
					if (match(s,/^("[^"]*"|'[^']*'|[^"'> ]*)/)) {
						value = substr(s,1,RLENGTH)
						attributes[attribute] = value
						s = substr(s,RLENGTH+1)
					}
				} else if (s ~ /^>/) {
					s = substr(s,2)
					break
				} else {
					break
				}
			}
		}

		tag_ = toupper(tag)
		if (ok_tag[tag_]) {
			if (tag_ == "A") {
				for (attr in attributes)
					if (toupper(attr) !~ /^(HREF|NAME)$/) delete attributes[attr]
			} else { #if (tag ~ /^(BR|STRONG)$/)
				delete attributes
			}
			printf("<%s", tag)
			for (attr in attributes) {
				value = urlencode(attributes[attr])
				gsub(/(^["']|["']$)/,"",value)
				if (toupper(attr) == "HREF") {
					printf(" %s=\"./%s\"", attr, value)
				} else {
					printf(" %s=\"%s\"", attr, value)
				}
			}
			printf(">")
		} else {
			printf("&lt;%s", tag) # substr(s,RSTART+RLENGTH)
			for (attr in attributes) {
				printf(" %s=%s", attr, escape(attributes[attr]))
			}
			printf("&gt;")
		}
	}
	
	if (match(s,/<\/[A-Za-z]+>/)) {
		content = substr(s,1,RSTART-1)
		close_tag = substr(s,RSTART+2,RLENGTH-3)
		
		s = substr(s,RSTART+RLENGTH)
		if (content != "") printf("%s", escape(content))

		tag_ = toupper(close_tag)
		if (ok_tag[tag_]) {
			printf("</%s>", close_tag)
		} else {
			printf("&lt;/%s&gt;", close_tag)
		}
	} else if (s != "") {
		printf("%s", s)
	}

	printf "\n"
	next
}

{ print }

function escape(s)
{
	gsub(/</,"\\&lt;",s)
	gsub(/>/,"\\&gt;",s)
	return s
}

function urlencode(s)
{
#	gsub(/ /,"%20",s)
	gsub(/</,"%3C",s)
#	gsub(/=/,"%3D",s)
	gsub(/>/,"%3E",s)
	return s
}

Index

Feed

Other

Link

Pathtraq

loading...