update exportcenter_ii and fix Sm2AttestorTests
This commit is contained in:
@@ -40,7 +40,7 @@
|
||||
| 6 | EXPORT-OBS-54-001 | DONE | Depends on EXPORT-OBS-53-001. | Exporter Service · Provenance Guild | Produce DSSE attestations per export artifact/target; expose `/exports/{id}/attestation`; integrate with CLI verify path. |
|
||||
| 7 | EXPORT-OBS-54-002 | DONE | Depends on EXPORT-OBS-54-001 and PROV-OBS-53-003. | Exporter Service · Provenance Guild | Add promotion attestation assembly; include SBOM/VEX digests, Rekor proofs, DSSE envelopes for Offline Kit. |
|
||||
| 8 | EXPORT-OBS-55-001 | DONE | Depends on EXPORT-OBS-54-001. | Exporter Service · DevOps | Incident mode enhancements; emit incident activation events to timeline + notifier. |
|
||||
| 9 | EXPORT-RISK-69-001 | TODO | Schema blockers resolved; AdvisoryAI evidence bundle schema available. | Exporter Service · Risk Bundle Export Guild | Add `risk-bundle` job handler with provider selection, manifest signing, audit logging. |
|
||||
| 9 | EXPORT-RISK-69-001 | DONE | Schema blockers resolved; AdvisoryAI evidence bundle schema available. | Exporter Service · Risk Bundle Export Guild | Add `risk-bundle` job handler with provider selection, manifest signing, audit logging. |
|
||||
| 10 | EXPORT-RISK-69-002 | TODO | Depends on EXPORT-RISK-69-001. | Exporter Service · Risk Engine Guild | Enable simulation report exports with scored data + explainability snapshots. |
|
||||
| 11 | EXPORT-RISK-70-001 | TODO | Depends on EXPORT-RISK-69-002. | Exporter Service · DevOps | Integrate risk bundle builds into offline kit packaging with checksum verification. |
|
||||
| 12 | EXPORT-SVC-35-001 | TODO | Schema blockers resolved; EvidenceLocker bundle spec available. | Exporter Service | Bootstrap exporter service project, config, Postgres migrations for `export_profiles/runs/inputs/distributions` with tenant scoping + tests. |
|
||||
@@ -93,6 +93,7 @@
|
||||
## Execution Log
|
||||
| Date (UTC) | Update | Owner |
|
||||
| --- | --- | --- |
|
||||
| 2025-12-07 | **EXPORT-RISK-69-001 DONE:** Implemented risk-bundle job handler with provider selection, manifest signing, and audit logging. Created `RiskBundle/` namespace with: `RiskBundleJobModels.cs` (RiskBundleJobSubmitRequest/Result, RiskBundleJobStatus enum, RiskBundleJobStatusDetail, RiskBundleProviderOverride, RiskBundleProviderResult, RiskBundleOutcomeSummary, RiskBundleAuditEvent, RiskBundleAvailableProvider, RiskBundleProvidersResponse), `IRiskBundleJobHandler` interface, `RiskBundleJobHandler` implementation with in-memory job store, provider selection (mandatory: cisa-kev; optional: nvd, osv, ghsa, epss), timeline audit event publishing, background job execution. Created `RiskBundleEndpoints.cs` with REST API: `GET /v1/risk-bundles/providers`, `POST /v1/risk-bundles/jobs`, `GET /v1/risk-bundles/jobs`, `GET /v1/risk-bundles/jobs/{jobId}`, `POST /v1/risk-bundles/jobs/{jobId}/cancel`. Added telemetry metrics: `export_risk_bundle_jobs_submitted_total`, `export_risk_bundle_jobs_completed_total`, `export_risk_bundle_job_duration_seconds`. Build succeeded with 0 errors. | Implementer |
|
||||
| 2025-12-07 | **EXPORT-OBS-55-001 DONE:** Implemented incident mode enhancements for ExportCenter. Created `Incident/` namespace with: `ExportIncidentModels.cs` (severity levels Info→Emergency, status Active→Resolved→FalsePositive, types ExportFailure/LatencyDegradation/StorageCapacity/DependencyFailure/IntegrityIssue/SecurityIncident/ConfigurationError/RateLimiting), `ExportIncidentEvents.cs` (IncidentActivated/Updated/Escalated/Deescalated/Resolved events), `IExportIncidentManager` interface and `ExportIncidentManager` implementation with in-memory store. `IExportNotificationEmitter` interface with `LoggingNotificationEmitter` for timeline + notifier integration. Added `PublishIncidentEventAsync` to `IExportTimelinePublisher`. REST endpoints at `/v1/incidents/*`: GET status, GET active, GET recent, GET {id}, POST activate, PATCH {id} update, POST {id}/resolve. Added metrics: `export_incidents_activated_total`, `export_incidents_resolved_total`, `export_incidents_escalated_total`, `export_incidents_deescalated_total`, `export_notifications_emitted_total`, `export_incident_duration_seconds`. | Implementer |
|
||||
| 2025-12-07 | **EXPORT-OBS-54-002 DONE:** Implemented promotion attestation assembly for Offline Kit delivery. Created `PromotionAttestationModels.cs` with models for SBOM/VEX digest references, Rekor proof entries (with inclusion proofs), DSSE envelope references, promotion predicates. Created `IPromotionAttestationAssembler` interface and `PromotionAttestationAssembler` implementation that: builds in-toto statements with promotion predicates, computes root hash from all artifact digests, signs with DSSE PAE encoding, exports to portable gzipped tar bundles with deterministic timestamps, includes verification scripts. Created `PromotionAttestationEndpoints.cs` with REST endpoints: `POST /v1/promotions/attestations`, `GET /v1/promotions/attestations/{id}`, `GET /v1/promotions/{promotionId}/attestations`, `POST /v1/promotions/attestations/{id}/verify`, `GET /v1/promotions/attestations/{id}/bundle`. Bundle export includes promotion-assembly.json, promotion.dsse.json, rekor-proofs.ndjson, envelopes/, checksums.txt, verify-promotion.sh. | Implementer |
|
||||
| 2025-12-07 | **EXPORT-OBS-54-001 DONE:** Implemented DSSE attestation service for export artifacts. Created `Attestation/` namespace with `ExportAttestationModels.cs` (DSSE envelope, in-toto statement, predicates, subjects, verification info), `IExportAttestationService` interface, `ExportAttestationService` implementation. Created `IExportAttestationSigner` interface and `ExportAttestationSigner` implementing DSSE PAE (Pre-Authentication Encoding) per spec with ECDSA-P256-SHA256 signing. REST endpoints at `/v1/exports/{id}/attestation` (GET), `/v1/exports/attestations/{attestationId}` (GET), `/v1/exports/{id}/attestation/verify` (POST). Includes base64url encoding, key ID computation, public key PEM export for verification. | Implementer |
|
||||
|
||||
@@ -107,9 +107,9 @@ internal static class Sm2TestKeyFactory
|
||||
var curve = Org.BouncyCastle.Asn1.GM.GMNamedCurves.GetByName("SM2P256V1");
|
||||
var domain = new Org.BouncyCastle.Crypto.Parameters.ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());
|
||||
var generator = new Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator("EC");
|
||||
generator.Init(new Org.BouncyCastle.Crypto.Generators.ECKeyGenerationParameters(domain, new Org.BouncyCastle.Security.SecureRandom()));
|
||||
generator.Init(new Org.BouncyCastle.Crypto.Parameters.ECKeyGenerationParameters(domain, new Org.BouncyCastle.Security.SecureRandom()));
|
||||
var pair = generator.GenerateKeyPair();
|
||||
var privInfo = Org.BouncyCastle.Asn1.Pkcs.PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private);
|
||||
var privInfo = Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private);
|
||||
var pem = Convert.ToBase64String(privInfo.GetDerEncoded());
|
||||
var path = System.IO.Path.GetTempFileName();
|
||||
System.IO.File.WriteAllText(path, "-----BEGIN PRIVATE KEY-----\n" + pem + "\n-----END PRIVATE KEY-----\n");
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MongoDB.Bson
|
||||
{
|
||||
public enum BsonType
|
||||
{
|
||||
Double,
|
||||
String,
|
||||
Document,
|
||||
Array,
|
||||
Binary,
|
||||
ObjectId,
|
||||
Boolean,
|
||||
DateTime,
|
||||
Null,
|
||||
Int32,
|
||||
Int64
|
||||
}
|
||||
|
||||
public class BsonValue : IEquatable<BsonValue?>
|
||||
{
|
||||
protected object? RawValue;
|
||||
@@ -13,16 +29,38 @@ namespace MongoDB.Bson
|
||||
RawValue = value;
|
||||
}
|
||||
|
||||
public bool IsString => RawValue is string;
|
||||
public bool IsBoolean => RawValue is bool;
|
||||
public bool IsBsonDocument => RawValue is BsonDocument;
|
||||
public bool IsBsonArray => RawValue is BsonArray;
|
||||
public virtual BsonType BsonType => RawValue switch
|
||||
{
|
||||
null => BsonType.Null,
|
||||
BsonDocument => BsonType.Document,
|
||||
BsonArray => BsonType.Array,
|
||||
string => BsonType.String,
|
||||
bool => BsonType.Boolean,
|
||||
int => BsonType.Int32,
|
||||
long => BsonType.Int64,
|
||||
double or float or decimal => BsonType.Double,
|
||||
DateTime or DateTimeOffset => BsonType.DateTime,
|
||||
ObjectId => BsonType.ObjectId,
|
||||
byte[] => BsonType.Binary,
|
||||
Guid => BsonType.String,
|
||||
_ => BsonType.String
|
||||
};
|
||||
|
||||
public bool IsString => BsonType == BsonType.String;
|
||||
public bool IsBoolean => BsonType == BsonType.Boolean;
|
||||
public bool IsBsonDocument => BsonType == BsonType.Document;
|
||||
public bool IsBsonArray => BsonType == BsonType.Array;
|
||||
public bool IsBsonNull => BsonType == BsonType.Null;
|
||||
public bool IsBsonDateTime => BsonType == BsonType.DateTime;
|
||||
public bool IsInt32 => BsonType == BsonType.Int32;
|
||||
public bool IsInt64 => BsonType == BsonType.Int64;
|
||||
|
||||
public string AsString => RawValue switch
|
||||
{
|
||||
null => string.Empty,
|
||||
string s => s,
|
||||
Guid g => g.ToString(),
|
||||
ObjectId o => o.ToString(),
|
||||
_ => Convert.ToString(RawValue, CultureInfo.InvariantCulture) ?? string.Empty
|
||||
};
|
||||
|
||||
@@ -44,6 +82,17 @@ namespace MongoDB.Bson
|
||||
_ => 0
|
||||
};
|
||||
|
||||
public int AsInt32 => ToInt32();
|
||||
|
||||
public long AsInt64 => RawValue switch
|
||||
{
|
||||
long l => l,
|
||||
int i => i,
|
||||
double d => (long)d,
|
||||
string s when long.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var l) => l,
|
||||
_ => 0L
|
||||
};
|
||||
|
||||
public Guid AsGuid => RawValue switch
|
||||
{
|
||||
Guid g => g,
|
||||
@@ -58,6 +107,24 @@ namespace MongoDB.Bson
|
||||
_ => ObjectId.Empty
|
||||
};
|
||||
|
||||
public byte[]? AsByteArray => RawValue as byte[];
|
||||
|
||||
public DateTimeOffset AsDateTimeOffset => RawValue switch
|
||||
{
|
||||
DateTimeOffset dto => dto.ToUniversalTime(),
|
||||
DateTime dt => DateTime.SpecifyKind(dt, DateTimeKind.Utc),
|
||||
string s when DateTimeOffset.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var dto) => dto.ToUniversalTime(),
|
||||
_ => DateTimeOffset.MinValue
|
||||
};
|
||||
|
||||
public DateTime ToUniversalTime() => RawValue switch
|
||||
{
|
||||
DateTime dt => dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime(),
|
||||
DateTimeOffset dto => dto.UtcDateTime,
|
||||
string s when DateTimeOffset.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var dto) => dto.UtcDateTime,
|
||||
_ => DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc)
|
||||
};
|
||||
|
||||
public BsonDocument AsBsonDocument => RawValue as BsonDocument ?? (this as BsonDocument ?? new BsonDocument());
|
||||
public BsonArray AsBsonArray => RawValue as BsonArray ?? (this as BsonArray ?? new BsonArray());
|
||||
|
||||
@@ -69,6 +136,8 @@ namespace MongoDB.Bson
|
||||
public override bool Equals(object? obj) => obj is BsonValue other && Equals(other);
|
||||
public override int GetHashCode() => RawValue?.GetHashCode() ?? 0;
|
||||
|
||||
public static BsonValue Create(object? value) => BsonDocument.ToBsonValue(value);
|
||||
|
||||
public static implicit operator BsonValue(string value) => new(value);
|
||||
public static implicit operator BsonValue(Guid value) => new(value);
|
||||
public static implicit operator BsonValue(int value) => new(value);
|
||||
@@ -76,6 +145,38 @@ namespace MongoDB.Bson
|
||||
public static implicit operator BsonValue(bool value) => new(value);
|
||||
public static implicit operator BsonValue(double value) => new(value);
|
||||
public static implicit operator BsonValue(DateTimeOffset value) => new(value);
|
||||
public static implicit operator BsonValue(DateTime value) => new(value);
|
||||
public static implicit operator BsonValue(byte[] value) => new(value);
|
||||
}
|
||||
|
||||
public sealed class BsonNull : BsonValue
|
||||
{
|
||||
public static BsonNull Value { get; } = new();
|
||||
|
||||
private BsonNull()
|
||||
: base(null)
|
||||
{
|
||||
}
|
||||
|
||||
public override BsonType BsonType => BsonType.Null;
|
||||
}
|
||||
|
||||
public sealed class BsonString : BsonValue
|
||||
{
|
||||
public BsonString(string value) : base(value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BsonBinaryData : BsonValue
|
||||
{
|
||||
public BsonBinaryData(byte[] bytes)
|
||||
: base(bytes)
|
||||
{
|
||||
Bytes = bytes ?? Array.Empty<byte>();
|
||||
}
|
||||
|
||||
public byte[] Bytes { get; }
|
||||
}
|
||||
|
||||
public sealed class BsonDocument : BsonValue, IDictionary<string, BsonValue>
|
||||
@@ -97,6 +198,27 @@ namespace MongoDB.Bson
|
||||
}
|
||||
}
|
||||
|
||||
public BsonDocument(IEnumerable<KeyValuePair<string, BsonValue>> values)
|
||||
: this()
|
||||
{
|
||||
foreach (var kvp in values)
|
||||
{
|
||||
_values[kvp.Key] = kvp.Value ?? new BsonValue();
|
||||
}
|
||||
}
|
||||
|
||||
public BsonDocument(string key, BsonValue value)
|
||||
: this()
|
||||
{
|
||||
Add(key, value);
|
||||
}
|
||||
|
||||
public BsonDocument(string key, object? value)
|
||||
: this()
|
||||
{
|
||||
Add(key, value);
|
||||
}
|
||||
|
||||
public int ElementCount => _values.Count;
|
||||
|
||||
public BsonValue this[string key]
|
||||
@@ -117,6 +239,7 @@ namespace MongoDB.Bson
|
||||
public void Clear() => _values.Clear();
|
||||
public bool Contains(KeyValuePair<string, BsonValue> item) => _values.Contains(item);
|
||||
public bool ContainsKey(string key) => _values.ContainsKey(key);
|
||||
public bool Contains(string key) => ContainsKey(key);
|
||||
public void CopyTo(KeyValuePair<string, BsonValue>[] array, int arrayIndex) => ((IDictionary<string, BsonValue>)_values).CopyTo(array, arrayIndex);
|
||||
public IEnumerator<KeyValuePair<string, BsonValue>> GetEnumerator() => _values.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _values.GetEnumerator();
|
||||
@@ -126,6 +249,9 @@ namespace MongoDB.Bson
|
||||
|
||||
public BsonValue GetValue(string key) => _values[key];
|
||||
|
||||
public BsonValue GetValue(string key, BsonValue defaultValue)
|
||||
=> _values.TryGetValue(key, out var value) ? value : defaultValue;
|
||||
|
||||
public BsonDocument DeepClone()
|
||||
{
|
||||
var copy = new BsonDocument();
|
||||
@@ -177,7 +303,7 @@ namespace MongoDB.Bson
|
||||
return array;
|
||||
}
|
||||
|
||||
internal static BsonValue ToBsonValue(object? value)
|
||||
public static BsonValue ToBsonValue(object? value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
@@ -190,9 +316,11 @@ namespace MongoDB.Bson
|
||||
bool b => new BsonValue(b),
|
||||
double d => new BsonValue(d),
|
||||
float f => new BsonValue(f),
|
||||
decimal dec => new BsonValue((double)dec),
|
||||
DateTime dt => new BsonValue(dt),
|
||||
DateTimeOffset dto => new BsonValue(dto),
|
||||
IEnumerable<object?> enumerable => new BsonArray(enumerable.Select(ToBsonValue)),
|
||||
byte[] bytes => new BsonBinaryData(bytes),
|
||||
IEnumerable enumerable when value is not string => new BsonArray(enumerable.Cast<object?>().Select(ToBsonValue)),
|
||||
_ => new BsonValue(value)
|
||||
};
|
||||
}
|
||||
@@ -216,6 +344,15 @@ namespace MongoDB.Bson
|
||||
_items.AddRange(items);
|
||||
}
|
||||
|
||||
public BsonArray(IEnumerable<object?> items)
|
||||
: this()
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public BsonValue this[int index]
|
||||
{
|
||||
get => _items[index];
|
||||
@@ -227,6 +364,13 @@ namespace MongoDB.Bson
|
||||
|
||||
public void Add(BsonValue item) => _items.Add(item ?? new BsonValue());
|
||||
public void Add(object? item) => _items.Add(BsonDocument.ToBsonValue(item));
|
||||
public void AddRange(IEnumerable<object?> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
public void Clear() => _items.Clear();
|
||||
public bool Contains(BsonValue item) => _items.Contains(item);
|
||||
public void CopyTo(BsonValue[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex);
|
||||
@@ -259,6 +403,26 @@ namespace MongoDB.Bson
|
||||
public override bool Equals(object? obj) => obj is ObjectId other && Equals(other);
|
||||
public override int GetHashCode() => _value?.GetHashCode(StringComparison.Ordinal) ?? 0;
|
||||
}
|
||||
|
||||
public static class BsonTypeMapper
|
||||
{
|
||||
public static object? MapToDotNetValue(BsonValue value)
|
||||
{
|
||||
if (value is null) return null;
|
||||
|
||||
return value.BsonType switch
|
||||
{
|
||||
BsonType.Document => value.AsBsonDocument.ToDictionary(static kvp => kvp.Key, static kvp => MapToDotNetValue(kvp.Value)),
|
||||
BsonType.Array => value.AsBsonArray.Select(MapToDotNetValue).ToArray(),
|
||||
BsonType.Null => null,
|
||||
BsonType.Boolean => value.AsBoolean,
|
||||
BsonType.Int32 => value.AsInt32,
|
||||
BsonType.Int64 => value.AsInt64,
|
||||
BsonType.DateTime => value.ToUniversalTime(),
|
||||
_ => value.AsString
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace MongoDB.Bson.Serialization.Attributes
|
||||
|
||||
Reference in New Issue
Block a user