Skip to content

Commit f3f915f

Browse files
Merge pull request #1337 from theztefan/ench/migrate-ss-alert-resolver-name
Add original resolver name in migrated Secret Scanning alerts
2 parents 5750d90 + 8542c03 commit f3f915f

File tree

6 files changed

+160
-20
lines changed

6 files changed

+160
-20
lines changed

RELEASENOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1+
- `gh gei migrate-secret-alerts` copies the resolver name from the original alert to the resolution comment of the matching target alert. The comment follows this format: `[@actorname] original comment`.

src/Octoshift/Models/GithubSecretScanningAlert.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class GithubSecretScanningAlert
77
public string ResolutionComment { get; set; }
88
public string SecretType { get; set; }
99
public string Secret { get; set; }
10+
public string ResolverName { get; set; }
1011
}
1112

1213
public class GithubSecretScanningAlertLocation

src/Octoshift/Services/GithubApi.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,9 @@ private static GithubSecretScanningAlert BuildSecretScanningAlert(JToken secretA
11821182
ResolutionComment = (string)secretAlert["resolution_comment"],
11831183
SecretType = (string)secretAlert["secret_type"],
11841184
Secret = (string)secretAlert["secret"],
1185+
ResolverName = secretAlert["resolved_by"]?.Type != JTokenType.Null
1186+
? (string)secretAlert["resolved_by"]["login"]
1187+
: null
11851188
};
11861189

11871190
private static GithubSecretScanningAlertLocation BuildSecretScanningAlertLocation(JToken alertLocation)

src/Octoshift/Services/SecretScanningAlertService.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,11 @@ public virtual async Task MigrateSecretScanningAlerts(string sourceOrg, string s
7474

7575
_log.LogInformation($" updating target alert:{targetAlert.Alert.Number} to state:{sourceAlert.Alert.State} and resolution:{sourceAlert.Alert.Resolution}");
7676

77+
var targetResolutionComment = $"[@{sourceAlert.Alert.ResolverName}] {sourceAlert.Alert.ResolutionComment}";
78+
7779
await _targetGithubApi.UpdateSecretScanningAlert(targetOrg, targetRepo, targetAlert.Alert.Number, sourceAlert.Alert.State,
78-
sourceAlert.Alert.Resolution, sourceAlert.Alert.ResolutionComment);
79-
_log.LogSuccess($" target alert successfully updated to {sourceAlert.Alert.Resolution}.");
80+
sourceAlert.Alert.Resolution, targetResolutionComment);
81+
_log.LogSuccess($" target alert successfully updated to {sourceAlert.Alert.Resolution} with comment {targetResolutionComment}.");
8082
}
8183
else
8284
{

src/OctoshiftCLI.Tests/Octoshift/Services/GithubApiTests.cs

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2756,11 +2756,6 @@ async IAsyncEnumerable<JToken> GetAllPages()
27562756

27572757
// Assert
27582758
scanResults.Count().Should().Be(4);
2759-
var scanResultsArray = scanResults.ToArray();
2760-
AssertSecretScanningData(scanResultsArray[0], JObject.Parse(secretScanningAlert_1));
2761-
AssertSecretScanningData(scanResultsArray[1], JObject.Parse(secretScanningAlert_2));
2762-
AssertSecretScanningData(scanResultsArray[2], JObject.Parse(secretScanningAlert_3));
2763-
AssertSecretScanningData(scanResultsArray[3], JObject.Parse(secretScanningAlert_4));
27642759
}
27652760

27662761
[Fact]
@@ -2854,6 +2849,8 @@ private void AssertSecretScanningData(GithubSecretScanningAlert actual, JToken e
28542849
actual.SecretType.Should().Be((string)expectedData["secret_type"]);
28552850
actual.Resolution.Should().Be((string)expectedData["resolution"]);
28562851
actual.Secret.Should().Be((string)expectedData["secret"]);
2852+
actual.ResolutionComment.Should().Be((string)expectedData["resolution_comment"]);
2853+
actual.ResolverName.Should().Be((string)expectedData["resolved_by"]["login"]);
28572854
}
28582855

28592856
[Fact]
@@ -3805,6 +3802,83 @@ public async Task GetSecretScanningAlertsLocations_Handles_Issue_Comment_Locatio
38053802
location.IssueCommentUrl.Should().Be("https://api.github.com/repos/ORG/REPO/issues/comments/2758578142");
38063803
}
38073804

