Skip to content

Commit 2ecde67

Browse files
authored
Add RubyGems, Go, Cargo, CocoaPods, Conda, Maven support to Linux scanner (#1632)
1 parent f1927df commit 2ecde67

17 files changed

+837
-44
lines changed

src/Microsoft.ComponentDetection.Detectors/linux/Factories/ArtifactComponentFactoryBase.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
1212
/// </summary>
1313
public abstract class ArtifactComponentFactoryBase : IArtifactComponentFactory
1414
{
15+
/// <inheritdoc/>
16+
public abstract ComponentType SupportedComponentType { get; }
17+
1518
/// <inheritdoc/>
1619
public abstract IEnumerable<string> SupportedArtifactTypes { get; }
1720

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#nullable disable
2+
namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
3+
4+
using System.Collections.Generic;
5+
using Microsoft.ComponentDetection.Contracts.TypedComponent;
6+
using Microsoft.ComponentDetection.Detectors.Linux.Contracts;
7+
8+
/// <summary>
9+
/// Factory for creating <see cref="CargoComponent"/> instances from Rust crate artifacts.
10+
/// </summary>
11+
public class CargoComponentFactory : ArtifactComponentFactoryBase
12+
{
13+
/// <inheritdoc/>
14+
public override ComponentType SupportedComponentType => ComponentType.Cargo;
15+
16+
/// <inheritdoc/>
17+
public override IEnumerable<string> SupportedArtifactTypes => ["rust-crate"];
18+
19+
/// <inheritdoc/>
20+
public override TypedComponent CreateComponent(ArtifactElement artifact, Distro distro)
21+
{
22+
if (artifact == null)
23+
{
24+
return null;
25+
}
26+
27+
if (string.IsNullOrWhiteSpace(artifact.Name) || string.IsNullOrWhiteSpace(artifact.Version))
28+
{
29+
return null;
30+
}
31+
32+
var author = GetAuthorFromArtifact(artifact);
33+
var license = GetLicenseFromArtifact(artifact);
34+
35+
// Syft provides the source in metadata.Source
36+
var source = artifact.Metadata?.Source?.String;
37+
38+
return new CargoComponent(
39+
name: artifact.Name,
40+
version: artifact.Version,
41+
author: author,
42+
license: license,
43+
source: source
44+
);
45+
}
46+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#nullable disable
2+
namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
3+
4+
using System.Collections.Generic;
5+
using Microsoft.ComponentDetection.Contracts.TypedComponent;
6+
using Microsoft.ComponentDetection.Detectors.Linux.Contracts;
7+
8+
/// <summary>
9+
/// Factory for creating <see cref="CondaComponent"/> instances from Conda package artifacts.
10+
/// </summary>
11+
public class CondaComponentFactory : ArtifactComponentFactoryBase
12+
{
13+
/// <inheritdoc/>
14+
public override ComponentType SupportedComponentType => ComponentType.Conda;
15+
16+
/// <inheritdoc/>
17+
public override IEnumerable<string> SupportedArtifactTypes => ["conda"];
18+
19+
/// <inheritdoc/>
20+
public override TypedComponent CreateComponent(ArtifactElement artifact, Distro distro)
21+
{
22+
if (artifact == null)
23+
{
24+
return null;
25+
}
26+
27+
if (string.IsNullOrWhiteSpace(artifact.Name) || string.IsNullOrWhiteSpace(artifact.Version))
28+
{
29+
return null;
30+
}
31+
32+
// Syft provides conda metadata including build, channel, subdir, url, and md5
33+
var metadata = artifact.Metadata;
34+
35+
return new CondaComponent(
36+
name: artifact.Name,
37+
version: artifact.Version,
38+
build: metadata?.Build,
39+
channel: metadata?.Channel,
40+
subdir: metadata?.Subdir,
41+
@namespace: null, // Syft doesn't provide namespace
42+
url: metadata?.Url?.String,
43+
md5: metadata?.Md5
44+
);
45+
}
46+
}

src/Microsoft.ComponentDetection.Detectors/linux/Factories/DotnetComponentFactory.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
1010
/// </summary>
1111
public class DotnetComponentFactory : ArtifactComponentFactoryBase
1212
{
13+
/// <inheritdoc/>
14+
public override ComponentType SupportedComponentType => ComponentType.NuGet;
15+
1316
/// <inheritdoc/>
1417
public override IEnumerable<string> SupportedArtifactTypes => ["dotnet"];
1518

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#nullable disable
2+
namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
3+
4+
using System.Collections.Generic;
5+
using Microsoft.ComponentDetection.Contracts.TypedComponent;
6+
using Microsoft.ComponentDetection.Detectors.Linux.Contracts;
7+
8+
/// <summary>
9+
/// Factory for creating <see cref="GoComponent"/> instances from Go module artifacts.
10+
/// </summary>
11+
public class GoComponentFactory : ArtifactComponentFactoryBase
12+
{
13+
/// <inheritdoc/>
14+
public override ComponentType SupportedComponentType => ComponentType.Go;
15+
16+
/// <inheritdoc/>
17+
public override IEnumerable<string> SupportedArtifactTypes => ["go-module"];
18+
19+
/// <inheritdoc/>
20+
public override TypedComponent CreateComponent(ArtifactElement artifact, Distro distro)
21+
{
22+
if (artifact == null)
23+
{
24+
return null;
25+
}
26+
27+
if (string.IsNullOrWhiteSpace(artifact.Name) || string.IsNullOrWhiteSpace(artifact.Version))
28+
{
29+
return null;
30+
}
31+
32+
// Syft provides the h1 digest hash in metadata.H1Digest
33+
var hash = artifact.Metadata?.H1Digest;
34+
35+
if (!string.IsNullOrWhiteSpace(hash))
36+
{
37+
return new GoComponent(name: artifact.Name, version: artifact.Version, hash: hash);
38+
}
39+
40+
return new GoComponent(name: artifact.Name, version: artifact.Version);
41+
}
42+
}

src/Microsoft.ComponentDetection.Detectors/linux/Factories/IArtifactComponentFactory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
1010
/// </summary>
1111
public interface IArtifactComponentFactory
1212
{
13+
/// <summary>
14+
/// Gets the component type that this factory produces.
15+
/// </summary>
16+
public ComponentType SupportedComponentType { get; }
17+
1318
/// <summary>
1419
/// Gets the artifact types (e.g., "npm", "apk", "deb") that this factory can handle.
1520
/// </summary>

src/Microsoft.ComponentDetection.Detectors/linux/Factories/LinuxComponentFactory.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
1010
/// </summary>
1111
public class LinuxComponentFactory : ArtifactComponentFactoryBase
1212
{
13+
/// <inheritdoc/>
14+
public override ComponentType SupportedComponentType => ComponentType.Linux;
15+
1316
/// <inheritdoc/>
1417
public override IEnumerable<string> SupportedArtifactTypes => ["apk", "deb", "rpm"];
1518

@@ -35,6 +38,7 @@ public override TypedComponent CreateComponent(ArtifactElement artifact, Distro
3538
name: artifact.Name,
3639
version: artifact.Version,
3740
license: license,
38-
author: supplier);
41+
author: supplier
42+
);
3943
}
4044
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#nullable disable
2+
namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
3+
4+
using System.Collections.Generic;
5+
using Microsoft.ComponentDetection.Contracts.TypedComponent;
6+
using Microsoft.ComponentDetection.Detectors.Linux.Contracts;
7+
8+
/// <summary>
9+
/// Factory for creating <see cref="MavenComponent"/> instances from Java archive artifacts.
10+
/// </summary>
11+
public class MavenComponentFactory : ArtifactComponentFactoryBase
12+
{
13+
/// <inheritdoc/>
14+
public override ComponentType SupportedComponentType => ComponentType.Maven;
15+
16+
/// <inheritdoc/>
17+
public override IEnumerable<string> SupportedArtifactTypes => ["java-archive"];
18+
19+
/// <inheritdoc/>
20+
public override TypedComponent CreateComponent(ArtifactElement artifact, Distro distro)
21+
{
22+
if (artifact == null)
23+
{
24+
return null;
25+
}
26+
27+
if (string.IsNullOrWhiteSpace(artifact.Version))
28+
{
29+
return null;
30+
}
31+
32+
// Syft provides Maven coordinates in pomProperties or pomProject
33+
var pomProperties = artifact.Metadata?.PomProperties;
34+
var pomProject = artifact.Metadata?.PomProject;
35+
36+
// Try pomProperties first (more reliable for resolved dependencies)
37+
var groupId = pomProperties?.GroupId ?? pomProject?.GroupId;
38+
var artifactId = pomProperties?.ArtifactId ?? pomProject?.ArtifactId;
39+
40+
// Fall back to artifact name if no pom metadata available
41+
// Syft uses the format "groupId:artifactId" or just the jar name
42+
if (string.IsNullOrWhiteSpace(groupId) || string.IsNullOrWhiteSpace(artifactId))
43+
{
44+
if (!TryParseFromName(artifact.Name, out groupId, out artifactId))
45+
{
46+
// Cannot determine Maven coordinates
47+
return null;
48+
}
49+
}
50+
51+
return new MavenComponent(
52+
groupId: groupId,
53+
artifactId: artifactId,
54+
version: artifact.Version
55+
);
56+
}
57+
58+
/// <summary>
59+
/// Attempts to parse groupId and artifactId from a name in the format "groupId:artifactId"
60+
/// or similar Maven coordinate notation.
61+
/// </summary>
62+
private static bool TryParseFromName(string name, out string groupId, out string artifactId)
63+
{
64+
groupId = null;
65+
artifactId = null;
66+
67+
if (string.IsNullOrWhiteSpace(name))
68+
{
69+
return false;
70+
}
71+
72+
// Handle "groupId:artifactId" format
73+
var colonIndex = name.IndexOf(':');
74+
if (colonIndex > 0 && colonIndex < name.Length - 1)
75+
{
76+
groupId = name[..colonIndex];
77+
artifactId = name[(colonIndex + 1)..];
78+
return !string.IsNullOrWhiteSpace(groupId) && !string.IsNullOrWhiteSpace(artifactId);
79+
}
80+
81+
return false;
82+
}
83+
}

src/Microsoft.ComponentDetection.Detectors/linux/Factories/NpmComponentFactory.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
1111
/// </summary>
1212
public class NpmComponentFactory : ArtifactComponentFactoryBase
1313
{
14+
/// <inheritdoc/>
15+
public override ComponentType SupportedComponentType => ComponentType.Npm;
16+
1417
/// <inheritdoc/>
1518
public override IEnumerable<string> SupportedArtifactTypes => ["npm"];
1619

@@ -34,7 +37,8 @@ public override TypedComponent CreateComponent(ArtifactElement artifact, Distro
3437
name: artifact.Name,
3538
version: artifact.Version,
3639
hash: hash,
37-
author: author);
40+
author: author
41+
);
3842
}
3943

4044
private static NpmAuthor GetNpmAuthorFromArtifact(ArtifactElement artifact)

src/Microsoft.ComponentDetection.Detectors/linux/Factories/PipComponentFactory.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ namespace Microsoft.ComponentDetection.Detectors.Linux.Factories;
1010
/// </summary>
1111
public class PipComponentFactory : ArtifactComponentFactoryBase
1212
{
13+
/// <inheritdoc/>
14+
public override ComponentType SupportedComponentType => ComponentType.Pip;
15+
1316
/// <inheritdoc/>
1417
public override IEnumerable<string> SupportedArtifactTypes => ["python"];
1518

@@ -33,6 +36,7 @@ public override TypedComponent CreateComponent(ArtifactElement artifact, Distro
3336
name: artifact.Name,
3437
version: artifact.Version,
3538
author: author,
36-
license: license);
39+
license: license
40+
);
3741
}
3842
}

0 commit comments

Comments
 (0)