Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 0 additions & 5 deletions DevCycle.SDK.Server.Cloud/Api/DevCycleCloudClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,6 @@ public override async Task<Variable<T>> Variable<T>(DevCycleUser user, string ke
throw new ArgumentException("key cannot be null or empty");
}

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

AddDefaults(user);

string lowerKey = key.ToLower();
Expand Down
4 changes: 3 additions & 1 deletion DevCycle.SDK.Server.Common/Model/Variable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ public static TypeEnum DetermineType(T variableValue)

try
{
var baseType = variableValue.GetType();
// A null default value is legitimate for JSON variables (and nullable strings),
// so fall back to the declared type rather than dereferencing the value.
var baseType = variableValue?.GetType() ?? typeof(T);

if (baseType == typeof(string))
{
Expand Down
69 changes: 68 additions & 1 deletion DevCycle.SDK.Server.Local.MSTests/DevCycleTest.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System;
using System;
using System.Threading.Tasks;
using DevCycle.SDK.Server.Local.Api;
using DevCycle.SDK.Server.Common.Model;
using DevCycle.SDK.Server.Common.Model.Local;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json.Linq;
using Environment = System.Environment;
using System.Collections.Generic;
using System.Text.Json;
Expand Down Expand Up @@ -233,6 +234,72 @@ public void Variable_NullUser_ThrowsException()
});
}

[TestMethod]
public void Variable_NullKey_ThrowsArgumentException()
{
// Reaching the WASM bucketing engine with a null/empty flag key
// triggers an internal abort() and corrupts the WASM heap. Match
// the Java/Python SDKs (and Cloud client) by failing fast with a
// clear ArgumentException before we ever enter WASM.
using DevCycleLocalClient api = DevCycleTestClient.getTestClient();
var user = new DevCycleUser("test_user");

Assert.Throws<ArgumentException>(() => api.Variable(user, null, true).Result);
}

[TestMethod]
public void Variable_EmptyKey_ThrowsArgumentException()
{
using DevCycleLocalClient api = DevCycleTestClient.getTestClient();
var user = new DevCycleUser("test_user");

Assert.Throws<ArgumentException>(() => api.Variable(user, "", true).Result);
}

[TestMethod]
public async Task VariableAsync_NullKey_ThrowsArgumentException()
{
using DevCycleLocalClient api = DevCycleTestClient.getTestClient();
var user = new DevCycleUser("test_user");

await Assert.ThrowsExactlyAsync<ArgumentException>(async () =>
await api.VariableAsync(user, null, true));
}

[TestMethod]
public async Task VariableAsync_EmptyKey_ThrowsArgumentException()
{
using DevCycleLocalClient api = DevCycleTestClient.getTestClient();
var user = new DevCycleUser("test_user");

await Assert.ThrowsExactlyAsync<ArgumentException>(async () =>
await api.VariableAsync(user, "", true));
}

[TestMethod]
public async Task Variable_NullJsonDefaultValue_IsAllowed()
{
// A null default is legitimate for JSON variables, so the key validation
// above must not be extended to defaultValue.
using DevCycleLocalClient api = DevCycleTestClient.getTestClient();
var user = new DevCycleUser("test_user");

var variable = await api.Variable<JObject>(user, "some_key", null);

Assert.IsNotNull(variable);
}

[TestMethod]
public async Task VariableAsync_NullJsonDefaultValue_IsAllowed()
{
using DevCycleLocalClient api = DevCycleTestClient.getTestClient();
var user = new DevCycleUser("test_user");

var variable = await api.VariableAsync<JObject>(user, "some_key", null);

Assert.IsNotNull(variable);
}

[TestMethod]
public void User_NullUserId_ThrowsException()
{
Expand Down
10 changes: 10 additions & 0 deletions DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
ILoggerFactory loggerFactory,
EnvironmentConfigManager configManager,
ILocalBucketing localBucketing,
DevCycleRestClientOptions restClientOptions = null

Check warning on line 90 in DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs

View workflow job for this annotation

GitHub Actions / run-example

Cannot convert null literal to non-nullable reference type.

Check warning on line 90 in DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs

View workflow job for this annotation

GitHub Actions / run-example

Cannot convert null literal to non-nullable reference type.

Check warning on line 90 in DevCycle.SDK.Server.Local/Api/DevCycleLocalClient.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
)
{
ValidateSDKKey(sdkKey);
Expand Down Expand Up @@ -330,6 +330,11 @@
{
var requestUser = new DevCyclePopulatedUser(user);

if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("key cannot be null or empty");
}

if (!configManager.Initialized)
{
logger.LogWarning("Variable called before DevCycleClient has initialized, returning default value");
Expand Down Expand Up @@ -389,6 +394,11 @@
{
var requestUser = new DevCyclePopulatedUser(user);

if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("key cannot be null or empty");
}
Comment thread
jonathannorris marked this conversation as resolved.

if (!configManager.Initialized)
{
logger.LogWarning("Variable called before DevCycleClient has initialized, returning default value");
Expand Down
Loading