예제 #1
0
    protected override bool TryValidateKeyIdInternal(string keyId, out ValidationStatus status, out string?message)
    {
        //B2 master keys are 12. Application keys are 25
        if (keyId.Length != 12 && keyId.Length != 25)
        {
            status  = ValidationStatus.WrongLength;
            message = "12 / 25";
            return(false);
        }

        foreach (char c in keyId)
        {
            if (CharHelper.InRange(c, 'a', 'f') || CharHelper.InRange(c, '0', '9'))
            {
                continue;
            }

            status  = ValidationStatus.WrongFormat;
            message = c.ToString();
            return(false);
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }
예제 #2
0
    protected override bool TryValidateKeyIdInternal(string keyId, out ValidationStatus status, out string?message)
    {
        if (keyId.Length != 20)
        {
            status  = ValidationStatus.WrongLength;
            message = null;
            return(false);
        }

        foreach (char c in keyId)
        {
            if (CharHelper.InRange(c, 'A', 'Z') || CharHelper.InRange(c, '0', '9'))
            {
                continue;
            }

            status  = ValidationStatus.WrongFormat;
            message = c.ToString();
            return(false);
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }
예제 #3
0
    protected override bool TryValidateKeyIdInternal(string keyId, out ValidationStatus status, out string?message)
    {
        //https://cloud.google.com/storage/docs/authentication/hmackeys

        //Google service keys are 61. User keys are 24
        if (keyId.Length != 61 && keyId.Length != 24)
        {
            status  = ValidationStatus.WrongLength;
            message = "24 / 61";
            return(false);
        }

        foreach (char c in keyId)
        {
            if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, 'A', 'Z') || CharHelper.InRange(c, '0', '9'))
            {
                continue;
            }

            status  = ValidationStatus.WrongFormat;
            message = c.ToString();
            return(false);
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }
예제 #4
0
    protected override bool TryValidateObjectKeyInternal(string objectKey, ObjectKeyValidationMode mode, out ValidationStatus status, out string?message)
    {
        //https://www.backblaze.com/b2/docs/files.html

        //Spec: Names can be pretty much any UTF-8 string up to 1024 bytes long
        if (objectKey.Length < 1 || Encoding.UTF8.GetByteCount(objectKey) > 1024)
        {
            status  = ValidationStatus.WrongLength;
            message = "1-1024";
            return(false);
        }

        foreach (char c in objectKey)
        {
            //Spec: No character codes below 32 are allowed. DEL characters (127) are not allowed
            if (CharHelper.InRange(c, (char)0, (char)31) || c == (char)127)
            {
                status  = ValidationStatus.WrongFormat;
                message = c.ToString();
                return(false);
            }
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }
예제 #5
0
    protected override bool TryValidateAccessKeyInternal(byte[] accessKey, out ValidationStatus status, out string?message)
    {
        //https://cloud.google.com/storage/docs/authentication/hmackeys

        //Spec: A 40-character Base-64 encoded string that is linked to a specific access ID.
        if (accessKey.Length != 40)
        {
            status  = ValidationStatus.WrongLength;
            message = "40";
            return(false);
        }

        //Check that it is actually base64
        foreach (byte b in accessKey)
        {
            char c = (char)b;

            if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, 'A', 'Z') || CharHelper.InRange(c, '0', '9') || CharHelper.OneOf(c, '/', '+', '='))
            {
                continue;
            }

            status  = ValidationStatus.WrongFormat;
            message = c.ToString();
            return(false);
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }
예제 #6
0
    protected bool TryValidateBucketDns(string input, out ValidationStatus status, out string?message)
    {
        int curPos = 0;
        int end    = input.Length;

        do
        {
            //find the dot or hit the end
            int newPos = curPos;
            while (newPos < end)
            {
                if (input[newPos] == '.')
                {
                    break;
                }

                ++newPos;
            }

            if (curPos == newPos || newPos - curPos > 63)
            {
                message = "1-63";
                status  = ValidationStatus.WrongLength;
                return(false);
            }

            char start = input[curPos];

            if (!CharHelper.InRange(start, 'a', 'z') && !CharHelper.InRange(start, '0', '9'))
            {
                message = start.ToString();
                status  = ValidationStatus.WrongFormat;
                return(false);
            }

            curPos++;

            //check the label content
            while (curPos < newPos)
            {
                char c = input[curPos++];

                if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, '0', '9') || c == '-')
                {
                    continue;
                }

                message = c.ToString();
                status  = ValidationStatus.WrongFormat;
                return(false);
            }

            ++curPos;
        } while (curPos < end);

        message = null;
        status  = ValidationStatus.Ok;
        return(true);
    }
