Skip to content

Commit 2a59775

Browse files
Implement sarif2.1 support
1 parent 439ad99 commit 2a59775

13 files changed

+328
-119
lines changed

CodeQualityToGitlab/CodeQualityToGitlab.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@
2525

2626
<ItemGroup>
2727
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="8.0.0" />
28-
<PackageReference Include="PolySharp" Version="1.13.2">
28+
<PackageReference Include="PolySharp" Version="1.14.1">
2929
<PrivateAssets>all</PrivateAssets>
3030
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3131
</PackageReference>
32-
<PackageReference Include="Sarif.Sdk" Version="4.3.7" />
32+
<PackageReference Include="Sarif.Sdk" Version="4.4.0" />
3333
<PackageReference Include="Serilog" Version="3.1.1" />
34-
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
34+
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
3535
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
3636
</ItemGroup>
3737

CodeQualityToGitlab/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.CommandLine;
2+
using CodeQualityToGitlab.SarifConverters;
23
using Serilog;
34

45
namespace CodeQualityToGitlab;

CodeQualityToGitlab/Roslynator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class Diagnostic
3131
public class Summary
3232
{
3333
[XmlElement(ElementName = "Diagnostic")]
34-
public required List<Diagnostic> Diagnostic { get; set; } = new();
34+
public required List<Diagnostic> Diagnostic { get; set; } = [];
3535
}
3636

3737
[XmlRoot(ElementName = "Location")]
@@ -48,7 +48,7 @@ public class Location
4848
public class Diagnostics
4949
{
5050
[XmlElement(ElementName = "Diagnostic")]
51-
public List<Diagnostic> Diagnostic { get; set; } = new();
51+
public List<Diagnostic> Diagnostic { get; set; } = [];
5252
}
5353

5454
[XmlRoot(ElementName = "Project")]
@@ -68,7 +68,7 @@ public class Project
6868
public class Projects
6969
{
7070
[XmlElement(ElementName = "Project")]
71-
public List<Project> Project { get; set; } = new();
71+
public List<Project> Project { get; set; } = [];
7272
}
7373

7474
[XmlRoot(ElementName = "CodeAnalysis")]

