Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting MaxDepth parameter in the JsonSerializerSettings as safe defaults #3045

Merged
merged 3 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions iothub/device/src/InternalClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Security.Cryptography.X509Certificates;
using System.IO;
using Microsoft.Azure.Devices.Client.Exceptions;
using Newtonsoft.Json;

#if NET451

Expand Down Expand Up @@ -156,6 +157,8 @@ public InternalClient(IotHubConnectionString iotHubConnectionString, ITransportS
Logging.Enter(this, transportSettings, pipelineBuilder, nameof(InternalClient) + "_ctor");
}

JsonConvert.DefaultSettings = JsonSerializerSettingsInitializer.GetDefaultJsonSerializerSettings();

TlsVersions.Instance.SetLegacyAcceptableVersions();

IotHubConnectionString = iotHubConnectionString;
Expand Down Expand Up @@ -989,7 +992,7 @@ internal async Task OnMethodCalledAsync(MethodRequestInternal methodRequestInter
methodResponseInternal?.Dispose();
}
finally
{
{
// Need to release this semaphore even if the above dispose call fails
_methodsDictionarySemaphore.Release();
}
Expand Down Expand Up @@ -1405,7 +1408,7 @@ private async Task OnDeviceMessageReceivedAsync(Message message)
return;
}

// Grab this semaphore so that there is no chance that the _deviceReceiveMessageCallback instance is set in between the read of the
// Grab this semaphore so that there is no chance that the _deviceReceiveMessageCallback instance is set in between the read of the
// item1 and the read of the item2
await _deviceReceiveMessageSemaphore.WaitAsync().ConfigureAwait(false);
ReceiveMessageCallback callback = _deviceReceiveMessageCallback?.Item1;
Expand Down
53 changes: 28 additions & 25 deletions iothub/service/src/DigitalTwin/DigitalTwinClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Azure.Devices.Authentication;
using Microsoft.Azure.Devices.Extensions;
using Microsoft.Azure.Devices.Generated;
using Microsoft.Azure.Devices.Shared;
using Microsoft.Rest;
using Newtonsoft.Json;