예제 #7
0
    protected override bool TryValidateBucketNameInternal(string bucketName, BucketNameValidationMode mode, out ValidationStatus status, out string?message)
    {
        //Source: https://wasabi.com/wp-content/themes/wasabi/docs/User_Guide/topics/Creating_a_Bucket.htm

        //Spec: A bucket name can consist of 3 to 63 characters
        if (bucketName.Length < 3 || bucketName.Length > 63)
        {
            status  = ValidationStatus.WrongLength;
            message = "3-63";
            return(false);
        }

        //Spec: lowercase letters, numbers, periods, and dashes.
        foreach (char c in bucketName)
        {
            if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, '0', '9') || CharHelper.OneOf(c, '.', '-'))
            {
                continue;
            }

            status  = ValidationStatus.WrongFormat;
            message = c.ToString();
            return(false);
        }

        //Spec: The name must begin with a lower­case letter or number.
        char start = bucketName[0];

        if (!CharHelper.InRange(start, 'a', 'z') && !CharHelper.InRange(start, '0', '9'))
        {
            status  = ValidationStatus.WrongFormat;
            message = start.ToString();
            return(false);
        }

        //Spec: The name must begin with a lower­case letter or number.
        char end = bucketName[bucketName.Length - 1];

        if (!CharHelper.InRange(end, 'a', 'z') && !CharHelper.InRange(end, '0', '9'))
        {
            status  = ValidationStatus.WrongFormat;
            message = end.ToString();
            return(false);
        }

        //Spec: name cannot be formatted as an IP address (123.45.678.90)
        if (_ipRegex.IsMatch(bucketName))
        {
            status  = ValidationStatus.WrongFormat;
            message = bucketName;
            return(false);
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }
예제 #8
0
    protected override bool TryValidateBucketNameInternal(string bucketName, BucketNameValidationMode mode, out ValidationStatus status, out string?message)
    {
        //https://cloud.google.com/storage/docs/naming-buckets

        //Spec: Bucket names must contain 3-63 characters. Names containing dots can contain up to 222 characters, but each dot-separated component can be no longer than 63 characters.
        if (bucketName.Length < 3 || bucketName.Length > 63)
        {
            status  = ValidationStatus.WrongLength;
            message = "3-63";
            return(false);
        }

        //Spec: Bucket names must start and end with a number or letter.
        char start = bucketName[0];
        char end   = bucketName[bucketName.Length - 1];

        if (!CharHelper.InRange(start, 'a', 'z') && !CharHelper.InRange(start, '0', '9'))
        {
            status  = ValidationStatus.WrongFormat;
            message = start.ToString();
            return(false);
        }

        if (!CharHelper.InRange(end, 'a', 'z') && !CharHelper.InRange(end, '0', '9'))
        {
            status  = ValidationStatus.WrongFormat;
            message = end.ToString();
            return(false);
        }

        //Spec: Bucket names must contain only lowercase letters, numbers, dashes (-), underscores (_), and dots (.). Spaces are not allowed. Names containing dots require verification.
        foreach (char c in bucketName)
        {
            if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, '0', '9') || CharHelper.OneOf(c, '-', '_', '.'))
            {
                continue;
            }

            status  = ValidationStatus.WrongFormat;
            message = c.ToString();
            return(false);
        }

        //Spec: Bucket names cannot begin with the "goog" prefix.
        //Spec: Bucket names cannot contain "google" or close misspellings, such as "g00gle".
        if (bucketName.StartsWith("goog", StringComparison.Ordinal) || bucketName.Contains("google") || bucketName.Contains("g00gle"))
        {
            status  = ValidationStatus.ReservedName;
            message = bucketName;
            return(false);
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }
예제 #9
0
    protected override bool TryValidateObjectKeyInternal(string objectKey, ObjectKeyValidationMode mode, out ValidationStatus status, out string?message)
    {
        //Source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html

        //Spec: The name for a key is a sequence of Unicode characters whose UTF-8 encoding is at most 1,024 bytes long.
        if (objectKey.Length < 1 || Constants.Utf8NoBom.GetByteCount(objectKey) > 1024)
        {
            status  = ValidationStatus.WrongLength;
            message = "1-1024";
            return(false);
        }

        //Spec: You can use any UTF-8 character in an object key name. However, using certain characters in key names can cause problems with some applications and protocols.
        foreach (char c in objectKey)
        {
            //Spec: Safe characters
            if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, 'A', 'Z') || CharHelper.InRange(c, '0', '9'))
            {
                continue;
            }

            //Spec: Safe characters
            if (CharHelper.OneOf(c, '/', '!', '-', '_', '.', '*', '\'', '(', ')'))
            {
                continue;
            }

            if (mode == ObjectKeyValidationMode.DefaultStrict)
            {
                //Spec: Characters that might require special handling
                if (CharHelper.OneOf(c, '&', '$', '@', '=', ';', ':', '+', ' ', ',', '?') || CharHelper.InRange(c, (char)0, (char)31) || c == (char)127)
                {
                    status  = ValidationStatus.WrongFormat;
                    message = c.ToString();
                    return(false);
                }

                //Spec: Characters to avoid
                if (CharHelper.OneOf(c, '\\', '{', '}', '^', '%', '`', '[', ']', '"', '<', '>', '~', '#', '|'))
                {
                    status  = ValidationStatus.WrongFormat;
                    message = c.ToString();
                    return(false);
                }
            }
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }
예제 #10
0
    protected bool TryValidateBlacklisted(string input, out ValidationStatus status, out string?message)
    {
        foreach (char c in input)
        {
            //0xD800 to 0xDFFF are reserved code points in UTF-16. Since they will always be URL encoded to %EF%BF%BD (the � char) in UTF-8
            if (CharHelper.InRange(c, '\uD800', '\uDFFF'))
            {
                status  = ValidationStatus.WrongFormat;
                message = c.ToString();
                return(false);
            }
        }

        message = null;
        status  = ValidationStatus.Ok;
        return(true);
    }
