Skip to content

Commit a990db1

Browse files
Initial implementation of VcpkgDetector and VcpkgComponent (#52)
* Initial implementation of VcpkgDetector and VcpkgComponent * Fix warnings * Initial implementation of VcpkgDetector and VcpkgComponent * Fix warnings * Update src/Microsoft.ComponentDetection.Detectors/vcpkg/VcpkgComponentDetector.cs * Address PR comments. Add parsing for Annotations. * Use DateTime property for annotation object * Add tests for VcpkgComponentDetector * Satisfy format detector * Update src/Microsoft.ComponentDetection.Detectors/vcpkg/VcpkgComponentDetector.cs Co-authored-by: Greg Villicana <58237075+grvillic@users.noreply.github.com>
1 parent 9c00871 commit a990db1

10 files changed

Lines changed: 659 additions & 2 deletions

File tree

src/Microsoft.ComponentDetection.Contracts/DetectorClass.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@ public enum DetectorClass
3535

3636
/// <summary>Indicates a detector applies to Conda packages.</summary>
3737
Conda,
38-
38+
3939
/// <summary>Indicates a detector applies to SPDX files.</summary>
4040
Spdx,
41+
42+
/// <summary>Indicates a detector applies to Vcpkg packages.</summary>
43+
Vcpkg,
4144
}
4245
}

src/Microsoft.ComponentDetection.Contracts/TypedComponent/ComponentType.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ public enum ComponentType : byte
4444

