Skip to content

Commit

Permalink
validate
Browse files Browse the repository at this point in the history
  • Loading branch information
timbussmann committed Aug 13, 2024
1 parent a75592c commit f6c25ce
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 130 deletions.
69 changes: 69 additions & 0 deletions Annoy-o-Bot.Tests/GitHub/Callbacks/CallbackModelTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.IO;
using Newtonsoft.Json;
using Xunit;

namespace Annoy_o_Bot.GitHub.Callbacks;

public class CallbackModelTests
{
[Fact]
public void NewFileAdded()
{
var result = JsonConvert.DeserializeObject<CallbackModel>(File.ReadAllText("requests/fileAdded.json"));

Assert.Equal("refs/heads/master", result.Ref);

Assert.Equal(7498230L, result.Installation.Id);

Assert.Equal(179716425L, result.Repository.Id);
Assert.Equal("master", result.Repository.DefaultBranch);
Assert.Equal("TitleTest", result.Repository.Name);

var commit = Assert.Single(result.Commits);
Assert.Equal("ba7c6f17f5beaafc603eca52b864356848865fec", commit.Id);
var addedFile = Assert.Single(commit.Added);
Assert.Equal(".reminder/testReminder.json", addedFile);
Assert.Equal("Create trigger4.json", commit.Message);

Assert.Equal("timbussmann", result.Pusher.Name);
}

[Fact]
public void MultiCommit()
{
var result = JsonConvert.DeserializeObject<CallbackModel>(File.ReadAllText("requests/multiCommitFileHistory.json"));

Assert.Equal(4, result.Commits.Length);

Assert.Equal(".reminder/newFile.json", Assert.Single(result.Commits[0].Added));
Assert.Empty(result.Commits[0].Modified);
Assert.Empty(result.Commits[0].Removed);
Assert.Equal("Create newFile.json", result.Commits[0].Message);

Assert.Equal(".reminder/newFile.json", Assert.Single(result.Commits[1].Modified));
Assert.Empty(result.Commits[1].Added);
Assert.Empty(result.Commits[1].Removed);
Assert.Equal("Update newFile.json", result.Commits[1].Message);

Assert.Equal(".reminder/newFile.json", Assert.Single(result.Commits[2].Removed));
Assert.Empty(result.Commits[2].Added);
Assert.Empty(result.Commits[2].Modified);
Assert.Equal("Delete newFile.json", result.Commits[2].Message);

Assert.Empty(result.Commits[3].Added);
Assert.Empty(result.Commits[3].Modified);
Assert.Empty(result.Commits[3].Removed);
Assert.Equal("Merge pull request #15 from timbussmann/file-ops-history\n\nCreate newFile.json", result.Commits[3].Message);

Assert.Equal("cb1ec97f51657c2718ab4e0b1d0bf2656aeb3127", result.HeadCommit.Id);
}

[Fact]
public void BranchDeleted()
{
var result = JsonConvert.DeserializeObject<CallbackModel>(File.ReadAllText("requests/branchDeleted.json"));

Assert.Null(result.HeadCommit);
Assert.Empty(result.Commits);
}
}
71 changes: 0 additions & 71 deletions Annoy-o-Bot.Tests/RequestParserTests.cs

This file was deleted.

