Пример #1
0
        /// <summary>
        /// Save the readme file from package stream. This method should throw if the package
        /// does not have an embedded readme file
        /// </summary>
        /// <param name="package">Package information.</param>
        /// <param name="packageStream">Package stream with .nupkg contents.</param>
        public async Task ExtractAndSaveReadmeFileAsync(Package package, Stream packageStream)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            if (packageStream == null)
            {
                throw new ArgumentNullException(nameof(packageStream));
            }

            packageStream.Seek(0, SeekOrigin.Begin);
            using (var packageArchiveReader = new PackageArchiveReader(packageStream, leaveStreamOpen: true))
            {
                var packageMetadata = PackageMetadata.FromNuspecReader(packageArchiveReader.GetNuspecReader(), strict: true);
                if (string.IsNullOrWhiteSpace(packageMetadata.ReadmeFile))
                {
                    throw new InvalidOperationException("No readme file specified in the nuspec");
                }

                var filename        = FileNameHelper.GetZipEntryPath(packageMetadata.ReadmeFile);
                var ReadmeFileEntry = packageArchiveReader.GetEntry(filename); // throws on non-existent file
                using (var readmeFileStream = ReadmeFileEntry.Open())
                {
                    await SaveReadmeFileAsync(package, readmeFileStream);
                }
            }
        }
Пример #2
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);
        }
Пример #3
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);
        }
        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);
        }