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> 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); }
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> 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); }
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); } result = CheckPackageDuplicatedEntries(nuGetPackage); 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)); }
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); }