9 changes: 4 additions & 5 deletions Annoy-o-Bot/CallbackHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@ public async Task<IActionResult> Run(
{
var cosmosWrapper = new CosmosClientWrapper(cosmosContainer);

if (!GitHubCallbackRequest.IsGitCommitCallback(req, log))
var secret = configuration.GetValue<string>("WebhookSecret") ??
throw new Exception("Missing 'WebhookSecret' setting to validate GitHub callbacks.");
var commitModel = await GitHubCallbackRequest.Validate(req, secret, log);
if (commitModel == null)
{
return new OkResult();
}

var secret = configuration.GetValue<string>("WebhookSecret") ??
throw new Exception("Missing 'WebhookSecret' setting to validate GitHub callbacks.");
var commitModel = await GitHubCallbackRequest.Validate(req, secret);

if (commitModel.HeadCommit == null)
{
// no commits on push (e.g. branch delete)
Expand Down
71 changes: 32 additions & 39 deletions Annoy-o-Bot/GitHub/Callbacks/CallbackModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,44 @@
using System;
using Newtonsoft.Json;

namespace Annoy_o_Bot.GitHub.Callbacks
namespace Annoy_o_Bot.GitHub.Callbacks;

public class CallbackModel
{
public class CallbackModel
{
public static CallbackModel FromJson(string json)
{
var requestObject = JsonConvert.DeserializeObject<CallbackModel>(json);
return requestObject;
}

public InstallationModel Installation { get; set; }
public RepositoryModel Repository { get; set; }
public CommitModel[] Commits { get; set; }
public string Ref { get; set; }
public InstallationModel Installation { get; set; }
public RepositoryModel Repository { get; set; }
public CommitModel[] Commits { get; set; }
public string Ref { get; set; }

[JsonProperty("head_commit")]
public CommitModel HeadCommit { get; set; }
public PusherModel Pusher { get; set; }
[JsonProperty("head_commit")]
public CommitModel HeadCommit { get; set; }
public PusherModel Pusher { get; set; }

public class CommitModel
{
public string Id { get; set; }
public string Message { get; set; }
public string[] Added { get; set; } = Array.Empty<string>();
public string[] Modified { get; set; } = Array.Empty<string>();
public string[] Removed { get; set; } = Array.Empty<string>();
}
public class CommitModel
{
public string Id { get; set; }
public string Message { get; set; }
public string[] Added { get; set; } = Array.Empty<string>();
public string[] Modified { get; set; } = Array.Empty<string>();
public string[] Removed { get; set; } = Array.Empty<string>();
}

public class InstallationModel
{
public long Id { get; set; }
}
public class InstallationModel
{
public long Id { get; set; }
}

public class RepositoryModel
{
public long Id { get; set; }
public class RepositoryModel
{
public long Id { get; set; }

[JsonProperty("default_branch")]
public string DefaultBranch { get; set; }
public string Name { get; set; }
}
[JsonProperty("default_branch")]
public string DefaultBranch { get; set; }
public string Name { get; set; }
}

public class PusherModel
{
public string Name { get; set; }
}
public class PusherModel
{
public string Name { get; set; }
}
}
36 changes: 21 additions & 15 deletions Annoy-o-Bot/GitHub/Callbacks/GitHubCallbackRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,32 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace Annoy_o_Bot.GitHub.Callbacks;

public class GitHubCallbackRequest
{
public static bool IsGitCommitCallback(HttpRequest callbackRequest, ILogger log)
public static async Task<CallbackModel?> Validate(HttpRequest callbackRequest, string gitHubSecret, ILogger log)
{
if (!IsGitCommitCallback(callbackRequest, log))
{
return null;
}

if (!callbackRequest.Headers.TryGetValue("X-Hub-Signature-256", out var sha256SignatureHeaderValue))
{
throw new Exception("Incoming callback request does not contain a 'X-Hub-Signature-256' header");
}

var requestBody = await new StreamReader(callbackRequest.Body).ReadToEndAsync();

await ValidateSignature(requestBody, gitHubSecret, sha256SignatureHeaderValue.ToString().Replace("sha256=", ""));

return JsonConvert.DeserializeObject<CallbackModel>(requestBody);
}

static bool IsGitCommitCallback(HttpRequest callbackRequest, ILogger log)
{
if (!callbackRequest.Headers.TryGetValue("X-GitHub-Event", out var callbackEvent) || callbackEvent != "push")
{
Expand All @@ -26,21 +46,7 @@ public static bool IsGitCommitCallback(HttpRequest callbackRequest, ILogger log)

return true;
}

public static async Task<CallbackModel> Validate(HttpRequest callbackRequest, string gitHubSecret)
{
if (!callbackRequest.Headers.TryGetValue("X-Hub-Signature-256", out var sha256SignatureHeaderValue))
{
throw new Exception("Incoming callback request does not contain a 'X-Hub-Signature-256' header");
}

var requestBody = await new StreamReader(callbackRequest.Body).ReadToEndAsync();

await ValidateSignature(requestBody, gitHubSecret, sha256SignatureHeaderValue.ToString().Replace("sha256=", ""));

return CallbackModel.FromJson(requestBody);
}

public static async Task ValidateSignature(string signedText, string secret, string sha256Signature)
{
var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
Expand Down

0 comments on commit f6c25ce

Please sign in to comment.