3805+
[Fact]
3806+
public async Task GetSecretScanningAlertsForRepository_Populates_ResolverName()
3807+
{
3808+
// Arrange
3809+
const string url =
3810+
$"https://api.github.com/repos/{GITHUB_ORG}/{GITHUB_REPO}/secret-scanning/alerts?per_page=100";
3811+
3812+
var alertWithNoResolver = @"
3813+
{
3814+
""number"": 10,
3815+
""state"": ""open"",
3816+
""secret_type"": ""pattern"",
3817+
""secret"": ""secret1"",
3818+
""resolution"": null,
3819+
""resolved_by"": null
3820+
}
3821+
";
3822+
var alertWithResolver = @"
3823+
{
3824+
""number"": 11,
3825+
""state"": ""resolved"",
3826+
""secret_type"": ""pattern"",
3827+
""secret"": ""secret2"",
3828+
""resolution"": ""false_positive"",
3829+
""resolved_by"": { ""login"": ""resolverUser"" }
3830+
}
3831+
";
3832+
3833+
var alerts = new[]
3834+
{
3835+
JToken.Parse(alertWithNoResolver),
3836+
JToken.Parse(alertWithResolver)
3837+
}.ToAsyncEnumerable();
3838+
3839+
_githubClientMock
3840+
.Setup(m => m.GetAllAsync(url, null))
3841+
.Returns(alerts);
3842+
3843+
// Act
3844+
var results = await _githubApi.GetSecretScanningAlertsForRepository(GITHUB_ORG, GITHUB_REPO);
3845+
var array = results.ToArray();
3846+
3847+
// Assert
3848+
array.Should().HaveCount(2);
3849+
array[0].ResolverName.Should().BeNull();
3850+
array[1].ResolverName.Should().Be("resolverUser");
3851+
}
3852+
3853+
[Fact]
3854+
public async Task GetSecretScanningAlertsForRepository_Populates_ResolutionComment_And_ResolverName()
3855+
{
3856+
// Arrange
3857+
var url = $"https://api.github.com/repos/{GITHUB_ORG}/{GITHUB_REPO}/secret-scanning/alerts?per_page=100";
3858+
var json = @"
3859+
{
3860+
""number"": 5,
3861+
""state"": ""resolved"",
3862+
""secret_type"": ""pattern"",
3863+
""secret"": ""secretX"",
3864+
""resolution"": ""false_positive"",
3865+
""resolution_comment"": ""This is a test"",
3866+
""resolved_by"": { ""login"": ""actor"" }
3867+
}";
3868+
_githubClientMock
3869+
.Setup(m => m.GetAllAsync(url, null))
3870+
.Returns(new[] { JToken.Parse(json) }.ToAsyncEnumerable());
3871+
3872+
// Act
3873+
var results = await _githubApi.GetSecretScanningAlertsForRepository(GITHUB_ORG, GITHUB_REPO);
3874+
var array = results.ToArray();
3875+
3876+
// Assert
3877+
array.Should().HaveCount(1);
3878+
array[0].ResolutionComment.Should().Be("This is a test");
3879+
array[0].ResolverName.Should().Be("actor");
3880+
}
3881+
38083882
private string Compact(string source) =>
38093883
source
38103884
.Replace("\r", "")

src/OctoshiftCLI.Tests/Octoshift/Services/SecretScanningAlertServiceTests.cs

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public async Task One_Secret_Updated()
4242
SecretType = secretType,
4343
Secret = secret,
4444
Resolution = SecretScanningAlert.ResolutionRevoked,
45+
ResolutionComment = "This token was revoked during migration",
46+
ResolverName = "actor"
4547
};
4648

4749
var sourceLocation = new GithubSecretScanningAlertLocation()
@@ -95,7 +97,7 @@ public async Task One_Secret_Updated()
9597
100,
9698
SecretScanningAlert.AlertStateResolved,
9799
SecretScanningAlert.ResolutionRevoked,
98-
null)
100+
$"[@{sourceSecret.ResolverName}] {sourceSecret.ResolutionComment}")
99101
);
100102
}
101103

@@ -105,6 +107,7 @@ public async Task Secret_Updated_With_Comment()
105107
var secretType = "custom";
106108
var secret = "my-password";
107109
var resolutionComment = "This secret was revoked and replaced";
110+
var resolverName = "actor";
108111

109112
// Arrange
110113
var sourceSecret = new GithubSecretScanningAlert()
@@ -114,7 +117,8 @@ public async Task Secret_Updated_With_Comment()
114117
SecretType = secretType,
115118
Secret = secret,
116119
Resolution = SecretScanningAlert.ResolutionRevoked,
117-
ResolutionComment = resolutionComment
120+
ResolutionComment = resolutionComment,
121+
ResolverName = resolverName
118122
};
119123

120124
var sourceLocation = new GithubSecretScanningAlertLocation()
@@ -168,7 +172,7 @@ public async Task Secret_Updated_With_Comment()
168172
100,
169173
SecretScanningAlert.AlertStateResolved,
170174
SecretScanningAlert.ResolutionRevoked,
171-
resolutionComment)
175+
$"[@{sourceSecret.ResolverName}] {sourceSecret.ResolutionComment}")
172176
);
173177
}
174178

