| File: HttpSource\HttpStreamValidation.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Protocol\NuGet.Protocol.csproj (NuGet.Protocol) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.IO; using System.Text; using System.Text.Json; using System.Xml; using Newtonsoft.Json; using NuGet.Packaging; using NuGet.Packaging.Core; using NuGet.Protocol.Core.Types; using NuGet.Shared; namespace NuGet.Protocol { public static class HttpStreamValidation { public static void ValidateJObject(string uri, Stream stream) { ValidateJObject(uri, stream, environmentVariableReader: null); } internal static void ValidateJObject(string uri, Stream stream, Common.IEnvironmentVariableReader? environmentVariableReader) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (NuGetFeatureFlags.UseSystemTextJsonDeserializationFeatureSwitch) { ValidateJsonObjectWithStj(uri, stream); return; } if (NuGetFeatureFlags.IsSystemTextJsonDeserializationEnabledByEnvironment(environmentVariableReader)) { ValidateJsonObjectWithStj(uri, stream); return; } ValidateJObjectWithNewtonsoftJson(uri, stream); } private static void ValidateJsonObjectWithStj(string uri, Stream stream) { try { using (JsonDocument document = JsonDocument.Parse(stream)) { if (document.RootElement.ValueKind != JsonValueKind.Object) { throw new InvalidOperationException("The JSON document is not an object."); } } } catch (Exception e) when (!(e is InvalidDataException)) { string message = string.Format( CultureInfo.CurrentCulture, Strings.Protocol_InvalidJsonObject, uri); throw new InvalidDataException(message, e); } } private static void ValidateJObjectWithNewtonsoftJson(string uri, Stream stream) { try { using (var reader = new StreamReader( stream: stream, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 4096, leaveOpen: true)) using (var jsonReader = new JsonTextReader(reader) { CloseInput = false }) { var firstTokenFound = jsonReader.Read(); if (!firstTokenFound || jsonReader.TokenType != JsonToken.StartObject) { throw new JsonReaderException("The JSON document is not an object."); } while (jsonReader.Read()) { } if (jsonReader.Depth != 0) { throw new JsonReaderException("The JSON document is not complete."); } } } catch (Exception e) when (!(e is InvalidDataException)) { string message = string.Format( CultureInfo.CurrentCulture, Strings.Protocol_InvalidJsonObject, uri); throw new InvalidDataException(message, e); } } public static void ValidateNupkg(string uri, Stream stream) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } if (stream == null) { throw new ArgumentNullException(nameof(stream)); } try { using (var reader = new PackageArchiveReader( stream: stream, leaveStreamOpen: true)) using (var nuspec = reader.GetNuspec()) // This method throws if no .nuspec exists. { _ = new NuspecReader(nuspec); // This method throws if reading the .nuspec fails } } catch (Exception e) { string message = string.Format( CultureInfo.CurrentCulture, Strings.Log_InvalidNupkgFromUrl, uri); throw new InvalidDataException(message, e); } } internal static void ValidatePackageIdentity(string uri, Stream stream, PackageIdentity expectedIdentity) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (expectedIdentity == null) { throw new ArgumentNullException(nameof(expectedIdentity)); } try { using (var reader = new PackageArchiveReader( stream: stream, leaveStreamOpen: true)) { using (var nuspecStream = reader.GetNuspec()) { var nuspec = new NuspecReader(nuspecStream); var actualIdentity = nuspec.GetIdentity(); if (!PackageIdentityComparer.Default.Equals(expectedIdentity, actualIdentity)) { string message = string.Format( CultureInfo.InvariantCulture, Strings.Error_PackageIdentityDoesNotMatch, expectedIdentity.Id, expectedIdentity.Version, actualIdentity.Id, actualIdentity.Version); throw new FatalProtocolException(message); } } } } catch (Exception e) { string message = string.Format( CultureInfo.CurrentCulture, Strings.Log_InvalidNupkgFromUrl, uri); throw new InvalidDataException(message, e); } } public static void ValidateXml(string uri, Stream stream) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } if (stream == null) { throw new ArgumentNullException(nameof(stream)); } try { using (var xmlReader = XmlReader.Create( input: stream, settings: new XmlReaderSettings { CloseInput = false })) { while (xmlReader.Read()) { } if (xmlReader.Depth != 0) { throw new InvalidOperationException("The XML document is not complete."); } } } catch (Exception e) { var message = string.Format( CultureInfo.CurrentCulture, Strings.Protocol_InvalidXml, uri); throw new InvalidDataException(message, e); } } } }