Example #1
0
        private async Task <PackageValidationResult> CheckPackageEntryCountAsync(
            PackageArchiveReader nuGetPackage,
            List <IValidationMessage> warnings)
        {
            if (!_config.RejectPackagesWithTooManyPackageEntries)
            {
                return(null);
            }

            const ushort maxPackageEntryCount = ushort.MaxValue - 1;

            var packageEntryCount = nuGetPackage.GetFiles().Count();

            if (await nuGetPackage.IsSignedAsync(CancellationToken.None))
            {
                if (packageEntryCount > maxPackageEntryCount)
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_PackageContainsTooManyEntries));
                }
            }
            else if (packageEntryCount >= maxPackageEntryCount)
            {
                return(PackageValidationResult.Invalid(Strings.UploadPackage_PackageContainsTooManyEntries));
            }

            return(null);
        }
Example #2
0
        public async Task <PackageValidationResult> ValidateAfterGeneratePackageAsync(
            Package package,
            PackageArchiveReader nuGetPackage,
            User owner,
            User currentUser,
            bool isNewPackageRegistration)
        {
            var result = await ValidateSignatureFilePresenceAsync(
                package.PackageRegistration,
                nuGetPackage,
                owner,
                currentUser);

            if (result != null)
            {
                return(result);
            }

            if (isNewPackageRegistration && _typosquattingService.IsUploadedPackageIdTyposquatting(package.Id, owner, out List <string> typosquattingCheckCollisionIds))
            {
                return(PackageValidationResult.Invalid(string.Format(Strings.TyposquattingCheckFails, string.Join(",", typosquattingCheckCollisionIds))));
            }

            return(PackageValidationResult.Accepted());
        }
        public async Task <PackageValidationResult> ValidateBeforeGeneratePackageAsync(PackageArchiveReader nuGetPackage, PackageMetadata packageMetadata)
        {
            var warnings = new List <string>();

            var result = await CheckPackageEntryCountAsync(nuGetPackage, warnings);

            if (result != null)
            {
                return(result);
            }

            result = await CheckForUnsignedPushAfterAuthorSignedAsync(
                nuGetPackage,
                warnings);

            if (result != null)
            {
                return(result);
            }

            result = CheckRepositoryMetadata(packageMetadata, warnings);

            if (result != null)
            {
                return(result);
            }

            return(PackageValidationResult.AcceptedWithWarnings(warnings));
        }
Example #4
0
        public async Task <PackageValidationResult> ValidateBeforeGeneratePackageAsync(
            PackageArchiveReader nuGetPackage,
            PackageMetadata packageMetadata,
            User currentUser)
        {
            var warnings = new List <IValidationMessage>();

            var result = await CheckPackageEntryCountAsync(nuGetPackage, warnings);

            if (result != null)
            {
                return(result);
            }

            var nuspecFileEntry = nuGetPackage.GetEntry(nuGetPackage.GetNuspecFile());

            using (var nuspecFileStream = await nuGetPackage.GetNuspecAsync(CancellationToken.None))
            {
                if (!await IsStreamLengthMatchesReportedAsync(nuspecFileStream, nuspecFileEntry.Length))
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_CorruptNupkg));
                }
            }

            result = await CheckForUnsignedPushAfterAuthorSignedAsync(
                nuGetPackage,
                warnings);

            if (result != null)
            {
                return(result);
            }

            result = CheckRepositoryMetadata(packageMetadata, warnings);

            if (result != null)
            {
                return(result);
            }

            result = await CheckLicenseMetadataAsync(nuGetPackage, warnings, currentUser);

            if (result != null)
            {
                _telemetryService.TrackLicenseValidationFailure();
                return(result);
            }

            result = await CheckIconMetadataAsync(nuGetPackage, warnings, currentUser);

            if (result != null)
            {
                //_telemetryService.TrackIconValidationFailure();
                return(result);
            }

            return(PackageValidationResult.AcceptedWithWarnings(warnings));
        }
Example #5
0
        private PackageValidationResult CheckPackageDuplicatedEntries(PackageArchiveReader nuGetPackage)
        {
            if (ValidationHelper.HasDuplicatedEntries(nuGetPackage))
            {
                return(PackageValidationResult.Invalid(Strings.UploadPackage_PackageContainsDuplicatedEntries));
            }

            return(null);
        }
        public async Task <PackageValidationResult> ValidateAfterGeneratePackageAsync(
            Package package,
            PackageArchiveReader nuGetPackage,
            User owner,
            User currentUser)
        {
            var result = await ValidateSignatureFilePresenceAsync(
                package.PackageRegistration,
                nuGetPackage,
                owner,
                currentUser);

            if (result != null)
            {
                return(result);
            }

            return(PackageValidationResult.Accepted());
        }
