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:
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user