c42084f9ca981e96342c74a8e217c77d6202b9ea
[mono.git] / mcs / class / System.Web / Test / System.Web / HttpRequestTest.cs
1 //
2 // System.Web.HttpRequestTest.cs - Unit tests for System.Web.HttpRequest
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //      Miguel de Icaza    <miguel@novell.com>
7 //      Gonzalo Paniagua Javier <gonzalo@novell.com>
8 //
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
10 //
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:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
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.
29 //
30 using System;
31 using System.Text;
32 using System.Web;
33 using System.Collections.Specialized;
34 using NUnit.Framework;
35 using System.Diagnostics;
36
37 namespace MonoTests.System.Web {
38
39         [TestFixture]
40         // Tons of failures on msft here.
41         [Category ("NotDotNet")]
42         public class HttpRequestTest {
43
44 #if NET_1_1
45                 [Test]
46                 [ExpectedException (typeof (HttpRequestValidationException))]
47                 public void ValidateInput_XSS ()
48                 {
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");
56                 }
57
58                 // Notes:
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).
64
65                 [Test]
66                 [Category ("NotDotNet")]
67                 [ExpectedException (typeof (HttpRequestValidationException))]
68                 public void ValidateInput_XSS_Unicode ()
69                 {
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");
77                 }
78
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
82                 [Test]
83                 [ExpectedException (typeof (HttpRequestValidationException))]
84                 public void ValidateInput_XSS_Null ()
85                 {
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");
93                 }
94
95                 //
96                 // Tests the properties from the simple constructor.
97                 [Test]
98                 public void Test_PropertiesSimpleConstructor ()
99                 {
100                         string url = "http://www.gnome.org/";
101                         string qs = "key=value&key2=value%32second";
102                         
103                         HttpRequest r = new HttpRequest ("file", url, qs);
104
105                         Assert.AreEqual ("/?" + qs, r.RawUrl, "U1");
106                         Assert.AreEqual (url, r.Url.ToString (), "U2");
107
108                         r = new HttpRequest ("file", "http://www.gnome.org", qs);
109                         Assert.AreEqual (url, r.Url.ToString (), "U3");
110
111                         qs = "a&b=1&c=d&e&b=2&d=";
112                         r = new HttpRequest ("file", url, qs);
113                         NameValueCollection nvc = r.QueryString;
114
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");
120
121                         Assert.AreEqual (null, r.ApplicationPath, "U7");
122                 }
123
124                 [Test][ExpectedException(typeof(NullReferenceException))]
125                 public void Test_AccessToVars ()
126                 {
127                         string url = "http://www.gnome.org/";
128                         string qs = "key=value&key2=value%32second";
129                         
130                         HttpRequest r = new HttpRequest ("file", url, qs);
131                         string s = r.PhysicalApplicationPath;
132                 }
133         }
134
135         [TestFixture]
136         [Category ("NotDotNet")]
137         public class Test_HttpFakeRequest {
138                 class FakeHttpWorkerRequest : HttpWorkerRequest {
139                         public int return_kind;
140
141                         [Conditional ("REQUEST_TEST_VERY_VERBOSE")]
142                         void WhereAmI ()
143                         {
144                                 Console.WriteLine (Environment.StackTrace);
145                         }
146                         
147
148                         public FakeHttpWorkerRequest (int re)
149                         {
150                                 return_kind = re;
151                         }
152                         
153                         public override string GetUriPath()
154                         {
155                                 WhereAmI ();
156                                 return "/uri.aspx";
157                         }
158         
159                         public override string GetQueryString()
160                         {
161                                 WhereAmI ();
162
163                                 switch (return_kind) {
164                                 case 20:
165                                         return null;
166                                 case 16:
167                                         return "key1=value1&key2=value2";
168                                 case 25: // HEAD
169                                 case 30: // POST
170                                         return "mapa.x=10&mapa.y=20";
171                                 case 26: // HEAD
172                                 case 31: // POST
173                                         return "mapa.x=10&mapa=20";
174                                 case 27: // GET
175                                         return "mapa.x=10&mapa.y=20";
176                                 case 28: // GET
177                                         return "mapa.x=10";
178                                 case 29: // GET
179                                         return "mapa=10";
180                                 case 32: // GET
181                                         return "mapa.x=pi&mapa.y=20";
182                                 default:
183                                         return "GetQueryString";
184                                 }
185                         }
186         
187                         public override string GetRawUrl()
188                         {
189                                 WhereAmI ();
190                                 return "/bb.aspx";
191                         }
192         
193                         public override string GetHttpVerbName()
194                         {
195                                 WhereAmI ();
196                                 if (return_kind == 25 || return_kind == 26)
197                                         return "HEAD";
198                                 if (return_kind == 30 || return_kind == 31)
199                                         return "POST";
200                                 return "GET";
201                         }
202         
203                         public override string GetHttpVersion()
204                         {
205                                 WhereAmI ();
206                                 return "HTTP/1.1";
207                         }
208         
209                         public override byte [] GetPreloadedEntityBody ()
210                         {
211                                 if (return_kind != 30 && return_kind != 31)
212                                         return base.GetPreloadedEntityBody ();
213
214                                 return Encoding.UTF8.GetBytes (GetQueryString ());
215                         }
216
217                         public override bool IsEntireEntityBodyIsPreloaded ()
218                         {
219                                 if (return_kind != 30 && return_kind != 31)
220                                         return base.IsEntireEntityBodyIsPreloaded ();
221
222                                 return true;
223                         }
224
225                         public override int GetRemotePort()
226                         {
227                                 return 1010;
228                         }
229         
230                         public override string GetLocalAddress()
231                         {
232                                 return "localhost";
233                         }
234
235                         public override string GetAppPath ()
236                         {
237                                 return "AppPath";
238                         }
239
240                         public override string GetRemoteName ()
241                         {
242                                 return "RemoteName";
243                         }
244
245                         public override string GetRemoteAddress ()
246                         {
247                                 return "RemoteAddress";
248                         }
249
250                         public override string GetServerName ()
251                         {
252                                 return "localhost";
253                         }
254                         
255                         public override int GetLocalPort()
256                         {
257                                 return 2020;
258                         }
259         
260                         public override void SendStatus(int s, string x)
261                         {
262                         }
263         
264                         public override void SendKnownResponseHeader(int x, string j)
265                         {
266                         }
267         
268                         public override void SendUnknownResponseHeader(string a, string b)
269                         {
270                         }
271                 
272                         public override void SendResponseFromMemory(byte[] arr, int x)
273                         {
274                         }
275         
276                         public override void SendResponseFromFile(string a, long b , long c)
277                         {
278                                 WhereAmI ();
279                         }
280         
281                         public override void SendResponseFromFile (IntPtr a, long b, long c)
282                         {
283                         }
284         
285                         public override void FlushResponse(bool x)
286                         {
287                         }
288         
289                         public override void EndOfRequest() {
290                         }
291
292                         public override string GetKnownRequestHeader (int index)
293                         {
294                                 switch (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";
302                                         case 30:
303                                         case 31:
304                                                 return "application/x-www-form-urlencoded";
305                                         }
306                                         break;
307                                         
308                                 case HttpWorkerRequest.HeaderContentLength:
309                                         switch (return_kind){
310                                         case 0:  return "1024";
311                                         case 1:  return "-1024";
312                                         case 30:
313                                         case 31:
314                                                 return GetQueryString ().Length.ToString ();
315                                         case -1: return "Blah";
316                                         case -2: return "";
317                                         }
318                                                 break;
319
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";
327                                         }
328                                         break;
329                                 case HttpWorkerRequest.HeaderReferer:
330                                         switch (return_kind){
331                                         case 1: return null;
332                                         case 2: return "http://www.mono-project.com/test.aspx";
333                                         case 15: return "http://www.mono-project.com";
334                                         }
335                                         break;
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";
339                                         }
340                                         break;
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";
344                                         }
345                                         break;
346                                 case HttpWorkerRequest.HeaderAcceptLanguage:
347                                         switch (return_kind){
348                                         case 21: return "en-us, en;q=0.5";
349                                         }
350                                         break;
351                                 }
352                                 return "";
353                         }
354
355                         public override string [][] GetUnknownRequestHeaders ()
356                         {
357                                 if (return_kind == 0)
358                                         return new string [0][];
359
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" };
366
367                                         return x;
368                                 }
369
370                                 if (return_kind == 4){
371                                         //
372                                         // This tests the bad values and the extra row with an error
373                                         //
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", "   " };
378
379                                         return x;
380                                 }
381
382                                 if (return_kind == 2){
383                                         string [][] x = new string [2][];
384                                         x [0] = new string [] { "k1", "" };
385
386                                         // Returns an empty row.
387                                         return x;
388                                 }
389
390                                 return null;
391                         }
392                 }
393
394                 HttpContext Cook (int re)
395                 {
396                         FakeHttpWorkerRequest f = new FakeHttpWorkerRequest (re);
397                         HttpContext c = new HttpContext (f);
398
399                         return c;
400                 }
401                 
402                 [Test] public void Test_BrokenContentLength ()
403                 {
404                         HttpContext c = Cook (-1);
405                         Assert.AreEqual (0, c.Request.ContentLength, "C1");
406
407                         c = Cook (-2);
408                         Assert.AreEqual (0, c.Request.ContentLength, "C2");
409
410                 }
411
412                 [Test][ExpectedException(typeof(NullReferenceException))]
413                 public void Test_EmptyUnknownRow ()
414                 {
415                         HttpContext c = Cook (2);
416                         NameValueCollection x = c.Request.Headers;
417                 }
418                 
419                 [Test] public void Test_RequestFields ()
420                 {
421                         HttpContext c = Cook (1);
422
423                         Assert.AreEqual ("AppPath", c.Request.ApplicationPath, "A1");
424                         Assert.AreEqual ("text/plain", c.Request.ContentType, "A2");
425
426                         c = Cook (0);
427                         Assert.AreEqual (1024, c.Request.ContentLength, "A3");
428                         
429                         c = Cook (3);
430                         Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A4");
431                         NameValueCollection x = c.Request.Headers;
432                         
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");
439
440                         c = Cook (2);
441                         Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A5");
442                         Assert.AreEqual ("text/plain; charset=latin1", c.Request.ContentType, "A5-1");
443
444                         c = Cook (4);
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");
451
452                         c = Cook (5);
453                         Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A7");
454
455                         Assert.AreEqual ("RemoteName", c.Request.UserHostName, "A8");
456                         Assert.AreEqual ("RemoteAddress", c.Request.UserHostAddress, "A9");
457
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");
461                 }
462                 
463                 [Test] public void Test_Cookies ()
464                 {
465                         HttpContext c;
466
467                         c = Cook (10);
468                         c.Request.ValidateInput ();
469                         Assert.AreEqual ("Value", c.Request.Cookies ["Key"].Value, "cookie1");
470                 }
471
472                 [Test][ExpectedException(typeof (HttpRequestValidationException))]
473                 public void Test_DangerousCookie ()
474                 {
475                         HttpContext c;
476
477                         c = Cook (11);
478                         c.Request.ValidateInput ();
479                         object a = c.Request.Cookies;
480                 }
481                 
482                 [Test][ExpectedException(typeof(HttpRequestValidationException))]
483                 public void Test_DangerousCookie2 ()
484                 {
485                         HttpContext c;
486
487                         c = Cook (12);
488                         c.Request.ValidateInput ();
489                         object a = c.Request.Cookies;
490                 }
491                 
492                 [Test][ExpectedException(typeof(HttpRequestValidationException))]
493                 public void Test_DangerousCookie3 ()
494                 {
495                         HttpContext c;
496
497                         c = Cook (13);
498                         c.Request.ValidateInput ();
499                         object a = c.Request.Cookies;
500                 }
501                 
502                 [Test][ExpectedException(typeof(HttpRequestValidationException))]
503                 public void Test_DangerousCookie4 ()
504                 {
505                         HttpContext c;
506
507                         c = Cook (14);
508                         c.Request.ValidateInput ();
509                         object a = c.Request.Cookies;
510                 }
511
512                 [Test]
513                 public void Test_MiscHeaders ()
514                 {
515                         HttpContext c = Cook (15);
516
517                         // The uri ToString contains the trailing slash.
518                         Assert.AreEqual ("http://www.mono-project.com/", c.Request.UrlReferrer.ToString (), "ref1");
519                         
520                         Assert.AreEqual ("Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040913 Firefox/0.10", c.Request.UserAgent, "ref2");
521
522                         // All the AcceptTypes and UserLanguages tests below here pass under MS
523                         c = Cook (20);
524                         string [] at = c.Request.AcceptTypes;
525                         string [] ul = c.Request.UserLanguages;
526                         Assert.IsNull (at, "AT1");
527                         Assert.IsNull (ul, "UL1");
528                         c = Cook (21);
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" };
533
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));
537
538                         ul = c.Request.UserLanguages;
539                         Assert.IsNotNull (ul, "UL2");
540                         expected = new string [] { "en-us", "en;q=0.5" };
541
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));
545
546                 }
547
548                 [Test]
549                 public void Empty_WorkerRequest_QueryString ()
550                 {
551                         HttpContext c = Cook (20);
552
553                         //
554                         // Checks that the following line does not throw an exception if
555                         // the querystring returned by the HttpWorkerRequest is null.
556                         //
557                         NameValueCollection nvc = c.Request.QueryString;
558                 }
559                         
560                 [Test]
561                 public void Leading_qm_in_QueryString ()
562                 {
563                         HttpContext c = Cook (16);
564                         NameValueCollection nvc = c.Request.QueryString;
565                         foreach (string id in nvc.AllKeys) {
566                                 if (id.StartsWith ("?"))
567                                         Assert.Fail (id);
568                         }
569                 }
570
571                 [Test]
572                 public void TestPath ()
573                 {
574                         HttpContext c = Cook (16);
575
576                         // This used to crash, ifolder exposed this
577                         string x = c.Request.Path;
578                 }
579                 
580                 [Test]
581                 public void MapImageCoordinatesHEAD ()
582                 {
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");
588
589                         c = Cook (26);
590                         coords = c.Request.MapImageCoordinates ("mapa");
591                         Assert.AreEqual (null, coords, "coords");
592                 }
593
594                 [Test]
595                 public void MapImageCoordinatesGET ()
596                 {
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");
601
602                         coords = c.Request.MapImageCoordinates ("m");
603                         Assert.AreEqual (null, coords, "coords1");
604
605                         c = Cook (28);
606                         coords = c.Request.MapImageCoordinates ("mapa");
607                         Assert.AreEqual (null, coords, "coords2");
608                         c = Cook (29);
609                         coords = c.Request.MapImageCoordinates ("mapa");
610                         Assert.AreEqual (null, coords, "coords3");
611                         c = Cook (32);
612                         coords = c.Request.MapImageCoordinates ("mapa");
613                         Assert.AreEqual (null, coords, "coords4");
614                 }
615
616                 [Test]
617                 public void MapImageCoordinatesPOST ()
618                 {
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");
624
625                         c = Cook (31);
626                         coords = c.Request.MapImageCoordinates ("mapa");
627                         Assert.AreEqual (null, coords, "coords2");
628                 }
629
630                 [Test]
631                 public void TestReferer ()
632                 {
633                         HttpContext c = Cook (1);
634
635                         Assert.AreEqual (null, c.Request.UrlReferrer, "REF1");
636
637                         c = Cook (2);
638                         Assert.AreEqual ("http://www.mono-project.com/test.aspx", c.Request.UrlReferrer.ToString (), "REF1");                   
639                 }
640                 
641
642                 [Test]
643                 public void NegativeContentLength ()
644                 {
645                         HttpContext c = Cook (1);
646                         HttpRequest req = c.Request;
647                         Assert.AreEqual (0, req.ContentLength, "#01");
648                 }
649         }
650 #endif
651 }
652