Example #7
0
        private PackageValidationResult CheckLicenseMetadata(PackageArchiveReader nuGetPackage)
        {
            if (!_config.RejectPackagesWithLicense)
            {
                return(null);
            }

            LicenseCheckingNuspecReader nuspecReader = null;

            using (var nuspec = nuGetPackage.GetNuspec())
            {
                nuspecReader = new LicenseCheckingNuspecReader(nuspec);
            }

            if (nuspecReader.HasLicenseMetadata())
            {
                return(PackageValidationResult.Invalid(Strings.UploadPackage_NotAcceptingPackagesWithLicense));
            }

            return(null);
        }
Example #8
0
        private async Task <PackageValidationResult> ValidateSignatureFilePresenceAsync(
            PackageRegistration packageRegistration,
            PackageArchiveReader nugetPackage,
            User owner,
            User currentUser)
        {
            if (await nugetPackage.IsSignedAsync(CancellationToken.None))
            {
                if (_config.RejectSignedPackagesWithNoRegisteredCertificate &&
                    !packageRegistration.IsSigningAllowed())
                {
                    var requiredSigner    = packageRegistration.RequiredSigners.FirstOrDefault();
                    var hasRequiredSigner = requiredSigner != null;

                    if (hasRequiredSigner)
                    {
                        if (requiredSigner == currentUser)
                        {
                            return(PackageValidationResult.Invalid(new PackageShouldNotBeSignedUserFixableValidationMessage()));
                        }
                        else
                        {
                            return(PackageValidationResult.Invalid(
                                       string.Format(
                                           Strings.UploadPackage_PackageIsSignedButMissingCertificate_RequiredSigner,
                                           requiredSigner.Username)));
                        }
                    }
                    else
                    {
                        var isCurrentUserAnOwner = packageRegistration.Owners.Contains(currentUser);

                        // Technically, if there is no required signer, any one of the owners can register a
                        // certificate to resolve this issue. However, we favor either the current user or the provided
                        // owner since these are both accounts the current user can push on behalf of. In other words
                        // we provide a message that leads the current user to remedying the problem rather than asking
                        // someone else for help.
                        if (isCurrentUserAnOwner)
                        {
                            return(PackageValidationResult.Invalid(new PackageShouldNotBeSignedUserFixableValidationMessage()));
                        }
                        else
                        {
                            return(PackageValidationResult.Invalid(
                                       string.Format(
                                           Strings.UploadPackage_PackageIsSignedButMissingCertificate_RequiredSigner,
                                           owner.Username)));
                        }
                    }
                }
            }
            else
            {
                if (packageRegistration.IsSigningRequired())
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_PackageIsNotSigned));
                }
            }

            return(null);
        }
Example #9
0
        private async Task <PackageValidationResult> CheckIconMetadataAsync(PackageArchiveReader nuGetPackage, List <IValidationMessage> warnings, User user)
        {
            var nuspecReader         = GetNuspecReader(nuGetPackage);
            var iconElement          = nuspecReader.IconElement;
            var embeddedIconsEnabled = _featureFlagService.AreEmbeddedIconsEnabled(user);

            if (iconElement == null)
            {
                if (embeddedIconsEnabled && !string.IsNullOrWhiteSpace(nuspecReader.GetIconUrl()))
                {
                    warnings.Add(new IconUrlDeprecationValidationMessage());
                }
                return(null);
            }

            if (!embeddedIconsEnabled)
            {
                return(PackageValidationResult.Invalid(Strings.UploadPackage_EmbeddedIconNotAccepted));
            }

            if (HasChildElements(iconElement))
            {
                return(PackageValidationResult.Invalid(string.Format(Strings.UploadPackage_NodeContainsChildren, IconNodeName)));
            }

            var iconFilePath = FileNameHelper.GetZipEntryPath(iconElement.Value);

            if (!FileExists(nuGetPackage, iconFilePath))
            {
                return(PackageValidationResult.Invalid(
                           string.Format(
                               Strings.UploadPackage_FileDoesNotExist,
                               Strings.UploadPackage_IconFileType,
                               iconFilePath)));
            }

            var iconFileExtension = Path.GetExtension(iconFilePath);

            if (!AllowedIconFileExtensions.Contains(iconFileExtension, StringComparer.OrdinalIgnoreCase))
            {
                return(PackageValidationResult.Invalid(
                           string.Format(
                               Strings.UploadPackage_InvalidIconFileExtension,
                               iconFileExtension,
                               string.Join(", ", AllowedIconFileExtensions.Where(x => x != string.Empty).Select(extension => $"'{extension}'")))));
            }

            var iconFileEntry = nuGetPackage.GetEntry(iconFilePath);

            if (iconFileEntry.Length > MaxAllowedIconLengthForUploading)
            {
                return(PackageValidationResult.Invalid(
                           string.Format(
                               Strings.UploadPackage_FileTooLong,
                               Strings.UploadPackage_IconFileType,
                               MaxAllowedLicenseLengthForUploading.ToUserFriendlyBytesLabel())));
            }

            if (!await IsStreamLengthMatchesReportedAsync(nuGetPackage, iconFilePath, iconFileEntry.Length))
            {
                return(PackageValidationResult.Invalid(Strings.UploadPackage_CorruptNupkg));
            }

            bool isJpg = await IsJpegAsync(nuGetPackage, iconFilePath);

            bool isPng = await IsPngAsync(nuGetPackage, iconFilePath);

            if (!isPng && !isJpg)
            {
                return(PackageValidationResult.Invalid(Strings.UploadPackage_UnsupportedIconImageFormat));
            }

            return(null);
        }
