diff --git a/VAR.Focus.Web/Code/BusinessLogic/Persistence.cs b/VAR.Focus.Web/Code/BusinessLogic/Persistence.cs index d8abb6e..7158ddd 100644 --- a/VAR.Focus.Web/Code/BusinessLogic/Persistence.cs +++ b/VAR.Focus.Web/Code/BusinessLogic/Persistence.cs @@ -28,7 +28,7 @@ namespace VAR.Focus.Web.Code.BusinessLogic public static List LoadList(string file, List types) { List listResult = new List(); - JSONParser parser = new JSONParser(); + JsonParser parser = new JsonParser(); Type typeResult = typeof(T); if (typeResult.IsInterface == false) { @@ -62,7 +62,7 @@ namespace VAR.Focus.Web.Code.BusinessLogic public static bool SaveList(string file, object data) { - JSONWriter writter = new JSONWriter(true); + JsonWriter writter = new JsonWriter(true); string strJsonUsers = writter.Write(data); string filePath = GetLocalPath(string.Format("priv/{0}.json", file)); File.WriteAllText(filePath, strJsonUsers); diff --git a/VAR.Focus.Web/Code/ExtensionMethods.cs b/VAR.Focus.Web/Code/ExtensionMethods.cs index 7666c14..ae236f6 100644 --- a/VAR.Focus.Web/Code/ExtensionMethods.cs +++ b/VAR.Focus.Web/Code/ExtensionMethods.cs @@ -21,7 +21,7 @@ namespace VAR.Focus.Web.Code public static void ResponseObject(this HttpContext context, object obj) { - var jsonWritter = new JSONWriter(true); + var jsonWritter = new JsonWriter(true); context.Response.ContentType = "text/json"; string strObject = jsonWritter.Write(obj); context.Response.Write(strObject); diff --git a/VAR.Focus.Web/Code/JSON/JSONParser.cs b/VAR.Focus.Web/Code/JSON/JsonParser.cs similarity index 60% rename from VAR.Focus.Web/Code/JSON/JSONParser.cs rename to VAR.Focus.Web/Code/JSON/JsonParser.cs index 4c491df..733d38c 100644 --- a/VAR.Focus.Web/Code/JSON/JSONParser.cs +++ b/VAR.Focus.Web/Code/JSON/JsonParser.cs @@ -1,20 +1,23 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Reflection; using System.Text; namespace VAR.Focus.Web.Code.JSON { - public class JSONParser + public class JsonParser { #region Declarations + private const int MaxRecursiveCount = 20; + private ParserContext _ctx; private bool _tainted = false; private List _knownTypes = new List(); - #endregion + #endregion Declarations #region Properties @@ -28,7 +31,7 @@ namespace VAR.Focus.Web.Code.JSON get { return _knownTypes; } } - #endregion + #endregion Properties #region Private methods @@ -40,8 +43,8 @@ namespace VAR.Focus.Web.Code.JSON if (_dictProperties.ContainsKey(type)) { typeProperties = _dictProperties[type]; } else { - lock(_dictProperties){ - + lock (_dictProperties) + { if (_dictProperties.ContainsKey(type)) { typeProperties = _dictProperties[type]; } else { @@ -64,7 +67,7 @@ namespace VAR.Focus.Web.Code.JSON count++; } } - return ((float)count / typeProperties.Length); + return ((float)count / (float)typeProperties.Length); } private object ConvertToType(Dictionary obj, Type type) @@ -172,32 +175,116 @@ namespace VAR.Focus.Web.Code.JSON { scratch.Append((char)ParseHexShort()); } + else + { + // StrictRules: Mark as tainted on unknown escaped character + _tainted = true; + } c = _ctx.Next(); } else if (c == '"') { + _ctx.Next(); break; } else { + // StrictRules: Mark as tainted on ilegal characters + if (c == '\t' || c == '\n') { _tainted = true; } + scratch.Append(c); c = _ctx.Next(); } } while (!_ctx.AtEnd()); - if (c == '"') - { - _ctx.Next(); - } return scratch.ToString(); } - private string ParseString() + private string ParseSingleQuotedString() + { + StringBuilder scratch = new StringBuilder(); + char c = _ctx.SkipWhite(); + if (c == '\'') + { + c = _ctx.Next(); + } + do + { + if (c == '\\') + { + c = _ctx.Next(); + if (c == '\'') + { + scratch.Append('\''); + } + else if (c == '\\') + { + scratch.Append('\\'); + } + else if (c == '/') + { + scratch.Append('/'); + } + else if (c == 'b') + { + scratch.Append('\b'); + } + else if (c == 'f') + { + scratch.Append('\f'); + } + else if (c == 'n') + { + scratch.Append('\n'); + } + else if (c == 'r') + { + scratch.Append('\r'); + } + else if (c == 't') + { + scratch.Append('\t'); + } + else if (c == 'u') + { + scratch.Append((char)ParseHexShort()); + } + else + { + // StrictRules: Mark as tainted on unknown escaped character + _tainted = true; + } + c = _ctx.Next(); + } + else if (c == '\'') + { + _ctx.Next(); + break; + } + else + { + // StrictRules: Mark as tainted on ilegal characters + if (c == '\t' || c == '\n') { _tainted = true; } + + scratch.Append(c); + c = _ctx.Next(); + } + } while (!_ctx.AtEnd()); + return scratch.ToString(); + } + + private string ParseString(bool mustBeQuoted = false) { char c = _ctx.SkipWhite(); if (c == '"') { return ParseQuotedString(); } + if (c == '\'') + { + _tainted = true; + return ParseSingleQuotedString(); + } + if (mustBeQuoted) { _tainted = true; } StringBuilder scratch = new StringBuilder(); while (!_ctx.AtEnd() @@ -214,7 +301,9 @@ namespace VAR.Focus.Web.Code.JSON { StringBuilder scratch = new StringBuilder(); bool isFloat = false; + bool isExp = false; int numberLenght = 0; + int expLenght = 0; char c; c = _ctx.SkipWhite(); @@ -226,18 +315,30 @@ namespace VAR.Focus.Web.Code.JSON } // Integer part + bool leadingZeroes = true; + int leadingZeroesLenght = 0; while (char.IsDigit(c)) { + // Count leading zeroes + if (leadingZeroes && c == '0') { leadingZeroesLenght++; } + else { leadingZeroes = false; } + scratch.Append(c); c = _ctx.Next(); numberLenght++; } + // StrictRules: Mark as tainted with leading zeroes + if ((leadingZeroesLenght > 0 && leadingZeroesLenght != numberLenght) || leadingZeroesLenght > 1) + { + _tainted = true; + } + // Decimal part if (c == '.') { isFloat = true; - scratch.Append('.'); + scratch.Append("."); c = _ctx.Next(); while (char.IsDigit(c)) { @@ -257,105 +358,169 @@ namespace VAR.Focus.Web.Code.JSON if (c == 'e' || c == 'E') { isFloat = true; + isExp = true; scratch.Append('E'); c = _ctx.Next(); if (c == '+' || c == '-') { scratch.Append(c); + c = _ctx.Next(); } while (char.IsDigit(c)) { scratch.Append(c); c = _ctx.Next(); numberLenght++; + expLenght++; } } - // Build number object from the parsed string + if (isExp && expLenght == 0) + { + _tainted = true; + return null; + } + + // Build number from the parsed string string s = scratch.ToString(); - return isFloat ? (numberLenght < 17) ? (object)double.Parse(s) - : decimal.Parse(s) : (numberLenght < 19) ? int.Parse(s) - : (object)int.Parse(s); + if (isFloat) + { + if (numberLenght < 17) + { + return Convert.ToDouble(s, CultureInfo.InvariantCulture); + } + else + { + return Convert.ToDecimal(s, CultureInfo.InvariantCulture); + } + } + else + { + return Convert.ToInt32(s); + } } - private List ParseArray() + private List ParseArray(int recursiveCount = 1) { + // StrictRules: Mark as tainted when MaxRecursiveCount is exceeded + if (recursiveCount >= MaxRecursiveCount) { _tainted = true; } + + bool correct = false; char c = _ctx.SkipWhite(); List array = new List(); if (c == '[') { _ctx.Next(); } + bool? expectValue = null; do { c = _ctx.SkipWhite(); if (c == ']') { + // StrictRules: Mark as tainted when unexpected end of array + if (expectValue == true) { _tainted = true; } + correct = true; _ctx.Next(); break; } else if (c == ',') { + // StrictRules: Mark as tainted when unexpected comma on array + if (expectValue == true || array.Count == 0) { _tainted = true; } + _ctx.Next(); + expectValue = true; } else { - array.Add(ParseValue()); + // StrictRules: Mark as tainted when unexpected value on array + if (expectValue == false) { _tainted = true; } + + array.Add(ParseValue(recursiveCount + 1)); + expectValue = false; } } while (!_ctx.AtEnd()); + if (correct == false) + { + _tainted = true; + } return array; } - private Dictionary ParseObject() + private Dictionary ParseObject(int recursiveCount = 1) { + // StrictRules: Mark as tainted when MaxRecursiveCount is exceeded + if (recursiveCount >= MaxRecursiveCount) { _tainted = true; } + + bool correct = false; char c = _ctx.SkipWhite(); Dictionary obj = new Dictionary(); if (c == '{') { _ctx.Next(); - c = _ctx.SkipWhite(); } - string attributeName; + string attributeName = null; object attributeValue; + bool? expectedKey = null; + bool? expectedValue = null; do { - attributeName = ParseString(); c = _ctx.SkipWhite(); if (c == ':') { _ctx.Next(); - attributeValue = ParseValue(); - if (attributeName.Length > 0) + if (expectedValue == true) { + attributeValue = ParseValue(recursiveCount + 1); obj.Add(attributeName, attributeValue); + expectedKey = null; + expectedValue = false; } } else if (c == ',') { _ctx.Next(); c = _ctx.SkipWhite(); + expectedKey = true; + expectedValue = false; } else if (c == '}') { + // StrictRules: Mark as tainted on unexpected end of object + if (expectedValue == true || expectedKey == true) + { + _tainted = true; + } + correct = true; _ctx.Next(); break; } else { - // Unexpected character - _tainted = true; - break; + if (expectedKey != false) + { + attributeName = ParseString(true); + c = _ctx.SkipWhite(); + expectedKey = false; + expectedValue = true; + } + else + { + // Unexpected character + _tainted = true; + break; + } } } while (!_ctx.AtEnd()); - if (obj.Count == 0) + if (correct == false) { - return null; + _tainted = true; } - return obj; } - private object ParseValue() + private object ParseValue(int recusiveCount = 1) { object token = null; char c = _ctx.SkipWhite(); @@ -364,13 +529,22 @@ namespace VAR.Focus.Web.Code.JSON case '"': token = ParseQuotedString(); break; + + case '\'': + // StrictRules: Mark as tainted when parsing single quoted strings + _tainted = true; + token = ParseSingleQuotedString(); + break; + case '{': - Dictionary obj = ParseObject(); + Dictionary obj = ParseObject(recusiveCount); token = TryConvertToTypes(obj); break; + case '[': - token = ParseArray(); + token = ParseArray(recusiveCount); break; + default: if (char.IsDigit(c) || c == '-') { @@ -429,7 +603,7 @@ namespace VAR.Focus.Web.Code.JSON return input.Substring(i + 1); } - #endregion + #endregion Private methods #region Public methods @@ -440,60 +614,24 @@ namespace VAR.Focus.Web.Code.JSON _tainted = false; _ctx.Mark(); object obj = ParseValue(); + _ctx.SkipWhite(); if (_ctx.AtEnd()) { + // StrictRules: Mark as tainted when top level is not object or array + if (obj is string || obj is decimal || obj is int || obj is double || obj is float) + { + _tainted = true; + } + return obj; } - // "But wait, there is more!" - int idx = 0; - string name = ""; - string strInvalidPrev = ""; - Dictionary superObject = new Dictionary(); - do - { - // Add the object to the superObject - if (!_tainted && name.Length > 0 && obj != null) - { - if (name.Length == 0) - { - name = string.Format("{0:D2}", idx); - } - superObject.Add(name, obj); - idx++; - name = ""; - } - else - { - string strInvalid = _ctx.GetMarked(); - strInvalid = strInvalid.Trim(); - if (strInvalidPrev.Length > 0 - && "=".CompareTo(strInvalid) == 0) - { - name = CleanIdentifier(strInvalidPrev); - } - else - { - name = ""; - } - strInvalidPrev = strInvalid; - } + // StrictRules: Mark as tainted when there is more content + _tainted = true; - // Check end - if (_ctx.AtEnd()) - { - break; - } - - // Get next object - _tainted = false; - _ctx.Mark(); - obj = ParseValue(); - - } while (true); - return superObject; + return obj; } - #endregion + #endregion Public methods } } diff --git a/VAR.Focus.Web/Code/JSON/JSONWriter.cs b/VAR.Focus.Web/Code/JSON/JsonWriter.cs similarity index 84% rename from VAR.Focus.Web/Code/JSON/JSONWriter.cs rename to VAR.Focus.Web/Code/JSON/JsonWriter.cs index dc729d3..da83782 100644 --- a/VAR.Focus.Web/Code/JSON/JSONWriter.cs +++ b/VAR.Focus.Web/Code/JSON/JsonWriter.cs @@ -6,7 +6,7 @@ using System.Text; namespace VAR.Focus.Web.Code.JSON { - public class JSONWriter + public class JsonWriter { #region Declarations @@ -15,38 +15,40 @@ namespace VAR.Focus.Web.Code.JSON private int _indentChars = 4; private int _indentThresold = 3; - #endregion + #endregion Declarations #region Creator - public JSONWriter() { } - - public JSONWriter(int indentChars) + public JsonWriter() { - _indent = true; - _indentChars = indentChars; - _useTabForIndent = false; } - public JSONWriter(bool useTabForIndent) + public JsonWriter(int indentChars) { - _indent = true; - _useTabForIndent = useTabForIndent; + this._indent = true; + this._indentChars = indentChars; + this._useTabForIndent = false; } - #endregion + public JsonWriter(bool useTabForIndent) + { + this._indent = true; + this._useTabForIndent = useTabForIndent; + } + + #endregion Creator #region Private methods - private bool IsValue(object obj) + private bool IsValue(Object obj) { if (obj == null) { return true; } if ((obj is float) || (obj is double) || - (obj is short) || (obj is int) || (obj is long) - || (obj is string) || (obj is bool)) + (obj is System.Int16) || (obj is System.Int32) || (obj is System.Int64) + || (obj is String) || (obj is Boolean)) { return true; } @@ -87,8 +89,8 @@ namespace VAR.Focus.Web.Code.JSON else if (c == '\n') { sbOutput.Append("\\n"); } else if (c == '\r') { sbOutput.Append("\\r"); } else if (c == '\t') { sbOutput.Append("\\t"); } + else if (c < 32 || c >= 127) { sbOutput.AppendFormat("\\u{0:X04}", (int)c); } else { sbOutput.Append(c); } - // FIXME: Unicode characters } sbOutput.Append('"'); } @@ -101,20 +103,20 @@ namespace VAR.Focus.Web.Code.JSON sbOutput.Append("null"); } else if ((obj is float) || (obj is double) || - (obj is short) || (obj is int) || (obj is long)) + (obj is System.Int16) || (obj is System.Int32) || (obj is System.Int64)) { // Numbers sbOutput.Append(obj.ToString()); } - else if (obj is string) + else if (obj is String) { // Strings - WriteString(sbOutput, (string)obj); + WriteString(sbOutput, (String)obj); } - else if (obj is bool) + else if (obj is Boolean) { // Booleans - sbOutput.Append(((bool)obj) ? "true" : "false"); + sbOutput.Append(((Boolean)obj) ? "true" : "false"); } else if (obj is DateTime) { @@ -147,7 +149,7 @@ namespace VAR.Focus.Web.Code.JSON } } - private void WriteList(StringBuilder sbOutput, object obj, int level) + private void WriteList(StringBuilder sbOutput, Object obj, int level) { IEnumerable list = (IEnumerable)obj; int n = 0; @@ -197,7 +199,7 @@ namespace VAR.Focus.Web.Code.JSON sbOutput.Append(" ]"); } - private void WriteObject(StringBuilder sbOutput, object obj, int level) + private void WriteObject(StringBuilder sbOutput, Object obj, int level) { IDictionary map = (IDictionary)obj; int n = map.Count; @@ -250,7 +252,7 @@ namespace VAR.Focus.Web.Code.JSON sbOutput.Append(" }"); } - private void WriteReflectedObject(StringBuilder sbOutput, object obj, int level) + private void WriteReflectedObject(StringBuilder sbOutput, Object obj, int level) { Type type = obj.GetType(); PropertyInfo[] rawProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); @@ -319,17 +321,17 @@ namespace VAR.Focus.Web.Code.JSON sbOutput.Append(" }"); } - #endregion + #endregion Private methods #region Public methods - public string Write(object obj) + public String Write(Object obj) { StringBuilder sbOutput = new StringBuilder(); WriteValue(sbOutput, obj, 0, true); return sbOutput.ToString(); } - #endregion + #endregion Public methods } } diff --git a/VAR.Focus.Web/Code/JSON/ParserContext.cs b/VAR.Focus.Web/Code/JSON/ParserContext.cs index 3b6b80d..e2fd8b8 100644 --- a/VAR.Focus.Web/Code/JSON/ParserContext.cs +++ b/VAR.Focus.Web/Code/JSON/ParserContext.cs @@ -6,71 +6,71 @@ namespace VAR.Focus.Web.Code.JSON { #region Declarations - private string text; - private int length; - private int i; - private int markStart; + private string _text; + private int _length; + private int _i; + private int _markStart; - #endregion + #endregion Declarations #region Creator public ParserContext(string text) { - this.text = text; - length = text.Length; - i = 0; - markStart = 0; + _text = text; + _length = text.Length; + _i = 0; + _markStart = 0; } - #endregion + #endregion Creator #region Public methods public char SkipWhite() { - while (i < length && char.IsWhiteSpace(text[i])) + while (_i < _length && char.IsWhiteSpace(_text[_i])) { - i++; + _i++; } if (AtEnd()) { return (char)0; } - return text[i]; + return _text[_i]; } public char Next() { - i++; + _i++; if (AtEnd()) { return (char)0; } - return text[i]; + return _text[_i]; } public bool AtEnd() { - return i >= length; + return _i >= _length; } public void Mark() { - markStart = i; + _markStart = _i; } public string GetMarked() { - if (i < length && markStart < length) + if (_i < _length && _markStart < _length) { - return text.Substring(markStart, i); + return _text.Substring(_markStart, _i - _markStart); } else { - if (markStart < length) + if (_markStart < _length) { - return text.Substring(markStart, length); + return _text.Substring(_markStart, _length - _markStart); } else { @@ -79,6 +79,6 @@ namespace VAR.Focus.Web.Code.JSON } } - #endregion + #endregion Public methods } } diff --git a/VAR.Focus.Web/Controls/CardBoardControl.cs b/VAR.Focus.Web/Controls/CardBoardControl.cs index 063c2d4..abcf69f 100644 --- a/VAR.Focus.Web/Controls/CardBoardControl.cs +++ b/VAR.Focus.Web/Controls/CardBoardControl.cs @@ -114,7 +114,7 @@ namespace VAR.Focus.Web.Controls {"ConfirmDelete", "Are you sure to delete?"}, } }, }; - JSONWriter jsonWriter = new JSONWriter(); + JsonWriter jsonWriter = new JsonWriter(); StringBuilder sbCfg = new StringBuilder(); sbCfg.AppendFormat("