Expand Down Expand Up @@ -38,6 +39,8 @@ private DigitalTwinClient(Uri uri, IotServiceClientCredentials credentials, para
{
_client = new IotHubGatewayServiceAPIs(uri, credentials, handlers);
_protocolLayer = new DigitalTwin(_client);

JsonConvert.DefaultSettings = JsonSerializerSettingsInitializer.GetDefaultJsonSerializerSettings();
}

/// <summary>
Expand Down Expand Up @@ -69,9 +72,9 @@ public async Task<HttpOperationResponse<T, DigitalTwinGetHeaders>> GetDigitalTwi
/// <param name="cancellationToken">The cancellationToken.</param>
/// <returns>The http response.</returns>
public Task<HttpOperationHeaderResponse<DigitalTwinUpdateHeaders>> UpdateDigitalTwinAsync(
string digitalTwinId,
string digitalTwinUpdateOperations,
DigitalTwinUpdateRequestOptions requestOptions = default,
string digitalTwinId,
string digitalTwinUpdateOperations,
DigitalTwinUpdateRequestOptions requestOptions = default,
CancellationToken cancellationToken = default)
{
return _protocolLayer.UpdateDigitalTwinWithHttpMessagesAsync(digitalTwinId, digitalTwinUpdateOperations, requestOptions?.IfMatch, null, cancellationToken);
Expand All @@ -87,19 +90,19 @@ public Task<HttpOperationHeaderResponse<DigitalTwinUpdateHeaders>> UpdateDigital
/// <param name="cancellationToken">The cancellationToken.</param>
/// <returns>The application/json command invocation response and the http response. </returns>
public async Task<HttpOperationResponse<DigitalTwinCommandResponse, DigitalTwinInvokeCommandHeaders>> InvokeCommandAsync(
string digitalTwinId,
string commandName,
string payload = default,
DigitalTwinInvokeCommandRequestOptions requestOptions = default,
string digitalTwinId,
string commandName,
string payload = default,
DigitalTwinInvokeCommandRequestOptions requestOptions = default,
CancellationToken cancellationToken = default)
{
using HttpOperationResponse<string, DigitalTwinInvokeRootLevelCommandHeaders> response = await _protocolLayer.InvokeRootLevelCommandWithHttpMessagesAsync(
digitalTwinId,
commandName,
payload,
requestOptions?.ConnectTimeoutInSeconds,
requestOptions?.ResponseTimeoutInSeconds,
null,
digitalTwinId,
commandName,
payload,
requestOptions?.ConnectTimeoutInSeconds,
requestOptions?.ResponseTimeoutInSeconds,
null,
cancellationToken)
.ConfigureAwait(false);
return new HttpOperationResponse<DigitalTwinCommandResponse, DigitalTwinInvokeCommandHeaders>
Expand All @@ -122,21 +125,21 @@ public async Task<HttpOperationResponse<DigitalTwinCommandResponse, DigitalTwinI
/// <param name="cancellationToken">The cancellationToken.</param>
/// <returns>The application/json command invocation response and the http response. </returns>
public async Task<HttpOperationResponse<DigitalTwinCommandResponse, DigitalTwinInvokeCommandHeaders>> InvokeComponentCommandAsync(
string digitalTwinId,
string componentName,
string commandName,
string payload = default,
DigitalTwinInvokeCommandRequestOptions requestOptions = default,
string digitalTwinId,
string componentName,
string commandName,
string payload = default,
DigitalTwinInvokeCommandRequestOptions requestOptions = default,
CancellationToken cancellationToken = default)
{
using HttpOperationResponse<string, DigitalTwinInvokeComponentCommandHeaders> response = await _protocolLayer.InvokeComponentCommandWithHttpMessagesAsync(
digitalTwinId,
componentName,
commandName,
payload,
requestOptions?.ConnectTimeoutInSeconds,
requestOptions?.ResponseTimeoutInSeconds,
null,
digitalTwinId,
componentName,
commandName,
payload,
requestOptions?.ConnectTimeoutInSeconds,
requestOptions?.ResponseTimeoutInSeconds,
null,
cancellationToken)
.ConfigureAwait(false);
return new HttpOperationResponse<DigitalTwinCommandResponse, DigitalTwinInvokeCommandHeaders>
Expand Down
5 changes: 4 additions & 1 deletion iothub/service/src/JobClient/JobClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Microsoft.Azure.Devices
{
using Microsoft.Azure.Devices.Shared;
using Newtonsoft.Json;
using System;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -35,6 +36,7 @@ public static JobClient CreateFromConnectionString(string connectionString, Http
{
throw new ArgumentNullException(nameof(transportSettings), "HTTP Transport settings cannot be null.");
}
JsonConvert.DefaultSettings = JsonSerializerSettingsInitializer.GetDefaultJsonSerializerSettings();
TlsVersions.Instance.SetLegacyAcceptableVersions();

var iotHubConnectionString = IotHubConnectionString.Parse(connectionString);
Expand All @@ -52,7 +54,8 @@ public void Dispose()
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing) { }
protected virtual void Dispose(bool disposing)
{ }

/// <summary>
/// Explicitly open the JobClient instance.
Expand Down
6 changes: 5 additions & 1 deletion iothub/service/src/RegistryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Shared;
using Newtonsoft.Json;

namespace Microsoft.Azure.Devices
{
Expand Down Expand Up @@ -40,6 +41,8 @@ public static RegistryManager CreateFromConnectionString(string connectionString
{
throw new ArgumentNullException(nameof(transportSettings), "The HTTP transport settings cannot be null.");
}

JsonConvert.DefaultSettings = JsonSerializerSettingsInitializer.GetDefaultJsonSerializerSettings();
TlsVersions.Instance.SetLegacyAcceptableVersions();

var iotHubConnectionString = IotHubConnectionString.Parse(connectionString);
Expand All @@ -57,7 +60,8 @@ public void Dispose()
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing) { }
protected virtual void Dispose(bool disposing)
{ }

/// <summary>
/// Explicitly open the RegistryManager instance.
Expand Down
5 changes: 4 additions & 1 deletion iothub/service/src/ServiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Shared;
using Newtonsoft.Json;

namespace Microsoft.Azure.Devices
{
Expand Down Expand Up @@ -39,6 +40,7 @@ public abstract class ServiceClient : IDisposable
/// </summary>
internal ServiceClient()
{
JsonConvert.DefaultSettings = JsonSerializerSettingsInitializer.GetDefaultJsonSerializerSettings();
TlsVersions.Instance.SetLegacyAcceptableVersions();
}

Expand All @@ -64,7 +66,8 @@ public void Dispose()
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing) { }
protected virtual void Dispose(bool disposing)
{ }

/// <summary>
/// Create an instance of ServiceClient from the specified IoT Hub connection string using specified Transport Type.
Expand Down
32 changes: 31 additions & 1 deletion iothub/service/tests/QueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Microsoft.Azure.Devices.Api.Test
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Microsoft.Azure.Devices.Shared;
using FluentAssertions;

[TestClass]
[TestCategory("Unit")]
Expand Down Expand Up @@ -292,5 +293,34 @@ public void QueryResultUserSuppliedContinuationTest()
Assert.AreEqual("test", r.ElementAt(0).DeviceId);
Assert.IsTrue(q.HasMoreResults);
}

[TestMethod]
public void QueryResult_OverrideDefaultJsonSerializer_ExceedMaxDepthThrows()
{
var settings = new JsonSerializerSettings { MaxDepth = 2 };
// simulate json deserialize
const string jsonString = @"
{
""type"":""twin"",
""items"":
[{
""deviceId"": ""test"",
""etag"":null,
""version"":null,
""properties"":
{
""desired"":{},
""reported"":{}
}
}],
""continuationToken"":""GYUVJDBJFKJ""
}";
// deserialize
// act
Func<QueryResult> act = () => JsonConvert.DeserializeObject<QueryResult>(jsonString, settings);

// assert
act.Should().Throw<Newtonsoft.Json.JsonReaderException>();
}
}
}
}
27 changes: 27 additions & 0 deletions iothub/service/tests/SerializationTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using FluentAssertions;
using Microsoft.Azure.Devices.Shared;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
Expand Down Expand Up @@ -68,5 +69,31 @@ public void Configuration_TestNullSchema_Ok()

JsonConvert.DeserializeObject<Configuration>(jsonString);
}

[TestMethod]
public void Twin_Json_OverrideDefaultJsonSerializer_ExceedMaxDepthThrows()
{
// arrange
const string jsonString = @"
{
""deviceId"": ""test"",
""etag"": ""AAAAAAAAAAM="",
""version"": 5,
""status"": ""enabled"",
""statusUpdateTime"": ""2018-06-29T21:17:08.7759733"",
""connectionState"": ""Connected"",
""lastActivityTime"": ""2018-06-29T21:17:08.7759733"",
""Capabilities"": {
""IotEdge"": false
}
}";
var settings = new JsonSerializerSettings { MaxDepth = 1 };

// act
Func<Twin> act = () => JsonConvert.DeserializeObject<Twin>(jsonString, settings);

// assert
act.Should().Throw<Newtonsoft.Json.JsonReaderException>();
}
}
}
4 changes: 4 additions & 0 deletions provisioning/service/src/ProvisioningServiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Common.Service.Auth;
using Microsoft.Azure.Devices.Shared;
using Newtonsoft.Json;

