2 // System.Web.HttpRequestTest.cs - Unit tests for System.Web.HttpRequest
5 // Sebastien Pouliot <sebastien@ximian.com>
6 // Miguel de Icaza <miguel@novell.com>
7 // Gonzalo Paniagua Javier <gonzalo@novell.com>
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections.Specialized;
34 using NUnit.Framework;
35 using System.Diagnostics;
37 namespace MonoTests.System.Web {
40 // Tons of failures on msft here.
41 [Category ("NotDotNet")]
42 public class HttpRequestTest {
46 [ExpectedException (typeof (HttpRequestValidationException))]
47 public void ValidateInput_XSS ()
49 string problem = "http://server.com/attack2.aspx?test=<script>alert('vulnerability')</script>";
50 string decoded = HttpUtility.UrlDecode (problem);
51 int n = decoded.IndexOf ('?');
52 HttpRequest request = new HttpRequest (null, decoded.Substring (0,n), decoded.Substring (n+1));
53 request.ValidateInput ();
54 // the next statement throws
55 Assert.AreEqual ("<script>alert('vulnerability')</script>", request.QueryString ["test"], "QueryString");
59 // * this is to avoid a regression that would cause Mono to
60 // fail again on item #2 of the XSS vulnerabilities listed at:
61 // http://it-project.ru/andir/docs/aspxvuln/aspxvuln.en.xml
62 // * The author notes that Microsoft has decided not to fix
63 // this issue (hence the NotDotNet category).
66 [Category ("NotDotNet")]
67 [ExpectedException (typeof (HttpRequestValidationException))]
68 public void ValidateInput_XSS_Unicode ()
70 string problem = "http://server.com/attack2.aspx?test=%uff1cscript%uff1ealert('vulnerability')%uff1c/script%uff1e";
71 string decoded = HttpUtility.UrlDecode (problem);
72 int n = decoded.IndexOf ('?');
73 HttpRequest request = new HttpRequest (null, decoded.Substring (0,n), decoded.Substring (n+1));
74 request.ValidateInput ();
75 // the next statement throws
76 Assert.AreEqual ("\xff1cscript\xff1ealert('vulnerability')\xff1c/script\xff1e", request.QueryString ["test"], "QueryString");
79 // This has affected ASP.NET 1.1 but it seems fixed now
80 // http://secunia.com/advisories/9716/
81 // http://weblogs.asp.net/kaevans/archive/2003/11/12/37169.aspx
83 [ExpectedException (typeof (HttpRequestValidationException))]
84 public void ValidateInput_XSS_Null ()
86 string problem = "http://secunia.com/?test=<%00SCRIPT>alert(document.cookie)</SCRIPT>";
87 string decoded = HttpUtility.UrlDecode (problem);
88 int n = decoded.IndexOf ('?');
89 HttpRequest request = new HttpRequest (null, decoded.Substring (0,n), decoded.Substring (n+1));
90 request.ValidateInput ();
91 // the next statement throws
92 Assert.AreEqual ("<SCRIPT>alert(document.cookie)</SCRIPT>", request.QueryString ["test"], "QueryString");
96 // Tests the properties from the simple constructor.
98 public void Test_PropertiesSimpleConstructor ()
100 string url = "http://www.gnome.org/";
101 string qs = "key=value&key2=value%32second";
103 HttpRequest r = new HttpRequest ("file", url, qs);
105 Assert.AreEqual ("/?" + qs, r.RawUrl, "U1");
106 Assert.AreEqual (url, r.Url.ToString (), "U2");
108 r = new HttpRequest ("file", "http://www.gnome.org", qs);
109 Assert.AreEqual (url, r.Url.ToString (), "U3");
111 qs = "a&b=1&c=d&e&b=2&d=";
112 r = new HttpRequest ("file", url, qs);
113 NameValueCollection nvc = r.QueryString;
115 Assert.AreEqual ("a,e", nvc [null], "U4");
116 Assert.AreEqual ("1,2", nvc ["b"], "U5");
117 Assert.AreEqual ("d", nvc ["c"], "U5");
118 Assert.AreEqual ("", nvc ["d"], "U6");
119 Assert.AreEqual (4, nvc.Count, "U6");
121 Assert.AreEqual (null, r.ApplicationPath, "U7");
124 [Test][ExpectedException(typeof(NullReferenceException))]
125 public void Test_AccessToVars ()
127 string url = "http://www.gnome.org/";
128 string qs = "key=value&key2=value%32second";
130 HttpRequest r = new HttpRequest ("file", url, qs);
131 string s = r.PhysicalApplicationPath;
136 [Category ("NotDotNet")]
137 public class Test_HttpFakeRequest {
138 class FakeHttpWorkerRequest : HttpWorkerRequest {
139 public int return_kind;
141 [Conditional ("REQUEST_TEST_VERY_VERBOSE")]
144 Console.WriteLine (Environment.StackTrace);
148 public FakeHttpWorkerRequest (int re)
153 public override string GetUriPath()
159 public override string GetQueryString()
163 switch (return_kind) {
167 return "key1=value1&key2=value2";
170 return "mapa.x=10&mapa.y=20";
173 return "mapa.x=10&mapa=20";
175 return "mapa.x=10&mapa.y=20";
181 return "mapa.x=pi&mapa.y=20";
183 return "GetQueryString";
187 public override string GetRawUrl()
193 public override string GetHttpVerbName()
196 if (return_kind == 25 || return_kind == 26)
198 if (return_kind == 30 || return_kind == 31)
203 public override string GetHttpVersion()
209 public override byte [] GetPreloadedEntityBody ()
211 if (return_kind != 30 && return_kind != 31)
212 return base.GetPreloadedEntityBody ();
214 return Encoding.UTF8.GetBytes (GetQueryString ());
217 public override bool IsEntireEntityBodyIsPreloaded ()
219 if (return_kind != 30 && return_kind != 31)
220 return base.IsEntireEntityBodyIsPreloaded ();
225 public override int GetRemotePort()
230 public override string GetLocalAddress()
235 public override string GetAppPath ()
240 public override string GetRemoteName ()
245 public override string GetRemoteAddress ()
247 return "RemoteAddress";
250 public override string GetServerName ()
255 public override int GetLocalPort()
260 public override void SendStatus(int s, string x)
264 public override void SendKnownResponseHeader(int x, string j)
268 public override void SendUnknownResponseHeader(string a, string b)
272 public override void SendResponseFromMemory(byte[] arr, int x)
276 public override void SendResponseFromFile(string a, long b , long c)
281 public override void SendResponseFromFile (IntPtr a, long b, long c)
285 public override void FlushResponse(bool x)
289 public override void EndOfRequest() {
292 public override string GetKnownRequestHeader (int index)
295 case HttpWorkerRequest.HeaderContentType:
296 switch (return_kind){
297 case 1: return "text/plain";
298 case 2: return "text/plain; charset=latin1";
299 case 3: return "text/plain; charset=iso-8859-1";
300 case 4: return "text/plain; charset=\"iso-8859-1\"";
301 case 5: return "text/plain; charset=\"iso-8859-1\" ; other";
304 return "application/x-www-form-urlencoded";
308 case HttpWorkerRequest.HeaderContentLength:
309 switch (return_kind){
310 case 0: return "1024";
311 case 1: return "-1024";
314 return GetQueryString ().Length.ToString ();
315 case -1: return "Blah";
320 case HttpWorkerRequest.HeaderCookie:
321 switch (return_kind){
322 case 10: return "Key=Value";
323 case 11: return "Key=<value>";
324 case 12: return "Key=>";
325 case 13: return "Key=\xff1c";
326 case 14: return "Key=\xff1e";
329 case HttpWorkerRequest.HeaderReferer:
330 switch (return_kind){
332 case 2: return "http://www.mono-project.com/test.aspx";
333 case 15: return "http://www.mono-project.com";
336 case HttpWorkerRequest.HeaderUserAgent:
337 switch (return_kind){
338 case 15: return "Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040913 Firefox/0.10";
341 case HttpWorkerRequest.HeaderAccept:
342 switch (return_kind){
343 case 21: return "text/xml,application/xml, application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
346 case HttpWorkerRequest.HeaderAcceptLanguage:
347 switch (return_kind){
348 case 21: return "en-us, en;q=0.5";
355 public override string [][] GetUnknownRequestHeaders ()
357 if (return_kind == 0)
358 return new string [0][];
360 if (return_kind == 3){
361 string [][] x = new string [4][];
362 x [0] = new string [] { "k1", "v1" };
363 x [1] = new string [] { "k2", "v2" };
364 x [2] = new string [] { "k3", "v3" };
365 x [3] = new string [] { "k4", "v4" };
370 if (return_kind == 4){
372 // This tests the bad values and the extra row with an error
374 string [][] x = new string [3][];
375 x [0] = new string [] { "k1", "" };
376 x [1] = new string [] { "k2", null };
377 x [2] = new string [] { "k3", " " };
382 if (return_kind == 2){
383 string [][] x = new string [2][];
384 x [0] = new string [] { "k1", "" };
386 // Returns an empty row.
394 HttpContext Cook (int re)
396 FakeHttpWorkerRequest f = new FakeHttpWorkerRequest (re);
397 HttpContext c = new HttpContext (f);
402 [Test] public void Test_BrokenContentLength ()
404 HttpContext c = Cook (-1);
405 Assert.AreEqual (0, c.Request.ContentLength, "C1");
408 Assert.AreEqual (0, c.Request.ContentLength, "C2");
412 [Test][ExpectedException(typeof(NullReferenceException))]
413 public void Test_EmptyUnknownRow ()
415 HttpContext c = Cook (2);
416 NameValueCollection x = c.Request.Headers;
419 [Test] public void Test_RequestFields ()
421 HttpContext c = Cook (1);
423 Assert.AreEqual ("AppPath", c.Request.ApplicationPath, "A1");
424 Assert.AreEqual ("text/plain", c.Request.ContentType, "A2");
427 Assert.AreEqual (1024, c.Request.ContentLength, "A3");
430 Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A4");
431 NameValueCollection x = c.Request.Headers;
433 Assert.AreEqual ("v1", x ["k1"], "K1");
434 Assert.AreEqual ("v2", x ["k2"], "K2");
435 Assert.AreEqual ("v3", x ["k3"], "K3");
436 Assert.AreEqual ("v4", x ["k4"], "K4");
437 Assert.AreEqual ("text/plain; charset=iso-8859-1", x ["Content-Type"], "K4");
438 Assert.AreEqual (5, x.Count, "K5");
441 Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A5");
442 Assert.AreEqual ("text/plain; charset=latin1", c.Request.ContentType, "A5-1");
445 Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A6");
446 x = c.Request.Headers;
447 Assert.AreEqual ("", x ["k1"], "K6");
448 Assert.AreEqual (null, x ["k2"], "K7");
449 Assert.AreEqual (" ", x ["k3"], "K8");
450 Assert.AreEqual (4, x.Count, "K9");
453 Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A7");
455 Assert.AreEqual ("RemoteName", c.Request.UserHostName, "A8");
456 Assert.AreEqual ("RemoteAddress", c.Request.UserHostAddress, "A9");
458 // Difference between Url property and RawUrl one: one is resolved, the other is not
459 Assert.AreEqual ("/bb.aspx", c.Request.RawUrl, "A10");
460 Assert.AreEqual ("http://localhost:2020/uri.aspx?GetQueryString", c.Request.Url.ToString (), "A11");
463 [Test] public void Test_Cookies ()
468 c.Request.ValidateInput ();
469 Assert.AreEqual ("Value", c.Request.Cookies ["Key"].Value, "cookie1");
472 [Test][ExpectedException(typeof (HttpRequestValidationException))]
473 public void Test_DangerousCookie ()
478 c.Request.ValidateInput ();
479 object a = c.Request.Cookies;
482 [Test][ExpectedException(typeof(HttpRequestValidationException))]
483 public void Test_DangerousCookie2 ()
488 c.Request.ValidateInput ();
489 object a = c.Request.Cookies;
492 [Test][ExpectedException(typeof(HttpRequestValidationException))]
493 public void Test_DangerousCookie3 ()
498 c.Request.ValidateInput ();
499 object a = c.Request.Cookies;
502 [Test][ExpectedException(typeof(HttpRequestValidationException))]
503 public void Test_DangerousCookie4 ()
508 c.Request.ValidateInput ();
509 object a = c.Request.Cookies;
513 public void Test_MiscHeaders ()
515 HttpContext c = Cook (15);
517 // The uri ToString contains the trailing slash.
518 Assert.AreEqual ("http://www.mono-project.com/", c.Request.UrlReferrer.ToString (), "ref1");
520 Assert.AreEqual ("Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040913 Firefox/0.10", c.Request.UserAgent, "ref2");
522 // All the AcceptTypes and UserLanguages tests below here pass under MS
524 string [] at = c.Request.AcceptTypes;
525 string [] ul = c.Request.UserLanguages;
526 Assert.IsNull (at, "AT1");
527 Assert.IsNull (ul, "UL1");
529 at = c.Request.AcceptTypes;
530 Assert.IsNotNull (at, "AT2");
531 string [] expected = { "text/xml", "application/xml", "application/xhtml+xml", "text/html;q=0.9",
532 "text/plain;q=0.8", "image/png", "*/*;q=0.5" };
534 Assert.AreEqual (expected.Length, at.Length, "AT3");
535 for (int i = expected.Length - 1; i >= 0; i--)
536 Assert.AreEqual (expected [i], at [i], "AT" + (3 + i));
538 ul = c.Request.UserLanguages;
539 Assert.IsNotNull (ul, "UL2");
540 expected = new string [] { "en-us", "en;q=0.5" };
542 Assert.AreEqual (expected.Length, ul.Length, "UL3");
543 for (int i = expected.Length - 1; i >= 0; i--)
544 Assert.AreEqual (expected [i], ul [i], "UL" + (3 + i));
549 public void Empty_WorkerRequest_QueryString ()
551 HttpContext c = Cook (20);
554 // Checks that the following line does not throw an exception if
555 // the querystring returned by the HttpWorkerRequest is null.
557 NameValueCollection nvc = c.Request.QueryString;
561 public void Leading_qm_in_QueryString ()
563 HttpContext c = Cook (16);
564 NameValueCollection nvc = c.Request.QueryString;
565 foreach (string id in nvc.AllKeys) {
566 if (id.StartsWith ("?"))
572 public void TestPath ()
574 HttpContext c = Cook (16);
576 // This used to crash, ifolder exposed this
577 string x = c.Request.Path;
581 public void MapImageCoordinatesHEAD ()
583 HttpContext c = Cook (25);
584 int [] coords = c.Request.MapImageCoordinates ("mapa");
585 Assert.IsNotNull (coords, "A1");
586 Assert.AreEqual (10, coords [0], "X");
587 Assert.AreEqual (20, coords [1], "Y");
590 coords = c.Request.MapImageCoordinates ("mapa");
591 Assert.AreEqual (null, coords, "coords");
595 public void MapImageCoordinatesGET ()
597 HttpContext c = Cook (27);
598 int [] coords = c.Request.MapImageCoordinates ("mapa");
599 Assert.AreEqual (10, coords [0], "X");
600 Assert.AreEqual (20, coords [1], "Y");
602 coords = c.Request.MapImageCoordinates ("m");
603 Assert.AreEqual (null, coords, "coords1");
606 coords = c.Request.MapImageCoordinates ("mapa");
607 Assert.AreEqual (null, coords, "coords2");
609 coords = c.Request.MapImageCoordinates ("mapa");
610 Assert.AreEqual (null, coords, "coords3");
612 coords = c.Request.MapImageCoordinates ("mapa");
613 Assert.AreEqual (null, coords, "coords4");
617 public void MapImageCoordinatesPOST ()
619 HttpContext c = Cook (30);
620 int [] coords = c.Request.MapImageCoordinates ("mapa");
621 Assert.IsNotNull (coords, "A1");
622 Assert.AreEqual (10, coords [0], "X");
623 Assert.AreEqual (20, coords [1], "Y");
626 coords = c.Request.MapImageCoordinates ("mapa");
627 Assert.AreEqual (null, coords, "coords2");
631 public void TestReferer ()
633 HttpContext c = Cook (1);
635 Assert.AreEqual (null, c.Request.UrlReferrer, "REF1");
638 Assert.AreEqual ("http://www.mono-project.com/test.aspx", c.Request.UrlReferrer.ToString (), "REF1");
643 public void NegativeContentLength ()
645 HttpContext c = Cook (1);
646 HttpRequest req = c.Request;
647 Assert.AreEqual (0, req.ContentLength, "#01");