feat: Implement BerkeleyDB reader for RPM databases
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build 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
console-runner-image / build-runner-image (push) Has been cancelled
wine-csp-build / Build Wine CSP Image (push) Has been cancelled
wine-csp-build / Integration Tests (push) Has been cancelled
wine-csp-build / Security Scan (push) Has been cancelled
wine-csp-build / Generate SBOM (push) Has been cancelled
wine-csp-build / Publish Image (push) Has been cancelled
wine-csp-build / Air-Gap Bundle (push) Has been cancelled
wine-csp-build / Test Summary (push) Has been cancelled

- Added BerkeleyDbReader class to read and extract RPM header blobs from BerkeleyDB hash databases.
- Implemented methods to detect BerkeleyDB format and extract values, including handling of page sizes and magic numbers.
- Added tests for BerkeleyDbReader to ensure correct functionality and header extraction.

feat: Add Yarn PnP data tests

- Created YarnPnpDataTests to validate package resolution and data loading from Yarn PnP cache.
- Implemented tests for resolved keys, package presence, and loading from cache structure.

test: Add egg-info package fixtures for Python tests

- Created egg-info package fixtures for testing Python analyzers.
- Included PKG-INFO, entry_points.txt, and installed-files.txt for comprehensive coverage.

test: Enhance RPM database reader tests

- Added tests for RpmDatabaseReader to validate fallback to legacy packages when SQLite is missing.
- Implemented helper methods to create legacy package files and RPM headers for testing.

test: Implement dual signing tests

- Added DualSignTests to validate secondary signature addition when configured.
- Created stub implementations for crypto providers and key resolvers to facilitate testing.

chore: Update CI script for Playwright Chromium installation

- Modified ci-console-exports.sh to ensure deterministic Chromium binary installation for console exports tests.
- Added checks for Windows compatibility and environment variable setups for Playwright browsers.
This commit is contained in:
StellaOps Bot
2025-12-07 16:24:45 +02:00
parent e3f28a21ab
commit 11597679ed
199 changed files with 9809 additions and 4404 deletions

View File

