Example #1
0
        private static void CheckValueFormat(string?value)
        {
            // Either value is null/empty or a valid token/quoted string https://tools.ietf.org/html/rfc7230#section-3.2.6
            if (string.IsNullOrEmpty(value))
            {
                return;
            }

            // Trailing/leading space are not allowed
            if (value.StartsWith(' ') || value.StartsWith('\t') || value.EndsWith(' ') || value.EndsWith('\t'))
            {
                ThrowFormatException(value);
            }

            if (value[0] == '"')
            {
                HttpParseResult parseResult = HttpRuleParser.GetQuotedStringLength(value, 0, out int valueLength);
                if (parseResult != HttpParseResult.Parsed || valueLength != value.Length)
                {
                    ThrowFormatException(value);
                }
            }
            else if (HttpRuleParser.ContainsNewLine(value))
            {
                ThrowFormatException(value);
            }
Example #2
0
        private static void AssertGetCommentLength(string input, int startIndex, int expectedLength,
                                                   HttpParseResult expectedResult)
        {
            int             length = 0;
            HttpParseResult result = HttpRuleParser.GetCommentLength(input, startIndex, out length);

            Assert.Equal(expectedResult, result);
            Assert.Equal(expectedLength, length);
        }
        private static void CheckValueFormat(string?value)
        {
            // Either value is null/empty or a valid token/quoted string https://tools.ietf.org/html/rfc7230#section-3.2.6
            if (string.IsNullOrEmpty(value))
            {
                return;
            }

            // Trailing/leading space are not allowed
            if (value[0] == ' ' || value[0] == '\t' || value[^ 1] == ' ' || value[^ 1] == '\t')
            {
                throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_headers_invalid_value, value));
            }

            // If it's not a token we check if it's a valid quoted string
            if (HttpRuleParser.GetTokenLength(value, 0) == 0)
            {
                HttpParseResult parseResult = HttpRuleParser.GetQuotedStringLength(value, 0, out int valueLength);
                if ((parseResult == HttpParseResult.Parsed && valueLength != value.Length) || parseResult != HttpParseResult.Parsed)
                {
                    throw new FormatException(SR.Format(System.Globalization.CultureInfo.InvariantCulture, SR.net_http_headers_invalid_value, value));
                }
            }
        }
Example #4
0
        // TEXT = <any OCTET except CTLs, but including LWS>
        // LWS = [CRLF] 1*( SP | HT )
        // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
        //
        // Since we don't really care about the content of a quoted string or comment, we're more tolerant and
        // allow these characters. We only want to find the delimiters ('"' for quoted string and '(', ')' for comment).
        //
        // 'nestedCount': Comments can be nested. We allow a depth of up to 5 nested comments, i.e. something like
        // "(((((comment)))))". If we wouldn't define a limit an attacker could send a comment with hundreds of nested
        // comments, resulting in a stack overflow exception. In addition having more than 1 nested comment (if any)
        // is unusual.
        private static HttpParseResult GetExpressionLength(string input, int startIndex, char openChar,
                                                           char closeChar, bool supportsNesting, ref int nestedCount, out int length)
        {
            Debug.Assert(input != null);
            Debug.Assert((startIndex >= 0) && (startIndex < input.Length));

            length = 0;

            if (input[startIndex] != openChar)
            {
                return(HttpParseResult.NotParsed);
            }

            int current = startIndex + 1; // Start parsing with the character next to the first open-char.

            while (current < input.Length)
            {
                // Only check whether we have a quoted char, if we have at least 3 characters left to read (i.e.
                // quoted char + closing char). Otherwise the closing char may be considered part of the quoted char.
                int quotedPairLength = 0;
                if ((current + 2 < input.Length) &&
                    (GetQuotedPairLength(input, current, out quotedPairLength) == HttpParseResult.Parsed))
                {
                    // We ignore invalid quoted-pairs. Invalid quoted-pairs may mean that it looked like a quoted pair,
                    // but we actually have a quoted-string: e.g. "\ΓΌ" ('\' followed by a char >127 - quoted-pair only
                    // allows ASCII chars after '\'; qdtext allows both '\' and >127 chars).
                    current = current + quotedPairLength;
                    continue;
                }

                // If we support nested expressions and we find an open-char, then parse the nested expressions.
                if (supportsNesting && (input[current] == openChar))
                {
                    nestedCount++;
                    try
                    {
                        // Check if we exceeded the number of nested calls.
                        if (nestedCount > maxNestedCount)
                        {
                            return(HttpParseResult.InvalidFormat);
                        }

                        int             nestedLength = 0;
                        HttpParseResult nestedResult = GetExpressionLength(input, current, openChar, closeChar,
                                                                           supportsNesting, ref nestedCount, out nestedLength);

                        switch (nestedResult)
                        {
                        case HttpParseResult.Parsed:
                            current += nestedLength; // Add the length of the nested expression and continue.
                            break;

                        case HttpParseResult.NotParsed:
                            Debug.Assert(false, "'NotParsed' is unexpected: We started nested expression " +
                                         "parsing, because we found the open-char. So either it's a valid nested " +
                                         "expression or it has invalid format.");
                            break;

                        case HttpParseResult.InvalidFormat:
                            // If the nested expression is invalid, we can't continue, so we fail with invalid format.
                            return(HttpParseResult.InvalidFormat);

                        default:
                            Debug.Assert(false, "Unknown enum result: " + nestedResult);
                            break;
                        }
                    }
                    finally
                    {
                        nestedCount--;
                    }
                }

                if (input[current] == closeChar)
                {
                    length = current - startIndex + 1;
                    return(HttpParseResult.Parsed);
                }
                current++;
            }

            // We didn't find the final quote, therefore we have an invalid expression string.
            return(HttpParseResult.InvalidFormat);
        }
Example #5
0
        private static void AssertGetCommentLength(string input, int startIndex, int expectedLength,
            HttpParseResult expectedResult)
        {
            int length = 0;
            HttpParseResult result = HttpRuleParser.GetCommentLength(input, startIndex, out length);

            Assert.Equal(expectedResult, result);
            Assert.Equal(expectedLength, length);
        }