HttpRequestTest.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. //
  2. // System.Web.HttpRequestTest.cs - Unit tests for System.Web.HttpRequest
  3. //
  4. // Author:
  5. // Sebastien Pouliot <[email protected]>
  6. // Miguel de Icaza <[email protected]>
  7. // Gonzalo Paniagua Javier <[email protected]>
  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. namespace MonoTests.System.Web {
  37. [TestFixture]
  38. // Tons of failures on msft here.
  39. [Category ("NotDotNet")]
  40. public class HttpRequestTest {
  41. #if NET_1_1
  42. [Test]
  43. [ExpectedException (typeof (HttpRequestValidationException))]
  44. public void ValidateInput_XSS ()
  45. {
  46. string problem = "http://server.com/attack2.aspx?test=<script>alert('vulnerability')</script>";
  47. string decoded = HttpUtility.UrlDecode (problem);
  48. int n = decoded.IndexOf ('?');
  49. HttpRequest request = new HttpRequest (null, decoded.Substring (0,n), decoded.Substring (n+1));
  50. request.ValidateInput ();
  51. // the next statement throws
  52. Assert.AreEqual ("<script>alert('vulnerability')</script>", request.QueryString ["test"], "QueryString");
  53. }
  54. // Notes:
  55. // * this is to avoid a regression that would cause Mono to
  56. // fail again on item #2 of the XSS vulnerabilities listed at:
  57. // http://it-project.ru/andir/docs/aspxvuln/aspxvuln.en.xml
  58. // * The author notes that Microsoft has decided not to fix
  59. // this issue (hence the NotDotNet category).
  60. [Test]
  61. [Category ("NotDotNet")]
  62. [ExpectedException (typeof (HttpRequestValidationException))]
  63. public void ValidateInput_XSS_Unicode ()
  64. {
  65. string problem = "http://server.com/attack2.aspx?test=%uff1cscript%uff1ealert('vulnerability')%uff1c/script%uff1e";
  66. string decoded = HttpUtility.UrlDecode (problem);
  67. int n = decoded.IndexOf ('?');
  68. HttpRequest request = new HttpRequest (null, decoded.Substring (0,n), decoded.Substring (n+1));
  69. request.ValidateInput ();
  70. // the next statement throws
  71. Assert.AreEqual ("\xff1cscript\xff1ealert('vulnerability')\xff1c/script\xff1e", request.QueryString ["test"], "QueryString");
  72. }
  73. // This has affected ASP.NET 1.1 but it seems fixed now
  74. // http://secunia.com/advisories/9716/
  75. // http://weblogs.asp.net/kaevans/archive/2003/11/12/37169.aspx
  76. [Test]
  77. [ExpectedException (typeof (HttpRequestValidationException))]
  78. public void ValidateInput_XSS_Null ()
  79. {
  80. string problem = "http://secunia.com/?test=<%00SCRIPT>alert(document.cookie)</SCRIPT>";
  81. string decoded = HttpUtility.UrlDecode (problem);
  82. int n = decoded.IndexOf ('?');
  83. HttpRequest request = new HttpRequest (null, decoded.Substring (0,n), decoded.Substring (n+1));
  84. request.ValidateInput ();
  85. // the next statement throws
  86. Assert.AreEqual ("<SCRIPT>alert(document.cookie)</SCRIPT>", request.QueryString ["test"], "QueryString");
  87. }
  88. //
  89. // Tests the properties from the simple constructor.
  90. [Test]
  91. public void Test_PropertiesSimpleConstructor ()
  92. {
  93. string url = "http://www.gnome.org/";
  94. string qs = "key=value&key2=value%32second";
  95. HttpRequest r = new HttpRequest ("file", url, qs);
  96. Assert.AreEqual ("/?" + qs, r.RawUrl, "U1");
  97. Assert.AreEqual (url, r.Url.ToString (), "U2");
  98. r = new HttpRequest ("file", "http://www.gnome.org", qs);
  99. Assert.AreEqual (url, r.Url.ToString (), "U3");
  100. qs = "a&b=1&c=d&e&b=2&d=";
  101. r = new HttpRequest ("file", url, qs);
  102. NameValueCollection nvc = r.QueryString;
  103. Assert.AreEqual ("a,e", nvc [null], "U4");
  104. Assert.AreEqual ("1,2", nvc ["b"], "U5");
  105. Assert.AreEqual ("d", nvc ["c"], "U5");
  106. Assert.AreEqual ("", nvc ["d"], "U6");
  107. Assert.AreEqual (4, nvc.Count, "U6");
  108. Assert.AreEqual (null, r.ApplicationPath, "U7");
  109. }
  110. [Test][ExpectedException(typeof(NullReferenceException))]
  111. public void Test_AccessToVars ()
  112. {
  113. string url = "http://www.gnome.org/";
  114. string qs = "key=value&key2=value%32second";
  115. HttpRequest r = new HttpRequest ("file", url, qs);
  116. string s = r.PhysicalApplicationPath;
  117. }
  118. }
  119. [TestFixture]
  120. [Category ("NotDotNet")]
  121. public class Test_HttpFakeRequest {
  122. class FakeHttpWorkerRequest : HttpWorkerRequest {
  123. public int return_kind;
  124. [Conditional ("REQUEST_TEST_VERY_VERBOSE")]
  125. void WhereAmI ()
  126. {
  127. Console.WriteLine (Environment.StackTrace);
  128. }
  129. public FakeHttpWorkerRequest (int re)
  130. {
  131. return_kind = re;
  132. }
  133. public override string GetUriPath()
  134. {
  135. WhereAmI ();
  136. return "/uri.aspx";
  137. }
  138. public override string GetQueryString()
  139. {
  140. WhereAmI ();
  141. switch (return_kind) {
  142. case 20:
  143. return null;
  144. case 16:
  145. return "key1=value1&key2=value2";
  146. case 25: // HEAD
  147. case 30: // POST
  148. return "mapa.x=10&mapa.y=20";
  149. case 26: // HEAD
  150. case 31: // POST
  151. return "mapa.x=10&mapa=20";
  152. case 27: // GET
  153. return "mapa.x=10&mapa.y=20";
  154. case 28: // GET
  155. return "mapa.x=10";
  156. case 29: // GET
  157. return "mapa=10";
  158. case 32: // GET
  159. return "mapa.x=pi&mapa.y=20";
  160. default:
  161. return "GetQueryString";
  162. }
  163. }
  164. public override string GetRawUrl()
  165. {
  166. WhereAmI ();
  167. return "/bb.aspx";
  168. }
  169. public override string GetHttpVerbName()
  170. {
  171. WhereAmI ();
  172. if (return_kind == 25 || return_kind == 26)
  173. return "HEAD";
  174. if (return_kind == 30 || return_kind == 31)
  175. return "POST";
  176. return "GET";
  177. }
  178. public override string GetHttpVersion()
  179. {
  180. WhereAmI ();
  181. return "HTTP/1.1";
  182. }
  183. public override byte [] GetPreloadedEntityBody ()
  184. {
  185. if (return_kind != 30 && return_kind != 31)
  186. return base.GetPreloadedEntityBody ();
  187. return Encoding.UTF8.GetBytes (GetQueryString ());
  188. }
  189. public override bool IsEntireEntityBodyIsPreloaded ()
  190. {
  191. if (return_kind != 30 && return_kind != 31)
  192. return base.IsEntireEntityBodyIsPreloaded ();
  193. return true;
  194. }
  195. public override int GetRemotePort()
  196. {
  197. return 1010;
  198. }
  199. public override string GetLocalAddress()
  200. {
  201. return "localhost";
  202. }
  203. public override string GetAppPath ()
  204. {
  205. return "AppPath";
  206. }
  207. public override string GetRemoteName ()
  208. {
  209. return "RemoteName";
  210. }
  211. public override string GetRemoteAddress ()
  212. {
  213. return "RemoteAddress";
  214. }
  215. public override string GetServerName ()
  216. {
  217. return "localhost";
  218. }
  219. public override int GetLocalPort()
  220. {
  221. return 2020;
  222. }
  223. public override void SendStatus(int s, string x)
  224. {
  225. }
  226. public override void SendKnownResponseHeader(int x, string j)
  227. {
  228. }
  229. public override void SendUnknownResponseHeader(string a, string b)
  230. {
  231. }
  232. public override void SendResponseFromMemory(byte[] arr, int x)
  233. {
  234. }
  235. public override void SendResponseFromFile(string a, long b , long c)
  236. {
  237. WhereAmI ();
  238. }
  239. public override void SendResponseFromFile (IntPtr a, long b, long c)
  240. {
  241. }
  242. public override void FlushResponse(bool x)
  243. {
  244. }
  245. public override void EndOfRequest() {
  246. }
  247. public override string GetKnownRequestHeader (int index)
  248. {
  249. switch (index){
  250. case HttpWorkerRequest.HeaderContentType:
  251. switch (return_kind){
  252. case 1: return "text/plain";
  253. case 2: return "text/plain; charset=latin1";
  254. case 3: return "text/plain; charset=iso-8859-1";
  255. case 4: return "text/plain; charset=\"iso-8859-1\"";
  256. case 5: return "text/plain; charset=\"iso-8859-1\" ; other";
  257. case 30:
  258. case 31:
  259. return "application/x-www-form-urlencoded";
  260. }
  261. break;
  262. case HttpWorkerRequest.HeaderContentLength:
  263. switch (return_kind){
  264. case 0: return "1024";
  265. case 1: return "-1024";
  266. case 30:
  267. case 31:
  268. return GetQueryString ().Length.ToString ();
  269. case -1: return "Blah";
  270. case -2: return "";
  271. }
  272. break;
  273. case HttpWorkerRequest.HeaderCookie:
  274. switch (return_kind){
  275. case 10: return "Key=Value";
  276. case 11: return "Key=<value>";
  277. case 12: return "Key=>";
  278. case 13: return "Key=\xff1c";
  279. case 14: return "Key=\xff1e";
  280. }
  281. break;
  282. case HttpWorkerRequest.HeaderReferer:
  283. switch (return_kind){
  284. case 15: return "http://www.mono-project.com";
  285. }
  286. break;
  287. case HttpWorkerRequest.HeaderUserAgent:
  288. switch (return_kind){
  289. case 15: return "Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040913 Firefox/0.10";
  290. }
  291. break;
  292. case HttpWorkerRequest.HeaderAccept:
  293. switch (return_kind){
  294. 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";
  295. }
  296. break;
  297. case HttpWorkerRequest.HeaderAcceptLanguage:
  298. switch (return_kind){
  299. case 21: return "en-us, en;q=0.5";
  300. }
  301. break;
  302. }
  303. return "";
  304. }
  305. public override string [][] GetUnknownRequestHeaders ()
  306. {
  307. if (return_kind == 0)
  308. return new string [0][];
  309. if (return_kind == 3){
  310. string [][] x = new string [4][];
  311. x [0] = new string [] { "k1", "v1" };
  312. x [1] = new string [] { "k2", "v2" };
  313. x [2] = new string [] { "k3", "v3" };
  314. x [3] = new string [] { "k4", "v4" };
  315. return x;
  316. }
  317. if (return_kind == 4){
  318. //
  319. // This tests the bad values and the extra row with an error
  320. //
  321. string [][] x = new string [3][];
  322. x [0] = new string [] { "k1", "" };
  323. x [1] = new string [] { "k2", null };
  324. x [2] = new string [] { "k3", " " };
  325. return x;
  326. }
  327. if (return_kind == 2){
  328. string [][] x = new string [2][];
  329. x [0] = new string [] { "k1", "" };
  330. // Returns an empty row.
  331. return x;
  332. }
  333. return null;
  334. }
  335. }
  336. HttpContext Cook (int re)
  337. {
  338. FakeHttpWorkerRequest f = new FakeHttpWorkerRequest (re);
  339. HttpContext c = new HttpContext (f);
  340. return c;
  341. }
  342. [Test] public void Test_BrokenContentLength ()
  343. {
  344. HttpContext c = Cook (-1);
  345. Assert.AreEqual (0, c.Request.ContentLength, "C1");
  346. c = Cook (-2);
  347. Assert.AreEqual (0, c.Request.ContentLength, "C2");
  348. }
  349. [Test][ExpectedException(typeof(NullReferenceException))]
  350. public void Test_EmptyUnknownRow ()
  351. {
  352. HttpContext c = Cook (2);
  353. NameValueCollection x = c.Request.Headers;
  354. }
  355. [Test] public void Test_RequestFields ()
  356. {
  357. HttpContext c = Cook (1);
  358. Assert.AreEqual ("AppPath", c.Request.ApplicationPath, "A1");
  359. Assert.AreEqual ("text/plain", c.Request.ContentType, "A2");
  360. c = Cook (0);
  361. Assert.AreEqual (1024, c.Request.ContentLength, "A3");
  362. c = Cook (3);
  363. Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A4");
  364. NameValueCollection x = c.Request.Headers;
  365. Assert.AreEqual ("v1", x ["k1"], "K1");
  366. Assert.AreEqual ("v2", x ["k2"], "K2");
  367. Assert.AreEqual ("v3", x ["k3"], "K3");
  368. Assert.AreEqual ("v4", x ["k4"], "K4");
  369. Assert.AreEqual ("text/plain; charset=iso-8859-1", x ["Content-Type"], "K4");
  370. Assert.AreEqual (5, x.Count, "K5");
  371. c = Cook (2);
  372. Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A5");
  373. Assert.AreEqual ("text/plain; charset=latin1", c.Request.ContentType, "A5-1");
  374. c = Cook (4);
  375. Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A6");
  376. x = c.Request.Headers;
  377. Assert.AreEqual ("", x ["k1"], "K6");
  378. Assert.AreEqual (null, x ["k2"], "K7");
  379. Assert.AreEqual (" ", x ["k3"], "K8");
  380. Assert.AreEqual (4, x.Count, "K9");
  381. c = Cook (5);
  382. Assert.AreEqual ("iso-8859-1", c.Request.ContentEncoding.WebName, "A7");
  383. Assert.AreEqual ("RemoteName", c.Request.UserHostName, "A8");
  384. Assert.AreEqual ("RemoteAddress", c.Request.UserHostAddress, "A9");
  385. // Difference between Url property and RawUrl one: one is resolved, the other is not
  386. Assert.AreEqual ("/bb.aspx", c.Request.RawUrl, "A10");
  387. Assert.AreEqual ("http://localhost:2020/uri.aspx?GetQueryString", c.Request.Url.ToString (), "A11");
  388. }
  389. [Test] public void Test_Cookies ()
  390. {
  391. HttpContext c;
  392. c = Cook (10);
  393. c.Request.ValidateInput ();
  394. Assert.AreEqual ("Value", c.Request.Cookies ["Key"].Value, "cookie1");
  395. }
  396. [Test][ExpectedException(typeof (HttpRequestValidationException))]
  397. public void Test_DangerousCookie ()
  398. {
  399. HttpContext c;
  400. c = Cook (11);
  401. c.Request.ValidateInput ();
  402. object a = c.Request.Cookies;
  403. }
  404. [Test][ExpectedException(typeof(HttpRequestValidationException))]
  405. public void Test_DangerousCookie2 ()
  406. {
  407. HttpContext c;
  408. c = Cook (12);
  409. c.Request.ValidateInput ();
  410. object a = c.Request.Cookies;
  411. }
  412. [Test][ExpectedException(typeof(HttpRequestValidationException))]
  413. public void Test_DangerousCookie3 ()
  414. {
  415. HttpContext c;
  416. c = Cook (13);
  417. c.Request.ValidateInput ();
  418. object a = c.Request.Cookies;
  419. }
  420. [Test][ExpectedException(typeof(HttpRequestValidationException))]
  421. public void Test_DangerousCookie4 ()
  422. {
  423. HttpContext c;
  424. c = Cook (14);
  425. c.Request.ValidateInput ();
  426. object a = c.Request.Cookies;
  427. }
  428. [Test]
  429. public void Test_MiscHeaders ()
  430. {
  431. HttpContext c = Cook (15);
  432. // The uri ToString contains the trailing slash.
  433. Assert.AreEqual ("http://www.mono-project.com/", c.Request.UrlReferrer.ToString (), "ref1");
  434. Assert.AreEqual ("Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20040913 Firefox/0.10", c.Request.UserAgent, "ref2");
  435. // All the AcceptTypes and UserLanguages tests below here pass under MS
  436. c = Cook (20);
  437. string [] at = c.Request.AcceptTypes;
  438. string [] ul = c.Request.UserLanguages;
  439. Assert.IsNull (at, "AT1");
  440. Assert.IsNull (ul, "UL1");
  441. c = Cook (21);
  442. at = c.Request.AcceptTypes;
  443. Assert.IsNotNull (at, "AT2");
  444. string [] expected = { "text/xml", "application/xml", "application/xhtml+xml", "text/html;q=0.9",
  445. "text/plain;q=0.8", "image/png", "*/*;q=0.5" };
  446. Assert.AreEqual (expected.Length, at.Length, "AT3");
  447. for (int i = expected.Length - 1; i >= 0; i--)
  448. Assert.AreEqual (expected [i], at [i], "AT" + (3 + i));
  449. ul = c.Request.UserLanguages;
  450. Assert.IsNotNull (ul, "UL2");
  451. expected = new string [] { "en-us", "en;q=0.5" };
  452. Assert.AreEqual (expected.Length, ul.Length, "UL3");
  453. for (int i = expected.Length - 1; i >= 0; i--)
  454. Assert.AreEqual (expected [i], ul [i], "UL" + (3 + i));
  455. }
  456. [Test]
  457. public void Empty_WorkerRequest_QueryString ()
  458. {
  459. HttpContext c = Cook (20);
  460. //
  461. // Checks that the following line does not throw an exception if
  462. // the querystring returned by the HttpWorkerRequest is null.
  463. //
  464. NameValueCollection nvc = c.Request.QueryString;
  465. }
  466. [Test]
  467. public void Leading_qm_in_QueryString ()
  468. {
  469. HttpContext c = Cook (16);
  470. NameValueCollection nvc = c.Request.QueryString;
  471. foreach (string id in nvc.AllKeys) {
  472. if (id.StartsWith ("?"))
  473. Assert.Fail (id);
  474. }
  475. }
  476. [Test]
  477. public void TestPath ()
  478. {
  479. HttpContext c = Cook (16);
  480. // This used to crash, ifolder exposed this
  481. string x = c.Request.Path;
  482. }
  483. [Test]
  484. public void MapImageCoordinatesHEAD ()
  485. {
  486. HttpContext c = Cook (25);
  487. int [] coords = c.Request.MapImageCoordinates ("mapa");
  488. Assert.IsNotNull (coords, "A1");
  489. Assert.AreEqual (10, coords [0], "X");
  490. Assert.AreEqual (20, coords [1], "Y");
  491. c = Cook (26);
  492. coords = c.Request.MapImageCoordinates ("mapa");
  493. Assert.AreEqual (null, coords, "coords");
  494. }
  495. [Test]
  496. public void MapImageCoordinatesGET ()
  497. {
  498. HttpContext c = Cook (27);
  499. int [] coords = c.Request.MapImageCoordinates ("mapa");
  500. Assert.AreEqual (10, coords [0], "X");
  501. Assert.AreEqual (20, coords [1], "Y");
  502. coords = c.Request.MapImageCoordinates ("m");
  503. Assert.AreEqual (null, coords, "coords1");
  504. c = Cook (28);
  505. coords = c.Request.MapImageCoordinates ("mapa");
  506. Assert.AreEqual (null, coords, "coords2");
  507. c = Cook (29);
  508. coords = c.Request.MapImageCoordinates ("mapa");
  509. Assert.AreEqual (null, coords, "coords3");
  510. c = Cook (32);
  511. coords = c.Request.MapImageCoordinates ("mapa");
  512. Assert.AreEqual (null, coords, "coords4");
  513. }
  514. [Test]
  515. public void MapImageCoordinatesPOST ()
  516. {
  517. HttpContext c = Cook (30);
  518. int [] coords = c.Request.MapImageCoordinates ("mapa");
  519. Assert.IsNotNull (coords, "A1");
  520. Assert.AreEqual (10, coords [0], "X");
  521. Assert.AreEqual (20, coords [1], "Y");
  522. c = Cook (31);
  523. coords = c.Request.MapImageCoordinates ("mapa");
  524. Assert.AreEqual (null, coords, "coords2");
  525. }
  526. [Test]
  527. public void NegativeContentLength ()
  528. {
  529. HttpContext c = Cook (1);
  530. HttpRequest req = c.Request;
  531. Assert.AreEqual (0, req.ContentLength, "#01");
  532. }
  533. }
  534. #endif
  535. }