예제 #11
0
    protected bool TryValidateObjectSafeOnly(string input, out ValidationStatus status, out string?message)
    {
        foreach (char c in input)
        {
            if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, 'A', 'Z') || CharHelper.InRange(c, '0', '9'))
            {
                continue;
            }

            message = c.ToString();
            status  = ValidationStatus.WrongFormat;
            return(false);
        }

        message = null;
        status  = ValidationStatus.Ok;
        return(true);
    }
예제 #12
0
    protected bool TryValidateObjectExtAsciiOnly(string input, out ValidationStatus status, out string?message)
    {
        foreach (char c in input)
        {
            if (CharHelper.InRange(c, (char)0, (char)255))
            {
                continue;
            }

            message = c.ToString();
            status  = ValidationStatus.WrongFormat;
            return(false);
        }

        message = null;
        status  = ValidationStatus.Ok;
        return(true);
    }
예제 #13
0
        private bool IsValidChar(char c)
        {
            // According to https://www.w3.org/TR/xml/#charsets #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] are valid
            if (_xmlStandard == XmlStandard.Xml10)
            {
                return(c == '\u0009' ||
                       c == '\u000A' ||
                       c == '\u000D' ||
                       CharHelper.InRange(c, '\u0020', '\uD7FF') ||
                       CharHelper.InRange(c, '\uE000', '\uFFFD'));
            }

            // According to https://www.w3.org/TR/xml11/#charsets [#x1-#xD7FF] | [#xE000-#xFFFD] are valid characters
            if (_xmlStandard == XmlStandard.Xml11)
            {
                return(CharHelper.InRange(c, '\u0001', '\uD7FF') ||
                       CharHelper.InRange(c, '\uE000', '\uFFFD'));
            }

            throw new S3Exception("Invalid XML standard");
        }
예제 #14
0
        private bool IsDiscouraged(char c)
        {
            // According to https://www.w3.org/TR/xml/#charsets [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDEF] are discouraged
            if (_xmlStandard == XmlStandard.Xml10)
            {
                return(CharHelper.InRange(c, '\u007F', '\u0084') ||
                       CharHelper.InRange(c, '\u0086', '\u009F') ||
                       CharHelper.InRange(c, '\uFDD0', '\uFDEF'));   //Unicode non-characters
            }

            // According to https://www.w3.org/TR/xml11/#charsets [#x1-#x8] | [#xB-#xC] | [#xE-#x1F] | [#x7F-#x84] | [#x86-#x9F] are discouraged
            if (_xmlStandard == XmlStandard.Xml11)
            {
                return(CharHelper.InRange(c, '\u0001', '\u0008') ||
                       CharHelper.InRange(c, '\u000B', '\u000C') ||
                       CharHelper.InRange(c, '\u000E', '\u001F') ||
                       CharHelper.InRange(c, '\u007F', '\u0084') ||
                       CharHelper.InRange(c, '\u0086', '\u009F') ||
                       CharHelper.InRange(c, '\uFDD0', '\uFDEF'));   //Unicode non-characters
            }

            throw new S3Exception("Invalid XML standard");
        }