@@ -186,6 +190,8 @@ public async Task No_Matching_Location()
186190
SecretType = secretType,
187191
Secret = secret,
188192
Resolution = SecretScanningAlert.ResolutionRevoked,
193+
ResolutionComment = "This token was revoked during migration",
194+
ResolverName = "actor"
189195
};
190196

191197
var sourceLocation = new GithubSecretScanningAlertLocation()
@@ -256,6 +262,8 @@ public async Task No_Matching_Secret()
256262
SecretType = secretType,
257263
Secret = secret,
258264
Resolution = SecretScanningAlert.ResolutionRevoked,
265+
ResolutionComment = "This token was revoked during migration",
266+
ResolverName = "actor"
259267
};
260268

261269
var sourceLocation = new GithubSecretScanningAlertLocation()
@@ -375,6 +383,8 @@ public async Task Migrates_Multiple_Alerts()
375383
SecretType = secretType,
376384
Secret = secretOne,
377385
Resolution = SecretScanningAlert.ResolutionRevoked,
386+
ResolutionComment = "This token was revoked during migration",
387+
ResolverName = "actor"
378388
};
379389

380390
var sourceSecretTwo = new GithubSecretScanningAlert()
@@ -384,6 +394,8 @@ public async Task Migrates_Multiple_Alerts()
384394
SecretType = secretType,
385395
Secret = secretTwo,
386396
Resolution = SecretScanningAlert.ResolutionRevoked,
397+
ResolutionComment = "This token was revoked during migration",
398+
ResolverName = "actor"
387399
};
388400

389401
var sourceSecretThree = new GithubSecretScanningAlert()
@@ -393,6 +405,8 @@ public async Task Migrates_Multiple_Alerts()
393405
SecretType = secretType,
394406
Secret = secretThree,
395407
Resolution = SecretScanningAlert.ResolutionFalsePositive,
408+
ResolutionComment = "This token was revoked during migration",
409+
ResolverName = "actor"
396410
};
397411

398412
var sourceLocation = new GithubSecretScanningAlertLocation()
@@ -443,7 +457,7 @@ public async Task Migrates_Multiple_Alerts()
443457
100,
444458
SecretScanningAlert.AlertStateResolved,
445459
SecretScanningAlert.ResolutionRevoked,
446-
null)
460+
$"[@{sourceSecretOne.ResolverName}] {sourceSecretOne.ResolutionComment}")
447461
);
448462

449463
_mockTargetGithubApi.Verify(m => m.UpdateSecretScanningAlert(
@@ -452,7 +466,7 @@ public async Task Migrates_Multiple_Alerts()
452466
300,
453467
SecretScanningAlert.AlertStateResolved,
454468
SecretScanningAlert.ResolutionFalsePositive,
455-
null)
469+
$"[@{sourceSecretThree.ResolverName}] {sourceSecretThree.ResolutionComment}")
456470
);
457471
}
458472

@@ -687,6 +701,8 @@ public async Task Migrate_Matching_Alerts_With_Different_Resolutions()
687701
SecretType = secretType,
688702
Secret = secret,
689703
Resolution = SecretScanningAlert.ResolutionRevoked,
704+
ResolutionComment = "This token was revoked",
705+
ResolverName = "actor-source"
690706
};
691707

692708
var sourceLocation = new GithubSecretScanningAlertLocation()
@@ -712,6 +728,8 @@ public async Task Migrate_Matching_Alerts_With_Different_Resolutions()
712728
SecretType = secretType,
713729
Secret = secret,
714730
Resolution = SecretScanningAlert.ResolutionFalsePositive,
731+
ResolutionComment = "This token was resolved as false positive",
732+
ResolverName = "actor-target"
715733
};
716734

717735
var targetSecretLocation = new GithubSecretScanningAlertLocation()
@@ -740,7 +758,7 @@ public async Task Migrate_Matching_Alerts_With_Different_Resolutions()
740758
100,
741759
SecretScanningAlert.AlertStateResolved,
742760
SecretScanningAlert.ResolutionRevoked,
743-
null)
761+
$"[@{sourceSecret.ResolverName}] {sourceSecret.ResolutionComment}")
744762
);
745763
}
746764

@@ -901,7 +919,7 @@ public async Task Alerts_With_Extra_Fields_Are_Handled_Gracefully()
901919
100,
902920
SecretScanningAlert.AlertStateResolved,
903921
SecretScanningAlert.ResolutionRevoked,
904-
null)
922+
"[@] ")
905923
);
906924
}
907925