Example #10
0
        private async Task <PackageValidationResult> CheckLicenseMetadataAsync(PackageArchiveReader nuGetPackage, List <IValidationMessage> warnings, User user)
        {
            var nuspecReader = GetNuspecReader(nuGetPackage);

            var licenseElement = nuspecReader.LicenseElement;

            if (licenseElement != null)
            {
                if (licenseElement.Value.Length > MaxAllowedLicenseNodeValueLength)
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_LicenseNodeValueTooLong));
                }

                if (HasChildElements(licenseElement))
                {
                    return(PackageValidationResult.Invalid(string.Format(Strings.UploadPackage_NodeContainsChildren, LicenseNodeName)));
                }

                var typeText = GetLicenseType(licenseElement);

                if (!AllowedLicenseTypes.Contains(typeText, StringComparer.OrdinalIgnoreCase))
                {
                    return(PackageValidationResult.Invalid(string.Format(Strings.UploadPackage_UnsupportedLicenseType, typeText)));
                }

                var versionText = GetLicenseVersion(licenseElement);

                if (versionText != null && AllowedLicenseVersion != versionText)
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_UnsupportedLicenseVersion,
                                   versionText)));
                }
            }

            var licenseUrl            = nuspecReader.GetLicenseUrl();
            var licenseMetadata       = nuspecReader.GetLicenseMetadata();
            var licenseDeprecationUrl = GetExpectedLicenseUrl(licenseMetadata);

            if (licenseMetadata == null)
            {
                if (string.IsNullOrWhiteSpace(licenseUrl))
                {
                    if (!_config.AllowLicenselessPackages)
                    {
                        return(PackageValidationResult.Invalid(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_MissingLicenseInformation)));
                    }
                    else
                    {
                        warnings.Add(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_LicenseShouldBeSpecified));
                    }
                }

                if (licenseDeprecationUrl == licenseUrl)
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_DeprecationUrlUsage));
                }

                if (!string.IsNullOrWhiteSpace(licenseUrl))
                {
                    if (_config.BlockLegacyLicenseUrl)
                    {
                        return(PackageValidationResult.Invalid(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_LegacyLicenseUrlNotAllowed)));
                    }
                    else
                    {
                        warnings.Add(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_DeprecatingLicenseUrl));
                    }
                }

                // we will return here, so the code below would not need to check for licenseMetadata to be non-null over and over.
                return(null);
            }

            if (licenseMetadata.WarningsAndErrors != null && licenseMetadata.WarningsAndErrors.Any())
            {
                _telemetryService.TrackInvalidLicenseMetadata(licenseMetadata.License);
                return(PackageValidationResult.Invalid(
                           string.Format(
                               Strings.UploadPackage_InvalidLicenseMetadata,
                               string.Join(" ", licenseMetadata.WarningsAndErrors))));
            }

            if (licenseDeprecationUrl != licenseUrl)
            {
                if (IsMalformedDeprecationUrl(licenseUrl))
                {
                    return(PackageValidationResult.Invalid(new InvalidUrlEncodingForLicenseUrlValidationMessage()));
                }

                if (licenseMetadata.Type == LicenseType.File)
                {
                    return(PackageValidationResult.Invalid(
                               new InvalidLicenseUrlValidationMessage(
                                   string.Format(Strings.UploadPackage_DeprecationUrlRequiredForLicenseFiles, licenseDeprecationUrl))));
                }
                else if (licenseMetadata.Type == LicenseType.Expression)
                {
                    return(PackageValidationResult.Invalid(
                               new InvalidLicenseUrlValidationMessage(
                                   string.Format(Strings.UploadPackage_DeprecationUrlRequiredForLicenseExpressions, licenseDeprecationUrl))));
                }
            }

            if (licenseMetadata.Type == LicenseType.File)
            {
                // fix the path separator. Client enforces forward slashes in all file paths when packing
                var licenseFilename = FileNameHelper.GetZipEntryPath(licenseMetadata.License);
                if (licenseFilename != licenseMetadata.License)
                {
                    var packageIdentity = nuspecReader.GetIdentity();
                    _trace.Information($"Transformed license file name from `{licenseMetadata.License}` to `{licenseFilename}` for package {packageIdentity.Id} {packageIdentity.Version}");
                }

                // check if specified file is present in the package
                if (!FileExists(nuGetPackage, licenseFilename))
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_FileDoesNotExist,
                                   Strings.UploadPackage_LicenseFileType,
                                   licenseFilename)));
                }

                // check if specified file has allowed extension
                var licenseFileExtension = Path.GetExtension(licenseFilename);
                if (!AllowedLicenseFileExtensions.Contains(licenseFileExtension, StringComparer.OrdinalIgnoreCase))
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_InvalidLicenseFileExtension,
                                   licenseFileExtension,
                                   string.Join(", ", AllowedLicenseFileExtensions.Where(x => x != string.Empty).Select(extension => $"'{extension}'")))));
                }

                var licenseFileEntry = nuGetPackage.GetEntry(licenseFilename);
                if (licenseFileEntry.Length > MaxAllowedLicenseLengthForUploading)
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_FileTooLong,
                                   Strings.UploadPackage_LicenseFileType,
                                   MaxAllowedLicenseLengthForUploading.ToUserFriendlyBytesLabel())));
                }

                if (!await IsStreamLengthMatchesReportedAsync(nuGetPackage, licenseFilename, licenseFileEntry.Length))
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_CorruptNupkg));
                }

                // zip streams do not support seeking, so we'll have to reopen them
                using (var licenseFileStream = nuGetPackage.GetStream(licenseFilename))
                {
                    // check if specified file is a text file
                    if (!await TextHelper.LooksLikeUtf8TextStreamAsync(licenseFileStream))
                    {
                        return(PackageValidationResult.Invalid(Strings.UploadPackage_LicenseMustBePlainText));
                    }
                }
            }

            if (licenseMetadata.Type == LicenseType.Expression)
            {
                if (licenseMetadata.LicenseExpression == null)
                {
                    throw new InvalidOperationException($"Unexpected value of {nameof(licenseMetadata.LicenseExpression)} property");
                }

                var licenseList        = GetLicenseList(licenseMetadata.LicenseExpression);
                var unapprovedLicenses = licenseList.Where(license => !license.IsOsiApproved && !license.IsFsfLibre).ToList();
                if (unapprovedLicenses.Any())
                {
                    _telemetryService.TrackNonFsfOsiLicenseUse(licenseMetadata.License);
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_NonFsfOrOsiLicense, string.Join(", ", unapprovedLicenses.Select(l => l.LicenseID)))));
                }
            }

            return(null);
        }
