Restructure solution layout by module
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,118 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "golang",
|
||||
"componentKey": "purl::pkg:golang/example.com/app@v1.2.3",
|
||||
"purl": "pkg:golang/example.com/app@v1.2.3",
|
||||
"name": "example.com/app",
|
||||
"version": "v1.2.3",
|
||||
"type": "golang",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"binaryPath": "app",
|
||||
"build.GOARCH": "amd64",
|
||||
"build.GOOS": "linux",
|
||||
"build.vcs": "git",
|
||||
"build.vcs.modified": "false",
|
||||
"build.vcs.revision": "1234567890abcdef1234567890abcdef12345678",
|
||||
"build.vcs.time": "2025-09-14T12:34:56Z",
|
||||
"go.version": "go1.22.5",
|
||||
"modulePath": "example.com/app",
|
||||
"modulePath.main": "example.com/app",
|
||||
"moduleSum": "h1:mainchecksum",
|
||||
"moduleVersion": "v1.2.3"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo.setting",
|
||||
"locator": "GOARCH",
|
||||
"value": "amd64"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo.setting",
|
||||
"locator": "GOOS",
|
||||
"value": "linux"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo.setting",
|
||||
"locator": "vcs.modified",
|
||||
"value": "false"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo.setting",
|
||||
"locator": "vcs.revision",
|
||||
"value": "1234567890abcdef1234567890abcdef12345678"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo.setting",
|
||||
"locator": "vcs.time",
|
||||
"value": "2025-09-14T12:34:56Z"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo.setting",
|
||||
"locator": "vcs",
|
||||
"value": "git"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo",
|
||||
"locator": "module:example.com/app",
|
||||
"value": "v1.2.3",
|
||||
"sha256": "h1:mainchecksum"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.dwarf",
|
||||
"locator": "vcs.modified",
|
||||
"value": "false"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.dwarf",
|
||||
"locator": "vcs.revision",
|
||||
"value": "1234567890abcdef1234567890abcdef12345678"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.dwarf",
|
||||
"locator": "vcs.time",
|
||||
"value": "2025-09-14T12:34:56Z"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.dwarf",
|
||||
"locator": "vcs",
|
||||
"value": "git"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "golang",
|
||||
"componentKey": "purl::pkg:golang/example.com/lib@v1.0.0",
|
||||
"purl": "pkg:golang/example.com/lib@v1.0.0",
|
||||
"name": "example.com/lib",
|
||||
"version": "v1.0.0",
|
||||
"type": "golang",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"binaryPath": "app",
|
||||
"modulePath": "example.com/lib",
|
||||
"moduleSum": "h1:depchecksum",
|
||||
"moduleVersion": "v1.0.0"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo",
|
||||
"locator": "module:example.com/lib",
|
||||
"value": "v1.0.0",
|
||||
"sha256": "h1:depchecksum"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "golang",
|
||||
"componentKey": "purl::pkg:golang/example.com/app@v0.0.0",
|
||||
"purl": "pkg:golang/example.com/app@v0.0.0",
|
||||
"name": "example.com/app",
|
||||
"version": "v0.0.0",
|
||||
"type": "golang",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"binaryPath": "app",
|
||||
"build.vcs": "git",
|
||||
"build.vcs.modified": "true",
|
||||
"build.vcs.revision": "abcdef0123456789abcdef0123456789abcdef01",
|
||||
"build.vcs.time": "2025-01-02T03:04:05Z",
|
||||
"go.version": "go1.20.3",
|
||||
"modulePath": "example.com/app",
|
||||
"modulePath.main": "example.com/app",
|
||||
"moduleSum": "h1:dwarfchecksum",
|
||||
"moduleVersion": "v0.0.0"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo",
|
||||
"locator": "module:example.com/app",
|
||||
"value": "v0.0.0",
|
||||
"sha256": "h1:dwarfchecksum"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.dwarf",
|
||||
"locator": "vcs.modified",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.dwarf",
|
||||
"locator": "vcs.revision",
|
||||
"value": "abcdef0123456789abcdef0123456789abcdef01"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.dwarf",
|
||||
"locator": "vcs.time",
|
||||
"value": "2025-01-02T03:04:05Z"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.dwarf",
|
||||
"locator": "vcs",
|
||||
"value": "git"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "golang",
|
||||
"componentKey": "purl::pkg:golang/example.com/lib@v0.1.0",
|
||||
"purl": "pkg:golang/example.com/lib@v0.1.0",
|
||||
"name": "example.com/lib",
|
||||
"version": "v0.1.0",
|
||||
"type": "golang",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"binaryPath": "app",
|
||||
"modulePath": "example.com/lib",
|
||||
"moduleSum": "h1:libchecksum",
|
||||
"moduleVersion": "v0.1.0"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.buildinfo",
|
||||
"locator": "module:example.com/lib",
|
||||
"value": "v0.1.0",
|
||||
"sha256": "h1:libchecksum"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
MZfakebinaryheader
|
||||
Go build ID: "random-go-build-id"
|
||||
....gopclntab....
|
||||
runtime.buildVersion=go1.22.8
|
||||
padding0000000000000000000000000000000000000000000000000000000000000000
|
||||
@@ -0,0 +1,30 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "golang",
|
||||
"componentKey": "golang::bin::sha256:7125d65230b913faa744a33acd884899c81a1dbc6d88cbf251a74b19621cde99",
|
||||
"name": "app",
|
||||
"type": "bin",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"binary.sha256": "7125d65230b913faa744a33acd884899c81a1dbc6d88cbf251a74b19621cde99",
|
||||
"binaryPath": "app",
|
||||
"go.version.hint": "go1.22.8",
|
||||
"languageHint": "golang",
|
||||
"provenance": "binary"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "binary",
|
||||
"locator": "app",
|
||||
"sha256": "7125d65230b913faa744a33acd884899c81a1dbc6d88cbf251a74b19621cde99"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "go.heuristic",
|
||||
"locator": "classification",
|
||||
"value": "build-id"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Go;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Go.Tests;
|
||||
|
||||
public sealed class GoLanguageAnalyzerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task BuildInfoFixtureProducesDeterministicOutputAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "go", "basic");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new GoLanguageAnalyzer(),
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DwarfOnlyFixtureFallsBackToMetadataAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "go", "dwarf-only");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new GoLanguageAnalyzer(),
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StrippedBinaryFallsBackToHeuristicBinHashAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "go", "stripped");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new GoLanguageAnalyzer(),
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParallelRunsRemainDeterministicAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "go", "basic");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new GoLanguageAnalyzer(),
|
||||
};
|
||||
|
||||
var tasks = Enumerable
|
||||
.Range(0, Environment.ProcessorCount)
|
||||
.Select(_ => LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken));
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HeuristicMetricCounterIncrementsAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "go", "stripped");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new GoLanguageAnalyzer(),
|
||||
};
|
||||
|
||||
var total = 0L;
|
||||
|
||||
using var listener = new MeterListener
|
||||
{
|
||||
InstrumentPublished = (instrument, meterListener) =>
|
||||
{
|
||||
if (instrument.Meter.Name == "StellaOps.Scanner.Analyzers.Lang.Go"
|
||||
&& instrument.Name == "scanner_analyzer_golang_heuristic_total")
|
||||
{
|
||||
meterListener.EnableMeasurementEvents(instrument);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
listener.SetMeasurementEventCallback<long>((_, measurement, _, _) =>
|
||||
{
|
||||
Interlocked.Add(ref total, measurement);
|
||||
});
|
||||
|
||||
listener.Start();
|
||||
|
||||
await LanguageAnalyzerTestHarness.RunToJsonAsync(
|
||||
fixturePath,
|
||||
analyzers,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
listener.Dispose();
|
||||
|
||||
Assert.Equal(1, Interlocked.Read(ref total));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Remove="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Remove="xunit" />
|
||||
<PackageReference Remove="xunit.runner.visualstudio" />
|
||||
<PackageReference Remove="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Remove="Mongo2Go" />
|
||||
<PackageReference Remove="coverlet.collector" />
|
||||
<PackageReference Remove="Microsoft.Extensions.TimeProvider.Testing" />
|
||||
<ProjectReference Remove="..\StellaOps.Concelier.Testing\StellaOps.Concelier.Testing.csproj" />
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\AssemblyInfo.cs" />
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\MongoFixtureCollection.cs" />
|
||||
<Using Remove="StellaOps.Concelier.Testing" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="xunit.v3" Version="3.0.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Scanner.Analyzers.Lang.Tests\StellaOps.Scanner.Analyzers.Lang.Tests.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang/StellaOps.Scanner.Analyzers.Lang.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang.Go/StellaOps.Scanner.Analyzers.Lang.Go.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Core/StellaOps.Scanner.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Fixtures\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,35 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "java",
|
||||
"componentKey": "purl::pkg:maven/com/example/demo@1.0.0",
|
||||
"purl": "pkg:maven/com/example/demo@1.0.0",
|
||||
"name": "demo",
|
||||
"version": "1.0.0",
|
||||
"type": "maven",
|
||||
"usedByEntrypoint": true,
|
||||
"metadata": {
|
||||
"artifactId": "demo",
|
||||
"displayName": "Demo Library",
|
||||
"groupId": "com.example",
|
||||
"jarPath": "libs/demo.jar",
|
||||
"manifestTitle": "Demo",
|
||||
"manifestVendor": "Example Corp",
|
||||
"manifestVersion": "1.0.0",
|
||||
"packaging": "jar"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "MANIFEST.MF",
|
||||
"locator": "libs/demo.jar!META-INF/MANIFEST.MF",
|
||||
"value": "title=Demo;version=1.0.0;vendor=Example Corp"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "pom.properties",
|
||||
"locator": "libs/demo.jar!META-INF/maven/com.example/demo/pom.properties",
|
||||
"sha256": "c20f36aa1b9d89d28cf9ed131519ffd6287a4dac0c7cb926130496f3f8157bf1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,172 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.ClassPath;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests;
|
||||
|
||||
public sealed class JavaClassPathBuilderTests
|
||||
{
|
||||
[Fact]
|
||||
public void Build_ClassPathForSimpleJar()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
JavaFixtureBuilder.CreateSampleJar(root, "libs/simple.jar");
|
||||
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, CancellationToken.None);
|
||||
var analysis = JavaClassPathBuilder.Build(workspace, CancellationToken.None);
|
||||
|
||||
var segment = Assert.Single(analysis.Segments);
|
||||
Assert.Equal("libs/simple.jar", segment.Identifier.Replace('\\', '/'));
|
||||
Assert.Contains("com.example.Demo", segment.Classes);
|
||||
|
||||
var package = Assert.Single(segment.Packages);
|
||||
Assert.Equal("com.example", package.Key);
|
||||
Assert.Equal(1, package.Value.ClassCount);
|
||||
|
||||
Assert.Empty(analysis.DuplicateClasses);
|
||||
Assert.Empty(analysis.SplitPackages);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_CapturesServiceDefinitions()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
var services = new Dictionary<string, string[]>
|
||||
{
|
||||
["java.sql.Driver"] = new[] { "com.example.DriverImpl" },
|
||||
};
|
||||
|
||||
CreateJarWithClasses(root, "libs/spi.jar", new[] { "com.example.DriverImpl" }, services);
|
||||
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, CancellationToken.None);
|
||||
var analysis = JavaClassPathBuilder.Build(workspace, CancellationToken.None);
|
||||
|
||||
var segment = Assert.Single(analysis.Segments);
|
||||
var providers = Assert.Single(segment.ServiceDefinitions);
|
||||
Assert.Equal("java.sql.Driver", providers.Key);
|
||||
Assert.Contains("com.example.DriverImpl", providers.Value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_FatJarIncludesNestedLibraries()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
JavaFixtureBuilder.CreateSpringBootFatJar(root, "apps/app-fat.jar");
|
||||
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, CancellationToken.None);
|
||||
var analysis = JavaClassPathBuilder.Build(workspace, CancellationToken.None);
|
||||
|
||||
Assert.Equal(2, analysis.Segments.Length);
|
||||
|
||||
var classesSegment = analysis.Segments[0];
|
||||
Assert.Equal("apps/app-fat.jar!BOOT-INF/classes/", classesSegment.Identifier.Replace('\\', '/'));
|
||||
Assert.Contains("com.example.App", classesSegment.Classes);
|
||||
|
||||
var librarySegment = analysis.Segments[1];
|
||||
Assert.Equal("apps/app-fat.jar!BOOT-INF/lib/library.jar", librarySegment.Identifier.Replace('\\', '/'));
|
||||
Assert.Contains("com.example.Lib", librarySegment.Classes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_ReportsDuplicateClassesAndSplitPackages()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
CreateJarWithClasses(root, "libs/a.jar", "com.example.Demo");
|
||||
CreateJarWithClasses(root, "libs/b.jar", "com.example.Demo", "com.example.Other");
|
||||
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, CancellationToken.None);
|
||||
var analysis = JavaClassPathBuilder.Build(workspace, CancellationToken.None);
|
||||
|
||||
Assert.Equal(2, analysis.Segments.Length);
|
||||
|
||||
var duplicate = Assert.Single(analysis.DuplicateClasses);
|
||||
Assert.Equal("com.example.Demo", duplicate.ClassName);
|
||||
Assert.Equal(2, duplicate.SegmentIdentifiers.Length);
|
||||
|
||||
var split = Assert.Single(analysis.SplitPackages);
|
||||
Assert.Equal("com.example", split.PackageName);
|
||||
Assert.Equal(2, split.SegmentIdentifiers.Length);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateJarWithClasses(string rootDirectory, string relativePath, params string[] classNames)
|
||||
=> CreateJarWithClasses(rootDirectory, relativePath, classNames.AsEnumerable(), serviceDefinitions: null);
|
||||
|
||||
private static void CreateJarWithClasses(
|
||||
string rootDirectory,
|
||||
string relativePath,
|
||||
IEnumerable<string> classNames,
|
||||
IDictionary<string, string[]>? serviceDefinitions)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(rootDirectory);
|
||||
ArgumentException.ThrowIfNullOrEmpty(relativePath);
|
||||
|
||||
var jarPath = Path.Combine(rootDirectory, relativePath.Replace('/', Path.DirectorySeparatorChar));
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
|
||||
|
||||
using var fileStream = new FileStream(jarPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
|
||||
using var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, leaveOpen: false);
|
||||
|
||||
var timestamp = new DateTimeOffset(2024, 01, 01, 0, 0, 0, TimeSpan.Zero);
|
||||
|
||||
foreach (var className in classNames)
|
||||
{
|
||||
var entryPath = className.Replace('.', '/') + ".class";
|
||||
var entry = archive.CreateEntry(entryPath, CompressionLevel.NoCompression);
|
||||
entry.LastWriteTime = timestamp;
|
||||
using var writer = new BinaryWriter(entry.Open(), Encoding.UTF8, leaveOpen: false);
|
||||
writer.Write(new byte[] { 0xCA, 0xFE, 0xBA, 0xBE });
|
||||
}
|
||||
|
||||
if (serviceDefinitions is not null)
|
||||
{
|
||||
foreach (var pair in serviceDefinitions)
|
||||
{
|
||||
var entryPath = "META-INF/services/" + pair.Key;
|
||||
var entry = archive.CreateEntry(entryPath, CompressionLevel.NoCompression);
|
||||
entry.LastWriteTime = timestamp;
|
||||
using var writer = new StreamWriter(entry.Open(), Encoding.UTF8, leaveOpen: false);
|
||||
foreach (var provider in pair.Value)
|
||||
{
|
||||
writer.WriteLine(provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests;
|
||||
|
||||
public sealed class JavaLanguageAnalyzerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ExtractsMavenArtifactFromJarAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
var jarPath = JavaFixtureBuilder.CreateSampleJar(root);
|
||||
var usageHints = new LanguageUsageHints(new[] { jarPath });
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var goldenPath = TestPaths.ResolveFixture("java", "basic", "expected.json");
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath: root,
|
||||
goldenPath: goldenPath,
|
||||
analyzers: analyzers,
|
||||
cancellationToken: cancellationToken,
|
||||
usageHints: usageHints);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.IO.Compression;
|
||||
using System.Threading;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.ClassPath;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.Reflection;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests;
|
||||
|
||||
public sealed class JavaReflectionAnalyzerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Analyze_ClassForNameLiteral_ProducesEdge()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
var jarPath = Path.Combine(root, "libs", "reflect.jar");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
|
||||
using (var archive = new ZipArchive(new FileStream(jarPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None), ZipArchiveMode.Create, leaveOpen: false))
|
||||
{
|
||||
var entry = archive.CreateEntry("com/example/Reflective.class");
|
||||
var bytes = JavaClassFileFactory.CreateClassForNameInvoker("com/example/Reflective", "com.example.Plugin");
|
||||
using var stream = entry.Open();
|
||||
stream.Write(bytes);
|
||||
}
|
||||
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, cancellationToken);
|
||||
var classPath = JavaClassPathBuilder.Build(workspace, cancellationToken);
|
||||
var analysis = JavaReflectionAnalyzer.Analyze(classPath, cancellationToken);
|
||||
|
||||
var edge = Assert.Single(analysis.Edges);
|
||||
Assert.Equal("com.example.Reflective", edge.SourceClass);
|
||||
Assert.Equal("com.example.Plugin", edge.TargetType);
|
||||
Assert.Equal(JavaReflectionReason.ClassForName, edge.Reason);
|
||||
Assert.Equal(JavaReflectionConfidence.High, edge.Confidence);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_TcclUsage_ProducesWarning()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
var jarPath = Path.Combine(root, "libs", "tccl.jar");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
|
||||
using (var archive = new ZipArchive(new FileStream(jarPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None), ZipArchiveMode.Create, leaveOpen: false))
|
||||
{
|
||||
var entry = archive.CreateEntry("com/example/Tccl.class");
|
||||
var bytes = JavaClassFileFactory.CreateTcclChecker("com/example/Tccl");
|
||||
using var stream = entry.Open();
|
||||
stream.Write(bytes);
|
||||
}
|
||||
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, cancellationToken);
|
||||
var classPath = JavaClassPathBuilder.Build(workspace, cancellationToken);
|
||||
var analysis = JavaReflectionAnalyzer.Analyze(classPath, cancellationToken);
|
||||
|
||||
Assert.Empty(analysis.Edges);
|
||||
var warning = Assert.Single(analysis.Warnings);
|
||||
Assert.Equal("tccl", warning.WarningCode);
|
||||
Assert.Equal("com.example.Tccl", warning.SourceClass);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyze_SpringBootFatJar_ScansEmbeddedAndBootSegments()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
JavaFixtureBuilder.CreateSpringBootFatJar(root, "apps/app-fat.jar");
|
||||
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, cancellationToken);
|
||||
var classPath = JavaClassPathBuilder.Build(workspace, cancellationToken);
|
||||
var analysis = JavaReflectionAnalyzer.Analyze(classPath, cancellationToken);
|
||||
|
||||
// Expect at least one edge originating from BOOT-INF classes
|
||||
Assert.Contains(analysis.Edges, edge => edge.SourceClass == "com.example.App" && edge.Reason == JavaReflectionReason.ClassForName);
|
||||
Assert.Contains(analysis.Edges, edge => edge.SourceClass == "com.example.Lib" && edge.Reason == JavaReflectionReason.ClassForName);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.ClassPath;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.ServiceProviders;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests;
|
||||
|
||||
public sealed class JavaServiceProviderScannerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Scan_SelectsFirstProviderByClasspathOrder()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
var servicesA = new Dictionary<string, string[]>
|
||||
{
|
||||
["java.sql.Driver"] = new[] { "com.example.ADriver" },
|
||||
};
|
||||
|
||||
var servicesB = new Dictionary<string, string[]>
|
||||
{
|
||||
["java.sql.Driver"] = new[] { "com.example.BDriver" },
|
||||
};
|
||||
|
||||
CreateJarWithClasses(root, "libs/a.jar", new[] { "com.example.ADriver" }, servicesA);
|
||||
CreateJarWithClasses(root, "libs/b.jar", new[] { "com.example.BDriver" }, servicesB);
|
||||
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, cancellationToken);
|
||||
var classPath = JavaClassPathBuilder.Build(workspace, cancellationToken);
|
||||
var analysis = JavaServiceProviderScanner.Scan(classPath, JavaSpiCatalog.Default, cancellationToken);
|
||||
|
||||
var service = Assert.Single(analysis.Services, record => record.ServiceId == "java.sql.Driver");
|
||||
Assert.Equal("jdk", service.Category);
|
||||
|
||||
var selected = Assert.Single(service.Candidates.Where(candidate => candidate.IsSelected));
|
||||
Assert.Equal("com.example.ADriver", selected.ProviderClass);
|
||||
Assert.Empty(service.Warnings);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Scan_FlagsDuplicateProviders()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
var services = new Dictionary<string, string[]>
|
||||
{
|
||||
["java.sql.Driver"] = new[] { "com.example.DuplicateDriver" },
|
||||
};
|
||||
|
||||
CreateJarWithClasses(root, "libs/a.jar", new[] { "com.example.DuplicateDriver" }, services);
|
||||
CreateJarWithClasses(root, "libs/b.jar", new[] { "com.example.Other" }, services);
|
||||
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, cancellationToken);
|
||||
var classPath = JavaClassPathBuilder.Build(workspace, cancellationToken);
|
||||
var analysis = JavaServiceProviderScanner.Scan(classPath, JavaSpiCatalog.Default, cancellationToken);
|
||||
|
||||
var service = Assert.Single(analysis.Services, record => record.ServiceId == "java.sql.Driver");
|
||||
Assert.NotEmpty(service.Warnings);
|
||||
Assert.Contains(service.Warnings, warning => warning.Contains("duplicate-provider", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Scan_RespectsBootFatJarOrdering()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
JavaFixtureBuilder.CreateSpringBootFatJar(root, "apps/app-fat.jar");
|
||||
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, cancellationToken);
|
||||
var classPath = JavaClassPathBuilder.Build(workspace, cancellationToken);
|
||||
var analysis = JavaServiceProviderScanner.Scan(classPath, JavaSpiCatalog.Default, cancellationToken);
|
||||
|
||||
var service = Assert.Single(analysis.Services, record => record.ServiceId == "java.sql.Driver");
|
||||
var selected = Assert.Single(service.Candidates.Where(candidate => candidate.IsSelected));
|
||||
Assert.Equal("com.example.AppDriver", selected.ProviderClass);
|
||||
Assert.Contains(service.Candidates.Select(candidate => candidate.ProviderClass), provider => provider == "com.example.LibDriver");
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateJarWithClasses(
|
||||
string rootDirectory,
|
||||
string relativePath,
|
||||
IEnumerable<string> classNames,
|
||||
IDictionary<string, string[]> serviceDefinitions)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(rootDirectory);
|
||||
ArgumentException.ThrowIfNullOrEmpty(relativePath);
|
||||
|
||||
var jarPath = Path.Combine(rootDirectory, relativePath.Replace('/', Path.DirectorySeparatorChar));
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
|
||||
|
||||
using var fileStream = new FileStream(jarPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
|
||||
using var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, leaveOpen: false);
|
||||
|
||||
var timestamp = new DateTimeOffset(2024, 01, 01, 0, 0, 0, TimeSpan.Zero);
|
||||
|
||||
foreach (var className in classNames)
|
||||
{
|
||||
var entryPath = className.Replace('.', '/') + ".class";
|
||||
var entry = archive.CreateEntry(entryPath, CompressionLevel.NoCompression);
|
||||
entry.LastWriteTime = timestamp;
|
||||
using var writer = new BinaryWriter(entry.Open(), Encoding.UTF8, leaveOpen: false);
|
||||
writer.Write(new byte[] { 0xCA, 0xFE, 0xBA, 0xBE });
|
||||
}
|
||||
|
||||
foreach (var pair in serviceDefinitions)
|
||||
{
|
||||
var entryPath = "META-INF/services/" + pair.Key;
|
||||
var entry = archive.CreateEntry(entryPath, CompressionLevel.NoCompression);
|
||||
entry.LastWriteTime = timestamp;
|
||||
using var writer = new StreamWriter(entry.Open(), Encoding.UTF8, leaveOpen: false);
|
||||
foreach (var provider in pair.Value)
|
||||
{
|
||||
writer.WriteLine(provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests;
|
||||
|
||||
public sealed class JavaWorkspaceNormalizerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Normalize_ClassifiesPackagingAndLayers()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
JavaFixtureBuilder.CreateSampleJar(root, "libs/simple.jar");
|
||||
JavaFixtureBuilder.CreateSpringBootFatJar(root, "libs/app-fat.jar");
|
||||
JavaFixtureBuilder.CreateWarArchive(root, "apps/sample.war");
|
||||
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, CancellationToken.None);
|
||||
|
||||
var archivesByPath = workspace.Archives.ToDictionary(
|
||||
archive => archive.RelativePath.Replace('\\', '/'),
|
||||
archive => archive,
|
||||
StringComparer.Ordinal);
|
||||
|
||||
var simpleJar = Assert.Contains("libs/simple.jar", archivesByPath);
|
||||
Assert.Equal(JavaPackagingKind.Jar, simpleJar.Packaging);
|
||||
Assert.Empty(simpleJar.LayeredDirectories);
|
||||
|
||||
var fatJar = Assert.Contains("libs/app-fat.jar", archivesByPath);
|
||||
Assert.Equal(JavaPackagingKind.SpringBootFatJar, fatJar.Packaging);
|
||||
Assert.Contains("BOOT-INF", fatJar.LayeredDirectories);
|
||||
|
||||
var war = Assert.Contains("apps/sample.war", archivesByPath);
|
||||
Assert.Equal(JavaPackagingKind.War, war.Packaging);
|
||||
Assert.Contains("WEB-INF", war.LayeredDirectories);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Normalize_SelectsMultiReleaseOverlay()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
JavaFixtureBuilder.CreateMultiReleaseJar(root, "libs/mr.jar");
|
||||
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, CancellationToken.None);
|
||||
|
||||
var archive = Assert.Single(workspace.Archives);
|
||||
|
||||
Assert.True(archive.IsMultiRelease);
|
||||
Assert.False(archive.HasModuleInfo);
|
||||
|
||||
Assert.True(archive.TryGetEntry("com/example/App.class", out var entry));
|
||||
Assert.Equal(11, entry.Version);
|
||||
Assert.Equal("META-INF/versions/11/com/example/App.class", entry.OriginalPath.Replace('\\', '/'));
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Normalize_DetectsRuntimeImageMetadata()
|
||||
{
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
JavaFixtureBuilder.CreateRuntimeImage(root, "runtime/jre");
|
||||
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var workspace = JavaWorkspaceNormalizer.Normalize(context, CancellationToken.None);
|
||||
|
||||
var runtime = Assert.Single(workspace.RuntimeImages);
|
||||
Assert.Equal("17.0.8", runtime.JavaVersion);
|
||||
Assert.Equal("Eclipse Adoptium", runtime.Vendor);
|
||||
Assert.Equal("runtime/jre", runtime.RelativePath.Replace('\\', '/'));
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Remove="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Remove="xunit" />
|
||||
<PackageReference Remove="xunit.runner.visualstudio" />
|
||||
<PackageReference Remove="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Remove="Mongo2Go" />
|
||||
<PackageReference Remove="coverlet.collector" />
|
||||
<PackageReference Remove="Microsoft.Extensions.TimeProvider.Testing" />
|
||||
<ProjectReference Remove="..\StellaOps.Concelier.Testing\StellaOps.Concelier.Testing.csproj" />
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\AssemblyInfo.cs" />
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\MongoFixtureCollection.cs" />
|
||||
<Using Remove="StellaOps.Concelier.Testing" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="xunit.v3" Version="3.0.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Scanner.Analyzers.Lang.Tests\StellaOps.Scanner.Analyzers.Lang.Tests.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang/StellaOps.Scanner.Analyzers.Lang.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang.Java/StellaOps.Scanner.Analyzers.Lang.Java.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Core/StellaOps.Scanner.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Fixtures\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,134 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "node",
|
||||
"componentKey": "purl::pkg:npm/left-pad@1.3.0",
|
||||
"purl": "pkg:npm/left-pad@1.3.0",
|
||||
"name": "left-pad",
|
||||
"version": "1.3.0",
|
||||
"type": "npm",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"integrity": "sha512-LEFTPAD",
|
||||
"path": "packages/app/node_modules/left-pad",
|
||||
"resolved": "https://registry.example/left-pad-1.3.0.tgz"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "package.json",
|
||||
"locator": "packages/app/node_modules/left-pad/package.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "node",
|
||||
"componentKey": "purl::pkg:npm/lib@2.0.1",
|
||||
"purl": "pkg:npm/lib@2.0.1",
|
||||
"name": "lib",
|
||||
"version": "2.0.1",
|
||||
"type": "npm",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"integrity": "sha512-LIB",
|
||||
"path": "packages/lib",
|
||||
"resolved": "https://registry.example/lib-2.0.1.tgz",
|
||||
"workspaceLink": "packages/app/node_modules/lib",
|
||||
"workspaceMember": "true",
|
||||
"workspaceRoot": "packages/lib"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "package.json",
|
||||
"locator": "packages/app/node_modules/lib/package.json"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "package.json",
|
||||
"locator": "packages/lib/package.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "node",
|
||||
"componentKey": "purl::pkg:npm/root-workspace@1.0.0",
|
||||
"purl": "pkg:npm/root-workspace@1.0.0",
|
||||
"name": "root-workspace",
|
||||
"version": "1.0.0",
|
||||
"type": "npm",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"path": ".",
|
||||
"private": "true"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "package.json",
|
||||
"locator": "package.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "node",
|
||||
"componentKey": "purl::pkg:npm/shared@3.1.4",
|
||||
"purl": "pkg:npm/shared@3.1.4",
|
||||
"name": "shared",
|
||||
"version": "3.1.4",
|
||||
"type": "npm",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"integrity": "sha512-SHARED",
|
||||
"path": "packages/shared",
|
||||
"resolved": "https://registry.example/shared-3.1.4.tgz",
|
||||
"workspaceLink": "packages/app/node_modules/shared",
|
||||
"workspaceMember": "true",
|
||||
"workspaceRoot": "packages/shared",
|
||||
"workspaceTargets": "packages/lib"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "package.json",
|
||||
"locator": "packages/app/node_modules/shared/package.json"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "package.json",
|
||||
"locator": "packages/shared/package.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "node",
|
||||
"componentKey": "purl::pkg:npm/workspace-app@1.0.0",
|
||||
"purl": "pkg:npm/workspace-app@1.0.0",
|
||||
"name": "workspace-app",
|
||||
"version": "1.0.0",
|
||||
"type": "npm",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"installScripts": "true",
|
||||
"path": "packages/app",
|
||||
"policyHint.installLifecycle": "postinstall",
|
||||
"script.postinstall": "node scripts/setup.js",
|
||||
"workspaceMember": "true",
|
||||
"workspaceRoot": "packages/app",
|
||||
"workspaceTargets": "packages/lib;packages/shared"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "package.json",
|
||||
"locator": "packages/app/package.json"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "package.json:scripts",
|
||||
"locator": "packages/app/package.json#scripts.postinstall",
|
||||
"value": "node scripts/setup.js",
|
||||
"sha256": "f9ae4e4c9313857d1acc31947cee9984232cbefe93c8a56c718804744992728a"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "root-workspace",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "root-workspace",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
]
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "workspace-app",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"packages/lib": {
|
||||
"name": "lib",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.example/lib-2.0.1.tgz",
|
||||
"integrity": "sha512-LIB"
|
||||
},
|
||||
"packages/shared": {
|
||||
"name": "shared",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.example/shared-3.1.4.tgz",
|
||||
"integrity": "sha512-SHARED"
|
||||
},
|
||||
"packages/app/node_modules/lib": {
|
||||
"name": "lib",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.example/lib-2.0.1.tgz",
|
||||
"integrity": "sha512-LIB"
|
||||
},
|
||||
"packages/app/node_modules/shared": {
|
||||
"name": "shared",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.example/shared-3.1.4.tgz",
|
||||
"integrity": "sha512-SHARED"
|
||||
},
|
||||
"packages/app/node_modules/left-pad": {
|
||||
"name": "left-pad",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.example/left-pad-1.3.0.tgz",
|
||||
"integrity": "sha512-LEFTPAD"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "root-workspace",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/app",
|
||||
"packages/lib",
|
||||
"packages/shared"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "workspace-app",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"lib": "workspace:../lib",
|
||||
"shared": "workspace:../shared"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node scripts/setup.js"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
console.log('setup');
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "lib",
|
||||
"version": "2.0.1",
|
||||
"dependencies": {
|
||||
"left-pad": "1.3.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "shared",
|
||||
"version": "3.1.4",
|
||||
"dependencies": {
|
||||
"lib": "workspace:../lib"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Node;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Node.Tests;
|
||||
|
||||
public sealed class NodeLanguageAnalyzerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task WorkspaceFixtureProducesDeterministicOutputAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "node", "workspaces");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new NodeLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Remove="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Remove="xunit" />
|
||||
<PackageReference Remove="xunit.runner.visualstudio" />
|
||||
<PackageReference Remove="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Remove="Mongo2Go" />
|
||||
<PackageReference Remove="coverlet.collector" />
|
||||
<PackageReference Remove="Microsoft.Extensions.TimeProvider.Testing" />
|
||||
<ProjectReference Remove="..\StellaOps.Concelier.Testing\StellaOps.Concelier.Testing.csproj" />
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\AssemblyInfo.cs" />
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\MongoFixtureCollection.cs" />
|
||||
<Using Remove="StellaOps.Concelier.Testing" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="xunit.v3" Version="3.0.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Scanner.Analyzers.Lang.Tests\StellaOps.Scanner.Analyzers.Lang.Tests.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang/StellaOps.Scanner.Analyzers.Lang.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang.Node/StellaOps.Scanner.Analyzers.Lang.Node.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Core/StellaOps.Scanner.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Fixtures\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,110 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "python",
|
||||
"componentKey": "purl::pkg:pypi/layered@2.0",
|
||||
"purl": "pkg:pypi/layered@2.0",
|
||||
"name": "layered",
|
||||
"version": "2.0",
|
||||
"type": "pypi",
|
||||
"usedByEntrypoint": true,
|
||||
"metadata": {
|
||||
"author": "Layered Maintainer",
|
||||
"authorEmail": "maintainer@example.com",
|
||||
"classifier[0]": "Programming Language :: Python :: 3",
|
||||
"classifiers": "Programming Language :: Python :: 3",
|
||||
"distInfoPath": "layer1/usr/lib/python3.11/site-packages/layered-2.0.dist-info",
|
||||
"editable": "true",
|
||||
"entryPoints.console_scripts": "layered-cli=layered.cli:main",
|
||||
"entryPoints.layered.hooks": "register=layered.plugins:register",
|
||||
"installer": "pip",
|
||||
"license": "Apache-2.0",
|
||||
"license.classifier[0]": "License :: OSI Approved :: Apache Software License",
|
||||
"license.file[0]": "layer2/usr/lib/python3.11/site-packages/LICENSE",
|
||||
"licenseExpression": "Apache-2.0",
|
||||
"name": "layered",
|
||||
"normalizedName": "layered",
|
||||
"projectUrl": "Documentation, https://example.com/layered/docs",
|
||||
"provenance": "dist-info",
|
||||
"record.hashMismatches": "0",
|
||||
"record.hashedEntries": "8",
|
||||
"record.ioErrors": "0",
|
||||
"record.missingFiles": "0",
|
||||
"record.totalEntries": "9",
|
||||
"requiresDist": "requests",
|
||||
"requiresPython": "\u003E=3.9",
|
||||
"sourceCommit": "abc123",
|
||||
"sourceSubdirectory": "src/layered",
|
||||
"sourceUrl": "https://git.example.com/layered",
|
||||
"sourceVcs": "git",
|
||||
"summary": "Base layer metadata",
|
||||
"version": "2.0",
|
||||
"wheel.generator": "pip 24.0",
|
||||
"wheel.rootIsPurelib": "true",
|
||||
"wheel.tags": "py3-none-any",
|
||||
"wheel.version": "1.0"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "INSTALLER",
|
||||
"locator": "layer1/usr/lib/python3.11/site-packages/layered-2.0.dist-info/INSTALLER"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "INSTALLER",
|
||||
"locator": "layer2/usr/lib/python3.11/site-packages/layered-2.0.dist-info/INSTALLER"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "METADATA",
|
||||
"locator": "layer1/usr/lib/python3.11/site-packages/layered-2.0.dist-info/METADATA"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "METADATA",
|
||||
"locator": "layer2/usr/lib/python3.11/site-packages/layered-2.0.dist-info/METADATA"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "RECORD",
|
||||
"locator": "layer1/usr/lib/python3.11/site-packages/layered-2.0.dist-info/RECORD"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "RECORD",
|
||||
"locator": "layer2/usr/lib/python3.11/site-packages/layered-2.0.dist-info/RECORD"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "WHEEL",
|
||||
"locator": "layer1/usr/lib/python3.11/site-packages/layered-2.0.dist-info/WHEEL"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "WHEEL",
|
||||
"locator": "layer2/usr/lib/python3.11/site-packages/layered-2.0.dist-info/WHEEL"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "entry_points.txt",
|
||||
"locator": "layer1/usr/lib/python3.11/site-packages/layered-2.0.dist-info/entry_points.txt"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "entry_points.txt",
|
||||
"locator": "layer2/usr/lib/python3.11/site-packages/layered-2.0.dist-info/entry_points.txt"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "license",
|
||||
"locator": "layer2/usr/lib/python3.11/site-packages/LICENSE"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "direct_url.json",
|
||||
"locator": "layer2/usr/lib/python3.11/site-packages/layered-2.0.dist-info/direct_url.json",
|
||||
"value": "https://git.example.com/layered"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,8 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: layered
|
||||
Version: 2.0
|
||||
Summary: Base layer metadata
|
||||
License: Apache-2.0
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Requires-Python: >=3.9
|
||||
Requires-Dist: requests
|
||||
@@ -0,0 +1,9 @@
|
||||
layered/__init__.py,sha256=3Q3bv/BWCZLW2p2dVYw8QfAfBYV2YBtuYtT9TIJCmFM=,139
|
||||
layered/core.py,sha256=izBXI4cRE/cgf7hgc/gHfTp1OyIIJ23NgUJXaHWVFpU=,80
|
||||
layered/cli.py,sha256=xQrTznF7ch6C9qyQALJpsqRTIh9DVCRG4IXoQ1eLLnY=,126
|
||||
../../../bin/layered-cli,sha256=6IGTGCEapolFoAUnXGNrWIfPSXU8W7bsZ07DYF/wmNc=,91
|
||||
layered-2.0.dist-info/METADATA,sha256=jNEi7xsj4V+SSzOJJToxMoZmZ7gxyto7zuKxCjxUFjk=,193
|
||||
layered-2.0.dist-info/WHEEL,sha256=m8MHT7vQnqC5W8H/y4uJdEpx9ijH0jJGpuoaxRbcsQg=,79
|
||||
layered-2.0.dist-info/entry_points.txt,sha256=hm8bgJUYe2zoYNATyAsQzQKQTdQtFe4ctbf5kSlxFj0=,47
|
||||
layered-2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ+UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg=,4
|
||||
layered-2.0.dist-info/RECORD,,
|
||||
@@ -0,0 +1,4 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: pip 24.0
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
@@ -0,0 +1,2 @@
|
||||
[console_scripts]
|
||||
layered-cli=layered.cli:main
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Layered package demonstrating merged metadata across layers."""
|
||||
|
||||
from .core import get_version # noqa: F401
|
||||
|
||||
__all__ = ["get_version"]
|
||||
@@ -0,0 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .core import get_version
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print(f"layered {get_version()}")
|
||||
@@ -0,0 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def get_version() -> str:
|
||||
return "2.0"
|
||||
@@ -0,0 +1 @@
|
||||
Apache License Version 2.0
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,10 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: layered
|
||||
Version: 2.0
|
||||
Summary: Overlay metadata adding direct URL information
|
||||
License-Expression: Apache-2.0
|
||||
License-File: LICENSE
|
||||
Author: Layered Maintainer
|
||||
Author-email: maintainer@example.com
|
||||
Project-URL: Documentation, https://example.com/layered/docs
|
||||
Classifier: License :: OSI Approved :: Apache Software License
|
||||
@@ -0,0 +1,9 @@
|
||||
layered/plugins/__init__.py,sha256=hMd8TidtznWDaiA4biHZ04ZoVXcAc7z/p77bIdAsPyE=,53
|
||||
layered/plugins/plugin.py,sha256=PBVqd9coVIzSBTQ2qdL5qxoK0fnsRZZ1DkhqnaVySPA=,87
|
||||
LICENSE,sha256=cXKP+wQk9Jyqh8mUi7nURl9jOOjojDqrabZ119S2EzM=,27
|
||||
layered-2.0.dist-info/METADATA,sha256=V09W93ILksWKLP8My6UatmScZ+5MCLiQ/5ieWzb585M=,346
|
||||
layered-2.0.dist-info/WHEEL,sha256=m8MHT7vQnqC5W8H/y4uJdEpx9ijH0jJGpuoaxRbcsQg=,79
|
||||
layered-2.0.dist-info/entry_points.txt,sha256=JYpkYczwozo6Ek7diDPgPj8ReYv5wTpaW0pFjL82bGU=,50
|
||||
layered-2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ+UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg=,4
|
||||
layered-2.0.dist-info/direct_url.json,sha256=8NtnZQZq2S5tcEn+P5fH6/EpABJ9+Ha5aIq8Sn2szig=,189
|
||||
layered-2.0.dist-info/RECORD,,
|
||||
@@ -0,0 +1,4 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: pip 24.0
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"url": "https://git.example.com/layered",
|
||||
"dir_info": {
|
||||
"editable": true,
|
||||
"subdirectory": "src/layered"
|
||||
},
|
||||
"vcs_info": {
|
||||
"vcs": "git",
|
||||
"commit_id": "abc123"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
[layered.hooks]
|
||||
register=layered.plugins:register
|
||||
@@ -0,0 +1,3 @@
|
||||
from .plugin import register
|
||||
|
||||
__all__ = ["register"]
|
||||
@@ -0,0 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def register() -> str:
|
||||
return "layer2-plugin"
|
||||
@@ -0,0 +1,87 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "python",
|
||||
"componentKey": "purl::pkg:pypi/cache-pkg@1.2.3",
|
||||
"purl": "pkg:pypi/cache-pkg@1.2.3",
|
||||
"name": "Cache-Pkg",
|
||||
"version": "1.2.3",
|
||||
"type": "pypi",
|
||||
"usedByEntrypoint": true,
|
||||
"metadata": {
|
||||
"classifier[0]": "Intended Audience :: Developers",
|
||||
"classifier[1]": "License :: OSI Approved :: BSD License",
|
||||
"classifier[2]": "Programming Language :: Python :: 3",
|
||||
"classifiers": "Intended Audience :: Developers;License :: OSI Approved :: BSD License;Programming Language :: Python :: 3",
|
||||
"distInfoPath": "lib/python3.11/site-packages/cache_pkg-1.2.3.dist-info",
|
||||
"entryPoints.console_scripts": "cache-tool=cache_pkg:main",
|
||||
"installer": "pip",
|
||||
"license": "BSD-3-Clause",
|
||||
"license.classifier[0]": "License :: OSI Approved :: BSD License",
|
||||
"license.file[0]": "LICENSE",
|
||||
"name": "Cache-Pkg",
|
||||
"normalizedName": "cache-pkg",
|
||||
"projectUrl": "Source, https://example.com/cache-pkg",
|
||||
"provenance": "dist-info",
|
||||
"record.hashMismatches": "1",
|
||||
"record.hashedEntries": "9",
|
||||
"record.ioErrors": "0",
|
||||
"record.missingFiles": "2",
|
||||
"record.totalEntries": "12",
|
||||
"record.unsupportedAlgorithms": "md5",
|
||||
"requiresDist": "click",
|
||||
"requiresPython": "\u003E=3.8",
|
||||
"summary": "Cache test package for hashed RECORD coverage",
|
||||
"version": "1.2.3",
|
||||
"wheel.generator": "pip 24.0",
|
||||
"wheel.rootIsPurelib": "true",
|
||||
"wheel.tags": "py3-none-any",
|
||||
"wheel.version": "1.0"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "derived",
|
||||
"source": "RECORD",
|
||||
"locator": "../etc/passwd",
|
||||
"value": "outside-root"
|
||||
},
|
||||
{
|
||||
"kind": "derived",
|
||||
"source": "RECORD",
|
||||
"locator": "lib/python3.11/site-packages/cache_pkg/LICENSE",
|
||||
"value": "sha256 mismatch expected=pdUY6NGoyWCpcK8ThpBPUIVXbLvU9PzP8lsXEVEnFd0= actual=pdUY6NGoyWCpcK8ThpBPUIVXbLvU9PzP8lsXEVEnFdk=",
|
||||
"sha256": "pdUY6NGoyWCpcK8ThpBPUIVXbLvU9PzP8lsXEVEnFdk="
|
||||
},
|
||||
{
|
||||
"kind": "derived",
|
||||
"source": "RECORD",
|
||||
"locator": "lib/python3.11/site-packages/cache_pkg/missing/data.json",
|
||||
"value": "missing"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "INSTALLER",
|
||||
"locator": "lib/python3.11/site-packages/cache_pkg-1.2.3.dist-info/INSTALLER"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "METADATA",
|
||||
"locator": "lib/python3.11/site-packages/cache_pkg-1.2.3.dist-info/METADATA"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "RECORD",
|
||||
"locator": "lib/python3.11/site-packages/cache_pkg-1.2.3.dist-info/RECORD"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "WHEEL",
|
||||
"locator": "lib/python3.11/site-packages/cache_pkg-1.2.3.dist-info/WHEEL"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "entry_points.txt",
|
||||
"locator": "lib/python3.11/site-packages/cache_pkg-1.2.3.dist-info/entry_points.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
from cache_pkg import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,12 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: Cache-Pkg
|
||||
Version: 1.2.3
|
||||
Summary: Cache test package for hashed RECORD coverage
|
||||
License: BSD-3-Clause
|
||||
License-File: LICENSE
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Requires-Python: >=3.8
|
||||
Requires-Dist: click
|
||||
Project-URL: Source, https://example.com/cache-pkg
|
||||
@@ -0,0 +1,12 @@
|
||||
cache_pkg/__init__.py,sha256=iw2XGXcGU2Si1KAQ7o82tPSKxwEFc6UNZfITpGPh7mM=,189
|
||||
cache_pkg/data/config.json,sha256=Oa/wlgi1qJHC93RF5vwoOFffyviGtm5ccL7lrI0gkeY=,49
|
||||
cache_pkg/LICENSE,sha256=pdUY6NGoyWCpcK8ThpBPUIVXbLvU9PzP8lsXEVEnFd0=,73
|
||||
cache_pkg/md5only.txt,md5=Zm9v,4
|
||||
cache_pkg-1.2.3.data/scripts/cache-tool,sha256=2rsv/gnYOtlJZCy75Wz0rCADxYPnQAkyKvNbuoquZQ4=,89
|
||||
cache_pkg-1.2.3.dist-info/METADATA,sha256=DXPSItxOR1kPkVzjyq8F50jf8FOR9brSs/TGcZmcEHo=,390
|
||||
cache_pkg-1.2.3.dist-info/WHEEL,sha256=m8MHT7vQnqC5W8H/y4uJdEpx9ijH0jJGpuoaxRbcsQg=,79
|
||||
cache_pkg-1.2.3.dist-info/entry_points.txt,sha256=S1tGoBGlzWL6jeECCTw1pZP09HM8voEm/qQ7DtOHPyc=,44
|
||||
cache_pkg-1.2.3.dist-info/INSTALLER,sha256=zuuue4knoyJ+UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg=,4
|
||||
cache_pkg-1.2.3.dist-info/RECORD,,
|
||||
cache_pkg/missing/data.json,sha256=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,12
|
||||
../../../../etc/passwd,,
|
||||
@@ -0,0 +1,4 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: pip 24.0
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
@@ -0,0 +1,2 @@
|
||||
[console_scripts]
|
||||
cache-tool=cache_pkg:main
|
||||
@@ -0,0 +1,4 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2025, StellaOps
|
||||
All rights reserved.
|
||||
@@ -0,0 +1,7 @@
|
||||
"""Cache fixture package for determinism tests."""
|
||||
|
||||
from .data import config # noqa: F401
|
||||
|
||||
def main() -> None:
|
||||
"""Entry point used by console script."""
|
||||
print("cache-pkg running")
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"cacheEnabled": true,
|
||||
"ttlSeconds": 3600
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
foo
|
||||
@@ -0,0 +1,85 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "python",
|
||||
"componentKey": "purl::pkg:pypi/simple@1.0.0",
|
||||
"purl": "pkg:pypi/simple@1.0.0",
|
||||
"name": "simple",
|
||||
"version": "1.0.0",
|
||||
"type": "pypi",
|
||||
"usedByEntrypoint": true,
|
||||
"metadata": {
|
||||
"author": "Example Dev",
|
||||
"authorEmail": "dev@example.com",
|
||||
"classifier[0]": "License :: OSI Approved :: Apache Software License",
|
||||
"classifier[1]": "Programming Language :: Python :: 3",
|
||||
"classifiers": "License :: OSI Approved :: Apache Software License;Programming Language :: Python :: 3",
|
||||
"distInfoPath": "lib/python3.11/site-packages/simple-1.0.0.dist-info",
|
||||
"editable": "true",
|
||||
"entryPoints.console_scripts": "simple-tool=simple.core:main",
|
||||
"homePage": "https://example.com/simple",
|
||||
"installer": "pip",
|
||||
"license": "Apache-2.0",
|
||||
"license.classifier[0]": "License :: OSI Approved :: Apache Software License",
|
||||
"name": "simple",
|
||||
"normalizedName": "simple",
|
||||
"projectUrl": "Source, https://example.com/simple/src",
|
||||
"provenance": "dist-info",
|
||||
"record.hashMismatches": "0",
|
||||
"record.hashedEntries": "8",
|
||||
"record.ioErrors": "0",
|
||||
"record.missingFiles": "1",
|
||||
"record.totalEntries": "10",
|
||||
"requiresDist": "requests (\u003E=2.0)",
|
||||
"requiresPython": "\u003E=3.9",
|
||||
"sourceCommit": "abc123def",
|
||||
"sourceSubdirectory": "src/simple",
|
||||
"sourceUrl": "https://example.com/simple-1.0.0.tar.gz",
|
||||
"sourceVcs": "git",
|
||||
"summary": "Simple fixture package",
|
||||
"version": "1.0.0",
|
||||
"wheel.generator": "pip 24.0",
|
||||
"wheel.rootIsPurelib": "true",
|
||||
"wheel.tags": "py3-none-any",
|
||||
"wheel.version": "1.0"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "derived",
|
||||
"source": "RECORD",
|
||||
"locator": "bin/simple-tool",
|
||||
"value": "missing"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "INSTALLER",
|
||||
"locator": "lib/python3.11/site-packages/simple-1.0.0.dist-info/INSTALLER"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "METADATA",
|
||||
"locator": "lib/python3.11/site-packages/simple-1.0.0.dist-info/METADATA"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "RECORD",
|
||||
"locator": "lib/python3.11/site-packages/simple-1.0.0.dist-info/RECORD"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "WHEEL",
|
||||
"locator": "lib/python3.11/site-packages/simple-1.0.0.dist-info/WHEEL"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "entry_points.txt",
|
||||
"locator": "lib/python3.11/site-packages/simple-1.0.0.dist-info/entry_points.txt"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
"source": "direct_url.json",
|
||||
"locator": "lib/python3.11/site-packages/simple-1.0.0.dist-info/direct_url.json",
|
||||
"value": "https://example.com/simple-1.0.0.tar.gz"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,13 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: simple
|
||||
Version: 1.0.0
|
||||
Summary: Simple fixture package
|
||||
Home-page: https://example.com/simple
|
||||
Author: Example Dev
|
||||
Author-email: dev@example.com
|
||||
License: Apache-2.0
|
||||
Project-URL: Source, https://example.com/simple/src
|
||||
Requires-Python: >=3.9
|
||||
Requires-Dist: requests (>=2.0)
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: License :: OSI Approved :: Apache Software License
|
||||
@@ -0,0 +1,10 @@
|
||||
simple/__init__.py,sha256=03NWG/tm5eky+tnGlynp/vcyjtR944EtQMKtwrutl/U=,79
|
||||
simple/__main__.py,sha256=7pHsIZX9uNTyp1e1AkmZ1vXZxw/dYB1TbR1rYDJca6c=,62
|
||||
simple/core.py,sha256=8HaF+vPTo2roSP7kivqePnjG+d/WqotH269Qey/BM+s=,67
|
||||
../../../bin/simple-tool,sha256=E7eVnffg2E4646m1Ml/5ixyROcpc24GJvy03sEkg6DA=,91
|
||||
simple-1.0.0.dist-info/METADATA,sha256=Da/AG+nYa85WfbUSNmmRjpTeEEM8Kinf6Z197xb8X2o=,408
|
||||
simple-1.0.0.dist-info/WHEEL,sha256=m8MHT7vQnqC5W8H/y4uJdEpx9ijH0jJGpuoaxRbcsQg=,79
|
||||
simple-1.0.0.dist-info/entry_points.txt,sha256=A2WCkblioa0YbdUDurLzv+sIbx7TRSJ9zfBLYMYwpBQ=,49
|
||||
simple-1.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ+UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg=,4
|
||||
simple-1.0.0.dist-info/direct_url.json,sha256=EXd4Xj5iohEIqiF7mlR7sCLGhqXiU1/LuOPjijstKCU=,199
|
||||
simple-1.0.0.dist-info/RECORD,,
|
||||
@@ -0,0 +1,4 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: pip 24.0
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"url": "https://example.com/simple-1.0.0.tar.gz",
|
||||
"dir_info": {
|
||||
"editable": true,
|
||||
"subdirectory": "src/simple"
|
||||
},
|
||||
"vcs_info": {
|
||||
"vcs": "git",
|
||||
"commit_id": "abc123def"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
[console_scripts]
|
||||
simple-tool = simple.core:main
|
||||
@@ -0,0 +1,4 @@
|
||||
__all__ = ["main"]
|
||||
__version__ = "1.0.0"
|
||||
|
||||
from .core import main # noqa: F401
|
||||
@@ -0,0 +1,4 @@
|
||||
from .core import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,4 @@
|
||||
import sys
|
||||
|
||||
def main() -> None:
|
||||
print("simple core", sys.argv)
|
||||
@@ -0,0 +1,83 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Python;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Python.Tests;
|
||||
|
||||
public sealed class PythonLanguageAnalyzerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SimpleVenvFixtureProducesDeterministicOutputAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "python", "simple-venv");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var usageHints = new LanguageUsageHints(new[]
|
||||
{
|
||||
Path.Combine(fixturePath, "bin", "simple-tool")
|
||||
});
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new PythonLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken,
|
||||
usageHints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PipCacheFixtureProducesDeterministicOutputAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "python", "pip-cache");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var usageHints = new LanguageUsageHints(new[]
|
||||
{
|
||||
Path.Combine(fixturePath, "lib", "python3.11", "site-packages", "cache_pkg-1.2.3.data", "scripts", "cache-tool")
|
||||
});
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new PythonLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken,
|
||||
usageHints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LayeredEditableFixtureMergesAcrossLayersAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "python", "layered-editable");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var usageHints = new LanguageUsageHints(new[]
|
||||
{
|
||||
Path.Combine(fixturePath, "layer1", "usr", "bin", "layered-cli")
|
||||
});
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new PythonLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken,
|
||||
usageHints);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Remove="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Remove="xunit" />
|
||||
<PackageReference Remove="xunit.runner.visualstudio" />
|
||||
<PackageReference Remove="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Remove="Mongo2Go" />
|
||||
<PackageReference Remove="coverlet.collector" />
|
||||
<PackageReference Remove="Microsoft.Extensions.TimeProvider.Testing" />
|
||||
<ProjectReference Remove="..\StellaOps.Concelier.Testing\StellaOps.Concelier.Testing.csproj" />
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\AssemblyInfo.cs" />
|
||||
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\MongoFixtureCollection.cs" />
|
||||
<Using Remove="StellaOps.Concelier.Testing" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="xunit.v3" Version="3.0.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Scanner.Analyzers.Lang.Tests\StellaOps.Scanner.Analyzers.Lang.Tests.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang/StellaOps.Scanner.Analyzers.Lang.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang.Python/StellaOps.Scanner.Analyzers.Lang.Python.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Core/StellaOps.Scanner.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Fixtures\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,88 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Core;
|
||||
|
||||
public sealed class LanguageAnalyzerResultTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task MergesDuplicateComponentsDeterministicallyAsync()
|
||||
{
|
||||
var analyzer = new DuplicateComponentAnalyzer();
|
||||
var engine = new LanguageAnalyzerEngine(new[] { analyzer });
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
try
|
||||
{
|
||||
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
|
||||
var result = await engine.AnalyzeAsync(context, CancellationToken.None);
|
||||
|
||||
var component = Assert.Single(result.Components);
|
||||
Assert.Equal("purl::pkg:example/acme@2.0.0", component.ComponentKey);
|
||||
Assert.Equal("pkg:example/acme@2.0.0", component.Purl);
|
||||
Assert.True(component.UsedByEntrypoint);
|
||||
Assert.Equal(2, component.Evidence.Count);
|
||||
Assert.Equal(3, component.Metadata.Count);
|
||||
|
||||
// Metadata retains stable ordering (sorted by key)
|
||||
var keys = component.Metadata.Keys.ToArray();
|
||||
Assert.Equal(new[] { "artifactId", "groupId", "path" }, keys);
|
||||
|
||||
// Evidence de-duplicates via comparison key
|
||||
Assert.Equal(2, component.Evidence.Count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DuplicateComponentAnalyzer : ILanguageAnalyzer
|
||||
{
|
||||
public string Id => "duplicate";
|
||||
|
||||
public string DisplayName => "Duplicate Analyzer";
|
||||
|
||||
public async ValueTask AnalyzeAsync(LanguageAnalyzerContext context, LanguageComponentWriter writer, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
var metadataA = new[]
|
||||
{
|
||||
new KeyValuePair<string, string?>("groupId", "example"),
|
||||
new KeyValuePair<string, string?>("artifactId", "acme")
|
||||
};
|
||||
|
||||
var metadataB = new[]
|
||||
{
|
||||
new KeyValuePair<string, string?>("artifactId", "acme"),
|
||||
new KeyValuePair<string, string?>("path", ".")
|
||||
};
|
||||
|
||||
var evidence = new[]
|
||||
{
|
||||
new LanguageComponentEvidence(LanguageEvidenceKind.File, "manifest", "META-INF/MANIFEST.MF", null, null),
|
||||
new LanguageComponentEvidence(LanguageEvidenceKind.Metadata, "pom", "pom.xml", "groupId=example", null)
|
||||
};
|
||||
|
||||
writer.AddFromPurl(
|
||||
analyzerId: Id,
|
||||
purl: "pkg:example/acme@2.0.0",
|
||||
name: "acme",
|
||||
version: "2.0.0",
|
||||
type: "example",
|
||||
metadata: metadataA,
|
||||
evidence: evidence,
|
||||
usedByEntrypoint: true);
|
||||
|
||||
// duplicate insert with different metadata ordering
|
||||
writer.AddFromPurl(
|
||||
analyzerId: Id,
|
||||
purl: "pkg:example/acme@2.0.0",
|
||||
name: "acme",
|
||||
version: "2.0.0",
|
||||
type: "example",
|
||||
metadata: metadataB,
|
||||
evidence: evidence,
|
||||
usedByEntrypoint: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using StellaOps.Scanner.Core.Contracts;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Core;
|
||||
|
||||
public sealed class LanguageComponentMapperTests
|
||||
{
|
||||
[Fact]
|
||||
public void ToComponentRecordsProjectsDeterministicComponents()
|
||||
{
|
||||
// Arrange
|
||||
var analyzerId = "node";
|
||||
var records = new[]
|
||||
{
|
||||
LanguageComponentRecord.FromPurl(
|
||||
analyzerId: analyzerId,
|
||||
purl: "pkg:npm/example@1.0.0",
|
||||
name: "example",
|
||||
version: "1.0.0",
|
||||
type: "npm",
|
||||
metadata: new Dictionary<string, string?>()
|
||||
{
|
||||
["path"] = "packages/app",
|
||||
["license"] = "MIT"
|
||||
},
|
||||
evidence: new[]
|
||||
{
|
||||
new LanguageComponentEvidence(LanguageEvidenceKind.File, "package.json", "packages/app/package.json", null, "abc123")
|
||||
},
|
||||
usedByEntrypoint: true),
|
||||
LanguageComponentRecord.FromExplicitKey(
|
||||
analyzerId: analyzerId,
|
||||
componentKey: "bin::sha256:deadbeef",
|
||||
purl: null,
|
||||
name: "app-binary",
|
||||
version: null,
|
||||
type: "binary",
|
||||
metadata: new Dictionary<string, string?>()
|
||||
{
|
||||
["description"] = "Utility binary"
|
||||
},
|
||||
evidence: new[]
|
||||
{
|
||||
new LanguageComponentEvidence(LanguageEvidenceKind.Derived, "entrypoint", "/usr/local/bin/app", "ENTRYPOINT", null)
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
var layerDigest = LanguageComponentMapper.ComputeLayerDigest(analyzerId);
|
||||
var results = LanguageComponentMapper.ToComponentRecords(analyzerId, records, layerDigest);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, results.Length);
|
||||
Assert.All(results, component => Assert.Equal(layerDigest, component.LayerDigest));
|
||||
|
||||
var first = results[0];
|
||||
Assert.Equal("bin::sha256:deadbeef", first.Identity.Key);
|
||||
Assert.Equal("Utility binary", first.Metadata!.Properties!["stellaops.lang.meta.description"]);
|
||||
Assert.Equal("derived", first.Evidence.Single().Kind);
|
||||
|
||||
var second = results[1];
|
||||
Assert.Equal("pkg:npm/example@1.0.0", second.Identity.Key); // prefix removed
|
||||
Assert.True(second.Usage.UsedByEntrypoint);
|
||||
Assert.Contains("MIT", second.Metadata!.Licenses!);
|
||||
Assert.Equal("packages/app", second.Metadata.Properties!["stellaops.lang.meta.path"]);
|
||||
Assert.Equal("abc123", second.Metadata.Properties!["stellaops.lang.evidence.0.sha256"]);
|
||||
Assert.Equal("file", second.Evidence.Single().Kind);
|
||||
Assert.Equal("packages/app/package.json", second.Evidence.Single().Value);
|
||||
Assert.Equal("package.json", second.Evidence.Single().Source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using StellaOps.Scanner.Analyzers.Lang;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Determinism;
|
||||
|
||||
public sealed class LanguageAnalyzerHarnessTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task HarnessProducesDeterministicOutputAsync()
|
||||
{
|
||||
var fixturePath = TestPaths.ResolveFixture("determinism", "basic", "input");
|
||||
var goldenPath = TestPaths.ResolveFixture("determinism", "basic", "expected.json");
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new FakeLanguageAnalyzer(
|
||||
"fake-java",
|
||||
LanguageComponentRecord.FromPurl(
|
||||
analyzerId: "fake-java",
|
||||
purl: "pkg:maven/org.example/example-lib@1.2.3",
|
||||
name: "example-lib",
|
||||
version: "1.2.3",
|
||||
type: "maven",
|
||||
metadata: new Dictionary<string, string?>
|
||||
{
|
||||
["groupId"] = "org.example",
|
||||
["artifactId"] = "example-lib",
|
||||
},
|
||||
evidence: new []
|
||||
{
|
||||
new LanguageComponentEvidence(LanguageEvidenceKind.File, "pom.properties", "META-INF/maven/org.example/example-lib/pom.properties", null, "abc123"),
|
||||
}),
|
||||
LanguageComponentRecord.FromExplicitKey(
|
||||
analyzerId: "fake-java",
|
||||
componentKey: "bin::sha256:deadbeef",
|
||||
purl: null,
|
||||
name: "example-cli",
|
||||
version: null,
|
||||
type: "bin",
|
||||
metadata: new Dictionary<string, string?>
|
||||
{
|
||||
["sha256"] = "deadbeef",
|
||||
},
|
||||
evidence: new []
|
||||
{
|
||||
new LanguageComponentEvidence(LanguageEvidenceKind.File, "binary", "usr/local/bin/example", null, "deadbeef"),
|
||||
})),
|
||||
new FakeLanguageAnalyzer(
|
||||
"fake-node",
|
||||
LanguageComponentRecord.FromPurl(
|
||||
analyzerId: "fake-node",
|
||||
purl: "pkg:npm/example-package@4.5.6",
|
||||
name: "example-package",
|
||||
version: "4.5.6",
|
||||
type: "npm",
|
||||
metadata: new Dictionary<string, string?>
|
||||
{
|
||||
["workspace"] = "packages/example",
|
||||
},
|
||||
evidence: new []
|
||||
{
|
||||
new LanguageComponentEvidence(LanguageEvidenceKind.File, "package.json", "packages/example/package.json", null, null),
|
||||
},
|
||||
usedByEntrypoint: true)),
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(fixturePath, goldenPath, analyzers, cancellationToken);
|
||||
|
||||
var first = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
var second = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
Assert.Equal(first, second);
|
||||
}
|
||||
|
||||
private sealed class FakeLanguageAnalyzer : ILanguageAnalyzer
|
||||
{
|
||||
private readonly IReadOnlyList<LanguageComponentRecord> _components;
|
||||
|
||||
public FakeLanguageAnalyzer(string id, params LanguageComponentRecord[] components)
|
||||
{
|
||||
Id = id;
|
||||
DisplayName = id;
|
||||
_components = components ?? Array.Empty<LanguageComponentRecord>();
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
public string DisplayName { get; }
|
||||
|
||||
public async ValueTask AnalyzeAsync(LanguageAnalyzerContext context, LanguageComponentWriter writer, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(5, cancellationToken).ConfigureAwait(false); // ensure asynchrony is handled
|
||||
|
||||
// Intentionally add in reverse order to prove determinism.
|
||||
foreach (var component in _components.Reverse())
|
||||
{
|
||||
writer.Add(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Scanner.Analyzers.Lang.DotNet;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Tests.DotNet;
|
||||
|
||||
public sealed class DotNetLanguageAnalyzerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SimpleFixtureProducesDeterministicOutputAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "simple");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignedFixtureCapturesAssemblyMetadataAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "signed");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
var inspector = new StubAuthenticodeInspector();
|
||||
var services = new SingleServiceProvider(inspector);
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken,
|
||||
usageHints: null,
|
||||
services: services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SelfContainedFixtureHandlesNativeAssetsAndUsageAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "selfcontained");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var usageHints = new LanguageUsageHints(new[]
|
||||
{
|
||||
Path.Combine(fixturePath, "lib", "net10.0", "StellaOps.Toolkit.dll"),
|
||||
Path.Combine(fixturePath, "runtimes", "linux-x64", "native", "libstellaopsnative.so")
|
||||
});
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken,
|
||||
usageHints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnalyzerIsThreadSafeUnderConcurrencyAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "selfcontained");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
var workers = Math.Max(Environment.ProcessorCount, 4);
|
||||
var tasks = Enumerable.Range(0, workers)
|
||||
.Select(_ => LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken));
|
||||
|
||||
var results = await Task.WhenAll(tasks);
|
||||
var first = results[0];
|
||||
foreach (var result in results)
|
||||
{
|
||||
Assert.Equal(first, result);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultiFixtureMergesRuntimeMetadataAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "multi");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(
|
||||
fixturePath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var root = document.RootElement;
|
||||
Assert.True(root.ValueKind == JsonValueKind.Array, "Result root should be an array.");
|
||||
Assert.Equal(2, root.GetArrayLength());
|
||||
|
||||
var loggingComponent = root.EnumerateArray()
|
||||
.First(element => element.GetProperty("name").GetString() == "StellaOps.Logging");
|
||||
|
||||
var metadata = loggingComponent.GetProperty("metadata");
|
||||
Assert.Equal("StellaOps.Logging", loggingComponent.GetProperty("name").GetString());
|
||||
Assert.Equal("2.5.1", loggingComponent.GetProperty("version").GetString());
|
||||
Assert.Equal("pkg:nuget/stellaops.logging@2.5.1", loggingComponent.GetProperty("purl").GetString());
|
||||
|
||||
var ridValues = metadata.EnumerateObject()
|
||||
.Where(property => property.Name.Contains(".rid", StringComparison.Ordinal))
|
||||
.Select(property => property.Value.GetString())
|
||||
.Where(value => !string.IsNullOrEmpty(value))
|
||||
.Select(value => value!)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
Assert.Contains("linux-x64", ridValues);
|
||||
Assert.Contains("osx-arm64", ridValues);
|
||||
Assert.Contains("win-arm64", ridValues);
|
||||
}
|
||||
|
||||
private sealed class StubAuthenticodeInspector : IDotNetAuthenticodeInspector
|
||||
{
|
||||
public DotNetAuthenticodeMetadata? TryInspect(string assemblyPath, CancellationToken cancellationToken)
|
||||
=> new DotNetAuthenticodeMetadata(
|
||||
Subject: "CN=StellaOps Test Signing",
|
||||
Issuer: "CN=StellaOps Root",
|
||||
NotBefore: new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
||||
NotAfter: new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
||||
Thumbprint: "AA11BB22CC33DD44EE55FF66GG77HH88II99JJ00",
|
||||
SerialNumber: "0123456789ABCDEF");
|
||||
}
|
||||
|
||||
private sealed class SingleServiceProvider : IServiceProvider
|
||||
{
|
||||
private readonly object _service;
|
||||
|
||||
public SingleServiceProvider(object service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
|
||||
public object? GetService(Type serviceType)
|
||||
=> serviceType == typeof(IDotNetAuthenticodeInspector) ? _service : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "fake-java",
|
||||
"componentKey": "bin::sha256:deadbeef",
|
||||
"name": "example-cli",
|
||||
"type": "bin",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"sha256": "deadbeef"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "binary",
|
||||
"locator": "usr/local/bin/example",
|
||||
"sha256": "deadbeef"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "fake-java",
|
||||
"componentKey": "purl::pkg:maven/org.example/example-lib@1.2.3",
|
||||
"purl": "pkg:maven/org.example/example-lib@1.2.3",
|
||||
"name": "example-lib",
|
||||
"version": "1.2.3",
|
||||
"type": "maven",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"artifactId": "example-lib",
|
||||
"groupId": "org.example"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "pom.properties",
|
||||
"locator": "META-INF/maven/org.example/example-lib/pom.properties",
|
||||
"sha256": "abc123"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "fake-node",
|
||||
"componentKey": "purl::pkg:npm/example-package@4.5.6",
|
||||
"purl": "pkg:npm/example-package@4.5.6",
|
||||
"name": "example-package",
|
||||
"version": "4.5.6",
|
||||
"type": "npm",
|
||||
"usedByEntrypoint": true,
|
||||
"metadata": {
|
||||
"workspace": "packages/example"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "package.json",
|
||||
"locator": "packages/example/package.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
sample
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0/osx-arm64"
|
||||
},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"AppA/2.0.0": {
|
||||
"dependencies": {
|
||||
"StellaOps.Toolkit": "1.2.3",
|
||||
"StellaOps.Logging": "2.5.1"
|
||||
}
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"dependencies": {
|
||||
"StellaOps.Logging": "2.5.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Toolkit.dll": {
|
||||
"assemblyVersion": "1.2.3.0",
|
||||
"fileVersion": "1.2.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Logging.dll": {
|
||||
"assemblyVersion": "2.5.1.0",
|
||||
"fileVersion": "2.5.1.12345"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/linux-x64": {
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/linux-x64/native/libstellaops.toolkit.so": {
|
||||
"rid": "linux-x64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"runtimes/linux-x64/lib/net10.0/StellaOps.Logging.dll": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/osx-arm64": {
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/osx-arm64/native/libstellaops.toolkit.dylib": {
|
||||
"rid": "osx-arm64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"runtimes/osx-arm64/lib/net10.0/StellaOps.Logging.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"AppA/2.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"path": "stellaops.toolkit/1.2.3",
|
||||
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_LOGGING_SHA==",
|
||||
"path": "stellaops.logging/2.5.1",
|
||||
"hashPath": "stellaops.logging.2.5.1.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net10.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.1"
|
||||
},
|
||||
"frameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.1"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.AspNetCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
{
|
||||
"name": "StellaOps.Hosting",
|
||||
"version": "2.0.0"
|
||||
}
|
||||
],
|
||||
"runtimeGraph": {
|
||||
"runtimes": {
|
||||
"osx-arm64": {
|
||||
"fallbacks": [
|
||||
"osx",
|
||||
"unix"
|
||||
]
|
||||
},
|
||||
"linux-x64": {
|
||||
"fallbacks": [
|
||||
"linux",
|
||||
"unix"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0/win-arm64"
|
||||
},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"AppB/3.1.0": {
|
||||
"dependencies": {
|
||||
"StellaOps.Toolkit": "1.2.3",
|
||||
"StellaOps.Logging": "2.5.1"
|
||||
}
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Toolkit.dll": {
|
||||
"assemblyVersion": "1.2.3.0",
|
||||
"fileVersion": "1.2.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Logging.dll": {
|
||||
"assemblyVersion": "2.5.1.0",
|
||||
"fileVersion": "2.5.1.12345"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/win-arm64": {
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/win-arm64/native/stellaops.toolkit.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/win-arm64/native/stellaops.logging.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/linux-arm64": {
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"runtimes/linux-arm64/lib/net10.0/StellaOps.Logging.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"AppB/3.1.0": {
|
||||
"type": "project",
|
||||
"serviceable": false
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"path": "stellaops.toolkit/1.2.3",
|
||||
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_LOGGING_SHA==",
|
||||
"path": "stellaops.logging/2.5.1",
|
||||
"hashPath": "stellaops.logging.2.5.1.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net10.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
"frameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.WindowsDesktop.App",
|
||||
"version": "10.0.0"
|
||||
}
|
||||
],
|
||||
"additionalProbingPaths": [
|
||||
"C:/Users/runner/.nuget/packages"
|
||||
],
|
||||
"runtimeGraph": {
|
||||
"runtimes": {
|
||||
"win-arm64": {
|
||||
"fallbacks": [
|
||||
"win",
|
||||
"any"
|
||||
]
|
||||
},
|
||||
"linux-arm64": {
|
||||
"fallbacks": [
|
||||
"linux",
|
||||
"unix"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "dotnet",
|
||||
"componentKey": "purl::pkg:nuget/stellaops.logging@2.5.1",
|
||||
"purl": "pkg:nuget/stellaops.logging@2.5.1",
|
||||
"name": "StellaOps.Logging",
|
||||
"version": "2.5.1",
|
||||
"type": "nuget",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"assembly[0].assetPath": "lib/net10.0/StellaOps.Logging.dll",
|
||||
"assembly[0].fileVersion": "2.5.1.12345",
|
||||
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[0].version": "2.5.1.0",
|
||||
"assembly[1].assetPath": "runtimes/linux-arm64/lib/net10.0/StellaOps.Logging.dll",
|
||||
"assembly[1].rid[0]": "linux-arm64",
|
||||
"assembly[1].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[2].assetPath": "runtimes/linux-x64/lib/net10.0/StellaOps.Logging.dll",
|
||||
"assembly[2].rid[0]": "linux-x64",
|
||||
"assembly[2].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[3].assetPath": "runtimes/osx-arm64/lib/net10.0/StellaOps.Logging.dll",
|
||||
"assembly[3].rid[0]": "osx-arm64",
|
||||
"assembly[3].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"deps.path[0]": "AppA.deps.json",
|
||||
"deps.path[1]": "AppB.deps.json",
|
||||
"deps.rid[0]": "linux-arm64",
|
||||
"deps.rid[1]": "linux-x64",
|
||||
"deps.rid[2]": "osx-arm64",
|
||||
"deps.rid[3]": "win-arm64",
|
||||
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"license.expression[0]": "Apache-2.0",
|
||||
"native[0].assetPath": "runtimes/win-arm64/native/stellaops.logging.dll",
|
||||
"native[0].rid[0]": "win-arm64",
|
||||
"native[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"package.hashPath[0]": "stellaops.logging.2.5.1.nupkg.sha512",
|
||||
"package.id": "StellaOps.Logging",
|
||||
"package.id.normalized": "stellaops.logging",
|
||||
"package.path[0]": "stellaops.logging/2.5.1",
|
||||
"package.serviceable": "true",
|
||||
"package.sha512[0]": "sha512-FAKE_LOGGING_SHA==",
|
||||
"package.version": "2.5.1",
|
||||
"provenance": "manifest"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "AppA.deps.json",
|
||||
"value": "StellaOps.Logging/2.5.1"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "AppB.deps.json",
|
||||
"value": "StellaOps.Logging/2.5.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "dotnet",
|
||||
"componentKey": "purl::pkg:nuget/stellaops.toolkit@1.2.3",
|
||||
"purl": "pkg:nuget/stellaops.toolkit@1.2.3",
|
||||
"name": "StellaOps.Toolkit",
|
||||
"version": "1.2.3",
|
||||
"type": "nuget",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"assembly[0].assetPath": "lib/net10.0/StellaOps.Toolkit.dll",
|
||||
"assembly[0].fileVersion": "1.2.3.0",
|
||||
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[0].version": "1.2.3.0",
|
||||
"deps.dependency[0]": "stellaops.logging",
|
||||
"deps.path[0]": "AppA.deps.json",
|
||||
"deps.path[1]": "AppB.deps.json",
|
||||
"deps.rid[0]": "linux-x64",
|
||||
"deps.rid[1]": "osx-arm64",
|
||||
"deps.rid[2]": "win-arm64",
|
||||
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"license.file.sha256[0]": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c",
|
||||
"license.file[0]": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
|
||||
"native[0].assetPath": "runtimes/linux-x64/native/libstellaops.toolkit.so",
|
||||
"native[0].rid[0]": "linux-x64",
|
||||
"native[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"native[1].assetPath": "runtimes/osx-arm64/native/libstellaops.toolkit.dylib",
|
||||
"native[1].rid[0]": "osx-arm64",
|
||||
"native[1].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"native[2].assetPath": "runtimes/win-arm64/native/stellaops.toolkit.dll",
|
||||
"native[2].rid[0]": "win-arm64",
|
||||
"native[2].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"package.hashPath[0]": "stellaops.toolkit.1.2.3.nupkg.sha512",
|
||||
"package.id": "StellaOps.Toolkit",
|
||||
"package.id.normalized": "stellaops.toolkit",
|
||||
"package.path[0]": "stellaops.toolkit/1.2.3",
|
||||
"package.serviceable": "true",
|
||||
"package.sha512[0]": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"package.version": "1.2.3",
|
||||
"provenance": "manifest"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "AppA.deps.json",
|
||||
"value": "StellaOps.Toolkit/1.2.3"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "AppB.deps.json",
|
||||
"value": "StellaOps.Toolkit/1.2.3"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "license",
|
||||
"locator": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
|
||||
"sha256": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
StellaOps Logging
|
||||
|
||||
Copyright (c) 2025 StellaOps.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>StellaOps.Logging</id>
|
||||
<version>2.5.1</version>
|
||||
<authors>StellaOps</authors>
|
||||
<description>Logging sample package for analyzer fixtures.</description>
|
||||
<license type="expression">Apache-2.0</license>
|
||||
<licenseUrl>https://stella-ops.example/licenses/logging</licenseUrl>
|
||||
<projectUrl>https://stella-ops.example/projects/logging</projectUrl>
|
||||
</metadata>
|
||||
</package>
|
||||
@@ -0,0 +1,7 @@
|
||||
StellaOps Toolkit License
|
||||
=========================
|
||||
|
||||
This sample license is provided for test fixtures only.
|
||||
|
||||
Permission is granted to use, copy, modify, and distribute this fixture
|
||||
for the purpose of automated testing.
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>StellaOps.Toolkit</id>
|
||||
<version>1.2.3</version>
|
||||
<authors>StellaOps</authors>
|
||||
<description>Toolkit sample package for analyzer fixtures.</description>
|
||||
<license type="file">LICENSE.txt</license>
|
||||
<licenseUrl>https://stella-ops.example/licenses/toolkit</licenseUrl>
|
||||
</metadata>
|
||||
</package>
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0",
|
||||
"signature": null
|
||||
},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0/linux-x64": {
|
||||
"MyApp/1.0.0": {
|
||||
"dependencies": {
|
||||
"StellaOps.Toolkit": "1.2.3",
|
||||
"StellaOps.Runtime.SelfContained": "2.1.0"
|
||||
},
|
||||
"runtime": {
|
||||
"MyApp.dll": {}
|
||||
}
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Toolkit.dll": {
|
||||
"assemblyVersion": "1.2.3.0",
|
||||
"fileVersion": "1.2.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Runtime.SelfContained/2.1.0": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/linux-x64/native/libstellaopsnative.so": {
|
||||
"rid": "linux-x64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/win-x64": {
|
||||
"MyApp/1.0.0": {
|
||||
"dependencies": {
|
||||
"StellaOps.Toolkit": "1.2.3",
|
||||
"StellaOps.Runtime.SelfContained": "2.1.0"
|
||||
},
|
||||
"runtime": {
|
||||
"MyApp.dll": {}
|
||||
}
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Toolkit.dll": {
|
||||
"assemblyVersion": "1.2.3.0",
|
||||
"fileVersion": "1.2.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Runtime.SelfContained/2.1.0": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/win-x64/native/stellaopsnative.dll": {
|
||||
"rid": "win-x64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"MyApp/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": "",
|
||||
"path": null,
|
||||
"hashPath": null
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"path": "stellaops.toolkit/1.2.3",
|
||||
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Runtime.SelfContained/2.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_RUNTIME_SHA==",
|
||||
"path": "stellaops.runtime.selfcontained/2.1.0",
|
||||
"hashPath": "stellaops.runtime.selfcontained.2.1.0.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net10.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
"includedFrameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.DotNetAppHost",
|
||||
"version": "10.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "dotnet",
|
||||
"componentKey": "purl::pkg:nuget/stellaops.runtime.selfcontained@2.1.0",
|
||||
"purl": "pkg:nuget/stellaops.runtime.selfcontained@2.1.0",
|
||||
"name": "StellaOps.Runtime.SelfContained",
|
||||
"version": "2.1.0",
|
||||
"type": "nuget",
|
||||
"usedByEntrypoint": true,
|
||||
"metadata": {
|
||||
"deps.path[0]": "MyApp.deps.json",
|
||||
"deps.rid[0]": "linux-x64",
|
||||
"deps.rid[1]": "win-x64",
|
||||
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"license.expression[0]": "Apache-2.0",
|
||||
"native[0].assetPath": "runtimes/linux-x64/native/libstellaopsnative.so",
|
||||
"native[0].path": "runtimes/linux-x64/native/libstellaopsnative.so",
|
||||
"native[0].rid[0]": "linux-x64",
|
||||
"native[0].sha256": "6cf3d2a487d6a42fc7c3e2edbc452224e99a3656287a534f1164ee6ec9daadf0",
|
||||
"native[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"native[1].assetPath": "runtimes/win-x64/native/stellaopsnative.dll",
|
||||
"native[1].rid[0]": "win-x64",
|
||||
"native[1].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"package.hashPath[0]": "stellaops.runtime.selfcontained.2.1.0.nupkg.sha512",
|
||||
"package.id": "StellaOps.Runtime.SelfContained",
|
||||
"package.id.normalized": "stellaops.runtime.selfcontained",
|
||||
"package.path[0]": "stellaops.runtime.selfcontained/2.1.0",
|
||||
"package.serviceable": "true",
|
||||
"package.sha512[0]": "sha512-FAKE_RUNTIME_SHA==",
|
||||
"package.version": "2.1.0",
|
||||
"provenance": "manifest"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "MyApp.deps.json",
|
||||
"value": "StellaOps.Runtime.SelfContained/2.1.0"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "native",
|
||||
"locator": "runtimes/linux-x64/native/libstellaopsnative.so",
|
||||
"value": "runtimes/linux-x64/native/libstellaopsnative.so",
|
||||
"sha256": "6cf3d2a487d6a42fc7c3e2edbc452224e99a3656287a534f1164ee6ec9daadf0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "dotnet",
|
||||
"componentKey": "purl::pkg:nuget/stellaops.toolkit@1.2.3",
|
||||
"purl": "pkg:nuget/stellaops.toolkit@1.2.3",
|
||||
"name": "StellaOps.Toolkit",
|
||||
"version": "1.2.3",
|
||||
"type": "nuget",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"assembly[0].assetPath": "lib/net10.0/StellaOps.Toolkit.dll",
|
||||
"assembly[0].fileVersion": "1.2.3.0",
|
||||
"assembly[0].rid[0]": "linux-x64",
|
||||
"assembly[0].rid[1]": "win-x64",
|
||||
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[0].version": "1.2.3.0",
|
||||
"deps.path[0]": "MyApp.deps.json",
|
||||
"deps.rid[0]": "linux-x64",
|
||||
"deps.rid[1]": "win-x64",
|
||||
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"license.file.sha256[0]": "f94d89a576c63e8ba6ee01760c52fa7861ba609491d7c6e6c01ead5ca66b6048",
|
||||
"license.file[0]": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
|
||||
"package.hashPath[0]": "stellaops.toolkit.1.2.3.nupkg.sha512",
|
||||
"package.id": "StellaOps.Toolkit",
|
||||
"package.id.normalized": "stellaops.toolkit",
|
||||
"package.path[0]": "stellaops.toolkit/1.2.3",
|
||||
"package.serviceable": "true",
|
||||
"package.sha512[0]": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"package.version": "1.2.3",
|
||||
"provenance": "manifest"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "MyApp.deps.json",
|
||||
"value": "StellaOps.Toolkit/1.2.3"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "license",
|
||||
"locator": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
|
||||
"sha256": "f94d89a576c63e8ba6ee01760c52fa7861ba609491d7c6e6c01ead5ca66b6048"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>StellaOps.Runtime.SelfContained</id>
|
||||
<version>2.1.0</version>
|
||||
<authors>StellaOps</authors>
|
||||
<description>Runtime bundle used for self-contained analyzer fixtures.</description>
|
||||
<license type="expression">Apache-2.0</license>
|
||||
<licenseUrl>https://stella-ops.example/licenses/runtime</licenseUrl>
|
||||
</metadata>
|
||||
</package>
|
||||
@@ -0,0 +1,6 @@
|
||||
StellaOps Toolkit License
|
||||
=========================
|
||||
|
||||
Reusable toolkit licensing terms for analyzer fixtures.
|
||||
|
||||
This document is intentionally short for deterministic hashing tests.
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>StellaOps.Toolkit</id>
|
||||
<version>1.2.3</version>
|
||||
<authors>StellaOps</authors>
|
||||
<description>Toolkit package for self-contained analyzer fixtures.</description>
|
||||
<license type="file">LICENSE.txt</license>
|
||||
<licenseUrl>https://stella-ops.example/licenses/toolkit</licenseUrl>
|
||||
</metadata>
|
||||
</package>
|
||||
@@ -0,0 +1 @@
|
||||
ELF FAKE STELLAOPS NATIVE LIB
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0/linux-x64"
|
||||
},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"Signed.App/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging": "9.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging/9.0.0": {
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.Extensions.Logging.dll": {
|
||||
"assemblyVersion": "9.0.0.0",
|
||||
"fileVersion": "9.0.24.52809"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/linux-x64": {
|
||||
"Microsoft.Extensions.Logging/9.0.0": {
|
||||
"runtime": {
|
||||
"runtimes/linux-x64/lib/net9.0/Microsoft.Extensions.Logging.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"Signed.App/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false
|
||||
},
|
||||
"Microsoft.Extensions.Logging/9.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_LOGGING_SHA==",
|
||||
"path": "microsoft.extensions.logging/9.0.0",
|
||||
"hashPath": "microsoft.extensions.logging.9.0.0.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net10.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "dotnet",
|
||||
"componentKey": "purl::pkg:nuget/microsoft.extensions.logging@9.0.0",
|
||||
"purl": "pkg:nuget/microsoft.extensions.logging@9.0.0",
|
||||
"name": "Microsoft.Extensions.Logging",
|
||||
"version": "9.0.0",
|
||||
"type": "nuget",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"assembly[0].assetPath": "lib/net9.0/Microsoft.Extensions.Logging.dll",
|
||||
"assembly[0].fileVersion": "9.0.24.52809",
|
||||
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[0].version": "9.0.0.0",
|
||||
"assembly[1].assetPath": "runtimes/linux-x64/lib/net9.0/Microsoft.Extensions.Logging.dll",
|
||||
"assembly[1].rid[0]": "linux-x64",
|
||||
"assembly[1].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"deps.path[0]": "Signed.App.deps.json",
|
||||
"deps.rid[0]": "linux-x64",
|
||||
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"license.expression[0]": "MIT",
|
||||
"package.hashPath[0]": "microsoft.extensions.logging.9.0.0.nupkg.sha512",
|
||||
"package.id": "Microsoft.Extensions.Logging",
|
||||
"package.id.normalized": "microsoft.extensions.logging",
|
||||
"package.path[0]": "microsoft.extensions.logging/9.0.0",
|
||||
"package.serviceable": "true",
|
||||
"package.sha512[0]": "sha512-FAKE_LOGGING_SHA==",
|
||||
"package.version": "9.0.0",
|
||||
"provenance": "manifest"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "Signed.App.deps.json",
|
||||
"value": "Microsoft.Extensions.Logging/9.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Microsoft.Extensions.Logging</id>
|
||||
<version>9.0.0</version>
|
||||
<authors>Microsoft</authors>
|
||||
<description>Signed logging package fixture.</description>
|
||||
<license type="expression">MIT</license>
|
||||
<licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
|
||||
</metadata>
|
||||
</package>
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0/linux-x64"
|
||||
},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"Sample.App/1.0.0": {
|
||||
"dependencies": {
|
||||
"StellaOps.Toolkit": "1.2.3",
|
||||
"Microsoft.Extensions.Logging": "9.0.0"
|
||||
}
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging": "9.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Toolkit.dll": {
|
||||
"assemblyVersion": "1.2.3.0",
|
||||
"fileVersion": "1.2.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging/9.0.0": {
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.Extensions.Logging.dll": {
|
||||
"assemblyVersion": "9.0.0.0",
|
||||
"fileVersion": "9.0.24.52809"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/linux-x64": {
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtime": {
|
||||
"runtimes/linux-x64/native/libstellaops.toolkit.so": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging/9.0.0": {
|
||||
"runtime": {
|
||||
"runtimes/linux-x64/lib/net9.0/Microsoft.Extensions.Logging.dll": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/win-x86": {
|
||||
"Microsoft.Extensions.Logging/9.0.0": {
|
||||
"runtime": {
|
||||
"runtimes/win-x86/lib/net9.0/Microsoft.Extensions.Logging.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"Sample.App/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"path": "stellaops.toolkit/1.2.3",
|
||||
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging/9.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_LOGGING_SHA==",
|
||||
"path": "microsoft.extensions.logging/9.0.0",
|
||||
"hashPath": "microsoft.extensions.logging.9.0.0.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net10.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
"frameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.AspNetCore.App",
|
||||
"version": "10.0.0"
|
||||
}
|
||||
],
|
||||
"runtimeGraph": {
|
||||
"runtimes": {
|
||||
"linux-x64": {
|
||||
"fallbacks": [
|
||||
"linux",
|
||||
"unix"
|
||||
]
|
||||
},
|
||||
"win-x86": {
|
||||
"fallbacks": [
|
||||
"win",
|
||||
"any"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "dotnet",
|
||||
"componentKey": "purl::pkg:nuget/microsoft.extensions.logging@9.0.0",
|
||||
"purl": "pkg:nuget/microsoft.extensions.logging@9.0.0",
|
||||
"name": "Microsoft.Extensions.Logging",
|
||||
"version": "9.0.0",
|
||||
"type": "nuget",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"assembly[0].assetPath": "lib/net9.0/Microsoft.Extensions.Logging.dll",
|
||||
"assembly[0].fileVersion": "9.0.24.52809",
|
||||
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[0].version": "9.0.0.0",
|
||||
"assembly[1].assetPath": "runtimes/linux-x64/lib/net9.0/Microsoft.Extensions.Logging.dll",
|
||||
"assembly[1].rid[0]": "linux-x64",
|
||||
"assembly[1].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[2].assetPath": "runtimes/win-x86/lib/net9.0/Microsoft.Extensions.Logging.dll",
|
||||
"assembly[2].rid[0]": "win-x86",
|
||||
"assembly[2].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"deps.path[0]": "Sample.App.deps.json",
|
||||
"deps.rid[0]": "linux-x64",
|
||||
"deps.rid[1]": "win-x86",
|
||||
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"license.expression[0]": "MIT",
|
||||
"package.hashPath[0]": "microsoft.extensions.logging.9.0.0.nupkg.sha512",
|
||||
"package.id": "Microsoft.Extensions.Logging",
|
||||
"package.id.normalized": "microsoft.extensions.logging",
|
||||
"package.path[0]": "microsoft.extensions.logging/9.0.0",
|
||||
"package.serviceable": "true",
|
||||
"package.sha512[0]": "sha512-FAKE_LOGGING_SHA==",
|
||||
"package.version": "9.0.0",
|
||||
"provenance": "manifest"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "Sample.App.deps.json",
|
||||
"value": "Microsoft.Extensions.Logging/9.0.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "dotnet",
|
||||
"componentKey": "purl::pkg:nuget/stellaops.toolkit@1.2.3",
|
||||
"purl": "pkg:nuget/stellaops.toolkit@1.2.3",
|
||||
"name": "StellaOps.Toolkit",
|
||||
"version": "1.2.3",
|
||||
"type": "nuget",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"assembly[0].assetPath": "lib/net10.0/StellaOps.Toolkit.dll",
|
||||
"assembly[0].fileVersion": "1.2.3.0",
|
||||
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[0].version": "1.2.3.0",
|
||||
"deps.dependency[0]": "microsoft.extensions.logging",
|
||||
"deps.path[0]": "Sample.App.deps.json",
|
||||
"deps.rid[0]": "linux-x64",
|
||||
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"license.file.sha256[0]": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c",
|
||||
"license.file[0]": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
|
||||
"package.hashPath[0]": "stellaops.toolkit.1.2.3.nupkg.sha512",
|
||||
"package.id": "StellaOps.Toolkit",
|
||||
"package.id.normalized": "stellaops.toolkit",
|
||||
"package.path[0]": "stellaops.toolkit/1.2.3",
|
||||
"package.serviceable": "true",
|
||||
"package.sha512[0]": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"package.version": "1.2.3",
|
||||
"provenance": "manifest"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "Sample.App.deps.json",
|
||||
"value": "StellaOps.Toolkit/1.2.3"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "license",
|
||||
"locator": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
|
||||
"sha256": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Microsoft.Extensions.Logging</id>
|
||||
<version>9.0.0</version>
|
||||
<authors>Microsoft</authors>
|
||||
<description>Logging abstractions for StellaOps test fixture.</description>
|
||||
<license type="expression">MIT</license>
|
||||
<licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
|
||||
</metadata>
|
||||
</package>
|
||||
@@ -0,0 +1,7 @@
|
||||
StellaOps Toolkit License
|
||||
=========================
|
||||
|
||||
This sample license is provided for test fixtures only.
|
||||
|
||||
Permission is granted to use, copy, modify, and distribute this fixture
|
||||
for the purpose of automated testing.
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>StellaOps.Toolkit</id>
|
||||
<version>1.2.3</version>
|
||||
<authors>StellaOps</authors>
|
||||
<description>Toolkit sample package for analyzer fixtures.</description>
|
||||
<license type="file">LICENSE.txt</license>
|
||||
<licenseUrl>https://stella-ops.example/licenses/toolkit</licenseUrl>
|
||||
</metadata>
|
||||
</package>
|
||||
12
src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Tests/Fixtures/lang/rust/simple/Cargo.lock
generated
Normal file
12
src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Tests/Fixtures/lang/rust/simple/Cargo.lock
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
[[package]]
|
||||
name = "my_app"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.188"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abc123"
|
||||
@@ -0,0 +1,62 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "rust",
|
||||
"componentKey": "purl::pkg:cargo/my_app@0.1.0",
|
||||
"purl": "pkg:cargo/my_app@0.1.0",
|
||||
"name": "my_app",
|
||||
"version": "0.1.0",
|
||||
"type": "cargo",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"cargo.lock.path": "Cargo.lock",
|
||||
"fingerprint.profile": "debug",
|
||||
"fingerprint.targetKind": "bin",
|
||||
"source": "registry\u002Bhttps://github.com/rust-lang/crates.io-index"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "cargo.fingerprint",
|
||||
"locator": "target/debug/.fingerprint/my_app-1234567890abcdef/bin-my_app-1234567890abcdef.json",
|
||||
"value": "bin"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "cargo.lock",
|
||||
"locator": "Cargo.lock",
|
||||
"value": "my_app 0.1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "rust",
|
||||
"componentKey": "purl::pkg:cargo/serde@1.0.188",
|
||||
"purl": "pkg:cargo/serde@1.0.188",
|
||||
"name": "serde",
|
||||
"version": "1.0.188",
|
||||
"type": "cargo",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"cargo.lock.path": "Cargo.lock",
|
||||
"checksum": "abc123",
|
||||
"fingerprint.profile": "release",
|
||||
"fingerprint.targetKind": "lib",
|
||||
"source": "registry\u002Bhttps://github.com/rust-lang/crates.io-index"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "cargo.fingerprint",
|
||||
"locator": "target/debug/.fingerprint/serde-abcdef1234567890/libserde-abcdef1234567890.json",
|
||||
"value": "lib"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "cargo.lock",
|
||||
"locator": "Cargo.lock",
|
||||
"value": "serde 1.0.188",
|
||||
"sha256": "abc123"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"pkgid": "my_app 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"profile": "debug",
|
||||
"target_kind": ["bin"]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user