@@ -919,7 +937,8 @@ public async Task Pull_Request_Comment_Location_Is_Matched_And_Secret_Is_Updated
919937
SecretType = secretType,
920938
Secret = secret,
921939
Resolution = SecretScanningAlert.ResolutionRevoked,
922-
ResolutionComment = "This token was revoked during migration"
940+
ResolutionComment = "This token was revoked during migration",
941+
ResolverName = "actor"
923942
};
924943

925944
var sourceLocation = new GithubSecretScanningAlertLocation
@@ -966,7 +985,7 @@ public async Task Pull_Request_Comment_Location_Is_Matched_And_Secret_Is_Updated
966985
100,
967986
SecretScanningAlert.AlertStateResolved,
968987
SecretScanningAlert.ResolutionRevoked,
969-
"This token was revoked during migration")
988+
$"[@{sourceSecret.ResolverName}] {sourceSecret.ResolutionComment}")
970989
);
971990
}
972991

@@ -1056,7 +1075,7 @@ public async Task Pull_Request_Comment_And_Commit_Locations_Are_Both_Matched()
10561075
100,
10571076
SecretScanningAlert.AlertStateResolved,
10581077
SecretScanningAlert.ResolutionRevoked,
1059-
null)
1078+
"[@] ")
10601079
);
10611080
}
10621081

@@ -1137,7 +1156,8 @@ public async Task Multiple_Pull_Request_Related_Location_Types_Are_Matched()
11371156
SecretType = secretType,
11381157
Secret = secret,
11391158
Resolution = SecretScanningAlert.ResolutionFalsePositive,
1140-
ResolutionComment = "This is a test token"
1159+
ResolutionComment = "This is a test token",
1160+
ResolverName = "actor"
11411161
};
11421162

11431163
var sourceLocations = new[]
@@ -1214,7 +1234,47 @@ public async Task Multiple_Pull_Request_Related_Location_Types_Are_Matched()
12141234
100,
12151235
SecretScanningAlert.AlertStateResolved,
12161236
SecretScanningAlert.ResolutionFalsePositive,
1217-
"This is a test token")
1237+
$"[@{sourceSecret.ResolverName}] {sourceSecret.ResolutionComment}")
1238+
);
1239+
}
1240+
1241+
[Fact]
1242+
public async Task Update_When_No_ResolutionComment_Still_Includes_ResolverName_Prefix()
1243+
{
1244+
// Arrange
1245+
var source = new GithubSecretScanningAlert
1246+
{
1247+
Number = 1,
1248+
State = SecretScanningAlert.AlertStateResolved,
1249+
SecretType = "foo",
1250+
Secret = "bar",
1251+
Resolution = SecretScanningAlert.ResolutionRevoked,
1252+
ResolverName = "actor",
1253+
ResolutionComment = null
1254+
};
1255+
var srcLoc = new GithubSecretScanningAlertLocation { LocationType = "commit", Path = "f", StartLine = 1, EndLine = 1, StartColumn = 1, EndColumn = 1, BlobSha = "x" };
1256+
_mockSourceGithubApi
1257+
.Setup(x => x.GetSecretScanningAlertsForRepository(SOURCE_ORG, SOURCE_REPO)).ReturnsAsync(new[] { source });
1258+
_mockSourceGithubApi
1259+
.Setup(x => x.GetSecretScanningAlertsLocations(SOURCE_ORG, SOURCE_REPO, 1)).ReturnsAsync(new[] { srcLoc });
1260+
1261+
var tgt = new GithubSecretScanningAlert { Number = 42, State = SecretScanningAlert.AlertStateOpen, SecretType = "foo", Secret = "bar" };
1262+
_mockTargetGithubApi
1263+
.Setup(x => x.GetSecretScanningAlertsForRepository(TARGET_ORG, TARGET_REPO)).ReturnsAsync(new[] { tgt });
1264+
_mockTargetGithubApi
1265+
.Setup(x => x.GetSecretScanningAlertsLocations(TARGET_ORG, TARGET_REPO, 42)).ReturnsAsync(new[] { srcLoc });
1266+
1267+
// Act
1268+
await _service.MigrateSecretScanningAlerts(SOURCE_ORG, SOURCE_REPO, TARGET_ORG, TARGET_REPO, false);
1269+
1270+
// Assert
1271+
_mockTargetGithubApi.Verify(m => m.UpdateSecretScanningAlert(
1272+
TARGET_ORG,
1273+
TARGET_REPO,
1274+
42,
1275+
SecretScanningAlert.AlertStateResolved,
1276+
SecretScanningAlert.ResolutionRevoked,
1277+
"[@actor] ") // even though comment was null
12181278
);
12191279
}
12201280
}

0 commit comments

Comments
 (0)