4545
[EnumMember]
4646
Conda = 13,
47-
47+
4848
[EnumMember]
4949
Spdx = 14,
50+
51+
[EnumMember]
52+
Vcpkg = 15,
5053
}
5154
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using PackageUrl;
2+
3+
namespace Microsoft.ComponentDetection.Contracts.TypedComponent
4+
{
5+
public class VcpkgComponent : TypedComponent
6+
{
7+
private VcpkgComponent()
8+
{
9+
/* Reserved for deserialization */
10+
}
11+
12+
public VcpkgComponent(string spdxid, string name, string version, string triplet = null, string portVersion = null, string description = null, string downloadLocation = null)
13+
{
14+
SPDXID = ValidateRequiredInput(spdxid, nameof(SPDXID), nameof(ComponentType.Vcpkg));
15+
Name = ValidateRequiredInput(name, nameof(Name), nameof(ComponentType.Vcpkg));
16+
Version = version;
17+
PortVersion = portVersion;
18+
Triplet = triplet;
19+
Description = description;
20+
DownloadLocation = downloadLocation;
21+
}
22+
23+
public string SPDXID { get; set; }
24+
25+
public string Name { get; set; }
26+
27+
public string DownloadLocation { get; set; }
28+
29+
public string Triplet { get; set; }
30+
31+
public string Version { get; set; }
32+
33+
public string Description { get; set; }
34+
35+
public string PortVersion { get; set; }
36+
37+
public override ComponentType Type => ComponentType.Vcpkg;
38+
39+
public override string Id
40+
{
41+
get
42+
{
43+
if (PortVersion != null)
44+
{
45+
return $"{Name} {Version}#{PortVersion} - {Type}";
46+
}
47+
else
48+
{
49+
return $"{Name} {Version} - {Type}";
50+
}
51+
}
52+
}
53+
54+
public override PackageURL PackageUrl
55+
{
56+
get
57+
{
58+
if (PortVersion != null)
59+
{
60+
return new PackageURL($"pkg:vcpkg/{Name}@{Version}?port_version={PortVersion}");
61+
}
62+
else if (Version != null)
63+
{
64+
return new PackageURL($"pkg:vcpkg/{Name}@{Version}");
65+
}
66+
else
67+
{
68+
return new PackageURL($"pkg:vcpkg/{Name}");
69+
}
70+
}
71+
}
72+
}
73+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
3+
namespace Microsoft.ComponentDetection.Detectors.Vcpkg.Contracts
4+
{
5+
public class Annotation
6+
{
7+
public DateTime Date { get; set; }
8+
9+
public string Comment { get; set; }
10+
11+
public string Type { get; set; }
12+
13+
public string Annotator { get; set; }
14+
}
15+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Microsoft.ComponentDetection.Detectors.Vcpkg.Contracts
2+
{
3+
public class Package
4+
{
5+
public string SPDXID { get; set; }
6+
7+
public string VersionInfo { get; set; }
8+
9+
public string DownloadLocation { get; set; }
10+
11+
public string Filename { get; set; }
12+
13+
public string Homepage { get; set; }
14+
15+
public string Description { get; set; }
16+
17+
public string Name { get; set; }
18+
19+
public Annotation[] Annotations { get; set; }
20+
}
21+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Microsoft.ComponentDetection.Detectors.Vcpkg.Contracts
2+
{
3+
/// <summary>
4+
/// Matches a subset of https://raw.githubusercontent.com/spdx/spdx-spec/v2.2.1/schemas/spdx-schema.json.
5+
/// </summary>
6+
public class VcpkgSBOM
7+
{
8+
public Package[] Packages { get; set; }
9+
10+
public string Name { get; set; }
11+
}
12+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Composition;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text.RegularExpressions;
7+
using System.Threading.Tasks;
8+
using Microsoft.ComponentDetection.Common;
9+
using Microsoft.ComponentDetection.Common.Telemetry.Records;
10+
using Microsoft.ComponentDetection.Contracts;
11+
using Microsoft.ComponentDetection.Contracts.Internal;
12+
using Microsoft.ComponentDetection.Contracts.TypedComponent;
13+
using Microsoft.ComponentDetection.Detectors.Vcpkg.Contracts;
14+
using Newtonsoft.Json;
15+
16+
namespace Microsoft.ComponentDetection.Detectors.Vcpkg
17+
{
18+
[Export(typeof(IComponentDetector))]
19+
public class VcpkgComponentDetector : FileComponentDetector, IDefaultOffComponentDetector
20+
{
21+
[Import]
22+
public ICommandLineInvocationService CommandLineInvocationService { get; set; }
23+
24+
[Import]
25+
public IEnvironmentVariableService EnvVarService { get; set; }
26+
27+
public override string Id { get; } = "Vcpkg";
28+
29+
public override IEnumerable<string> Categories => new[] { Enum.GetName(typeof(DetectorClass), DetectorClass.Vcpkg) };
30+
31+
public override IList<string> SearchPatterns { get; } = new List<string> { "vcpkg.spdx.json" };
32+
33+
public override IEnumerable<ComponentType> SupportedComponentTypes { get; } = new[] { ComponentType.Vcpkg };
34+
35+
public override int Version => 1;
36+
37+
private HashSet<string> projectRoots = new HashSet<string>();
38+
39+
protected override async Task OnFileFound(ProcessRequest processRequest, IDictionary<string, string> detectorArgs)
40+
{
41+
var singleFileComponentRecorder = processRequest.SingleFileComponentRecorder;
42+
var file = processRequest.ComponentStream;
43+
44+
Logger.LogWarning($"vcpkg detector found {file}");
45+
46+
var projectRootDirectory = Directory.GetParent(file.Location);
47+
if (projectRoots.Any(path => projectRootDirectory.FullName.StartsWith(path)))
48+
{
49+
return;
50+
}
51+
52+
await ParseSpdxFile(singleFileComponentRecorder, file);
53+
}
54+
55+
private async Task ParseSpdxFile(
56+
ISingleFileComponentRecorder singleFileComponentRecorder,
57+
IComponentStream file)
58+
{
59+
using var reader = new StreamReader(file.Stream);
60+
VcpkgSBOM sbom;
61+
try
62+
{
63+
sbom = JsonConvert.DeserializeObject<VcpkgSBOM>(await reader.ReadToEndAsync());
64+
}
65+
catch (Exception)
66+
{
67+
return;
68+
}
69+
70+
if (sbom?.Packages == null)
71+
{
72+
return;
73+
}
74+
75+
foreach (var item in sbom.Packages)
76+
{
77+
try
78+
{
79+
if (string.IsNullOrEmpty(item.Name))
80+
{
81+
continue;
82+
}
83+
84+
Logger.LogWarning($"parsed package {item.Name}");
85+
if (item.SPDXID == "SPDXRef-port")
86+
{
87+
var split = item.VersionInfo.Split('#');
88+
var component = new VcpkgComponent(item.SPDXID, item.Name, split[0], portVersion: split.Length >= 2 ? split[1] : "0", downloadLocation: item.DownloadLocation);
89+
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(component));
90+
}
91+
else if (item.SPDXID == "SPDXRef-binary")
92+
{
93+
var split = item.Name.Split(':');
94+
var component = new VcpkgComponent(item.SPDXID, item.Name, item.VersionInfo, triplet: split[1], downloadLocation: item.DownloadLocation);
95+
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(component));
96+
}
97+
else if (item.SPDXID.StartsWith("SPDXRef-resource-"))
98+
{
99+
var dl = item.DownloadLocation;
100+
var split = dl.Split("#");
101+
var subpath = split.Length > 1 ? split[1] : null;
102+
dl = split.Length > 1 ? split[0] : dl;
103+
split = dl.Split("@");
104+
var version = split.Length > 1 ? split[1] : null;
105+
dl = split.Length > 1 ? split[0] : dl;
106+
107+
var component = new VcpkgComponent(item.SPDXID, item.Name, version, downloadLocation: dl);
108+
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(component));
109+
}
110+
}
111+
catch (Exception)
112+
{
113+
Logger.LogWarning($"failed while handling {item.Name}");
114+
}
115+
}
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)