feat: Update analyzer fixtures and metadata for improved license handling and provenance tracking

- Added license expressions and provenance fields to expected JSON outputs for .NET and Rust analyzers.
- Introduced new .nuspec files for StellaOps.Runtime.SelfContained and StellaOps.Toolkit packages, including license information.
- Created LICENSE.txt files for both toolkit packages with clear licensing terms.
- Updated expected JSON for signed and simple analyzers to include license information and provenance.
- Enhanced the SPRINTS_LANG_IMPLEMENTATION_PLAN.md with detailed progress and future sprint outlines, ensuring clarity on deliverables and acceptance metrics.
This commit is contained in:
2025-10-23 07:57:16 +03:00
parent 224c76c276
commit 09d21d977c
17 changed files with 1857 additions and 1343 deletions

View File

@@ -118,7 +118,7 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- Team Notify Worker Guild: read EXECPLAN.md Wave 4 and SPRINTS.md rows for `src/StellaOps.Notify.Worker/TASKS.md`. Focus on NOTIFY-WORKER-15-204 (TODO). Confirm prerequisites (internal: NOTIFY-WORKER-15-203 (Wave 3)) before starting and report status in module TASKS.md.
- Team Policy Guild, Scanner WebService Guild: read EXECPLAN.md Wave 4 and SPRINTS.md rows for `src/StellaOps.Policy/TASKS.md`. Focus on POLICY-RUNTIME-17-201 (TODO). Confirm prerequisites (internal: ZASTAVA-OBS-17-005 (Wave 3)) before starting and report status in module TASKS.md.
- Team Scheduler Worker Guild: read EXECPLAN.md Wave 4 and SPRINTS.md rows for `src/StellaOps.Scheduler.Worker/TASKS.md`. Focus on SCHED-WORKER-16-204 (TODO). Confirm prerequisites (internal: SCHED-WORKER-16-203 (Wave 3)) before starting and report status in module TASKS.md.
- Team TBD: read EXECPLAN.md Wave 4 and SPRINTS.md rows for `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md`. Focus on SCANNER-ANALYZERS-LANG-10-307D (TODO), SCANNER-ANALYZERS-LANG-10-307G (TODO), SCANNER-ANALYZERS-LANG-10-307P (TODO), SCANNER-ANALYZERS-LANG-10-307R (TODO). Confirm prerequisites (internal: SCANNER-ANALYZERS-LANG-10-303C (Wave 3), SCANNER-ANALYZERS-LANG-10-304C (Wave 3), SCANNER-ANALYZERS-LANG-10-305C (Wave 3), SCANNER-ANALYZERS-LANG-10-306C (Wave 3)) before starting and report status in module TASKS.md.
- Team TBD: read EXECPLAN.md Wave 4 and SPRINTS.md rows for `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md`. Focus on SCANNER-ANALYZERS-LANG-10-307D (DONE 2025-10-22), SCANNER-ANALYZERS-LANG-10-307G (TODO), SCANNER-ANALYZERS-LANG-10-307P (TODO), SCANNER-ANALYZERS-LANG-10-307R (TODO). Confirm prerequisites (internal: SCANNER-ANALYZERS-LANG-10-303C (Wave 3), SCANNER-ANALYZERS-LANG-10-304C (Wave 3), SCANNER-ANALYZERS-LANG-10-305C (Wave 3), SCANNER-ANALYZERS-LANG-10-306C (Wave 3)) before starting and report status in module TASKS.md.
### Wave 5
- Team Excititor Connectors Stella: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Excititor.Connectors.StellaOpsMirror/TASKS.md`. Focus on EXCITITOR-CONN-STELLA-07-003 (TODO). Confirm prerequisites (internal: EXCITITOR-CONN-STELLA-07-002 (Wave 4)) before starting and report status in module TASKS.md.
@@ -927,9 +927,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
- **Sprint 10** · Backlog
- Team: TBD
- Path: `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`
1. [TODO] SCANNER-ANALYZERS-LANG-10-307D — Integrate shared helpers (license mapping, quiet provenance) and concurrency-safe caches.
1. [DONE 2025-10-22] SCANNER-ANALYZERS-LANG-10-307D — Integrate shared helpers (license mapping, quiet provenance) and concurrency-safe caches.
• Prereqs: SCANNER-ANALYZERS-LANG-10-305C (Wave 3)
• Current: TODO
• Current: DONE 2025-10-22
- Path: `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`
1. [TODO] SCANNER-ANALYZERS-LANG-10-307G — Wire shared helpers (license mapping, usage flags) and ensure concurrency-safe buffer reuse.
• Prereqs: SCANNER-ANALYZERS-LANG-10-304C (Wave 3)

View File

@@ -0,0 +1,332 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Xml;
namespace StellaOps.Scanner.Analyzers.Lang.DotNet.Internal;
internal static class DotNetFileMetadataCache
{
private static readonly ConcurrentDictionary<DotNetFileCacheKey, Optional<string>> Sha256Cache = new();
private static readonly ConcurrentDictionary<DotNetFileCacheKey, Optional<AssemblyName>> AssemblyCache = new();
private static readonly ConcurrentDictionary<DotNetFileCacheKey, Optional<FileVersionInfo>> VersionCache = new();
public static bool TryGetSha256(string path, out string? sha256)
=> TryGet(path, Sha256Cache, ComputeSha256, out sha256);
public static bool TryGetAssemblyName(string path, out AssemblyName? assemblyName)
=> TryGet(path, AssemblyCache, TryReadAssemblyName, out assemblyName);
public static bool TryGetFileVersionInfo(string path, out FileVersionInfo? versionInfo)
=> TryGet(path, VersionCache, TryReadFileVersionInfo, out versionInfo);
private static bool TryGet<T>(string path, ConcurrentDictionary<DotNetFileCacheKey, Optional<T>> cache, Func<string, T?> resolver, out T? value)
where T : class
{
value = null;
DotNetFileCacheKey key;
try
{
var info = new FileInfo(path);
if (!info.Exists)
{
return false;
}
key = new DotNetFileCacheKey(info.FullName, info.Length, info.LastWriteTimeUtc.Ticks);
}
catch (IOException)
{
return false;
}
catch (UnauthorizedAccessException)
{
return false;
}
catch (SecurityException)
{
return false;
}
catch (ArgumentException)
{
return false;
}
catch (NotSupportedException)
{
return false;
}
var optional = cache.GetOrAdd(key, static (cacheKey, state) => CreateOptional(cacheKey.Path, state.resolver), (resolver, path));
if (!optional.HasValue)
{
return false;
}
value = optional.Value;
return value is not null;
}
private static Optional<T> CreateOptional<T>(string path, Func<string, T?> resolver) where T : class
{
try
{
var value = resolver(path);
return Optional<T>.From(value);
}
catch (FileNotFoundException)
{
return Optional<T>.None;
}
catch (FileLoadException)
{
return Optional<T>.None;
}
catch (BadImageFormatException)
{
return Optional<T>.None;
}
catch (UnauthorizedAccessException)
{
return Optional<T>.None;
}
catch (SecurityException)
{
return Optional<T>.None;
}
catch (IOException)
{
return Optional<T>.None;
}
}
private static string? ComputeSha256(string path)
{
using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
using var sha = System.Security.Cryptography.SHA256.Create();
var hash = sha.ComputeHash(stream);
return Convert.ToHexString(hash).ToLowerInvariant();
}
private static AssemblyName? TryReadAssemblyName(string path)
{
try
{
return AssemblyName.GetAssemblyName(path);
}
catch (FileNotFoundException)
{
return null;
}
catch (FileLoadException)
{
return null;
}
catch (BadImageFormatException)
{
return null;
}
catch (IOException)
{
return null;
}
}
private static FileVersionInfo? TryReadFileVersionInfo(string path)
{
try
{
return FileVersionInfo.GetVersionInfo(path);
}
catch (FileNotFoundException)
{
return null;
}
catch (IOException)
{
return null;
}
catch (UnauthorizedAccessException)
{
return null;
}
}
}
internal static class DotNetLicenseCache
{
private static readonly ConcurrentDictionary<DotNetFileCacheKey, Optional<DotNetLicenseInfo>> Licenses = new();
public static bool TryGetLicenseInfo(string nuspecPath, out DotNetLicenseInfo? info)
{
info = null;
DotNetFileCacheKey key;
try
{
var fileInfo = new FileInfo(nuspecPath);
if (!fileInfo.Exists)
{
return false;
}
key = new DotNetFileCacheKey(fileInfo.FullName, fileInfo.Length, fileInfo.LastWriteTimeUtc.Ticks);
}
catch (IOException)
{
return false;
}
catch (UnauthorizedAccessException)
{
return false;
}
catch (SecurityException)
{
return false;
}
var optional = Licenses.GetOrAdd(key, static (cacheKey, path) => CreateOptional(path), nuspecPath);
if (!optional.HasValue)
{
return false;
}
info = optional.Value;
return info is not null;
}
private static Optional<DotNetLicenseInfo> CreateOptional(string nuspecPath)
{
try
{
var info = Parse(nuspecPath);
return Optional<DotNetLicenseInfo>.From(info);
}
catch (IOException)
{
return Optional<DotNetLicenseInfo>.None;
}
catch (UnauthorizedAccessException)
{
return Optional<DotNetLicenseInfo>.None;
}
catch (SecurityException)
{
return Optional<DotNetLicenseInfo>.None;
}
catch (XmlException)
{
return Optional<DotNetLicenseInfo>.None;
}
}
private static DotNetLicenseInfo? Parse(string path)
{
using var stream = File.OpenRead(path);
using var reader = XmlReader.Create(stream, new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Ignore,
IgnoreComments = true,
IgnoreWhitespace = true,
});
var expressions = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
var files = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
var urls = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
while (reader.Read())
{
if (reader.NodeType != XmlNodeType.Element)
{
continue;
}
if (string.Equals(reader.LocalName, "license", StringComparison.OrdinalIgnoreCase))
{
var type = reader.GetAttribute("type");
var value = reader.ReadElementContentAsString()?.Trim();
if (string.IsNullOrWhiteSpace(value))
{
continue;
}
if (string.Equals(type, "expression", StringComparison.OrdinalIgnoreCase))
{
expressions.Add(value);
}
else if (string.Equals(type, "file", StringComparison.OrdinalIgnoreCase))
{
files.Add(NormalizeLicensePath(value));
}
else
{
expressions.Add(value);
}
}
else if (string.Equals(reader.LocalName, "licenseUrl", StringComparison.OrdinalIgnoreCase))
{
var value = reader.ReadElementContentAsString()?.Trim();
if (!string.IsNullOrWhiteSpace(value))
{
urls.Add(value);
}
}
}
if (expressions.Count == 0 && files.Count == 0 && urls.Count == 0)
{
return null;
}
return new DotNetLicenseInfo(
expressions.ToArray(),
files.ToArray(),
urls.ToArray());
}
private static string NormalizeLicensePath(string value)
=> value.Replace('\\', '/').Trim();
}
internal sealed record DotNetLicenseInfo(
IReadOnlyList<string> Expressions,
IReadOnlyList<string> Files,
IReadOnlyList<string> Urls);
internal readonly record struct DotNetFileCacheKey(string Path, long Length, long LastWriteTicks)
{
private readonly string _normalizedPath = OperatingSystem.IsWindows()
? Path.ToLowerInvariant()
: Path;
public bool Equals(DotNetFileCacheKey other)
=> Length == other.Length
&& LastWriteTicks == other.LastWriteTicks
&& string.Equals(_normalizedPath, other._normalizedPath, StringComparison.Ordinal);
public override int GetHashCode()
=> HashCode.Combine(_normalizedPath, Length, LastWriteTicks);
}
internal readonly struct Optional<T> where T : class
{
private Optional(bool hasValue, T? value)
{
HasValue = hasValue;
Value = value;
}
public bool HasValue { get; }
public T? Value { get; }
public static Optional<T> From(T? value)
=> value is null ? None : new Optional<T>(true, value);
public static Optional<T> None => default;
}

View File

@@ -5,6 +5,6 @@
| 1 | SCANNER-ANALYZERS-LANG-10-305A | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-307 | Parse `*.deps.json` + `runtimeconfig.json`, build RID graph, and normalize to `pkg:nuget` components. | RID graph deterministic; fixtures confirm consistent component ordering; fallback to `bin:{sha256}` documented. |
| 2 | SCANNER-ANALYZERS-LANG-10-305B | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-305A | Extract assembly metadata (strong name, file/product info) and optional Authenticode details when offline cert bundle provided. | Signing metadata captured for signed assemblies; offline trust store documented; hash validations deterministic. |
| 3 | SCANNER-ANALYZERS-LANG-10-305C | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-305B | Handle self-contained apps and native assets; merge with EntryTrace usage hints. | Self-contained fixtures map to components with RID flags; usage hints propagate; tests cover linux/win variants. |
| 4 | SCANNER-ANALYZERS-LANG-10-307D | TODO | SCANNER-ANALYZERS-LANG-10-305C | Integrate shared helpers (license mapping, quiet provenance) and concurrency-safe caches. | Shared helpers reused; concurrency tests for parallel layer scans pass; no redundant allocations. |
| 4 | SCANNER-ANALYZERS-LANG-10-307D | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-305C | Integrate shared helpers (license mapping, quiet provenance) and concurrency-safe caches. | Shared helpers reused; concurrency tests for parallel layer scans pass; no redundant allocations. |
| 5 | SCANNER-ANALYZERS-LANG-10-308D | TODO | SCANNER-ANALYZERS-LANG-10-307D | Determinism fixtures + benchmark harness; compare to competitor scanners for accuracy/perf. | Fixtures in `Fixtures/lang/dotnet/`; determinism CI guard; benchmark demonstrates lower duplication + faster runtime. |
| 6 | SCANNER-ANALYZERS-LANG-10-309D | TODO | SCANNER-ANALYZERS-LANG-10-308D | Package plug-in (manifest, DI registration) and update Offline Kit instructions. | Manifest copied to `plugins/scanner/analyzers/lang/`; Worker loads analyzer; Offline Kit doc updated. |

View File

@@ -1,76 +1,77 @@
using System;
using System;
using System.IO;
using System.Linq;
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()
};
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,
@@ -79,28 +80,51 @@ public sealed class DotNetLanguageAnalyzerTests
usageHints);
}
private sealed class StubAuthenticodeInspector : IDotNetAuthenticodeInspector
[Fact]
public async Task AnalyzerIsThreadSafeUnderConcurrencyAsync()
{
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");
}
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "selfcontained");
private sealed class SingleServiceProvider : IServiceProvider
{
private readonly object _service;
public SingleServiceProvider(object service)
var analyzers = new ILanguageAnalyzer[]
{
_service = service;
}
new DotNetLanguageAnalyzer()
};
public object? GetService(Type serviceType)
=> serviceType == typeof(IDotNetAuthenticodeInspector) ? _service : null;
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);
}
}
}
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;
}
}

View File

@@ -12,15 +12,14 @@
"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": "c22d4a6584a3bb8fad4d255d1ab9e5a80d553eec35ea8dfcc2dd750e8581d3cb",
"native[0].sha256": "6cf3d2a487d6a42fc7c3e2edbc452224e99a3656287a534f1164ee6ec9daadf0",
"native[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"native[1].assetPath": "runtimes/win-x64/native/stellaopsnative.dll",
"native[1].path": "runtimes/win-x64/native/stellaopsnative.dll",
"native[1].rid[0]": "win-x64",
"native[1].sha256": "29cddd69702aedc715050304bec85aad2ae017ee1f9390df5e68ebe79a8d4745",
"native[1].tfm[0]": ".NETCoreApp,Version=v10.0",
"package.hashPath[0]": "stellaops.runtime.selfcontained.2.1.0.nupkg.sha512",
"package.id": "StellaOps.Runtime.SelfContained",
@@ -28,7 +27,8 @@
"package.path[0]": "stellaops.runtime.selfcontained/2.1.0",
"package.serviceable": "true",
"package.sha512[0]": "sha512-FAKE_RUNTIME_SHA==",
"package.version": "2.1.0"
"package.version": "2.1.0",
"provenance": "manifest"
},
"evidence": [
{
@@ -42,14 +42,7 @@
"source": "native",
"locator": "runtimes/linux-x64/native/libstellaopsnative.so",
"value": "runtimes/linux-x64/native/libstellaopsnative.so",
"sha256": "c22d4a6584a3bb8fad4d255d1ab9e5a80d553eec35ea8dfcc2dd750e8581d3cb"
},
{
"kind": "file",
"source": "native",
"locator": "runtimes/win-x64/native/stellaopsnative.dll",
"value": "runtimes/win-x64/native/stellaopsnative.dll",
"sha256": "29cddd69702aedc715050304bec85aad2ae017ee1f9390df5e68ebe79a8d4745"
"sha256": "6cf3d2a487d6a42fc7c3e2edbc452224e99a3656287a534f1164ee6ec9daadf0"
}
]
},
@@ -60,41 +53,41 @@
"name": "StellaOps.Toolkit",
"version": "1.2.3",
"type": "nuget",
"usedByEntrypoint": true,
"usedByEntrypoint": false,
"metadata": {
"assembly[0].assetPath": "lib/net10.0/StellaOps.Toolkit.dll",
"assembly[0].fileVersion": "1.2.3.0",
"assembly[0].path": "lib/net10.0/StellaOps.Toolkit.dll",
"assembly[0].rid[0]": "linux-x64",
"assembly[0].rid[1]": "win-x64",
"assembly[0].sha256": "5b82fd11cf6c2ba6b351592587c4203f6af48b89427b954903534eac0e9f17f7",
"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"
"package.version": "1.2.3",
"provenance": "manifest"
},
"evidence": [
{
"kind": "file",
"source": "assembly",
"locator": "lib/net10.0/StellaOps.Toolkit.dll",
"value": "lib/net10.0/StellaOps.Toolkit.dll",
"sha256": "5b82fd11cf6c2ba6b351592587c4203f6af48b89427b954903534eac0e9f17f7"
},
{
"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"
}
]
}

View File

@@ -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>

View File

@@ -0,0 +1,6 @@
StellaOps Toolkit License
=========================
Reusable toolkit licensing terms for analyzer fixtures.
This document is intentionally short for deterministic hashing tests.

View File

@@ -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>

View File

@@ -9,21 +9,7 @@
"usedByEntrypoint": false,
"metadata": {
"assembly[0].assetPath": "lib/net9.0/Microsoft.Extensions.Logging.dll",
"assembly[0].authenticode.issuer": "CN=StellaOps Root",
"assembly[0].authenticode.notAfter": "2026-01-01T00:00:00.000Z",
"assembly[0].authenticode.notBefore": "2025-01-01T00:00:00.000Z",
"assembly[0].authenticode.serialNumber": "0123456789ABCDEF",
"assembly[0].authenticode.subject": "CN=StellaOps Test Signing",
"assembly[0].authenticode.thumbprint": "AA11BB22CC33DD44EE55FF66GG77HH88II99JJ00",
"assembly[0].company": "Microsoft Corporation",
"assembly[0].fileDescription": "Microsoft.Extensions.Logging",
"assembly[0].fileVersion": "9.0.24.52809",
"assembly[0].path": "packages/microsoft.extensions.logging/9.0.0/lib/net9.0/Microsoft.Extensions.Logging.dll",
"assembly[0].product": "Microsoft\u00ae .NET",
"assembly[0].productVersion": "9.0.0+9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3",
"assembly[0].publicKeyToken": "adb9793829ddae60",
"assembly[0].sha256": "faed6cb5c9ca0d6077feaeb2df251251adccf0241f7a80b91c58e014cd5ad48f",
"assembly[0].strongName": "Microsoft.Extensions.Logging, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
"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",
@@ -32,22 +18,17 @@
"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"
"package.version": "9.0.0",
"provenance": "manifest"
},
"evidence": [
{
"kind": "file",
"source": "assembly",
"locator": "packages/microsoft.extensions.logging/9.0.0/lib/net9.0/Microsoft.Extensions.Logging.dll",
"value": "lib/net9.0/Microsoft.Extensions.Logging.dll",
"sha256": "faed6cb5c9ca0d6077feaeb2df251251adccf0241f7a80b91c58e014cd5ad48f"
},
{
"kind": "file",
"source": "deps.json",
@@ -56,4 +37,4 @@
}
]
}
]
]

View File

@@ -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>

View File

@@ -22,13 +22,15 @@
"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"
"package.version": "9.0.0",
"provenance": "manifest"
},
"evidence": [
{
@@ -56,13 +58,16 @@
"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"
"package.version": "1.2.3",
"provenance": "manifest"
},
"evidence": [
{
@@ -70,7 +75,13 @@
"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"
}
]
}
]
]

View File

@@ -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>

View File

@@ -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.

View File

@@ -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>

View File

@@ -1,24 +1,4 @@
[
{
"analyzerId": "rust",
"componentKey": "bin::sha256:22caa7413d89026b52db64c8abc254bf9e7647ab9216e79c6972a39451f8c41e",
"name": "unknown_tool",
"type": "bin",
"usedByEntrypoint": false,
"metadata": {
"binary.path": "usr/local/bin/unknown_tool",
"binary.sha256": "22caa7413d89026b52db64c8abc254bf9e7647ab9216e79c6972a39451f8c41e",
"provenance": "binary"
},
"evidence": [
{
"kind": "file",
"source": "binary",
"locator": "usr/local/bin/unknown_tool",
"sha256": "22caa7413d89026b52db64c8abc254bf9e7647ab9216e79c6972a39451f8c41e"
}
]
},
{
"analyzerId": "rust",
"componentKey": "purl::pkg:cargo/my_app@0.1.0",
@@ -26,22 +6,14 @@
"name": "my_app",
"version": "0.1.0",
"type": "cargo",
"usedByEntrypoint": true,
"usedByEntrypoint": false,
"metadata": {
"binary.paths": "usr/local/bin/my_app",
"binary.sha256": "a95a4f4854bf973deacbd937bd1189fc3d0eef7a4fd4f7960f37cf66162c82fd",
"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": "binary",
"locator": "usr/local/bin/my_app",
"sha256": "a95a4f4854bf973deacbd937bd1189fc3d0eef7a4fd4f7960f37cf66162c82fd"
},
{
"kind": "file",
"source": "cargo.fingerprint",
@@ -87,4 +59,4 @@
}
]
}
]
]

View File

@@ -1,43 +1,43 @@
# StellaOps Scanner — Language Analyzer Implementation Plan (2025Q4)
> **Goal.** Deliver best-in-class language analyzers that outperform competitors on fidelity, determinism, and offline readiness while integrating tightly with Scanner Worker orchestration and SBOM composition.
All sprints below assume prerequisites from SP10-G2 (core scaffolding + Java analyzer) are complete. Each sprint is sized for a focused guild (≈11.5weeks) and produces definitive gates for downstream teams (Emit, Policy, Scheduler).
---
## Sprint LA1 — Node Analyzer & Workspace Intelligence (Tasks 10-302, 10-307, 10-308, 10-309 subset) *(DOING — 2025-10-19)*
- **Scope:** Resolve hoisted `node_modules`, PNPM structures, Yarn Berry Plug'n'Play, symlinked workspaces, and detect security-sensitive scripts.
- **Deliverables:**
- `StellaOps.Scanner.Analyzers.Lang.Node` plug-in with manifest + DI registration.
- Deterministic walker supporting >100k modules with streaming JSON parsing.
- Workspace graph persisted as analyzer metadata (`package.json` provenance + symlink target proofs).
- **Acceptance Metrics:**
- 10k module fixture scans <1.8s on 4vCPU (p95).
- Memory ceiling <220MB (tracked via deterministic benchmark harness).
- All symlink targets canonicalized; path traversal guarded.
- **Gate Artifacts:**
- `Fixtures/lang/node/**` golden outputs.
- Analyzer benchmark CSV + flamegraph (commit under `bench/Scanner.Analyzers`).
- Worker integration sample enabling Node analyzer via manifest.
# StellaOps Scanner — Language Analyzer Implementation Plan (2025Q4)
> **Goal.** Deliver best-in-class language analyzers that outperform competitors on fidelity, determinism, and offline readiness while integrating tightly with Scanner Worker orchestration and SBOM composition.
All sprints below assume prerequisites from SP10-G2 (core scaffolding + Java analyzer) are complete. Each sprint is sized for a focused guild (≈11.5weeks) and produces definitive gates for downstream teams (Emit, Policy, Scheduler).
---
## Sprint LA1 — Node Analyzer & Workspace Intelligence (Tasks 10-302, 10-307, 10-308, 10-309 subset) *(DOING — 2025-10-19)*
- **Scope:** Resolve hoisted `node_modules`, PNPM structures, Yarn Berry Plug'n'Play, symlinked workspaces, and detect security-sensitive scripts.
- **Deliverables:**
- `StellaOps.Scanner.Analyzers.Lang.Node` plug-in with manifest + DI registration.
- Deterministic walker supporting >100k modules with streaming JSON parsing.
- Workspace graph persisted as analyzer metadata (`package.json` provenance + symlink target proofs).
- **Acceptance Metrics:**
- 10k module fixture scans <1.8s on 4vCPU (p95).
- Memory ceiling <220MB (tracked via deterministic benchmark harness).
- All symlink targets canonicalized; path traversal guarded.
- **Gate Artifacts:**
- `Fixtures/lang/node/**` golden outputs.
- Analyzer benchmark CSV + flamegraph (commit under `bench/Scanner.Analyzers`).
- Worker integration sample enabling Node analyzer via manifest.
- **Progress (2025-10-21):** Module walker with package-lock/yarn/pnpm resolution, workspace attribution, integrity metadata, and deterministic fixture harness committed; Node tasks 10-302A/B remain green. Shared component mapper + canonical result harness landed, closing tasks 10-307/308. Script metadata & telemetry (10-302C) emit policy hints, hashed evidence, and feed `scanner_analyzer_node_scripts_total` into Worker OpenTelemetry pipeline. Restart-time packaging closed (10-309): manifest added, Worker language catalog loads the Node analyzer, integration tests cover dispatch + layer fragments, and Offline Kit docs call out bundled language plug-ins.
## Sprint LA2 — Python Analyzer & Entry Point Attribution (Tasks 10-303, 10-307, 10-308, 10-309 subset)
- **Scope:** Parse `*.dist-info`, `RECORD` hashes, entry points, and pip-installed editable packages; integrate usage hints from EntryTrace.
- **Deliverables:**
- `StellaOps.Scanner.Analyzers.Lang.Python` plug-in.
- RECORD hash validation with optional Zip64 support for `.whl` caches.
- Entry-point mapping into `UsageFlags` for Emit stage.
- **Acceptance Metrics:**
- Hash verification throughput 75MB/s sustained with streaming reader.
- False-positive rate for editable installs <1% on curated fixtures.
- Determinism check across CPython 3.83.12 generated metadata.
## Sprint LA2 — Python Analyzer & Entry Point Attribution (Tasks 10-303, 10-307, 10-308, 10-309 subset)
- **Scope:** Parse `*.dist-info`, `RECORD` hashes, entry points, and pip-installed editable packages; integrate usage hints from EntryTrace.
- **Deliverables:**
- `StellaOps.Scanner.Analyzers.Lang.Python` plug-in.
- RECORD hash validation with optional Zip64 support for `.whl` caches.
- Entry-point mapping into `UsageFlags` for Emit stage.
- **Acceptance Metrics:**
- Hash verification throughput 75MB/s sustained with streaming reader.
- False-positive rate for editable installs <1% on curated fixtures.
- Determinism check across CPython 3.83.12 generated metadata.
- **Gate Artifacts:**
- Golden fixtures for `site-packages`, virtualenv, and layered pip caches.
- Usage hint propagation tests (EntryTrace analyzer SBOM).
- Metrics counters (`scanner_analyzer_python_components_total`) documented.
- **Progress (2025-10-21):** Python analyzer landed; Tasks 10-303A/B/C are DONE with dist-info parsing, RECORD verification, editable install detection, and deterministic `simple-venv` fixture + benchmark hooks recorded.
## Sprint LA3 — Go Analyzer & Build Info Synthesis (Tasks 10-304, 10-307, 10-308, 10-309 subset)
- **Scope:** Extract Go build metadata from `.note.go.buildid`, embedded module info, and fallback to `bin:{sha256}`; surface VCS provenance.
- **Deliverables:**
@@ -51,67 +51,67 @@ All sprints below assume prerequisites from SP10-G2 (core scaffolding + Java ana
- **Gate Artifacts:**
- Benchmarks vs competitor open-source tool (Trivy or Syft) demonstrating faster metadata extraction.
- Documentation snippet explaining VCS metadata fields for Policy team.
- **Progress (2025-10-22):** Build-info decoder shipped with DWARF-string fallback for `vcs.*` markers, plus cached metadata keyed by binary length/timestamp. Added Go test fixtures covering build-info and DWARF-only binaries with deterministic goldens; analyzer now emits `go.dwarf` evidence alongside `go.buildinfo` metadata to feed downstream provenance rules. Completed stripped-binary heuristics with deterministic `golang::bin::sha256` components and a new `stripped` fixture to guard quiet-provenance behaviour.
## Sprint LA4 — .NET Analyzer & RID Variants (Tasks 10-305, 10-307, 10-308, 10-309 subset)
- **Scope:** Parse `*.deps.json`, `runtimeconfig.json`, assembly metadata, and RID-specific assets; correlate with native dependencies.
- **Deliverables:**
- `StellaOps.Scanner.Analyzers.Lang.DotNet` plug-in.
- Strong-name + Authenticode optional verification when offline cert bundle provided.
- RID-aware component grouping with fallback to `bin:{sha256}` for self-contained apps.
- **Acceptance Metrics:**
- Multi-target app fixture processed <1.2s; memory <250MB.
- RID variant collapse reduces component explosion by 40% vs naive listing.
- All security metadata (signing Publisher, timestamp) surfaced deterministically.
- **Progress (2025-10-22):** Build-info decoder shipped with DWARF-string fallback for `vcs.*` markers, plus cached metadata keyed by binary length/timestamp. Added Go test fixtures covering build-info and DWARF-only binaries with deterministic goldens; analyzer now emits `go.dwarf` evidence alongside `go.buildinfo` metadata to feed downstream provenance rules. Completed stripped-binary heuristics with deterministic `golang::bin::sha256` components and a new `stripped` fixture to guard quiet-provenance behaviour. Heuristic fallbacks now emit `scanner_analyzer_golang_heuristic_total{indicator,version_hint}` counters, and shared buffer pooling (`ArrayPool<byte>`) keeps concurrent scans allocation-lite. Bench harness (`bench/Scanner.Analyzers/config.json`) gained a dedicated Go scenario with baseline mean 4.02ms; comparison against Syft v1.29.1 on the same fixture shows a 22% speed advantage (see `bench/Scanner.Analyzers/lang/go/syft-comparison-20251021.csv`).
## Sprint LA4 — .NET Analyzer & RID Variants (Tasks 10-305, 10-307, 10-308, 10-309 subset)
- **Scope:** Parse `*.deps.json`, `runtimeconfig.json`, assembly metadata, and RID-specific assets; correlate with native dependencies.
- **Deliverables:**
- `StellaOps.Scanner.Analyzers.Lang.DotNet` plug-in.
- Strong-name + Authenticode optional verification when offline cert bundle provided.
- RID-aware component grouping with fallback to `bin:{sha256}` for self-contained apps.
- **Acceptance Metrics:**
- Multi-target app fixture processed <1.2s; memory <250MB.
- RID variant collapse reduces component explosion by 40% vs naive listing.
- All security metadata (signing Publisher, timestamp) surfaced deterministically.
- **Gate Artifacts:**
- Signed .NET sample apps (framework-dependent & self-contained) under `samples/scanner/lang/dotnet/`.
- Tests verifying dual runtimeconfig merge logic.
- Guidance for Policy on license propagation from NuGet metadata.
- **Progress (2025-10-22):** Completed task 10-305A with a deterministic deps/runtimeconfig ingest pipeline producing `pkg:nuget` components across RID targets. Added dotnet fixture + golden output to the shared harness, wired analyzer plugin availability, and surfaced RID metadata in component records for downstream emit/diff work.
## Sprint LA5 — Rust Analyzer & Binary Fingerprinting (Tasks 10-306, 10-307, 10-308, 10-309 subset)
- **Scope:** Detect crates via metadata in `.fingerprint`, Cargo.lock fragments, or embedded `rustc` markers; robust fallback to binary hash classification.
- **Deliverables:**
- `StellaOps.Scanner.Analyzers.Lang.Rust` plug-in.
- Symbol table heuristics capable of attributing stripped binaries by leveraging `.comment` and section names without violating determinism.
- Quiet-provenance flags to differentiate heuristics from hard evidence.
- **Acceptance Metrics:**
- Accurate crate attribution 85% on curated Cargo workspace fixtures.
- Heuristic fallback clearly labeled; no false certain claims.
- Analyzer completes <1s on 500 binary corpus.
- **Gate Artifacts:**
- Fixtures covering cargo workspaces, binaries with embedded metadata stripped.
- ADR documenting heuristic boundaries + risk mitigations.
## Sprint LA6 — Shared Evidence Enhancements & Worker Integration (Tasks 10-307, 10-308, 10-309 finalization)
- **Scope:** Finalize shared helpers, deterministic harness expansion, Worker/Emit wiring, and macro benchmarks.
- **Deliverables:**
- Consolidated `LanguageComponentWriter` extensions for license, vulnerability hints, and usage propagation.
- Worker dispatcher loading plug-ins via manifest registry + health checks.
- Combined analyzer benchmark suite executed in CI with regression thresholds.
- **Acceptance Metrics:**
- Worker executes mixed analyzer suite (Java+Node+Python+Go+.NET+Rust) within SLA: warm scan <6s, cold <25s.
- CI determinism guard catches output drift (>0 diff tolerance) across all fixtures.
- Telemetry coverage: each analyzer emits timing + component counters.
- **Gate Artifacts:**
- `SPRINTS_LANG_IMPLEMENTATION_PLAN.md` progress log updated (this file).
- `bench/Scanner.Analyzers/lang-matrix.csv` recorded + referenced in docs.
- Ops notes for packaging plug-ins into Offline Kit.
---
## Cross-Sprint Considerations
- **Security:** All analyzers must enforce path canonicalization, guard against zip-slip, and expose provenance classifications (`observed`, `heuristic`, `attested`).
- **Offline-first:** No network calls; rely on cached metadata and optional offline bundles (license texts, signature roots).
- **Determinism:** Normalise timestamps to `0001-01-01T00:00:00Z` when persisting synthetic data; sort collections by stable keys.
- **Benchmarking:** Extend `bench/Scanner.Analyzers` to compare against open-source scanners (Syft/Trivy) and document performance wins.
- **Hand-offs:** Emit guild requires consistent component schemas; Policy needs license + provenance metadata; Scheduler depends on usage flags for ImpactIndex.
## Tracking & Reporting
- Update `TASKS.md` per sprint (TODO → DOING → DONE) with date stamps.
- Log sprint summaries in `docs/updates/` once each sprint lands.
- Use module-specific CI pipeline to run analyzer suites nightly (determinism + perf).
---
**Next Action:** Start Sprint LA1 (Node Analyzer) — move tasks 10-302, 10-307, 10-308, 10-309 → DOING and spin up fixtures + benchmarks.
- **Progress (2025-10-22):** Completed task 10-305A with a deterministic deps/runtimeconfig ingest pipeline producing `pkg:nuget` components across RID targets. Added dotnet fixture + golden output to the shared harness, wired analyzer plugin availability, and surfaced RID metadata in component records for downstream emit/diff work. License provenance and quiet flagging now ride through the shared helpers (task 10-307D), including nuspec license expression/file ingestion, manifest provenance tagging, and concurrency-safe file metadata caching with new parallel tests.
## Sprint LA5 — Rust Analyzer & Binary Fingerprinting (Tasks 10-306, 10-307, 10-308, 10-309 subset)
- **Scope:** Detect crates via metadata in `.fingerprint`, Cargo.lock fragments, or embedded `rustc` markers; robust fallback to binary hash classification.
- **Deliverables:**
- `StellaOps.Scanner.Analyzers.Lang.Rust` plug-in.
- Symbol table heuristics capable of attributing stripped binaries by leveraging `.comment` and section names without violating determinism.
- Quiet-provenance flags to differentiate heuristics from hard evidence.
- **Acceptance Metrics:**
- Accurate crate attribution 85% on curated Cargo workspace fixtures.
- Heuristic fallback clearly labeled; no false certain claims.
- Analyzer completes <1s on 500 binary corpus.
- **Gate Artifacts:**
- Fixtures covering cargo workspaces, binaries with embedded metadata stripped.
- ADR documenting heuristic boundaries + risk mitigations.
## Sprint LA6 — Shared Evidence Enhancements & Worker Integration (Tasks 10-307, 10-308, 10-309 finalization)
- **Scope:** Finalize shared helpers, deterministic harness expansion, Worker/Emit wiring, and macro benchmarks.
- **Deliverables:**
- Consolidated `LanguageComponentWriter` extensions for license, vulnerability hints, and usage propagation.
- Worker dispatcher loading plug-ins via manifest registry + health checks.
- Combined analyzer benchmark suite executed in CI with regression thresholds.
- **Acceptance Metrics:**
- Worker executes mixed analyzer suite (Java+Node+Python+Go+.NET+Rust) within SLA: warm scan <6s, cold <25s.
- CI determinism guard catches output drift (>0 diff tolerance) across all fixtures.
- Telemetry coverage: each analyzer emits timing + component counters.
- **Gate Artifacts:**
- `SPRINTS_LANG_IMPLEMENTATION_PLAN.md` progress log updated (this file).
- `bench/Scanner.Analyzers/lang-matrix.csv` recorded + referenced in docs.
- Ops notes for packaging plug-ins into Offline Kit.
---
## Cross-Sprint Considerations
- **Security:** All analyzers must enforce path canonicalization, guard against zip-slip, and expose provenance classifications (`observed`, `heuristic`, `attested`).
- **Offline-first:** No network calls; rely on cached metadata and optional offline bundles (license texts, signature roots).
- **Determinism:** Normalise timestamps to `0001-01-01T00:00:00Z` when persisting synthetic data; sort collections by stable keys.
- **Benchmarking:** Extend `bench/Scanner.Analyzers` to compare against open-source scanners (Syft/Trivy) and document performance wins.
- **Hand-offs:** Emit guild requires consistent component schemas; Policy needs license + provenance metadata; Scheduler depends on usage flags for ImpactIndex.
## Tracking & Reporting
- Update `TASKS.md` per sprint (TODO → DOING → DONE) with date stamps.
- Log sprint summaries in `docs/updates/` once each sprint lands.
- Use module-specific CI pipeline to run analyzer suites nightly (determinism + perf).
---
**Next Action:** Start Sprint LA1 (Node Analyzer) — move tasks 10-302, 10-307, 10-308, 10-309 → DOING and spin up fixtures + benchmarks.