CodeQualityToGitlab/RoslynatorConverter.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ public static List<CodeQuality> ConvertToCodeQualityRaw(FileInfo source, string?
2626
{
2727
Description = $"{diagnostic.Id}: {diagnostic.Message}",
2828
Severity = GetSeverity(diagnostic.Severity),
29-
Location = new LocationCq
29+
Location = new()
3030
{
3131
Path = GetPath(diagnostic, project, pathRoot),
32-
Lines = new Lines { Begin = lineNumber }
32+
Lines = new()
33+
{ Begin = lineNumber }
3334
},
3435
Fingerprint = Common.GetHash($"{project.Name}{diagnostic.Id}{lineNumber}")
3536
};

CodeQualityToGitlab/SarifConverter.cs

Lines changed: 0 additions & 107 deletions
This file was deleted.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using Microsoft.CodeAnalysis.Sarif.Readers;
2+
using Microsoft.CodeAnalysis.Sarif.VersionOne;
3+
using Newtonsoft.Json;
4+
using Serilog;
5+
6+
namespace CodeQualityToGitlab.SarifConverters;
7+
8+
public class Converter1(FileInfo source, string? pathRoot)
9+
{
10+
public List<CodeQuality> Convert()
11+
{
12+
Log.Information("Sarif Version 1 detected");
13+
14+
var logContents = File.ReadAllText(source.FullName);
15+
16+
var settings = new JsonSerializerSettings
17+
{
18+
ContractResolver = SarifContractResolverVersionOne.Instance
19+
};
20+
21+
var log = JsonConvert.DeserializeObject<SarifLogVersionOne>(logContents, settings);
22+
23+
var results = log.Runs
24+
.SelectMany(x => x.Results)
25+
.Where(r => r.SuppressionStates == SuppressionStatesVersionOne.None);
26+
27+
var cqrs = new List<CodeQuality>();
28+
foreach (var result in results)
29+
{
30+
var begin = result.Locations?.FirstOrDefault();
31+
32+
if (begin == null)
33+
{
34+
Log.Warning("An issue has no location, skipping: {Result}", result.Message);
35+
continue;
36+
}
37+
38+
try
39+
{
40+
var cqr = new CodeQuality
41+
{
42+
Description = $"{result.RuleId}: {result.Message}",
43+
Severity = GetSeverity(result.Level),
44+
Location = new()
45+
{
46+
Path = GetPath(pathRoot, begin),
47+
Lines = new()
48+
{ Begin = begin.ResultFile.Region.StartLine }
49+
},
50+
Fingerprint = Common.GetHash(
51+
$"{result.RuleId}|{begin.ResultFile.Uri}|{begin.ResultFile.Region.StartLine}"
52+
)
53+
};
54+
cqrs.Add(cqr);
55+
}
56+
catch (Exception e)
57+
{
58+
Log.Error(e, "Could not convert {@Result}, skipping", result);
59+
}
60+
}
61+
62+
return cqrs;
63+
}
64+
65+
private static string GetPath(string? pathRoot, LocationVersionOne begin)
66+
{
67+
// nullability says Uri is always set, but there are tools which omit this.
68+
if (begin.ResultFile.Uri == null)
69+
{
70+
Log.Error(
71+
"There is no valid Path for the issue {@Region}, cannot create a path. Check the source sarif for missing physicalLocation.uri",
72+
begin.ResultFile.Region
73+
);
74+
return "noPathInSourceSarif";
75+
}
76+
77+
if (!begin.ResultFile.Uri!.IsAbsoluteUri)
78+
{
79+
return begin.ResultFile.Uri.ToString();
80+
}
81+
82+
if (string.IsNullOrWhiteSpace(pathRoot))
83+
{
84+
return begin.ResultFile.Uri.LocalPath.Replace("//", "\\");
85+
}
86+
var uri = new Uri(pathRoot);
87+
return uri.MakeRelativeUri(begin.ResultFile.Uri).ToString().Replace("//", "\\");
88+
}
89+
90+
private static Severity GetSeverity(ResultLevelVersionOne resultLevel)
91+
{
92+
return resultLevel switch
93+
{
94+
ResultLevelVersionOne.NotApplicable => Severity.minor,
95+
ResultLevelVersionOne.Pass => Severity.minor,
96+
ResultLevelVersionOne.Note => Severity.minor,
97+
ResultLevelVersionOne.Warning => Severity.major,
98+
ResultLevelVersionOne.Default => Severity.major,
99+
ResultLevelVersionOne.Error => Severity.blocker,
100+
_ => throw new ArgumentOutOfRangeException(nameof(resultLevel), resultLevel, null)
101+
};
102+
}
103+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using Microsoft.CodeAnalysis.Sarif;
2+
using Serilog;
3+
4+
namespace CodeQualityToGitlab.SarifConverters;
5+
6+
public class Converter2(FileInfo source, string? pathRoot)
7+
{
8+
public List<CodeQuality> Convert()
9+
{
10+
Log.Information("Sarif Version 2 detected");
11+
12+
var log = SarifLog.Load(source.FullName);
13+
14+
var results = log.Runs
15+
.SelectMany(x => x.Results)
16+
.Where(r => r.Suppressions == null || r.Suppressions.Any());
17+
18+
var cqrs = new List<CodeQuality>();
19+
foreach (var result in results)
20+
{
21+
var begin = result.Locations?.FirstOrDefault();
22+
23+
if (begin == null)
24+
{
25+
Log.Warning("An issue has no location, skipping: {@Result}", result.Message);
26+
continue;
27+
}
28+
29+
try
30+
{
31+
var startLine = begin.PhysicalLocation.Region.StartLine;
32+
var cqr = new CodeQuality
33+
{
34+
Description = $"{result.RuleId}: {result.Message}",
35+
Severity = GetSeverity(result.Level),
36+
Location = new()
37+
{
38+
Path = GetPath(pathRoot, begin),
39+
Lines = new()
40+
{ Begin = startLine }
41+
},
42+
Fingerprint = Common.GetHash(
43+
$"{result.RuleId}|{begin.PhysicalLocation.ArtifactLocation.Uri}|{startLine}"
44+
)
45+
};
46+
cqrs.Add(cqr);
47+
}
48+
catch (Exception e)
49+
{
50+
Log.Error(e, "Could not convert {@Result}, skipping", result);
51+
}
52+
}
53+
54+
return cqrs;
55+
}
56+
57+
private static string GetPath(string? pathRoot, Microsoft.CodeAnalysis.Sarif.Location begin)
58+
{
59+
// nullability says Uri is always set, but there are tools which omit this.
60+
var artifactLocationUri = begin.PhysicalLocation.ArtifactLocation.Uri;
61+
if (artifactLocationUri == null)
62+
{
63+
Log.Error(
64+
"There is no valid Path for the issue {@Region}, cannot create a path. Check the source sarif for missing physicalLocation.ArtifactLocation.uri",
65+
begin.PhysicalLocation.ArtifactLocation
66+
);
67+
return "noPathInSourceSarif";
68+
}
69+
70+
if (!artifactLocationUri.IsAbsoluteUri)
71+
{
72+
return artifactLocationUri.ToString();
73+
}
74+
75+
if (string.IsNullOrWhiteSpace(pathRoot))
76+
{
77+
return artifactLocationUri.LocalPath.Replace("//", "\\");
78+
}
79+
var uri = new Uri(pathRoot);
80+
return uri.MakeRelativeUri(artifactLocationUri).ToString().Replace("//", "\\");
81+
}
82+
83+
private static Severity GetSeverity(FailureLevel resultLevel)
84+
{
85+
return resultLevel switch
86+
{
87+
FailureLevel.None => Severity.minor,
88+
FailureLevel.Note => Severity.minor,
89+
FailureLevel.Warning => Severity.major,
90+
FailureLevel.Error => Severity.blocker,
91+
_ => throw new ArgumentOutOfRangeException(nameof(resultLevel), resultLevel, null)
92+
};
93+
}
94+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace CodeQualityToGitlab.SarifConverters;
2+
3+
public static class SarifConverter
4+
{
5+
public static List<CodeQuality> ConvertToCodeQualityRaw(FileInfo source, string? pathRoot)
6+
{
7+
var logContents = File.ReadAllText(source.FullName);
8+
9+
return logContents.Contains(" \"$schema\": \"http://json.schemastore.org/sarif-2") ? new Converter2(source, pathRoot).Convert() : new Converter1(source, pathRoot).Convert();
10+
}
11+
12+
public static void ConvertToCodeQuality(FileInfo source, FileInfo target, string? pathRoot = null)
13+
{
14+
var cqrs = ConvertToCodeQualityRaw(source, pathRoot);
15+
Common.WriteToDisk(target, cqrs);
16+
}
17+
}

CodeQualityToGitlab/Transform.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using CodeQualityToGitlab.SarifConverters;
12
using Microsoft.Extensions.FileSystemGlobbing;
23
using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
34
using Serilog;

0 commit comments

Comments
 (0)