@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhutilMarkupTestCase extends PhutilTestCase {
4
5 public function testTagDefaults() {
6 $this->assertEqual(
7 (string)phutil_tag('br'),
8 (string)phutil_tag('br', array()));
9
10 $this->assertEqual(
11 (string)phutil_tag('br', array()),
12 (string)phutil_tag('br', array(), null));
13 }
14
15 public function testTagEmpty() {
16 $this->assertEqual(
17 '<br />',
18 (string)phutil_tag('br', array(), null));
19
20 $this->assertEqual(
21 '<div></div>',
22 (string)phutil_tag('div', array(), null));
23
24 $this->assertEqual(
25 '<div></div>',
26 (string)phutil_tag('div', array(), ''));
27 }
28
29 public function testTagBasics() {
30 $this->assertEqual(
31 '<br />',
32 (string)phutil_tag('br'));
33
34 $this->assertEqual(
35 '<div>y</div>',
36 (string)phutil_tag('div', array(), 'y'));
37 }
38
39 public function testTagAttributes() {
40 $this->assertEqual(
41 '<div u="v">y</div>',
42 (string)phutil_tag('div', array('u' => 'v'), 'y'));
43
44 $this->assertEqual(
45 '<br u="v" />',
46 (string)phutil_tag('br', array('u' => 'v')));
47 }
48
49 public function testTagEscapes() {
50 $this->assertEqual(
51 '<br u="<" />',
52 (string)phutil_tag('br', array('u' => '<')));
53
54 $this->assertEqual(
55 '<div><br /></div>',
56 (string)phutil_tag('div', array(), phutil_tag('br')));
57 }
58
59 public function testTagNullAttribute() {
60 $this->assertEqual(
61 '<br />',
62 (string)phutil_tag('br', array('y' => null)));
63 }
64
65 public function testTagJavascriptProtocolRejection() {
66 $hrefs = array(
67 'javascript:alert(1)' => true,
68 'JAVASCRIPT:alert(2)' => true,
69
70 // NOTE: When interpreted as a URI, this is dropped because of leading
71 // whitespace.
72 ' javascript:alert(3)' => array(true, false),
73 '/' => false,
74 '/path/to/stuff/' => false,
75 '' => false,
76 'http://example.com/' => false,
77 '#' => false,
78 'javascript://anything' => true,
79
80 // Chrome 33 and IE11, at a minimum, treat this as Javascript.
81 "javascript\n:alert(4)" => true,
82
83 // Opera currently accepts a variety of unicode spaces. This test case
84 // has a smattering of them.
85 "\xE2\x80\x89javascript:" => true,
86 "javascript\xE2\x80\x89:" => true,
87 "\xE2\x80\x84javascript:" => true,
88 "javascript\xE2\x80\x84:" => true,
89
90 // Because we're aggressive, all of unicode should trigger detection
91 // by default.
92 "\xE2\x98\x83javascript:" => true,
93 "javascript\xE2\x98\x83:" => true,
94 "\xE2\x98\x83javascript\xE2\x98\x83:" => true,
95
96 // We're aggressive about this, so we'll intentionally raise false
97 // positives in these cases.
98 'javascript~:alert(5)' => true,
99 '!!!javascript!!!!:alert(6)' => true,
100
101 // However, we should raise true negatives in these slightly more
102 // reasonable cases.
103 'javascript/:docs.html' => false,
104 'javascripts:x.png' => false,
105 'COOLjavascript:page' => false,
106 '/javascript:alert(1)' => false,
107 );
108
109 foreach (array(true, false) as $use_uri) {
110 foreach ($hrefs as $href => $expect) {
111 if (is_array($expect)) {
112 $expect = ($use_uri ? $expect[1] : $expect[0]);
113 }
114
115 if ($use_uri) {
116 $href_value = new PhutilURI($href);
117 } else {
118 $href_value = $href;
119 }
120
121 $caught = null;
122 try {
123 phutil_tag('a', array('href' => $href_value), 'click for candy');
124 } catch (Exception $ex) {
125 $caught = $ex;
126 }
127
128 $desc = pht(
129 'Unexpected result for "%s". <uri = %s, expect exception = %s>',
130 $href,
131 $use_uri ? pht('Yes') : pht('No'),
132 $expect ? pht('Yes') : pht('No'));
133
134 $this->assertEqual(
135 $expect,
136 $caught instanceof Exception,
137 $desc);
138 }
139 }
140 }
141
142 public function testURIEscape() {
143 $this->assertEqual(
144 '%2B/%20%3F%23%26%3A%21xyz%25',
145 phutil_escape_uri('+/ ?#&:!xyz%'));
146 }
147
148 public function testURIPathComponentEscape() {
149 $this->assertEqual(
150 'a%252Fb',
151 phutil_escape_uri_path_component('a/b'));
152
153 $str = '';
154 for ($ii = 0; $ii <= 255; $ii++) {
155 $str .= chr($ii);
156 }
157
158 $this->assertEqual(
159 $str,
160 phutil_unescape_uri_path_component(
161 rawurldecode( // Simulates webserver.
162 phutil_escape_uri_path_component($str))));
163 }
164
165 public function testHsprintf() {
166 $this->assertEqual(
167 '<div><3</div>',
168 (string)hsprintf('<div>%s</div>', '<3'));
169 }
170
171 public function testAppendHTML() {
172 $html = phutil_tag('hr');
173 $html->appendHTML(phutil_tag('br'), '<evil>');
174 $this->assertEqual('<hr /><br /><evil>', $html->getHTMLContent());
175 }
176
177 public function testArrayEscaping() {
178 $this->assertEqual(
179 '<div><div></div>',
180 phutil_escape_html(
181 array(
182 hsprintf('<div>'),
183 array(
184 array(
185 '<',
186 array(
187 'd',
188 array(
189 array(
190 hsprintf('i'),
191 ),
192 'v',
193 ),
194 ),
195 array(
196 array(
197 '>',
198 ),
199 ),
200 ),
201 ),
202 hsprintf('</div>'),
203 )));
204
205 $this->assertEqual(
206 '<div><br /><hr /><wbr /></div>',
207 phutil_tag(
208 'div',
209 array(),
210 array(
211 array(
212 array(
213 phutil_tag('br'),
214 array(
215 phutil_tag('hr'),
216 ),
217 phutil_tag('wbr'),
218 ),
219 ),
220 ))->getHTMLContent());
221 }
222
223}