namespace Microsoft.Azure.Devices.Provisioning.Service
{
Expand Down Expand Up @@ -112,6 +114,8 @@ public static ProvisioningServiceClient CreateFromConnectionString(string connec
/// <exception cref="ArgumentException">if the connectionString is <code>null</code>, empty, or invalid.</exception>
private ProvisioningServiceClient(string connectionString, HttpTransportSettings httpTransportSettings)
{
JsonConvert.DefaultSettings = JsonSerializerSettingsInitializer.GetDefaultJsonSerializerSettings();

/* SRS_PROVISIONING_SERVICE_CLIENT_21_002: [The constructor shall throw ArgumentException if the provided connectionString is null or empty.] */
if (string.IsNullOrWhiteSpace(connectionString ?? throw new ArgumentNullException(nameof(connectionString))))
{
Expand Down
29 changes: 29 additions & 0 deletions shared/src/JsonSerializerInitializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Newtonsoft.Json;

namespace Microsoft.Azure.Devices.Shared
{
/// <summary>
/// A static class to initialize JsonSerializerSettings with the default MaxDepth of 128 for the project
/// </summary>
public static class JsonSerializerSettingsInitializer
{
/// <summary>
/// A static instance of JsonSerializerSettings which sets MaxDepth to 128.
/// </summary>
public static readonly JsonSerializerSettings SettingsInstance = new JsonSerializerSettings
{
MaxDepth = 128
};

/// <summary>
/// Returns DefaultJsonSerializerSettings
/// </summary>
public static Func<JsonSerializerSettings> GetDefaultJsonSerializerSettings()
{
return () => SettingsInstance;
}
}
}