How to validate Azure storage blob names
Asked Answered
M

2

6

I am implementing an API to store files in Azure Blob Storage.

I am using the Microsoft library to validate the container and blob name.

NameValidator.ValidateContainerName(containerName);
NameValidator.ValidateBlobName(blobFullName);

However, it is returning some names as valid even though, by their own docs I know they are not and when I try to save them, blob storage is returning a 400 bad request, as expected. So aside from the question as to why the MS validation libraries are incomplete, how can I perform the rest of the validation in C#? Specifically, I am now failing on the part

"some ASCII or Unicode characters, like control characters (0x00 to 0x1F, \u0081, etc.)"

I have a file with \u0081 in the filename. What are the remaining invalid characters. They point us to ietf docs, but then say "some" of those characters are not allowed? Which ones? Just all control characters?

Just for clarity, here's the part that returns the 400

CloudBlockBlob blob = documentContainer.GetBlockBlobReference(blobFullName);
                await blob.UploadFromStreamAsync(fileStream, token).ConfigureAwait(false);

Thanks for your help!

Update: I've added this bit of logic to check for control characters at least. If I can get something somewhat robust, I will issue a PR against Microsoft's validation code

if (blobFullName.ToCharArray().Any(c => Char.IsControl(c))) {
    throw new HissyFitException();  // or do other stuff to fail validation
}
Morley answered 18/10, 2018 at 15:38 Comment(0)
R
2

I now wrote my own implementation for checking the validity of the storage account name, the container name and the blob name. In my implementation, the whole name has to be passed to the method in the form: Storage_Account_name/Container_name/Blob_name, e. g. mystorageaccount/containername/this/is_a/blob.jpg The blob name may contain "/".

     // Use like this:

           public static bool IsBlobFilenameValid(string blobFilename)
           {
                // e. g. blobFilename = "mystorageaccount/containername/this/is_a/blob.jpg
                string storageAccount = GetStorageAccount(blobFilename);
                string container = GetContainer(blobFilename);
                string blobName = GetBlobFilename(blobFilename);

                if(string.IsNullOrWhiteSpace(storageAccount)
                    || string.IsNullOrWhiteSpace(container)
                    || string.IsNullOrWhiteSpace(blobName)
                    || !IsAzureBlobFilenameValid(blobFilename))
                {
                    return false;
                }
                return true;
        }

        private static bool IsAzureBlobFilenameValid(string filename)
        {
            if (filename.Count(c => c == '/') < 2)
            {
                return false;
            }
            var storageAccount = GetStorageAccount(filename);
            var container = GetContainer(filename);
            var blob = GetBlobFilename(filename);

            string patternAccount = @"^[a-z0-9]{3,24}$";
            string patternContainer = @"^[a-z0-9-]{3,63}$";
            string patternBlob = @"^.{1,1024}$";

            if (!Regex.IsMatch(container, patternContainer)
                || !Regex.IsMatch(storageAccount, patternAccount)
                || !Regex.IsMatch(blob, patternBlob))
            {
                return false;
            }
            return true;
        }

        private static string GetStorageAccount(string file)
        {

            int charLocation = file.IndexOf('/', StringComparison.Ordinal);
            if (charLocation > 0)
            {
                return file.Substring(0, charLocation);
            }
            return string.Empty;

        }

        private static string GetContainer(string file)
        {
            int charLocation = IndexOfSecond(file, "/");
            if (charLocation > 0)
            {
                return file.Substring(0, charLocation);
            }
            return string.Empty;
        }

        private static string GetBlobFilename(string file)
        {
            int charLocation = IndexOfSecond(file, "/");
            if (charLocation > 0)
            {
                return file.Substring(charLocation + 1, file.Length - (charLocation + 1));
            }
            return string.Empty;
        }

        private static int IndexOfSecond(string theString, string toFind)
        {
            int first = theString.IndexOf(toFind);

            if (first == -1) return -1;

            // Find the "next" occurrence by starting just past the first
            return theString.IndexOf(toFind, first + 1);
        }

Resurge answered 10/12, 2020 at 13:22 Comment(0)
D
1

"some ASCII or Unicode characters, like control characters (0x00 to 0x1F, \u0081, etc.)"

The doc is not clear with the word "some", you can raise a doc issue at azure doc site and ask them update doc by providing the full list of these.

Dreibund answered 19/10, 2018 at 6:44 Comment(1)
Thanks! Posted! For those of you playing the at home game: github.com/MicrosoftDocs/feedback/issues/784Morley

© 2022 - 2024 — McMap. All rights reserved.