@@ -127,6 +127,12 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
var dependencies = ParseDependencies(data, format, relativePath, cancellationToken, out var observedBuildId);
var buildId = observedBuildId ?? identity?.BuildId ?? identity?.Uuid;
// Detect entry point
var entryPoint = DetectEntryPoint(data, format);
// Collect unknown/unresolved symbols
var unknowns = CollectUnknowns(data, format, cancellationToken);
var symbolId = SymbolId.ForBinaryAddressed(fileHash, ".text", "0x0", Path.GetFileName(path), "static");
var codeId = CodeId.ForBinarySegment(format, fileHash, "0x0", data.LongLength, ".text");
@@ -142,6 +148,13 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
attributes["build_id"] = buildId!;
}
// Add PURL binding for known library naming conventions
var purl = InferPurl(Path.GetFileName(path), format);
if (!string.IsNullOrWhiteSpace(purl))
{
attributes["purl"] = purl;
}
return new BinaryInfo(
SymbolId: symbolId,
CodeId: codeId,
@@ -151,7 +164,9 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
BuildId: buildId,
Dependencies: dependencies,
DisplayName: Path.GetFileName(path),
Attributes: attributes);
Attributes: attributes,
EntryPoint: entryPoint,
Unknowns: unknowns);
}
private static IReadOnlyList<BinaryDependency> ParseDependencies(
@@ -259,6 +274,41 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
display: info.DisplayName,
sourceFile: info.RelativePath,
attributes: info.Attributes);
// Emit synthetic root for entry point
if (info.EntryPoint is not null)
{
var entrySymbolId = SymbolId.ForBinaryAddressed(
info.FileHash,
".text",
info.EntryPoint.Address,
info.EntryPoint.Name,
"entry");
var entryAttributes = new Dictionary<string, string>(StringComparer.Ordinal)
{
["kind"] = "entry_point",
["is_synthetic_root"] = "true"
};
builder.AddNode(
symbolId: entrySymbolId,
lang: SymbolId.Lang.Binary,
kind: "entry_point",
display: info.EntryPoint.Name,
sourceFile: info.RelativePath,
attributes: entryAttributes);
// Edge from entry point to binary root
builder.AddEdge(
from: entrySymbolId,
to: info.SymbolId,
edgeType: EdgeTypes.Call,
confidence: EdgeConfidence.Certain,
origin: "static",
provenance: "elf-entry",
evidence: $"file:{info.RelativePath}:entry");
}
}
private static void EmitDependencies(ReachabilityGraphBuilder builder, BinaryInfo info)
@@ -277,6 +327,13 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
["reason"] = dep.Reason
};
// Add PURL for dependency if inferrable
var depPurl = InferPurl(dep.Name, info.Format);
if (!string.IsNullOrWhiteSpace(depPurl))
{
depAttributes["purl"] = depPurl;
}
builder.AddNode(
symbolId: depSymbolId,
lang: SymbolId.Lang.Binary,
@@ -293,6 +350,211 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
provenance: dep.Provenance,
evidence: dep.Evidence);
}
// Emit unknown/unresolved symbols
EmitUnknowns(builder, info);
}
private static void EmitUnknowns(ReachabilityGraphBuilder builder, BinaryInfo info)
{
if (info.Unknowns.Count == 0)
{
return;
}
foreach (var unknown in info.Unknowns)
{
var unknownSymbolId = SymbolId.ForBinaryAddressed(
info.FileHash,
".undef",
"0x0",
unknown.SymbolName,
"undefined");
var unknownAttributes = new Dictionary<string, string>(StringComparer.Ordinal)
{
["is_unknown"] = "true",
["reason"] = unknown.ReasonCode
};
builder.AddNode(
symbolId: unknownSymbolId,
lang: SymbolId.Lang.Binary,
kind: "unknown",
display: $"?{unknown.SymbolName}",
attributes: unknownAttributes);
builder.AddEdge(
from: info.SymbolId,
to: unknownSymbolId,
edgeType: EdgeTypes.Call,
confidence: EdgeConfidence.Medium,
origin: "static",
provenance: "symbol-undef",
evidence: $"file:{info.RelativePath}:undef");
}
}
private static BinaryEntryPoint? DetectEntryPoint(byte[] data, string format)
{
if (data.Length < 64)
{
return null;
}
switch (format)
{
case "elf":
return DetectElfEntryPoint(data);
case "pe":
return DetectPeEntryPoint(data);
case "macho":
return DetectMachOEntryPoint(data);
default:
return null;
}
}
private static BinaryEntryPoint? DetectElfEntryPoint(byte[] data)
{
if (data.Length < 32)
{
return null;
}
var is64Bit = data[4] == 2;
var isBigEndian = data[5] == 2;
ulong entryAddr;
if (is64Bit)
{
entryAddr = isBigEndian
? System.Buffers.Binary.BinaryPrimitives.ReadUInt64BigEndian(data.AsSpan(24, 8))
: System.Buffers.Binary.BinaryPrimitives.ReadUInt64LittleEndian(data.AsSpan(24, 8));
}
else
{
entryAddr = isBigEndian
? System.Buffers.Binary.BinaryPrimitives.ReadUInt32BigEndian(data.AsSpan(24, 4))
: System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(data.AsSpan(24, 4));
}
if (entryAddr == 0)
{
return null;
}
return new BinaryEntryPoint("_start", $"0x{entryAddr:x}");
}
private static BinaryEntryPoint? DetectPeEntryPoint(byte[] data)
{
if (data.Length < 0x40)
{
return null;
}
var peHeaderOffset = System.Buffers.Binary.BinaryPrimitives.ReadInt32LittleEndian(data.AsSpan(0x3C, 4));
if (peHeaderOffset < 0 || peHeaderOffset + 0x28 > data.Length)
{
return null;
}
// PE optional header AddressOfEntryPoint is at offset 16 from optional header start (pe+24)
var optionalHeaderOffset = peHeaderOffset + 24;
if (optionalHeaderOffset + 20 > data.Length)
{
return null;
}
var entryPointRva = System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(
data.AsSpan(optionalHeaderOffset + 16, 4));
if (entryPointRva == 0)
{
return null;
}
return new BinaryEntryPoint("_mainCRTStartup", $"0x{entryPointRva:x}");
}
private static BinaryEntryPoint? DetectMachOEntryPoint(byte[] data)
{
if (data.Length < 32)
{
return null;
}
var magic = System.Buffers.Binary.BinaryPrimitives.ReadUInt32BigEndian(data.AsSpan(0, 4));
var is64 = magic is 0xFEEDFACF or 0xCFFAEDFE;
var isBigEndian = magic is 0xFEEDFACE or 0xFEEDFACF;
// For Mach-O, entry point is in LC_MAIN load command
// For simplicity, we'll return a synthetic entry point
// Full parsing would require walking load commands
return new BinaryEntryPoint("main", "0x0");
}
private static IReadOnlyList<BinaryUnknown> CollectUnknowns(byte[] data, string format, CancellationToken cancellationToken)
{
// For now, return empty list - full implementation would parse symbol tables
// for undefined symbols (STT_NOTYPE with SHN_UNDEF in ELF, etc.)
// This is a placeholder for the baseline; full implementation would require
// parsing the symbol tables of ELF/PE/Mach-O files
return Array.Empty<BinaryUnknown>();
}
private static string? InferPurl(string fileName, string format)
{
if (string.IsNullOrWhiteSpace(fileName))
{
return null;
}
// Extract library name and version from common naming patterns
// ELF: libfoo.so.1.2.3 -> pkg:generic/libfoo
// PE: foo.dll -> pkg:generic/foo
// Mach-O: libfoo.dylib -> pkg:generic/libfoo
string? name = null;
string? version = null;
if (format == "elf" && fileName.Contains(".so"))
{
// libssl.so.3 or libcrypto.so.1.1
var soIndex = fileName.IndexOf(".so", StringComparison.Ordinal);
if (soIndex > 0)
{
name = fileName[..soIndex];
var afterSo = fileName[(soIndex + 3)..];
if (afterSo.StartsWith('.') && afterSo.Length > 1)
{
version = afterSo[1..];
}
}
}
else if (format == "pe" && fileName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
{
name = fileName[..^4]; // Remove .dll
}
else if (format == "macho" && fileName.EndsWith(".dylib", StringComparison.OrdinalIgnoreCase))
{
name = fileName[..^6]; // Remove .dylib
}
if (string.IsNullOrWhiteSpace(name))
{
return null;
}
// Build PURL - pkg:generic/name@version
var purl = $"pkg:generic/{Uri.EscapeDataString(name)}";
if (!string.IsNullOrWhiteSpace(version))
{
purl += $"@{Uri.EscapeDataString(version)}";
}
return purl;
}
private static bool TryDetect(byte[] data, out NativeBinaryIdentity identity)
@@ -330,7 +592,9 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
string? BuildId,
IReadOnlyList<BinaryDependency> Dependencies,
string DisplayName,
IReadOnlyDictionary<string, string> Attributes);
IReadOnlyDictionary<string, string> Attributes,
BinaryEntryPoint? EntryPoint,
IReadOnlyList<BinaryUnknown> Unknowns);
private sealed record BinaryDependency(
string Name,
@@ -338,4 +602,12 @@ public sealed class BinaryReachabilityLifter : IReachabilityLifter
EdgeConfidence Confidence,
string Provenance,
string Evidence);
private sealed record BinaryEntryPoint(
string Name,
string Address);
private sealed record BinaryUnknown(
string SymbolName,
string ReasonCode);
}