up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
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
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
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
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-13 00:20:26 +02:00
parent e1f1bef4c1
commit 564df71bfb
2376 changed files with 334389 additions and 328032 deletions

View File

@@ -46,12 +46,13 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
public override string AnalyzerId => "macos-bundle";
protected override ValueTask<IReadOnlyList<OSPackageRecord>> ExecuteCoreAsync(
protected override ValueTask<ExecutionResult> ExecuteCoreAsync(
OSPackageAnalyzerContext context,
CancellationToken cancellationToken)
{
var records = new List<OSPackageRecord>();
var warnings = new List<string>();
var warnings = new List<AnalyzerWarning>();
var evidenceFactory = OsFileEvidenceFactory.Create(context.RootPath, context.Metadata);
// Scan standard application paths
foreach (var appPath in ApplicationPaths)
@@ -66,7 +67,7 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
try
{
DiscoverBundles(fullPath, records, warnings, 0, cancellationToken);
DiscoverBundles(context.RootPath, evidenceFactory, fullPath, records, warnings, 0, cancellationToken);
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
@@ -87,7 +88,7 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
var userAppsPath = Path.Combine(userDir, "Applications");
if (Directory.Exists(userAppsPath))
{
DiscoverBundles(userAppsPath, records, warnings, 0, cancellationToken);
DiscoverBundles(context.RootPath, evidenceFactory, userAppsPath, records, warnings, 0, cancellationToken);
}
}
}
@@ -100,25 +101,27 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
if (records.Count == 0)
{
Logger.LogInformation("No application bundles found; skipping analyzer.");
return ValueTask.FromResult<IReadOnlyList<OSPackageRecord>>(EmptyPackages);
return ValueTask.FromResult(ExecutionResult.FromPackages(EmptyPackages));
}
foreach (var warning in warnings.Take(10)) // Limit warning output
{
Logger.LogWarning("Bundle scan warning: {Warning}", warning);
Logger.LogWarning("Bundle scan warning ({Code}): {Message}", warning.Code, warning.Message);
}
Logger.LogInformation("Discovered {Count} application bundles", records.Count);
// Sort for deterministic output
records.Sort();
return ValueTask.FromResult<IReadOnlyList<OSPackageRecord>>(records);
return ValueTask.FromResult(ExecutionResult.From(records, warnings));
}
private void DiscoverBundles(
string rootPath,
OsFileEvidenceFactory evidenceFactory,
string searchPath,
List<OSPackageRecord> records,
List<string> warnings,
List<AnalyzerWarning> warnings,
int depth,
CancellationToken cancellationToken)
{
@@ -150,7 +153,7 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
// Check if this is an app bundle
if (name.EndsWith(".app", StringComparison.OrdinalIgnoreCase))
{
var record = AnalyzeBundle(entry, warnings, cancellationToken);
var record = AnalyzeBundle(rootPath, evidenceFactory, entry, warnings, cancellationToken);
if (record is not null)
{
records.Add(record);
@@ -159,14 +162,16 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
else
{
// Recurse into subdirectories (e.g., for nested apps)
DiscoverBundles(entry, records, warnings, depth + 1, cancellationToken);
DiscoverBundles(rootPath, evidenceFactory, entry, records, warnings, depth + 1, cancellationToken);
}
}
}
private OSPackageRecord? AnalyzeBundle(
string rootPath,
OsFileEvidenceFactory evidenceFactory,
string bundlePath,
List<string> warnings,
List<AnalyzerWarning> warnings,
CancellationToken cancellationToken)
{
// Find and parse Info.plist
@@ -179,14 +184,18 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
if (!File.Exists(infoPlistPath))
{
warnings.Add($"No Info.plist found in {bundlePath}");
warnings.Add(AnalyzerWarning.From(
"macos-bundle/missing-info-plist",
$"No Info.plist found in {ToRootfsStylePath(OsPath.TryGetRootfsRelative(rootPath, bundlePath)) ?? "bundle"}"));
return null;
}
var bundleInfo = _infoPlistParser.Parse(infoPlistPath, cancellationToken);
if (bundleInfo is null)
{
warnings.Add($"Failed to parse Info.plist in {bundlePath}");
warnings.Add(AnalyzerWarning.From(
"macos-bundle/invalid-info-plist",
$"Failed to parse Info.plist in {ToRootfsStylePath(OsPath.TryGetRootfsRelative(rootPath, bundlePath)) ?? "bundle"}"));
return null;
}
@@ -208,10 +217,10 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
var purl = PackageUrlBuilder.BuildMacOsBundle(bundleInfo.BundleIdentifier, version);
// Build vendor metadata
var vendorMetadata = BuildVendorMetadata(bundleInfo, entitlements, codeResourcesHash, bundlePath);
var vendorMetadata = BuildVendorMetadata(rootPath, bundleInfo, entitlements, codeResourcesHash, bundlePath);
// Discover key files
var files = DiscoverBundleFiles(bundlePath, bundleInfo);
var files = DiscoverBundleFiles(rootPath, evidenceFactory, bundlePath, bundleInfo);
// Extract display name
var displayName = bundleInfo.BundleDisplayName ?? bundleInfo.BundleName;
@@ -221,7 +230,7 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
purl,
displayName,
version,
DetermineArchitecture(bundlePath),
DetermineArchitecture(bundlePath, bundleInfo),
PackageEvidenceSource.MacOsBundle,
epoch: null,
release: bundleInfo.Version != version ? bundleInfo.Version : null,
@@ -235,16 +244,19 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
}
private static Dictionary<string, string?> BuildVendorMetadata(
string rootPath,
BundleInfo bundleInfo,
BundleEntitlements entitlements,
string? codeResourcesHash,
string bundlePath)
{
var bundlePathRelative = OsPath.TryGetRootfsRelative(rootPath, bundlePath);
var metadata = new Dictionary<string, string?>(StringComparer.Ordinal)
{
["macos:bundle_id"] = bundleInfo.BundleIdentifier,
["macos:bundle_type"] = bundleInfo.BundlePackageType,
["macos:bundle_path"] = bundlePath,
["macos:bundle_path"] = ToRootfsStylePath(bundlePathRelative) ?? bundlePath,
};
if (!string.IsNullOrWhiteSpace(bundleInfo.MinimumSystemVersion))
@@ -304,7 +316,11 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
}
}
private static List<OSPackageFileEvidence> DiscoverBundleFiles(string bundlePath, BundleInfo bundleInfo)
private static List<OSPackageFileEvidence> DiscoverBundleFiles(
string rootPath,
OsFileEvidenceFactory evidenceFactory,
string bundlePath,
BundleInfo bundleInfo)
{
var files = new List<OSPackageFileEvidence>();
@@ -317,44 +333,99 @@ internal sealed class MacOsBundleAnalyzer : OsPackageAnalyzerBase
var execPath = Path.Combine(contentsPath, "MacOS", bundleInfo.Executable);
if (File.Exists(execPath))
{
files.Add(new OSPackageFileEvidence(
$"Contents/MacOS/{bundleInfo.Executable}",
layerDigest: null,
sha256: null,
sizeBytes: null,
isConfigFile: false));
var relative = OsPath.TryGetRootfsRelative(rootPath, execPath);
if (relative is not null)
{
files.Add(evidenceFactory.Create(relative, isConfigFile: false));
}
}
}
// Info.plist
var infoPlistRelative = "Contents/Info.plist";
if (File.Exists(Path.Combine(bundlePath, infoPlistRelative)))
var infoPlistPath = Path.Combine(bundlePath, "Contents", "Info.plist");
if (!File.Exists(infoPlistPath))
{
files.Add(new OSPackageFileEvidence(
infoPlistRelative,
layerDigest: null,
sha256: null,
sizeBytes: null,
isConfigFile: true));
infoPlistPath = Path.Combine(bundlePath, "Info.plist");
}
if (File.Exists(infoPlistPath))
{
var relative = OsPath.TryGetRootfsRelative(rootPath, infoPlistPath);
if (relative is not null)
{
files.Add(evidenceFactory.Create(relative, isConfigFile: true));
}
}
return files;
}
private static string DetermineArchitecture(string bundlePath)
private static string DetermineArchitecture(string bundlePath, BundleInfo bundleInfo)
{
// Check for universal binary indicators
if (!string.IsNullOrWhiteSpace(bundleInfo.Executable))
{
var execPath = Path.Combine(bundlePath, "Contents", "MacOS", bundleInfo.Executable);
var detected = TryDetectMachOArchitecture(execPath);
if (!string.IsNullOrWhiteSpace(detected))
{
return detected;
}
}
// Default to universal (noarch) for macOS bundles.
var macosPath = Path.Combine(bundlePath, "Contents", "MacOS");
if (Directory.Exists(macosPath))
{
// Look for architecture-specific subdirectories or lipo info
// For now, default to universal
return "universal";
}
return "universal";
}
private static string? TryDetectMachOArchitecture(string path)
{
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
return null;
}
try
{
using var stream = File.OpenRead(path);
Span<byte> header = stackalloc byte[16];
var read = stream.Read(header);
if (read < 12)
{
return null;
}
var magic = BitConverter.ToUInt32(header[..4]);
return magic switch
{
0xCAFEBABE or 0xBEBAFECA => "universal",
0xFEEDFACE or 0xCEFAEDFE => MapMachCpuType(BitConverter.ToUInt32(header.Slice(4, 4))),
0xFEEDFACF or 0xCFFAEDFE => MapMachCpuType(BitConverter.ToUInt32(header.Slice(4, 4))),
_ => null
};
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
{
return null;
}
}
private static string? MapMachCpuType(uint cpuType) => cpuType switch
{
0x00000007 => "x86",
0x01000007 => "x86_64",
0x0000000C => "arm",
0x0100000C => "arm64",
_ => null,
};
private static string? ToRootfsStylePath(string? relativePath)
=> relativePath is null ? null : "/" + relativePath.TrimStart('/');
private static string? ExtractVendorFromBundleId(string bundleId)
{
var parts = bundleId.Split('.', StringSplitOptions.RemoveEmptyEntries);