Example #11
0
        private async Task <PackageValidationResult> CheckLicenseMetadataAsync(PackageArchiveReader nuGetPackage, List <IValidationMessage> warnings)
        {
            LicenseCheckingNuspecReader nuspecReader = null;

            using (var nuspec = nuGetPackage.GetNuspec())
            {
                nuspecReader = new LicenseCheckingNuspecReader(nuspec);
            }

            var licenseElement = nuspecReader.LicenseElement;

            if (licenseElement != null)
            {
                if (_config.RejectPackagesWithLicense)
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_NotAcceptingPackagesWithLicense));
                }

                if (licenseElement.Value.Length > MaxAllowedLicenseNodeValueLength)
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_LicenseNodeValueTooLong));
                }

                if (HasChildElements(licenseElement))
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_LicenseNodeContainsChildren));
                }

                var typeText = GetLicenseType(licenseElement);

                if (!AllowedLicenseTypes.Contains(typeText, StringComparer.OrdinalIgnoreCase))
                {
                    return(PackageValidationResult.Invalid(string.Format(Strings.UploadPackage_UnsupportedLicenseType, typeText)));
                }

                var versionText = GetLicenseVersion(licenseElement);

                if (versionText != null && AllowedLicenseVersion != versionText)
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_UnsupportedLicenseVersion,
                                   versionText)));
                }

                // TODO: remove when all pipeline changes are done
                if (LicenseType.File.ToString().Equals(typeText, StringComparison.OrdinalIgnoreCase))
                {
                    _telemetryService.TrackLicenseFileRejected();
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_LicenseFilesAreNotAllowed));
                }
            }

            var licenseUrl            = nuspecReader.GetLicenseUrl();
            var licenseMetadata       = nuspecReader.GetLicenseMetadata();
            var licenseDeprecationUrl = GetExpectedLicenseUrl(licenseMetadata);

            if (licenseMetadata == null)
            {
                if (string.IsNullOrWhiteSpace(licenseUrl))
                {
                    if (!_config.AllowLicenselessPackages)
                    {
                        return(PackageValidationResult.Invalid(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_MissingLicenseInformation)));
                    }
                    else
                    {
                        // TODO: uncomment when we have full support for licenses.
                        // warnings.Add(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_LicenseShouldBeSpecified));
                    }
                }

                if (licenseDeprecationUrl == licenseUrl)
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_DeprecationUrlUsage));
                }

                if (!string.IsNullOrWhiteSpace(licenseUrl))
                {
                    if (_config.BlockLegacyLicenseUrl)
                    {
                        return(PackageValidationResult.Invalid(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_LegacyLicenseUrlNotAllowed)));
                    }
                    else
                    {
                        // TODO: uncomment when we have full support for licenses.
                        //warnings.Add(new LicenseUrlDeprecationValidationMessage(Strings.UploadPackage_DeprecatingLicenseUrl));
                    }
                }

                // we will return here, so the code below would not need to check for licenseMetadata to be non-null over and over.
                return(null);
            }

            if (licenseMetadata.WarningsAndErrors != null && licenseMetadata.WarningsAndErrors.Any())
            {
                _telemetryService.TrackInvalidLicenseMetadata(licenseMetadata.License);
                return(PackageValidationResult.Invalid(
                           string.Format(
                               Strings.UploadPackage_InvalidLicenseMetadata,
                               string.Join(" ", licenseMetadata.WarningsAndErrors))));
            }

            if (licenseDeprecationUrl != licenseUrl)
            {
                if (IsMalformedDeprecationUrl(licenseUrl))
                {
                    return(PackageValidationResult.Invalid(new InvalidUrlEncodingForLicenseUrlValidationMessage()));
                }

                if (licenseMetadata.Type == LicenseType.File)
                {
                    return(PackageValidationResult.Invalid(
                               new InvalidLicenseUrlValidationMessage(
                                   string.Format(Strings.UploadPackage_DeprecationUrlRequiredForLicenseFiles, licenseDeprecationUrl))));
                }
                else if (licenseMetadata.Type == LicenseType.Expression)
                {
                    return(PackageValidationResult.Invalid(
                               new InvalidLicenseUrlValidationMessage(
                                   string.Format(Strings.UploadPackage_DeprecationUrlRequiredForLicenseExpressions, licenseDeprecationUrl))));
                }
            }

            if (licenseMetadata.Type == LicenseType.File)
            {
                // check if specified file is present in the package
                var fileList = new HashSet <string>(nuGetPackage.GetFiles());
                if (!fileList.Contains(licenseMetadata.License))
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_LicenseFileDoesNotExist,
                                   licenseMetadata.License)));
                }

                // check if specified file has allowed extension
                var licenseFileExtension = Path.GetExtension(licenseMetadata.License);
                if (!AllowedLicenseFileExtensions.Contains(licenseFileExtension, StringComparer.OrdinalIgnoreCase))
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_InvalidLicenseFileExtension,
                                   licenseFileExtension,
                                   string.Join(", ", AllowedLicenseFileExtensions.Where(x => x != string.Empty).Select(extension => $"'{extension}'")))));
                }

                var licenseFileEntry = nuGetPackage.GetEntry(licenseMetadata.License);
                if (licenseFileEntry.Length > MaxAllowedLicenseLength)
                {
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_LicenseFileTooLong,
                                   MaxAllowedLicenseLength.ToUserFriendlyBytesLabel())));
                }

                using (var licenseFileStream = nuGetPackage.GetStream(licenseMetadata.License))
                {
                    if (!await IsStreamLengthMatchesReportedAsync(licenseFileStream, licenseFileEntry.Length))
                    {
                        return(PackageValidationResult.Invalid(Strings.UploadPackage_CorruptNupkg));
                    }
                }

                // zip streams do not support seeking, so we'll have to reopen them
                using (var licenseFileStream = nuGetPackage.GetStream(licenseMetadata.License))
                {
                    // check if specified file is a text file
                    if (!await TextHelper.LooksLikeUtf8TextStreamAsync(licenseFileStream))
                    {
                        return(PackageValidationResult.Invalid(Strings.UploadPackage_LicenseMustBePlainText));
                    }
                }
            }

            if (licenseMetadata.Type == LicenseType.Expression)
            {
                if (licenseMetadata.LicenseExpression == null)
                {
                    throw new InvalidOperationException($"Unexpected value of {nameof(licenseMetadata.LicenseExpression)} property");
                }

                var licenseList        = GetLicenseList(licenseMetadata.LicenseExpression);
                var unapprovedLicenses = licenseList.Where(license => !license.IsOsiApproved && !license.IsFsfLibre).ToList();
                if (unapprovedLicenses.Any())
                {
                    _telemetryService.TrackNonFsfOsiLicenseUse(licenseMetadata.License);
                    return(PackageValidationResult.Invalid(
                               string.Format(
                                   Strings.UploadPackage_NonFsfOrOsiLicense, string.Join(", ", unapprovedLicenses.Select(l => l.LicenseID)))));
                }
            }

            return(null);
        }
        private async Task <PackageValidationResult> CheckReadmeMetadataAsync(PackageArchiveReader nuGetPackage, User user)
        {
            var nuspecReader  = GetNuspecReader(nuGetPackage);
            var readmeElement = nuspecReader.ReadmeElement;

            if (readmeElement == null)
            {
                return(null);
            }

            var embeddedReadmeEnabled = _featureFlagService.AreEmbeddedReadmesEnabled(user);

            if (!embeddedReadmeEnabled)
            {
                return(PackageValidationResult.Invalid(Strings.UploadPackage_EmbeddedReadmeNotAccepted));
            }

            if (HasChildElements(readmeElement))
            {
                return(PackageValidationResult.Invalid(string.Format(Strings.UploadPackage_NodeContainsChildren, ReadmeNodeName)));
            }

            var readmeFilePath = FileNameHelper.GetZipEntryPath(readmeElement.Value);

            if (!FileExists(nuGetPackage, readmeFilePath))
            {
                return(PackageValidationResult.Invalid(
                           string.Format(
                               Strings.UploadPackage_FileDoesNotExist,
                               Strings.UploadPackage_ReadmeFileType,
                               readmeFilePath)));
            }

            var readmeFileExtension = Path.GetExtension(readmeFilePath);

            if (!AllowedReadmeFileExtensions.Contains(readmeFileExtension, StringComparer.OrdinalIgnoreCase))
            {
                var result = PackageValidationResult.Invalid(
                    string.Format(
                        Strings.UploadPackage_InvalidReadmeFileExtension,
                        readmeFileExtension,
                        string.Join(", ", AllowedReadmeFileExtensions.Where(x => x != string.Empty).Select(extension => $"'{extension}'"))));
                return(result);
            }

            var readmeFileEntry = nuGetPackage.GetEntry(readmeFilePath);

            if (readmeFileEntry.Length > MaxAllowedReadmeLengthForUploading)
            {
                return(PackageValidationResult.Invalid(
                           string.Format(
                               Strings.UploadPackage_FileTooLong,
                               Strings.UploadPackage_ReadmeFileType,
                               MaxAllowedReadmeLengthForUploading.ToUserFriendlyBytesLabel())));
            }

            if (!await IsStreamLengthMatchesReportedAsync(nuGetPackage, readmeFilePath, readmeFileEntry.Length))
            {
                return(PackageValidationResult.Invalid(Strings.UploadPackage_CorruptNupkg));
            }

            // zip streams do not support seeking, so we'll have to reopen them
            using (var readmeFileStream = nuGetPackage.GetStream(readmeFilePath))
            {
                // check if specified file is a text file
                if (!await TextHelper.LooksLikeUtf8TextStreamAsync(readmeFileStream))
                {
                    return(PackageValidationResult.Invalid(Strings.UploadPackage_ReadmeMustBePlainText));
                }
            }

            return(null);
        }