예제 #15
0
        public static bool ContainsInvalidXml(string xml)
        {
            if (string.IsNullOrEmpty(xml))
            {
                return(false);
            }

            foreach (char c in xml)
            {
                //Invalid
                if (c == '\"' || c == '\'' || c == '<' || c == '>' || c == '&' || c == '\uFFFE' || c == '\uFFFF')
                {
                    return(true);
                }

                //See https://www.w3.org/TR/unicode-xml/#Noncharacters
                if (CharHelper.InRange(c, '\uFDD0', '\uFDEF'))
                {
                    return(true);
                }
            }

            return(false);
        }
예제 #16
0
    protected override bool TryValidateBucketNameInternal(string bucketName, BucketNameValidationMode mode, out ValidationStatus status, out string?message)
    {
        //https://www.backblaze.com/b2/docs/buckets.html
        //Spec: A bucket name must be at least 6 characters long, and can be at most 50 characters
        if (bucketName.Length < 6 || bucketName.Length > 50)
        {
            status  = ValidationStatus.WrongLength;
            message = "6-50";
            return(false);
        }

        //Spec: Bucket names that start with "b2-" are reserved for BackBlaze use.
        if (bucketName.StartsWith("b2-", StringComparison.OrdinalIgnoreCase))
        {
            status  = ValidationStatus.ReservedName;
            message = bucketName;
            return(false);
        }

        //Spec: Bucket names can consist of upper-case letters, lower-case letters, numbers, and "-". No other characters are allowed.
        foreach (char c in bucketName)
        {
            if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, 'A', 'Z') || CharHelper.InRange(c, '0', '9') || c == '-')
            {
                continue;
            }

            status  = ValidationStatus.WrongFormat;
            message = c.ToString();
            return(false);
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }
예제 #17
0
        /// <summary>
        /// Validates a bucket name according to standard DNS rules. See
        /// https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html#bucketnamingrules for more details.
        /// </summary>
        /// <param name="bucketName">The bucket name</param>
        /// <param name="status">Contains the error if validation failed</param>
        /// <returns>True if validation succeeded, false otherwise</returns>
        public static bool TryValidateBucketName(string bucketName, out ValidationStatus status)
        {
            if (bucketName == null)
            {
                status = ValidationStatus.NullInput;
                return(false);
            }

            if (bucketName.Length < 3 || bucketName.Length > 63)
            {
                status = ValidationStatus.WrongLength;
                return(false);
            }

            int curPos = 0;
            int end    = bucketName.Length;

            do
            {
                //find the dot or hit the end
                int newPos = curPos;
                while (newPos < end)
                {
                    if (bucketName[newPos] == '.')
                    {
                        break;
                    }

                    ++newPos;
                }

                if (curPos == newPos || newPos - curPos > 63)
                {
                    status = ValidationStatus.WrongLength;
                    return(false);
                }

                char start = bucketName[curPos];

                if (!CharHelper.InRange(start, 'a', 'z') && !CharHelper.InRange(start, '0', '9'))
                {
                    status = ValidationStatus.WrongFormat;
                    return(false);
                }

                curPos++;

                //check the label content
                while (curPos < newPos)
                {
                    char c = bucketName[curPos++];

                    if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, '0', '9') || c == '-')
                    {
                        continue;
                    }

                    status = ValidationStatus.WrongFormat;
                    return(false);
                }

                ++curPos;
            } while (curPos < end);

            status = ValidationStatus.Ok;
            return(true);
        }
