Implement VEX document verification system with issuer management and signature verification

- Added IIssuerDirectory interface for managing VEX document issuers, including methods for registration, revocation, and trust validation.
- Created InMemoryIssuerDirectory class as an in-memory implementation of IIssuerDirectory for testing and single-instance deployments.
- Introduced ISignatureVerifier interface for verifying signatures on VEX documents, with support for multiple signature formats.
- Developed SignatureVerifier class as the default implementation of ISignatureVerifier, allowing extensibility for different signature formats.
- Implemented handlers for DSSE and JWS signature formats, including methods for verification and signature extraction.
- Defined various records and enums for issuer and signature metadata, enhancing the structure and clarity of the verification process.
This commit is contained in:
StellaOps Bot
2025-12-06 13:41:22 +02:00
parent 2141196496
commit 5e514532df
112 changed files with 24861 additions and 211 deletions

View File

@@ -12,4 +12,24 @@ internal sealed class BunLockEntry
public bool IsDev { get; init; }
public bool IsOptional { get; init; }
public bool IsPeer { get; init; }
/// <summary>
/// Source type: npm, git, tarball, file, link, workspace.
/// </summary>
public string SourceType { get; init; } = "npm";
/// <summary>
/// Git commit hash if this is a git dependency.
/// </summary>
public string? GitCommit { get; init; }
/// <summary>
/// Original specifier (e.g., "github:user/repo#tag").
/// </summary>
public string? Specifier { get; init; }
/// <summary>
/// Dependencies of this package (for transitive analysis).
/// </summary>
public IReadOnlyList<string> Dependencies { get; init; } = Array.Empty<string>();
}

View File

@@ -136,17 +136,35 @@ internal static class BunLockParser
{
if (element.ValueKind == JsonValueKind.Array && element.GetArrayLength() >= 1)
{
// bun.lock v1 format: [resolved, hash, deps, isDev?]
// bun.lock v1 format: [resolved, integrity, dependencies?, optionalPeers?]
// The resolved URL indicates the source type
var resolved = element[0].GetString();
var integrity = element.GetArrayLength() > 1 ? element[1].GetString() : null;
// Parse dependencies from element[2] if present
var dependencies = new List<string>();
if (element.GetArrayLength() > 2 && element[2].ValueKind == JsonValueKind.Object)
{
foreach (var dep in element[2].EnumerateObject())
{
dependencies.Add(dep.Name);
}
}
// Detect source type and extract additional metadata
var (sourceType, gitCommit, specifier) = ClassifyResolvedUrl(resolved);
return new BunLockEntry
{
Name = name,
Version = version,
Resolved = resolved,
Integrity = integrity,
IsDev = false // Will be determined by dependency graph analysis if needed
IsDev = false, // Bun lockfile doesn't mark dev in the array; determined by graph
SourceType = sourceType,
GitCommit = gitCommit,
Specifier = specifier,
Dependencies = dependencies
};
}
@@ -156,6 +174,10 @@ internal static class BunLockParser
var resolved = element.TryGetProperty("resolved", out var r) ? r.GetString() : null;
var integrity = element.TryGetProperty("integrity", out var i) ? i.GetString() : null;
var isDev = element.TryGetProperty("dev", out var d) && d.GetBoolean();
var isOptional = element.TryGetProperty("optional", out var o) && o.GetBoolean();
var isPeer = element.TryGetProperty("peer", out var p) && p.GetBoolean();
var (sourceType, gitCommit, specifier) = ClassifyResolvedUrl(resolved);
return new BunLockEntry
{
@@ -163,23 +185,108 @@ internal static class BunLockParser
Version = version,
Resolved = resolved,
Integrity = integrity,
IsDev = isDev
IsDev = isDev,
IsOptional = isOptional,
IsPeer = isPeer,
SourceType = sourceType,
GitCommit = gitCommit,
Specifier = specifier
};
}
// Simple string value (just the resolved URL)
if (element.ValueKind == JsonValueKind.String)
{
var resolved = element.GetString();
var (sourceType, gitCommit, specifier) = ClassifyResolvedUrl(resolved);
return new BunLockEntry
{
Name = name,
Version = version,
Resolved = element.GetString(),
Resolved = resolved,
Integrity = null,
IsDev = false
IsDev = false,
SourceType = sourceType,
GitCommit = gitCommit,
Specifier = specifier
};
}
return null;
}
/// <summary>
/// Classifies the resolved URL to detect git, tarball, file, or npm sources.
/// </summary>
private static (string SourceType, string? GitCommit, string? Specifier) ClassifyResolvedUrl(string? resolved)
{
if (string.IsNullOrEmpty(resolved))
{
return ("npm", null, null);
}
// Git dependencies: git+https://, git+ssh://, github:, gitlab:, bitbucket:
if (resolved.StartsWith("git+", StringComparison.OrdinalIgnoreCase) ||
resolved.StartsWith("git://", StringComparison.OrdinalIgnoreCase))
{
var commit = ExtractGitCommit(resolved);
return ("git", commit, resolved);
}
if (resolved.StartsWith("github:", StringComparison.OrdinalIgnoreCase) ||
resolved.StartsWith("gitlab:", StringComparison.OrdinalIgnoreCase) ||
resolved.StartsWith("bitbucket:", StringComparison.OrdinalIgnoreCase))
{
var commit = ExtractGitCommit(resolved);
return ("git", commit, resolved);
}
// Tarball URLs (not from npm registry)
if ((resolved.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
resolved.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) &&
!resolved.Contains("registry.npmjs.org", StringComparison.OrdinalIgnoreCase) &&
!resolved.Contains("registry.npm.", StringComparison.OrdinalIgnoreCase) &&
(resolved.EndsWith(".tgz", StringComparison.OrdinalIgnoreCase) ||
resolved.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase)))
{
return ("tarball", null, resolved);
}
// File dependencies: file:, link:
if (resolved.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
{
return ("file", null, resolved);
}
if (resolved.StartsWith("link:", StringComparison.OrdinalIgnoreCase))
{
return ("link", null, resolved);
}
// Workspace dependencies
if (resolved.StartsWith("workspace:", StringComparison.OrdinalIgnoreCase))
{
return ("workspace", null, resolved);
}
// Default to npm for standard registry URLs
return ("npm", null, null);
}
/// <summary>
/// Extracts git commit hash from a git URL (after # or @).
/// </summary>
private static string? ExtractGitCommit(string url)
{
// Format: git+https://github.com/user/repo#commit
// or: github:user/repo#tag
var hashIndex = url.LastIndexOf('#');
if (hashIndex > 0 && hashIndex < url.Length - 1)
{
return url[(hashIndex + 1)..];
}
return null;
}
}