Enforce that there no extra properties in request JSON, and that non-null properties are [Required] (#2328)

Closes #2314 via two fixes, one for additional properties and one for missing properties:

- Make all request types inherit from `BaseRequest` which has an `ExtensionData` property, and ensure that it is empty in `ParseRequest`.
- Add `[Required]` attribute to non-nullable properties that do not have defaults, and add a test that ensures we have this attribute where necessary.
This commit is contained in:
George Pollard
2022-09-02 13:59:24 +12:00
committed by GitHub
parent 52ba57bf0d
commit c54db04083
9 changed files with 294 additions and 103 deletions

View File

@ -0,0 +1,71 @@
using System;
using System.Net;
using System.Text.Json;
using System.Text.Json.Nodes;
using IntegrationTests.Fakes;
using Microsoft.OneFuzz.Service;
using Microsoft.OneFuzz.Service.Functions;
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
using Xunit;
using Xunit.Abstractions;
using Async = System.Threading.Tasks;
namespace IntegrationTests.Functions;
[Trait("Category", "Live")]
public class AzureStorageTasksTest : TasksTestBase {
public AzureStorageTasksTest(ITestOutputHelper output)
: base(output, Integration.AzureStorage.FromEnvironment()) { }
}
public class AzuriteTasksTest : TasksTestBase {
public AzuriteTasksTest(ITestOutputHelper output)
: base(output, new Integration.AzuriteStorage()) { }
}
public abstract class TasksTestBase : FunctionTestBase {
public TasksTestBase(ITestOutputHelper output, IStorage storage)
: base(output, storage) { }
[Fact]
public async Async.Task SpecifyingVmIsNotPermitted() {
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
var func = new Tasks(Logger, auth, Context);
var req = new TaskCreate(
Guid.NewGuid(),
null,
new TaskDetails(TaskType.DotnetCoverage, 100),
new TaskPool(1, PoolName.Parse("pool")));
// the 'vm' property used to be permitted but is no longer, add it:
var serialized = (JsonObject?)JsonSerializer.SerializeToNode(req, EntityConverter.GetJsonSerializerOptions());
serialized!["vm"] = new JsonObject { { "fake", 1 } };
var testData = new TestHttpRequestData("POST", new BinaryData(JsonSerializer.SerializeToUtf8Bytes(serialized, EntityConverter.GetJsonSerializerOptions())));
var result = await func.Run(testData);
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode);
var err = BodyAs<Error>(result);
Assert.Equal(new[] { "Unexpected property: \"vm\"" }, err.Errors);
}
[Fact]
public async Async.Task PoolIsRequired() {
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
var func = new Tasks(Logger, auth, Context);
// override the found user credentials - need these to check for admin
var userInfo = new UserInfo(ApplicationId: Guid.NewGuid(), ObjectId: Guid.NewGuid(), "upn");
Context.UserCredentials = new TestUserCredentials(Logger, Context.ConfigOperations, OneFuzzResult<UserInfo>.Ok(userInfo));
var req = new TaskCreate(
Guid.NewGuid(),
null,
new TaskDetails(TaskType.DotnetCoverage, 100),
null! /* <- here */);
var result = await func.Run(TestHttpRequestData.FromJson("POST", req));
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode);
var err = BodyAs<Error>(result);
Assert.Equal(new[] { "The Pool field is required." }, err.Errors);
}
}