Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.
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: 5 additions & 0 deletions src/ApiService/ApiService/Functions/NotificationsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public async Async.Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.An
}

var notificationTest = request.OkV;
var validConfig = await notificationTest.Notification.Config.Validate();
if (!validConfig.IsOk) {
return await _context.RequestHandling.NotOk(req, validConfig.ErrorV, context: "notification test");
}

var result = await _context.NotificationOperations.TriggerNotification(notificationTest.Notification.Container, notificationTest.Notification,
notificationTest.Report, isLastRetryAttempt: true);
var response = req.CreateResponse(HttpStatusCode.OK);
Expand Down
1 change: 1 addition & 0 deletions src/ApiService/ApiService/OneFuzzTypes/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public enum ErrorCode {
ADO_VALIDATION_UNEXPECTED_ERROR = 491,
ADO_VALIDATION_MISSING_PAT_SCOPES = 492,
ADO_WORKITEM_PROCESSING_DISABLED = 494,
ADO_VALIDATION_INVALID_PATH = 495,
// NB: if you update this enum, also update enums.py
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ public async Async.Task<OneFuzzResult<Notification>> Create(Container container,
if (await _context.FeatureManagerSnapshot.IsEnabledAsync(FeatureFlagConstants.SemanticNotificationConfigValidation)) {
var validConfig = await config.Validate();
if (!validConfig.IsOk) {
_logTracer.LogError($"Error(s) ocurred during template validation: {{title}}{Environment.NewLine}{{errors}}", validConfig.ErrorV.Title, string.Join(Environment.NewLine, validConfig.ErrorV.Errors ?? new()));
return OneFuzzResult<Notification>.Error(validConfig.ErrorV);
}
}
Expand Down
58 changes: 55 additions & 3 deletions src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,38 @@ private static bool IsTransient(Exception e) {
return errorCodes.Any(errorStr.Contains);
}

private static async Async.Task<OneFuzzResultVoid> ValidatePath(string project, string path, TreeStructureGroup structureGroup, WorkItemTrackingHttpClient client) {
var pathType = (structureGroup == TreeStructureGroup.Areas) ? "Area" : "Iteration";
var pathParts = path.Split('\\');
if (!string.Equals(pathParts[0], project, StringComparison.OrdinalIgnoreCase)) {
return OneFuzzResultVoid.Error(ErrorCode.ADO_VALIDATION_INVALID_PATH, new string[] {
$"Path \"{path}\" is invalid. It must start with the project name, \"{project}\".",
$"Example: \"{project}\\{path}\".",
});
}

var current = await client.GetClassificationNodeAsync(project, structureGroup, depth: pathParts.Length - 1);
if (current == null) {
return OneFuzzResultVoid.Error(ErrorCode.ADO_VALIDATION_INVALID_PATH, new string[] {
$"{pathType} Path \"{path}\" is invalid. \"{project}\" is not a valid project.",
});
}

foreach (var part in pathParts.Skip(1)) {
var child = current.Children?.FirstOrDefault(x => string.Equals(x.Name, part, StringComparison.OrdinalIgnoreCase));
if (child == null) {
return OneFuzzResultVoid.Error(ErrorCode.ADO_VALIDATION_INVALID_PATH, new string[] {
$"{pathType} Path \"{path}\" is invalid. \"{part}\" is not a valid child of \"{current.Name}\".",
$"Valid children of \"{current.Name}\" are: [{string.Join(',', current.Children?.Select(x => $"\"{x.Name}\"") ?? new List<string>())}].",
});
}

current = child;
}

return OneFuzzResultVoid.Ok;
}

public static async Async.Task<OneFuzzResultVoid> Validate(AdoTemplate config) {
// Validate PAT is valid for the base url
VssConnection connection;
Expand Down Expand Up @@ -124,10 +156,9 @@ await policy.ExecuteAsync(async () => {
return OneFuzzResultVoid.Error(ErrorCode.ADO_VALIDATION_INVALID_PAT, "Auth token is missing or invalid");
}

var witClient = await connection.GetClientAsync<WorkItemTrackingHttpClient>();
try {
// Validate unique_fields are part of the project's valid fields
var witClient = await connection.GetClientAsync<WorkItemTrackingHttpClient>();

// The set of valid fields for this project according to ADO
var projectValidFields = await GetValidFields(witClient, config.Project);

Expand Down Expand Up @@ -162,6 +193,27 @@ await policy.ExecuteAsync(async () => {
});
}

try {
// Validate AreaPath and IterationPath exist
if (config.AdoFields.TryGetValue("System.AreaPath", out var areaPathString)) {
var validateAreaPath = await ValidatePath(config.Project, areaPathString, TreeStructureGroup.Areas, witClient);
if (!validateAreaPath.IsOk) {
return validateAreaPath;
}
}
if (config.AdoFields.TryGetValue("System.IterationPath", out var iterationPathString)) {
var validateIterationPath = await ValidatePath(config.Project, iterationPathString, TreeStructureGroup.Iterations, witClient);
if (!validateIterationPath.IsOk) {
return validateIterationPath;
}
}
} catch (Exception e) {
return OneFuzzResultVoid.Error(ErrorCode.ADO_VALIDATION_UNEXPECTED_ERROR, new string[] {
"Failed to query and validate against the classification nodes for this project",
$"Exception: {e}",
});
}

return OneFuzzResultVoid.Ok;
}

Expand Down Expand Up @@ -362,7 +414,7 @@ public async Async.Task<bool> UpdateExisting(WorkItem item, IList<(string, strin
return false;
}

if (_config.OnDuplicate.Comment != null) {
if (!string.IsNullOrEmpty(_config.OnDuplicate.Comment)) {
var comment = _config.OnDuplicate.Comment;
_ = await _client.AddCommentAsync(
new CommentCreate() {
Expand Down
1 change: 1 addition & 0 deletions src/pytypes/onefuzztypes/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ class ErrorCode(Enum):
ADO_VALIDATION_UNEXPECTED_HTTP_EXCEPTION = 490
ADO_VALIDATION_UNEXPECTED_ERROR = 491
ADO_VALIDATION_MISSING_PAT_SCOPES = 492
ADO_VALIDATION_INVALID_PATH = 495
# NB: if you update this enum, also update Enums.cs


Expand Down