feat: Add native binary analyzer test utilities and implement SM2 signing tests
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled

- Introduced `NativeTestBase` class for ELF, PE, and Mach-O binary parsing helpers and assertions.
- Created `TestCryptoFactory` for SM2 cryptographic provider setup and key generation.
- Implemented `Sm2SigningTests` to validate signing functionality with environment gate checks.
- Developed console export service and store with comprehensive unit tests for export status management.
This commit is contained in:
StellaOps Bot
2025-12-07 13:12:41 +02:00
parent d907729778
commit e53a282fbe
387 changed files with 21941 additions and 1518 deletions

View File

@@ -0,0 +1,339 @@
using System.Text.Json;
using StellaOps.Scanner.Analyzers.Lang.Node;
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
namespace StellaOps.Scanner.Analyzers.Lang.Node.Tests.Node;
/// <summary>
/// Tests to verify deterministic output from the Node analyzer.
/// Output must be reproducible across multiple runs.
/// </summary>
public sealed class NodeDeterminismTests : IDisposable
{
private readonly string _tempDir;
public NodeDeterminismTests()
{
_tempDir = Path.Combine(Path.GetTempPath(), "node-determinism-tests-" + Guid.NewGuid().ToString("N")[..8]);
Directory.CreateDirectory(_tempDir);
}
public void Dispose()
{
try
{
if (Directory.Exists(_tempDir))
{
Directory.Delete(_tempDir, recursive: true);
}
}
catch
{
// Ignore cleanup errors
}
}
private void WriteFile(string relativePath, string content)
{
var fullPath = Path.Combine(_tempDir, relativePath);
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
File.WriteAllText(fullPath, content);
}
#region Multiple Runs Determinism
[Fact]
public async Task MultipleRuns_ProduceIdenticalOutput()
{
// Arrange
SetupComplexProject();
// Act - Run analyzer multiple times
var run1 = await RunAnalyzerAsync();
var run2 = await RunAnalyzerAsync();
var run3 = await RunAnalyzerAsync();
// Assert - All runs should produce identical output
Assert.Equal(run1, run2);
Assert.Equal(run2, run3);
}
[Fact]
public async Task MultipleRuns_PackageOrderIsStable()
{
// Arrange
WriteFile("package.json", JsonSerializer.Serialize(new
{
name = "root",
version = "1.0.0"
}));
// Create packages in non-alphabetical order
WriteFile("node_modules/zebra/package.json", JsonSerializer.Serialize(new { name = "zebra", version = "1.0.0" }));
WriteFile("node_modules/alpha/package.json", JsonSerializer.Serialize(new { name = "alpha", version = "1.0.0" }));
WriteFile("node_modules/mike/package.json", JsonSerializer.Serialize(new { name = "mike", version = "1.0.0" }));
WriteFile("node_modules/beta/package.json", JsonSerializer.Serialize(new { name = "beta", version = "1.0.0" }));
// Act
var result1 = await RunAnalyzerAsync();
var result2 = await RunAnalyzerAsync();
// Assert
var order1 = ExtractPackageNames(result1);
var order2 = ExtractPackageNames(result2);
Assert.Equal(order1, order2);
}
#endregion
#region Package Ordering
[Fact]
public async Task PackageOrdering_IsSortedByPurl()
{
// Arrange
WriteFile("package.json", JsonSerializer.Serialize(new { name = "root", version = "1.0.0" }));
WriteFile("node_modules/z-pkg/package.json", JsonSerializer.Serialize(new { name = "z-pkg", version = "1.0.0" }));
WriteFile("node_modules/a-pkg/package.json", JsonSerializer.Serialize(new { name = "a-pkg", version = "1.0.0" }));
WriteFile("node_modules/m-pkg/package.json", JsonSerializer.Serialize(new { name = "m-pkg", version = "1.0.0" }));
// Act
var result = await RunAnalyzerAsync();
// Assert - Packages should be sorted
var names = ExtractPackageNames(result);
var sortedNames = names.OrderBy(n => n, StringComparer.Ordinal).ToList();
Assert.Equal(sortedNames, names);
}
[Fact]
public async Task ScopedPackageOrdering_IsConsistent()
{
// Arrange
WriteFile("package.json", JsonSerializer.Serialize(new { name = "root", version = "1.0.0" }));
WriteFile("node_modules/@z-scope/pkg/package.json", JsonSerializer.Serialize(new { name = "@z-scope/pkg", version = "1.0.0" }));
WriteFile("node_modules/@a-scope/pkg/package.json", JsonSerializer.Serialize(new { name = "@a-scope/pkg", version = "1.0.0" }));
WriteFile("node_modules/regular-pkg/package.json", JsonSerializer.Serialize(new { name = "regular-pkg", version = "1.0.0" }));
// Act
var result1 = await RunAnalyzerAsync();
var result2 = await RunAnalyzerAsync();
// Assert
Assert.Equal(result1, result2);
}
#endregion
#region Entrypoint Ordering
[Fact]
public async Task EntrypointOrdering_IsStable()
{
// Arrange - Multiple entrypoints in various fields
var packageJson = new
{
name = "multi-entry-pkg",
version = "1.0.0",
main = "./dist/main.js",
module = "./dist/module.mjs",
bin = new
{
cli1 = "./bin/cli1.js",
cli2 = "./bin/cli2.js"
}
};
WriteFile("package.json", JsonSerializer.Serialize(packageJson));
WriteFile("dist/main.js", "// main");
WriteFile("dist/module.mjs", "// module");
WriteFile("bin/cli1.js", "// cli1");
WriteFile("bin/cli2.js", "// cli2");
// Act
var result1 = await RunAnalyzerAsync();
var result2 = await RunAnalyzerAsync();
// Assert
Assert.Equal(result1, result2);
}
[Fact]
public async Task ExportsOrdering_IsSortedAlphabetically()
{
// Arrange - Exports with conditions in non-alphabetical order
var packageJsonContent = @"{
""name"": ""exports-pkg"",
""version"": ""1.0.0"",
""exports"": {
""."": {
""require"": ""./dist/index.cjs"",
""import"": ""./dist/index.mjs"",
""default"": ""./dist/index.js""
}
}
}";
WriteFile("package.json", packageJsonContent);
WriteFile("dist/index.cjs", "// cjs");
WriteFile("dist/index.mjs", "// mjs");
WriteFile("dist/index.js", "// js");
// Act
var result1 = await RunAnalyzerAsync();
var result2 = await RunAnalyzerAsync();
// Assert - Order should be consistent
Assert.Equal(result1, result2);
}
#endregion
#region Evidence Ordering
[Fact]
public async Task EvidenceOrdering_IsStable()
{
// Arrange
var packageJson = new
{
name = "evidence-pkg",
version = "1.0.0",
main = "./index.js",
license = "MIT",
scripts = new
{
postinstall = "node setup.js"
}
};
WriteFile("package.json", JsonSerializer.Serialize(packageJson));
WriteFile("index.js", "// index");
// Act
var result1 = await RunAnalyzerAsync();
var result2 = await RunAnalyzerAsync();
// Assert
Assert.Equal(result1, result2);
}
#endregion
#region Dependency Resolution Ordering
[Fact]
public async Task DependencyIndex_ProducesDeterministicScopes()
{
// Arrange
var packageJson = new
{
name = "deps-pkg",
version = "1.0.0",
dependencies = new
{
dep1 = "^1.0.0",
dep2 = "^2.0.0"
},
devDependencies = new
{
devDep1 = "^3.0.0",
devDep2 = "^4.0.0"
},
peerDependencies = new
{
peerDep1 = "^5.0.0"
},
optionalDependencies = new
{
optDep1 = "^6.0.0"
}
};
WriteFile("package.json", JsonSerializer.Serialize(packageJson));
WriteFile("node_modules/dep1/package.json", JsonSerializer.Serialize(new { name = "dep1", version = "1.0.0" }));
WriteFile("node_modules/dep2/package.json", JsonSerializer.Serialize(new { name = "dep2", version = "2.0.0" }));
WriteFile("node_modules/devDep1/package.json", JsonSerializer.Serialize(new { name = "devDep1", version = "3.0.0" }));
WriteFile("node_modules/devDep2/package.json", JsonSerializer.Serialize(new { name = "devDep2", version = "4.0.0" }));
// Act
var result1 = await RunAnalyzerAsync();
var result2 = await RunAnalyzerAsync();
// Assert
Assert.Equal(result1, result2);
}
#endregion
#region Lockfile Ordering
[Fact]
public async Task LockfilePackages_ProduceDeterministicOutput()
{
// Arrange
WriteFile("package.json", JsonSerializer.Serialize(new { name = "lock-pkg", version = "1.0.0" }));
WriteFile("package-lock.json", @"{
""name"": ""lock-pkg"",
""version"": ""1.0.0"",
""lockfileVersion"": 3,
""packages"": {
"""": { ""name"": ""lock-pkg"", ""version"": ""1.0.0"" },
""node_modules/z-dep"": { ""version"": ""3.0.0"", ""resolved"": ""https://r.example/z"", ""integrity"": ""sha512-Z"" },
""node_modules/a-dep"": { ""version"": ""1.0.0"", ""resolved"": ""https://r.example/a"", ""integrity"": ""sha512-A"" },
""node_modules/m-dep"": { ""version"": ""2.0.0"", ""resolved"": ""https://r.example/m"", ""integrity"": ""sha512-M"" }
}
}");
// Act
var result1 = await RunAnalyzerAsync();
var result2 = await RunAnalyzerAsync();
// Assert
Assert.Equal(result1, result2);
}
#endregion
private void SetupComplexProject()
{
// Root package
var rootPackage = new
{
name = "complex-app",
version = "1.0.0",
dependencies = new
{
lodash = "^4.17.21",
express = "^4.18.0"
},
devDependencies = new
{
typescript = "^5.0.0"
}
};
WriteFile("package.json", JsonSerializer.Serialize(rootPackage));
// Dependencies
WriteFile("node_modules/lodash/package.json", JsonSerializer.Serialize(new { name = "lodash", version = "4.17.21" }));
WriteFile("node_modules/express/package.json", JsonSerializer.Serialize(new { name = "express", version = "4.18.2" }));
WriteFile("node_modules/typescript/package.json", JsonSerializer.Serialize(new { name = "typescript", version = "5.2.2" }));
// Nested dependencies
WriteFile("node_modules/express/node_modules/accepts/package.json", JsonSerializer.Serialize(new { name = "accepts", version = "1.3.8" }));
WriteFile("node_modules/express/node_modules/body-parser/package.json", JsonSerializer.Serialize(new { name = "body-parser", version = "1.20.1" }));
}
private async Task<string> RunAnalyzerAsync()
{
var analyzers = new ILanguageAnalyzer[] { new NodeLanguageAnalyzer() };
return await LanguageAnalyzerTestHarness.RunToJsonAsync(
_tempDir,
analyzers,
TestContext.Current.CancellationToken);
}
private static List<string> ExtractPackageNames(string json)
{
var doc = JsonDocument.Parse(json);
return doc.RootElement.EnumerateArray()
.Select(el => el.GetProperty("name").GetString()!)
.ToList();
}
}