예제 #18
0
        public static bool TryValidateObjectKey(string objectKey, KeyValidationMode mode, out ValidationStatus status)
        {
            if (string.IsNullOrEmpty(objectKey))
            {
                status = ValidationStatus.NullInput;
                return(false);
            }

            if (objectKey.Length > 1024)
            {
                status = ValidationStatus.WrongLength;
                return(false);
            }

            if (mode == KeyValidationMode.Unrestricted)
            {
                status = ValidationStatus.Ok;
                return(true);
            }

            foreach (char c in objectKey)
            {
                if (CharHelper.InRange(c, 'a', 'z'))
                {
                    continue;
                }

                if (CharHelper.InRange(c, 'A', 'Z'))
                {
                    continue;
                }

                if (CharHelper.InRange(c, '0', '9'))
                {
                    continue;
                }

                //See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html
                if (CharHelper.OneOf(c, '/', '!', '-', '_', '.', '*', '\'', '(', ')'))
                {
                    continue;
                }

                //0xD800 to 0xDFFF are reserved code points in UTF-16. Since they will always be URL encoded to %EF%BF%BD (the � char) in UTF-8
                if (CharHelper.InRange(c, '\uD800', '\uDFFF'))
                {
                    status = ValidationStatus.WrongFormat;
                    return(false);
                }

                if (mode == KeyValidationMode.SafeMode)
                {
                    status = ValidationStatus.WrongFormat;
                    return(false);
                }

                if (CharHelper.OneOf(c, '&', '$', '@', '=', ';', ':', '+', ' ', ',', '?'))
                {
                    continue;
                }

                if (CharHelper.InRange(c, (char)0, (char)31) || c == (char)127)
                {
                    continue;
                }

                if (mode == KeyValidationMode.AsciiMode)
                {
                    status = ValidationStatus.WrongFormat;
                    return(false);
                }

                if (CharHelper.OneOf(c, '\\', '{', '}', '^', '%', '`', '[', ']', '"', '<', '>', '~', '#', '|'))
                {
                    continue;
                }

                if (CharHelper.InRange(c, (char)128, (char)255))
                {
                    continue;
                }

                if (mode == KeyValidationMode.ExtendedAsciiMode)
                {
                    status = ValidationStatus.WrongFormat;
                    return(false);
                }
            }

            status = ValidationStatus.Ok;
            return(true);
        }
예제 #19
0
        public bool CharRangeWithHelper()
        {
            char c = 'f';

            return(CharHelper.InRange(c, 'a', 'z'));
        }
예제 #20
0
    protected override bool TryValidateBucketNameInternal(string bucketName, BucketNameValidationMode mode, out ValidationStatus status, out string?message)
    {
        //Source: https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html

        //Spec: Bucket names must be between 3 and 63 characters long.
        if (bucketName.Length < 3 || bucketName.Length > 63)
        {
            status  = ValidationStatus.WrongLength;
            message = "3-63";
            return(false);
        }

        //Spec: Bucket names can consist only of lowercase letters, numbers, dots (.), and hyphens (-).
        foreach (char c in bucketName)
        {
            if (CharHelper.InRange(c, 'a', 'z') || CharHelper.InRange(c, '0', '9') || CharHelper.OneOf(c, '.', '-'))
            {
                continue;
            }

            status  = ValidationStatus.WrongFormat;
            message = c.ToString();
            return(false);
        }

        //Spec: Bucket names must begin and end with a letter or number.
        char start = bucketName[0];

        if (!CharHelper.InRange(start, 'a', 'z') && !CharHelper.InRange(start, '0', '9'))
        {
            status  = ValidationStatus.WrongFormat;
            message = start.ToString();
            return(false);
        }

        //Spec: Bucket names must begin and end with a letter or number.
        char end = bucketName[bucketName.Length - 1];

        if (!CharHelper.InRange(end, 'a', 'z') && !CharHelper.InRange(end, '0', '9'))
        {
            status  = ValidationStatus.WrongFormat;
            message = end.ToString();
            return(false);
        }

        //Spec: Bucket names must not be formatted as an IP address (for example, 192.168.5.4).
        if (_ipRegex.IsMatch(bucketName))
        {
            status  = ValidationStatus.WrongFormat;
            message = bucketName;
            return(false);
        }

        //Spec: Bucket names must not start with the prefix xn--.
        if (bucketName.StartsWith("xn--", StringComparison.Ordinal))
        {
            status  = ValidationStatus.WrongFormat;
            message = "xn--";
            return(false);
        }

        //Spec: Bucket names must not end with the suffix -s3alias. This suffix is reserved for access point alias names. For more information, see Using a bucket-style alias for your access point.
        if (bucketName.EndsWith("-s3alias", StringComparison.Ordinal))
        {
            status  = ValidationStatus.WrongFormat;
            message = "-s3alias";
            return(false);
        }

        status  = ValidationStatus.Ok;
        message = null;
        return(true);
    }