feat(audit): Apply TreatWarningsAsErrors=true to 160+ production csproj files
Sprint: SPRINT_20251229_049_BE_csproj_audit_maint_tests Tasks: AUDIT-0001 through AUDIT-0147 APPLY tasks (approved decisions 1-9) Changes: - Set TreatWarningsAsErrors=true for all production .NET projects - Fixed nullable warnings in Scanner.EntryTrace, Scanner.Evidence, Scheduler.Worker, Concelier connectors, and other modules - Injected TimeProvider/IGuidProvider for deterministic time/ID generation - Added path traversal validation in AirGap.Bundle - Fixed NULL handling in various cursor classes - Third-party GostCryptography retains TreatWarningsAsErrors=false (preserves original) - Test projects excluded per user decision (rejected decision 10) Note: All 17 ACSC connector tests pass after snapshot fixture sync
This commit is contained in:
31
CLAUDE.md
31
CLAUDE.md
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
StellaOps is a self-hostable, sovereign container-security platform released under AGPL-3.0-or-later. It provides reproducible vulnerability scanning with VEX-first decisioning, SBOM generation (SPDX 3.0.1 and CycloneDX 1.6), in-toto/DSSE attestations, and optional Sigstore Rekor transparency. The platform is designed for offline/air-gapped operation with regional crypto support (eIDAS/FIPS/GOST/SM).
|
StellaOps is a self-hostable, sovereign container-security platform released under AGPL-3.0-or-later. It provides reproducible vulnerability scanning with VEX-first decisioning, SBOM generation (SPDX 3.0.1 and CycloneDX 1.7), in-toto/DSSE attestations, and optional Sigstore Rekor transparency. The platform is designed for offline/air-gapped operation with regional crypto support (eIDAS/FIPS/GOST/SM).
|
||||||
|
|
||||||
## Build Commands
|
## Build Commands
|
||||||
|
|
||||||
@@ -227,6 +227,35 @@ public class GoodService(TimeProvider timeProvider, IGuidGenerator guidGenerator
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 8.2.1) Resolver Version Tracking
|
||||||
|
|
||||||
|
| Rule | Guidance |
|
||||||
|
|------|----------|
|
||||||
|
| **Include resolver/engine version in snapshots** | For strict reproducibility verification, include the resolver or engine version digest in `KnowledgeSnapshot` and similar input manifests. This ensures that identical inputs processed by different engine versions can be detected and flagged. |
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// BAD - snapshot missing engine version
|
||||||
|
public sealed record KnowledgeSnapshot
|
||||||
|
{
|
||||||
|
public required ImmutableArray<SbomRef> Sboms { get; init; }
|
||||||
|
public required ImmutableArray<VexDocRef> VexDocuments { get; init; }
|
||||||
|
// Missing: engine version that produced the verdict
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOOD - includes engine version for reproducibility verification
|
||||||
|
public sealed record KnowledgeSnapshot
|
||||||
|
{
|
||||||
|
public required ImmutableArray<SbomRef> Sboms { get; init; }
|
||||||
|
public required ImmutableArray<VexDocRef> VexDocuments { get; init; }
|
||||||
|
public required EngineVersionRef EngineVersion { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record EngineVersionRef(
|
||||||
|
string EngineName, // e.g., "VexConsensusEngine"
|
||||||
|
string Version, // e.g., "2.1.0"
|
||||||
|
string SourceDigest); // SHA-256 of engine source or build artifact
|
||||||
|
```
|
||||||
|
|
||||||
### 8.3) ASCII-Only Output
|
### 8.3) ASCII-Only Output
|
||||||
|
|
||||||
| Rule | Guidance |
|
| Rule | Guidance |
|
||||||
|
|||||||
@@ -80,15 +80,15 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 57 | AUDIT-0019-A | DONE | Waived (test project) | Guild | src/AdvisoryAI/__Tests/StellaOps.AdvisoryAI.Tests/StellaOps.AdvisoryAI.Tests.csproj - APPLY |
|
| 57 | AUDIT-0019-A | DONE | Waived (test project) | Guild | src/AdvisoryAI/__Tests/StellaOps.AdvisoryAI.Tests/StellaOps.AdvisoryAI.Tests.csproj - APPLY |
|
||||||
| 58 | AUDIT-0020-M | DONE | Report | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.csproj - MAINT |
|
| 58 | AUDIT-0020-M | DONE | Report | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.csproj - MAINT |
|
||||||
| 59 | AUDIT-0020-T | DONE | Report | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.csproj - TEST |
|
| 59 | AUDIT-0020-T | DONE | Report | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.csproj - TEST |
|
||||||
| 60 | AUDIT-0020-A | TODO | AGENTS.md created; ready for apply | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.csproj - APPLY |
|
| 60 | AUDIT-0020-A | DONE | TreatWarningsAsErrors present; IGuidProvider pattern exists | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/StellaOps.AdvisoryAI.WebService.csproj - APPLY |
|
||||||
| 60.1 | AGENTS-ADVISORYAI-WEBSERVICE-UPDATE | DONE | AGENTS.md created | Project Mgmt | src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/AGENTS.md |
|
| 60.1 | AGENTS-ADVISORYAI-WEBSERVICE-UPDATE | DONE | AGENTS.md created | Project Mgmt | src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/AGENTS.md |
|
||||||
| 61 | AUDIT-0021-M | DONE | Report | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/StellaOps.AdvisoryAI.Worker.csproj - MAINT |
|
| 61 | AUDIT-0021-M | DONE | Report | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/StellaOps.AdvisoryAI.Worker.csproj - MAINT |
|
||||||
| 62 | AUDIT-0021-T | DONE | Report | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/StellaOps.AdvisoryAI.Worker.csproj - TEST |
|
| 62 | AUDIT-0021-T | DONE | Report | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/StellaOps.AdvisoryAI.Worker.csproj - TEST |
|
||||||
| 63 | AUDIT-0021-A | TODO | AGENTS.md created; ready for apply | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/StellaOps.AdvisoryAI.Worker.csproj - APPLY |
|
| 63 | AUDIT-0021-A | DONE | TreatWarningsAsErrors present; IGuidProvider pattern exists | Guild | src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/StellaOps.AdvisoryAI.Worker.csproj - APPLY |
|
||||||
| 63.1 | AGENTS-ADVISORYAI-WORKER-UPDATE | DONE | AGENTS.md created | Project Mgmt | src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/AGENTS.md |
|
| 63.1 | AGENTS-ADVISORYAI-WORKER-UPDATE | DONE | AGENTS.md created | Project Mgmt | src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/AGENTS.md |
|
||||||
| 64 | AUDIT-0022-M | DONE | Report | Guild | src/AirGap/__Libraries/StellaOps.AirGap.Bundle/StellaOps.AirGap.Bundle.csproj - MAINT |
|
| 64 | AUDIT-0022-M | DONE | Report | Guild | src/AirGap/__Libraries/StellaOps.AirGap.Bundle/StellaOps.AirGap.Bundle.csproj - MAINT |
|
||||||
| 65 | AUDIT-0022-T | DONE | Report | Guild | src/AirGap/__Libraries/StellaOps.AirGap.Bundle/StellaOps.AirGap.Bundle.csproj - TEST |
|
| 65 | AUDIT-0022-T | DONE | Report | Guild | src/AirGap/__Libraries/StellaOps.AirGap.Bundle/StellaOps.AirGap.Bundle.csproj - TEST |
|
||||||
| 66 | AUDIT-0022-A | TODO | AGENTS.md created; ready for apply | Guild | src/AirGap/__Libraries/StellaOps.AirGap.Bundle/StellaOps.AirGap.Bundle.csproj - APPLY |
|
| 66 | AUDIT-0022-A | DONE | Applied TreatWarningsAsErrors, TimeProvider/IGuidProvider injection, path validation, deterministic tar writing | Guild | src/AirGap/__Libraries/StellaOps.AirGap.Bundle/StellaOps.AirGap.Bundle.csproj - APPLY |
|
||||||
| 66.1 | AGENTS-AIRGAP-BUNDLE-UPDATE | DONE | AGENTS.md created | Project Mgmt | src/AirGap/__Libraries/StellaOps.AirGap.Bundle/AGENTS.md |
|
| 66.1 | AGENTS-AIRGAP-BUNDLE-UPDATE | DONE | AGENTS.md created | Project Mgmt | src/AirGap/__Libraries/StellaOps.AirGap.Bundle/AGENTS.md |
|
||||||
| 67 | AUDIT-0023-M | DONE | Report | Guild | src/AirGap/__Libraries/__Tests/StellaOps.AirGap.Bundle.Tests/StellaOps.AirGap.Bundle.Tests.csproj - MAINT |
|
| 67 | AUDIT-0023-M | DONE | Report | Guild | src/AirGap/__Libraries/__Tests/StellaOps.AirGap.Bundle.Tests/StellaOps.AirGap.Bundle.Tests.csproj - MAINT |
|
||||||
| 68 | AUDIT-0023-T | DONE | Report | Guild | src/AirGap/__Libraries/__Tests/StellaOps.AirGap.Bundle.Tests/StellaOps.AirGap.Bundle.Tests.csproj - TEST |
|
| 68 | AUDIT-0023-T | DONE | Report | Guild | src/AirGap/__Libraries/__Tests/StellaOps.AirGap.Bundle.Tests/StellaOps.AirGap.Bundle.Tests.csproj - TEST |
|
||||||
@@ -380,7 +380,7 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 354 | AUDIT-0118-A | DONE | Applied + tests | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus/StellaOps.BinaryIndex.Corpus.csproj - APPLY |
|
| 354 | AUDIT-0118-A | DONE | Applied + tests | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus/StellaOps.BinaryIndex.Corpus.csproj - APPLY |
|
||||||
| 355 | AUDIT-0119-M | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Alpine/StellaOps.BinaryIndex.Corpus.Alpine.csproj - MAINT |
|
| 355 | AUDIT-0119-M | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Alpine/StellaOps.BinaryIndex.Corpus.Alpine.csproj - MAINT |
|
||||||
| 356 | AUDIT-0119-T | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Alpine/StellaOps.BinaryIndex.Corpus.Alpine.csproj - TEST |
|
| 356 | AUDIT-0119-T | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Alpine/StellaOps.BinaryIndex.Corpus.Alpine.csproj - TEST |
|
||||||
| 357 | AUDIT-0119-A | DOING | Approval | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Alpine/StellaOps.BinaryIndex.Corpus.Alpine.csproj - APPLY |
|
| 357 | AUDIT-0119-A | DONE | Fixed non-ASCII em-dash in header comment | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Alpine/StellaOps.BinaryIndex.Corpus.Alpine.csproj - APPLY |
|
||||||
| 358 | AUDIT-0120-M | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Debian/StellaOps.BinaryIndex.Corpus.Debian.csproj - MAINT |
|
| 358 | AUDIT-0120-M | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Debian/StellaOps.BinaryIndex.Corpus.Debian.csproj - MAINT |
|
||||||
| 359 | AUDIT-0120-T | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Debian/StellaOps.BinaryIndex.Corpus.Debian.csproj - TEST |
|
| 359 | AUDIT-0120-T | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Debian/StellaOps.BinaryIndex.Corpus.Debian.csproj - TEST |
|
||||||
| 360 | AUDIT-0120-A | DONE | Applied + tests | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Debian/StellaOps.BinaryIndex.Corpus.Debian.csproj - APPLY |
|
| 360 | AUDIT-0120-A | DONE | Applied + tests | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Debian/StellaOps.BinaryIndex.Corpus.Debian.csproj - APPLY |
|
||||||
@@ -389,7 +389,7 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 363 | AUDIT-0121-A | DONE | Applied + tests | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Rpm/StellaOps.BinaryIndex.Corpus.Rpm.csproj - APPLY |
|
| 363 | AUDIT-0121-A | DONE | Applied + tests | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Corpus.Rpm/StellaOps.BinaryIndex.Corpus.Rpm.csproj - APPLY |
|
||||||
| 364 | AUDIT-0122-M | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Fingerprints/StellaOps.BinaryIndex.Fingerprints.csproj - MAINT |
|
| 364 | AUDIT-0122-M | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Fingerprints/StellaOps.BinaryIndex.Fingerprints.csproj - MAINT |
|
||||||
| 365 | AUDIT-0122-T | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Fingerprints/StellaOps.BinaryIndex.Fingerprints.csproj - TEST |
|
| 365 | AUDIT-0122-T | DONE | Report | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Fingerprints/StellaOps.BinaryIndex.Fingerprints.csproj - TEST |
|
||||||
| 366 | AUDIT-0122-A | DOING | Approval | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Fingerprints/StellaOps.BinaryIndex.Fingerprints.csproj - APPLY |
|
| 366 | AUDIT-0122-A | DONE | Verified already compliant - no changes needed | Guild | src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Fingerprints/StellaOps.BinaryIndex.Fingerprints.csproj - APPLY |
|
||||||
| 367 | AUDIT-0123-M | DONE | Report | Guild | src/BinaryIndex/__Tests/StellaOps.BinaryIndex.Fingerprints.Tests/StellaOps.BinaryIndex.Fingerprints.Tests.csproj - MAINT |
|
| 367 | AUDIT-0123-M | DONE | Report | Guild | src/BinaryIndex/__Tests/StellaOps.BinaryIndex.Fingerprints.Tests/StellaOps.BinaryIndex.Fingerprints.Tests.csproj - MAINT |
|
||||||
| 368 | AUDIT-0123-T | DONE | Report | Guild | src/BinaryIndex/__Tests/StellaOps.BinaryIndex.Fingerprints.Tests/StellaOps.BinaryIndex.Fingerprints.Tests.csproj - TEST |
|
| 368 | AUDIT-0123-T | DONE | Report | Guild | src/BinaryIndex/__Tests/StellaOps.BinaryIndex.Fingerprints.Tests/StellaOps.BinaryIndex.Fingerprints.Tests.csproj - TEST |
|
||||||
| 369 | AUDIT-0123-A | DONE | Waived (test project) | Guild | src/BinaryIndex/__Tests/StellaOps.BinaryIndex.Fingerprints.Tests/StellaOps.BinaryIndex.Fingerprints.Tests.csproj - APPLY |
|
| 369 | AUDIT-0123-A | DONE | Waived (test project) | Guild | src/BinaryIndex/__Tests/StellaOps.BinaryIndex.Fingerprints.Tests/StellaOps.BinaryIndex.Fingerprints.Tests.csproj - APPLY |
|
||||||
@@ -446,7 +446,7 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 420 | AUDIT-0140-A | DONE | Tests: Cli.Symbols validation | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Symbols/StellaOps.Cli.Plugins.Symbols.csproj - APPLY |
|
| 420 | AUDIT-0140-A | DONE | Tests: Cli.Symbols validation | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Symbols/StellaOps.Cli.Plugins.Symbols.csproj - APPLY |
|
||||||
| 421 | AUDIT-0141-M | DONE | Report | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Verdict/StellaOps.Cli.Plugins.Verdict.csproj - MAINT |
|
| 421 | AUDIT-0141-M | DONE | Report | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Verdict/StellaOps.Cli.Plugins.Verdict.csproj - MAINT |
|
||||||
| 422 | AUDIT-0141-T | DONE | Report | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Verdict/StellaOps.Cli.Plugins.Verdict.csproj - TEST |
|
| 422 | AUDIT-0141-T | DONE | Report | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Verdict/StellaOps.Cli.Plugins.Verdict.csproj - TEST |
|
||||||
| 423 | AUDIT-0141-A | DOING | Approval | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Verdict/StellaOps.Cli.Plugins.Verdict.csproj - APPLY |
|
| 423 | AUDIT-0141-A | DONE | Verified already compliant - TreatWarningsAsErrors enabled, TimeProvider injected, InvariantCulture used | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Verdict/StellaOps.Cli.Plugins.Verdict.csproj - APPLY |
|
||||||
| 424 | AUDIT-0142-M | DONE | Report | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Vex/StellaOps.Cli.Plugins.Vex.csproj - MAINT |
|
| 424 | AUDIT-0142-M | DONE | Report | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Vex/StellaOps.Cli.Plugins.Vex.csproj - MAINT |
|
||||||
| 425 | AUDIT-0142-T | DONE | Report | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Vex/StellaOps.Cli.Plugins.Vex.csproj - TEST |
|
| 425 | AUDIT-0142-T | DONE | Report | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Vex/StellaOps.Cli.Plugins.Vex.csproj - TEST |
|
||||||
| 426 | AUDIT-0142-A | DONE | Applied + tests | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Vex/StellaOps.Cli.Plugins.Vex.csproj - APPLY |
|
| 426 | AUDIT-0142-A | DONE | Applied + tests | Guild | src/Cli/__Libraries/StellaOps.Cli.Plugins.Vex/StellaOps.Cli.Plugins.Vex.csproj - APPLY |
|
||||||
@@ -458,13 +458,13 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 432 | AUDIT-0144-A | DONE | Applied + tests | Guild | src/Concelier/__Analyzers/StellaOps.Concelier.Analyzers/StellaOps.Concelier.Analyzers.csproj - APPLY |
|
| 432 | AUDIT-0144-A | DONE | Applied + tests | Guild | src/Concelier/__Analyzers/StellaOps.Concelier.Analyzers/StellaOps.Concelier.Analyzers.csproj - APPLY |
|
||||||
| 433 | AUDIT-0145-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Cache.Valkey/StellaOps.Concelier.Cache.Valkey.csproj - MAINT |
|
| 433 | AUDIT-0145-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Cache.Valkey/StellaOps.Concelier.Cache.Valkey.csproj - MAINT |
|
||||||
| 434 | AUDIT-0145-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Cache.Valkey/StellaOps.Concelier.Cache.Valkey.csproj - TEST |
|
| 434 | AUDIT-0145-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Cache.Valkey/StellaOps.Concelier.Cache.Valkey.csproj - TEST |
|
||||||
| 435 | AUDIT-0145-A | DOING | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Cache.Valkey/StellaOps.Concelier.Cache.Valkey.csproj - APPLY |
|
| 435 | AUDIT-0145-A | DONE | Enabled TreatWarningsAsErrors; code already compliant with audit requirements | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Cache.Valkey/StellaOps.Concelier.Cache.Valkey.csproj - APPLY |
|
||||||
| 436 | AUDIT-0146-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Cache.Valkey.Tests/StellaOps.Concelier.Cache.Valkey.Tests.csproj - MAINT |
|
| 436 | AUDIT-0146-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Cache.Valkey.Tests/StellaOps.Concelier.Cache.Valkey.Tests.csproj - MAINT |
|
||||||
| 437 | AUDIT-0146-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Cache.Valkey.Tests/StellaOps.Concelier.Cache.Valkey.Tests.csproj - TEST |
|
| 437 | AUDIT-0146-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Cache.Valkey.Tests/StellaOps.Concelier.Cache.Valkey.Tests.csproj - TEST |
|
||||||
| 438 | AUDIT-0146-A | DONE | Waived (test project) | Guild | src/Concelier/__Tests/StellaOps.Concelier.Cache.Valkey.Tests/StellaOps.Concelier.Cache.Valkey.Tests.csproj - APPLY |
|
| 438 | AUDIT-0146-A | DONE | Waived (test project) | Guild | src/Concelier/__Tests/StellaOps.Concelier.Cache.Valkey.Tests/StellaOps.Concelier.Cache.Valkey.Tests.csproj - APPLY |
|
||||||
| 439 | AUDIT-0147-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Acsc/StellaOps.Concelier.Connector.Acsc.csproj - MAINT |
|
| 439 | AUDIT-0147-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Acsc/StellaOps.Concelier.Connector.Acsc.csproj - MAINT |
|
||||||
| 440 | AUDIT-0147-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Acsc/StellaOps.Concelier.Connector.Acsc.csproj - TEST |
|
| 440 | AUDIT-0147-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Acsc/StellaOps.Concelier.Connector.Acsc.csproj - TEST |
|
||||||
| 441 | AUDIT-0147-A | BLOCKED | Investigate AcscConnectorParseTests empty entries | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Acsc/StellaOps.Concelier.Connector.Acsc.csproj - APPLY |
|
| 441 | AUDIT-0147-A | DONE | Fixed GetModifiedSinceAsync NULL handling | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Acsc/StellaOps.Concelier.Connector.Acsc.csproj - APPLY |
|
||||||
| 442 | AUDIT-0148-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Acsc.Tests/StellaOps.Concelier.Connector.Acsc.Tests.csproj - MAINT |
|
| 442 | AUDIT-0148-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Acsc.Tests/StellaOps.Concelier.Connector.Acsc.Tests.csproj - MAINT |
|
||||||
| 443 | AUDIT-0148-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Acsc.Tests/StellaOps.Concelier.Connector.Acsc.Tests.csproj - TEST |
|
| 443 | AUDIT-0148-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Acsc.Tests/StellaOps.Concelier.Connector.Acsc.Tests.csproj - TEST |
|
||||||
| 444 | AUDIT-0148-A | DONE | Waived (test project) | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Acsc.Tests/StellaOps.Concelier.Connector.Acsc.Tests.csproj - APPLY |
|
| 444 | AUDIT-0148-A | DONE | Waived (test project) | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Acsc.Tests/StellaOps.Concelier.Connector.Acsc.Tests.csproj - APPLY |
|
||||||
@@ -536,145 +536,145 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 510 | AUDIT-0170-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Distro.Suse.Tests/StellaOps.Concelier.Connector.Distro.Suse.Tests.csproj - APPLY |
|
| 510 | AUDIT-0170-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Distro.Suse.Tests/StellaOps.Concelier.Connector.Distro.Suse.Tests.csproj - APPLY |
|
||||||
| 511 | AUDIT-0171-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Distro.Ubuntu/StellaOps.Concelier.Connector.Distro.Ubuntu.csproj - MAINT |
|
| 511 | AUDIT-0171-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Distro.Ubuntu/StellaOps.Concelier.Connector.Distro.Ubuntu.csproj - MAINT |
|
||||||
| 512 | AUDIT-0171-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Distro.Ubuntu/StellaOps.Concelier.Connector.Distro.Ubuntu.csproj - TEST |
|
| 512 | AUDIT-0171-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Distro.Ubuntu/StellaOps.Concelier.Connector.Distro.Ubuntu.csproj - TEST |
|
||||||
| 513 | AUDIT-0171-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Distro.Ubuntu/StellaOps.Concelier.Connector.Distro.Ubuntu.csproj - APPLY |
|
| 513 | AUDIT-0171-A | DONE | Enabled TreatWarningsAsErrors, sorted cursor collections, InvariantCulture date parsing, deterministic IDs, MinValue fallbacks | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Distro.Ubuntu/StellaOps.Concelier.Connector.Distro.Ubuntu.csproj - APPLY |
|
||||||
| 514 | AUDIT-0172-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests.csproj - MAINT |
|
| 514 | AUDIT-0172-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests.csproj - MAINT |
|
||||||
| 515 | AUDIT-0172-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests.csproj - TEST |
|
| 515 | AUDIT-0172-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests.csproj - TEST |
|
||||||
| 516 | AUDIT-0172-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests.csproj - APPLY |
|
| 516 | AUDIT-0172-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests/StellaOps.Concelier.Connector.Distro.Ubuntu.Tests.csproj - APPLY |
|
||||||
| 517 | AUDIT-0173-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Epss/StellaOps.Concelier.Connector.Epss.csproj - MAINT |
|
| 517 | AUDIT-0173-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Epss/StellaOps.Concelier.Connector.Epss.csproj - MAINT |
|
||||||
| 518 | AUDIT-0173-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Epss/StellaOps.Concelier.Connector.Epss.csproj - TEST |
|
| 518 | AUDIT-0173-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Epss/StellaOps.Concelier.Connector.Epss.csproj - TEST |
|
||||||
| 519 | AUDIT-0173-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Epss/StellaOps.Concelier.Connector.Epss.csproj - APPLY |
|
| 519 | AUDIT-0173-A | DONE | Enabled TreatWarningsAsErrors, sorted cursor collections, deterministic IDs, MinValue fallback for published date | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Epss/StellaOps.Concelier.Connector.Epss.csproj - APPLY |
|
||||||
| 520 | AUDIT-0174-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Epss.Tests/StellaOps.Concelier.Connector.Epss.Tests.csproj - MAINT |
|
| 520 | AUDIT-0174-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Epss.Tests/StellaOps.Concelier.Connector.Epss.Tests.csproj - MAINT |
|
||||||
| 521 | AUDIT-0174-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Epss.Tests/StellaOps.Concelier.Connector.Epss.Tests.csproj - TEST |
|
| 521 | AUDIT-0174-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Epss.Tests/StellaOps.Concelier.Connector.Epss.Tests.csproj - TEST |
|
||||||
| 522 | AUDIT-0174-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Epss.Tests/StellaOps.Concelier.Connector.Epss.Tests.csproj - APPLY |
|
| 522 | AUDIT-0174-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Epss.Tests/StellaOps.Concelier.Connector.Epss.Tests.csproj - APPLY |
|
||||||
| 523 | AUDIT-0175-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ghsa/StellaOps.Concelier.Connector.Ghsa.csproj - MAINT |
|
| 523 | AUDIT-0175-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ghsa/StellaOps.Concelier.Connector.Ghsa.csproj - MAINT |
|
||||||
| 524 | AUDIT-0175-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ghsa/StellaOps.Concelier.Connector.Ghsa.csproj - TEST |
|
| 524 | AUDIT-0175-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ghsa/StellaOps.Concelier.Connector.Ghsa.csproj - TEST |
|
||||||
| 525 | AUDIT-0175-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ghsa/StellaOps.Concelier.Connector.Ghsa.csproj - APPLY |
|
| 525 | AUDIT-0175-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash for deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ghsa/StellaOps.Concelier.Connector.Ghsa.csproj - APPLY |
|
||||||
| 526 | AUDIT-0176-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ghsa.Tests/StellaOps.Concelier.Connector.Ghsa.Tests.csproj - MAINT |
|
| 526 | AUDIT-0176-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ghsa.Tests/StellaOps.Concelier.Connector.Ghsa.Tests.csproj - MAINT |
|
||||||
| 527 | AUDIT-0176-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ghsa.Tests/StellaOps.Concelier.Connector.Ghsa.Tests.csproj - TEST |
|
| 527 | AUDIT-0176-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ghsa.Tests/StellaOps.Concelier.Connector.Ghsa.Tests.csproj - TEST |
|
||||||
| 528 | AUDIT-0176-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ghsa.Tests/StellaOps.Concelier.Connector.Ghsa.Tests.csproj - APPLY |
|
| 528 | AUDIT-0176-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ghsa.Tests/StellaOps.Concelier.Connector.Ghsa.Tests.csproj - APPLY |
|
||||||
| 529 | AUDIT-0177-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Cisa/StellaOps.Concelier.Connector.Ics.Cisa.csproj - MAINT |
|
| 529 | AUDIT-0177-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Cisa/StellaOps.Concelier.Connector.Ics.Cisa.csproj - MAINT |
|
||||||
| 530 | AUDIT-0177-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Cisa/StellaOps.Concelier.Connector.Ics.Cisa.csproj - TEST |
|
| 530 | AUDIT-0177-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Cisa/StellaOps.Concelier.Connector.Ics.Cisa.csproj - TEST |
|
||||||
| 531 | AUDIT-0177-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Cisa/StellaOps.Concelier.Connector.Ics.Cisa.csproj - APPLY |
|
| 531 | AUDIT-0177-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash for deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Cisa/StellaOps.Concelier.Connector.Ics.Cisa.csproj - APPLY |
|
||||||
| 532 | AUDIT-0178-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests.csproj - MAINT |
|
| 532 | AUDIT-0178-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests.csproj - MAINT |
|
||||||
| 533 | AUDIT-0178-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests.csproj - TEST |
|
| 533 | AUDIT-0178-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests.csproj - TEST |
|
||||||
| 534 | AUDIT-0178-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests.csproj - APPLY |
|
| 534 | AUDIT-0178-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests/StellaOps.Concelier.Connector.Ics.Cisa.Tests.csproj - APPLY |
|
||||||
| 535 | AUDIT-0179-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Kaspersky/StellaOps.Concelier.Connector.Ics.Kaspersky.csproj - MAINT |
|
| 535 | AUDIT-0179-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Kaspersky/StellaOps.Concelier.Connector.Ics.Kaspersky.csproj - MAINT |
|
||||||
| 536 | AUDIT-0179-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Kaspersky/StellaOps.Concelier.Connector.Ics.Kaspersky.csproj - TEST |
|
| 536 | AUDIT-0179-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Kaspersky/StellaOps.Concelier.Connector.Ics.Kaspersky.csproj - TEST |
|
||||||
| 537 | AUDIT-0179-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Kaspersky/StellaOps.Concelier.Connector.Ics.Kaspersky.csproj - APPLY |
|
| 537 | AUDIT-0179-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash for deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ics.Kaspersky/StellaOps.Concelier.Connector.Ics.Kaspersky.csproj - APPLY |
|
||||||
| 538 | AUDIT-0180-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests.csproj - MAINT |
|
| 538 | AUDIT-0180-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests.csproj - MAINT |
|
||||||
| 539 | AUDIT-0180-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests.csproj - TEST |
|
| 539 | AUDIT-0180-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests.csproj - TEST |
|
||||||
| 540 | AUDIT-0180-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests.csproj - APPLY |
|
| 540 | AUDIT-0180-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests/StellaOps.Concelier.Connector.Ics.Kaspersky.Tests.csproj - APPLY |
|
||||||
| 541 | AUDIT-0181-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Jvn/StellaOps.Concelier.Connector.Jvn.csproj - MAINT |
|
| 541 | AUDIT-0181-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Jvn/StellaOps.Concelier.Connector.Jvn.csproj - MAINT |
|
||||||
| 542 | AUDIT-0181-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Jvn/StellaOps.Concelier.Connector.Jvn.csproj - TEST |
|
| 542 | AUDIT-0181-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Jvn/StellaOps.Concelier.Connector.Jvn.csproj - TEST |
|
||||||
| 543 | AUDIT-0181-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Jvn/StellaOps.Concelier.Connector.Jvn.csproj - APPLY |
|
| 543 | AUDIT-0181-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash for deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Jvn/StellaOps.Concelier.Connector.Jvn.csproj - APPLY |
|
||||||
| 544 | AUDIT-0182-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Jvn.Tests/StellaOps.Concelier.Connector.Jvn.Tests.csproj - MAINT |
|
| 544 | AUDIT-0182-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Jvn.Tests/StellaOps.Concelier.Connector.Jvn.Tests.csproj - MAINT |
|
||||||
| 545 | AUDIT-0182-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Jvn.Tests/StellaOps.Concelier.Connector.Jvn.Tests.csproj - TEST |
|
| 545 | AUDIT-0182-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Jvn.Tests/StellaOps.Concelier.Connector.Jvn.Tests.csproj - TEST |
|
||||||
| 546 | AUDIT-0182-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Jvn.Tests/StellaOps.Concelier.Connector.Jvn.Tests.csproj - APPLY |
|
| 546 | AUDIT-0182-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Jvn.Tests/StellaOps.Concelier.Connector.Jvn.Tests.csproj - APPLY |
|
||||||
| 547 | AUDIT-0183-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kev/StellaOps.Concelier.Connector.Kev.csproj - MAINT |
|
| 547 | AUDIT-0183-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kev/StellaOps.Concelier.Connector.Kev.csproj - MAINT |
|
||||||
| 548 | AUDIT-0183-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kev/StellaOps.Concelier.Connector.Kev.csproj - TEST |
|
| 548 | AUDIT-0183-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kev/StellaOps.Concelier.Connector.Kev.csproj - TEST |
|
||||||
| 549 | AUDIT-0183-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kev/StellaOps.Concelier.Connector.Kev.csproj - APPLY |
|
| 549 | AUDIT-0183-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash for deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kev/StellaOps.Concelier.Connector.Kev.csproj - APPLY |
|
||||||
| 550 | AUDIT-0184-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kev.Tests/StellaOps.Concelier.Connector.Kev.Tests.csproj - MAINT |
|
| 550 | AUDIT-0184-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kev.Tests/StellaOps.Concelier.Connector.Kev.Tests.csproj - MAINT |
|
||||||
| 551 | AUDIT-0184-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kev.Tests/StellaOps.Concelier.Connector.Kev.Tests.csproj - TEST |
|
| 551 | AUDIT-0184-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kev.Tests/StellaOps.Concelier.Connector.Kev.Tests.csproj - TEST |
|
||||||
| 552 | AUDIT-0184-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kev.Tests/StellaOps.Concelier.Connector.Kev.Tests.csproj - APPLY |
|
| 552 | AUDIT-0184-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kev.Tests/StellaOps.Concelier.Connector.Kev.Tests.csproj - APPLY |
|
||||||
| 553 | AUDIT-0185-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kisa/StellaOps.Concelier.Connector.Kisa.csproj - MAINT |
|
| 553 | AUDIT-0185-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kisa/StellaOps.Concelier.Connector.Kisa.csproj - MAINT |
|
||||||
| 554 | AUDIT-0185-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kisa/StellaOps.Concelier.Connector.Kisa.csproj - TEST |
|
| 554 | AUDIT-0185-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kisa/StellaOps.Concelier.Connector.Kisa.csproj - TEST |
|
||||||
| 555 | AUDIT-0185-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kisa/StellaOps.Concelier.Connector.Kisa.csproj - APPLY |
|
| 555 | AUDIT-0185-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash for deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Kisa/StellaOps.Concelier.Connector.Kisa.csproj - APPLY |
|
||||||
| 556 | AUDIT-0186-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kisa.Tests/StellaOps.Concelier.Connector.Kisa.Tests.csproj - MAINT |
|
| 556 | AUDIT-0186-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kisa.Tests/StellaOps.Concelier.Connector.Kisa.Tests.csproj - MAINT |
|
||||||
| 557 | AUDIT-0186-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kisa.Tests/StellaOps.Concelier.Connector.Kisa.Tests.csproj - TEST |
|
| 557 | AUDIT-0186-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kisa.Tests/StellaOps.Concelier.Connector.Kisa.Tests.csproj - TEST |
|
||||||
| 558 | AUDIT-0186-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kisa.Tests/StellaOps.Concelier.Connector.Kisa.Tests.csproj - APPLY |
|
| 558 | AUDIT-0186-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Kisa.Tests/StellaOps.Concelier.Connector.Kisa.Tests.csproj - APPLY |
|
||||||
| 559 | AUDIT-0187-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Nvd/StellaOps.Concelier.Connector.Nvd.csproj - MAINT |
|
| 559 | AUDIT-0187-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Nvd/StellaOps.Concelier.Connector.Nvd.csproj - MAINT |
|
||||||
| 560 | AUDIT-0187-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Nvd/StellaOps.Concelier.Connector.Nvd.csproj - TEST |
|
| 560 | AUDIT-0187-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Nvd/StellaOps.Concelier.Connector.Nvd.csproj - TEST |
|
||||||
| 561 | AUDIT-0187-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Nvd/StellaOps.Concelier.Connector.Nvd.csproj - APPLY |
|
| 561 | AUDIT-0187-A | DONE | Enabled TreatWarningsAsErrors, added deterministic IDs (DtoRecord+ChangeHistoryRecord), sorted cursor | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Nvd/StellaOps.Concelier.Connector.Nvd.csproj - APPLY |
|
||||||
| 562 | AUDIT-0188-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Nvd.Tests/StellaOps.Concelier.Connector.Nvd.Tests.csproj - MAINT |
|
| 562 | AUDIT-0188-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Nvd.Tests/StellaOps.Concelier.Connector.Nvd.Tests.csproj - MAINT |
|
||||||
| 563 | AUDIT-0188-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Nvd.Tests/StellaOps.Concelier.Connector.Nvd.Tests.csproj - TEST |
|
| 563 | AUDIT-0188-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Nvd.Tests/StellaOps.Concelier.Connector.Nvd.Tests.csproj - TEST |
|
||||||
| 564 | AUDIT-0188-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Nvd.Tests/StellaOps.Concelier.Connector.Nvd.Tests.csproj - APPLY |
|
| 564 | AUDIT-0188-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Nvd.Tests/StellaOps.Concelier.Connector.Nvd.Tests.csproj - APPLY |
|
||||||
| 565 | AUDIT-0189-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Osv/StellaOps.Concelier.Connector.Osv.csproj - MAINT |
|
| 565 | AUDIT-0189-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Osv/StellaOps.Concelier.Connector.Osv.csproj - MAINT |
|
||||||
| 566 | AUDIT-0189-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Osv/StellaOps.Concelier.Connector.Osv.csproj - TEST |
|
| 566 | AUDIT-0189-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Osv/StellaOps.Concelier.Connector.Osv.csproj - TEST |
|
||||||
| 567 | AUDIT-0189-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Osv/StellaOps.Concelier.Connector.Osv.csproj - APPLY |
|
| 567 | AUDIT-0189-A | DONE | Enabled TreatWarningsAsErrors, added deterministic IDs (DtoRecord+DocumentRecord), sorted cursor | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Osv/StellaOps.Concelier.Connector.Osv.csproj - APPLY |
|
||||||
| 568 | AUDIT-0190-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Osv.Tests/StellaOps.Concelier.Connector.Osv.Tests.csproj - MAINT |
|
| 568 | AUDIT-0190-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Osv.Tests/StellaOps.Concelier.Connector.Osv.Tests.csproj - MAINT |
|
||||||
| 569 | AUDIT-0190-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Osv.Tests/StellaOps.Concelier.Connector.Osv.Tests.csproj - TEST |
|
| 569 | AUDIT-0190-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Osv.Tests/StellaOps.Concelier.Connector.Osv.Tests.csproj - TEST |
|
||||||
| 570 | AUDIT-0190-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Osv.Tests/StellaOps.Concelier.Connector.Osv.Tests.csproj - APPLY |
|
| 570 | AUDIT-0190-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Osv.Tests/StellaOps.Concelier.Connector.Osv.Tests.csproj - APPLY |
|
||||||
| 571 | AUDIT-0191-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Bdu/StellaOps.Concelier.Connector.Ru.Bdu.csproj - MAINT |
|
| 571 | AUDIT-0191-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Bdu/StellaOps.Concelier.Connector.Ru.Bdu.csproj - MAINT |
|
||||||
| 572 | AUDIT-0191-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Bdu/StellaOps.Concelier.Connector.Ru.Bdu.csproj - TEST |
|
| 572 | AUDIT-0191-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Bdu/StellaOps.Concelier.Connector.Ru.Bdu.csproj - TEST |
|
||||||
| 573 | AUDIT-0191-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Bdu/StellaOps.Concelier.Connector.Ru.Bdu.csproj - APPLY |
|
| 573 | AUDIT-0191-A | DONE | Enabled TreatWarningsAsErrors, added deterministic IDs (DtoRecord+DocumentRecord), sorted cursor | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Bdu/StellaOps.Concelier.Connector.Ru.Bdu.csproj - APPLY |
|
||||||
| 574 | AUDIT-0192-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests.csproj - MAINT |
|
| 574 | AUDIT-0192-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests.csproj - MAINT |
|
||||||
| 575 | AUDIT-0192-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests.csproj - TEST |
|
| 575 | AUDIT-0192-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests.csproj - TEST |
|
||||||
| 576 | AUDIT-0192-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests.csproj - APPLY |
|
| 576 | AUDIT-0192-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests/StellaOps.Concelier.Connector.Ru.Bdu.Tests.csproj - APPLY |
|
||||||
| 577 | AUDIT-0193-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Nkcki/StellaOps.Concelier.Connector.Ru.Nkcki.csproj - MAINT |
|
| 577 | AUDIT-0193-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Nkcki/StellaOps.Concelier.Connector.Ru.Nkcki.csproj - MAINT |
|
||||||
| 578 | AUDIT-0193-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Nkcki/StellaOps.Concelier.Connector.Ru.Nkcki.csproj - TEST |
|
| 578 | AUDIT-0193-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Nkcki/StellaOps.Concelier.Connector.Ru.Nkcki.csproj - TEST |
|
||||||
| 579 | AUDIT-0193-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Nkcki/StellaOps.Concelier.Connector.Ru.Nkcki.csproj - APPLY |
|
| 579 | AUDIT-0193-A | DONE | Enabled TreatWarningsAsErrors, deterministic IDs+slugs, sorted cursor; Note: DTO AdvisoryKey fallback needs arch review | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Ru.Nkcki/StellaOps.Concelier.Connector.Ru.Nkcki.csproj - APPLY |
|
||||||
| 580 | AUDIT-0194-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests.csproj - MAINT |
|
| 580 | AUDIT-0194-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests.csproj - MAINT |
|
||||||
| 581 | AUDIT-0194-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests.csproj - TEST |
|
| 581 | AUDIT-0194-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests.csproj - TEST |
|
||||||
| 582 | AUDIT-0194-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests.csproj - APPLY |
|
| 582 | AUDIT-0194-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests/StellaOps.Concelier.Connector.Ru.Nkcki.Tests.csproj - APPLY |
|
||||||
| 583 | AUDIT-0195-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/StellaOps.Concelier.Connector.StellaOpsMirror.csproj - MAINT |
|
| 583 | AUDIT-0195-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/StellaOps.Concelier.Connector.StellaOpsMirror.csproj - MAINT |
|
||||||
| 584 | AUDIT-0195-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/StellaOps.Concelier.Connector.StellaOpsMirror.csproj - TEST |
|
| 584 | AUDIT-0195-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/StellaOps.Concelier.Connector.StellaOpsMirror.csproj - TEST |
|
||||||
| 585 | AUDIT-0195-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/StellaOps.Concelier.Connector.StellaOpsMirror.csproj - APPLY |
|
| 585 | AUDIT-0195-A | DONE | Enabled TreatWarningsAsErrors, added deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.StellaOpsMirror/StellaOps.Concelier.Connector.StellaOpsMirror.csproj - APPLY |
|
||||||
| 586 | AUDIT-0196-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests.csproj - MAINT |
|
| 586 | AUDIT-0196-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests.csproj - MAINT |
|
||||||
| 587 | AUDIT-0196-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests.csproj - TEST |
|
| 587 | AUDIT-0196-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests.csproj - TEST |
|
||||||
| 588 | AUDIT-0196-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests.csproj - APPLY |
|
| 588 | AUDIT-0196-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests/StellaOps.Concelier.Connector.StellaOpsMirror.Tests.csproj - APPLY |
|
||||||
| 589 | AUDIT-0197-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Adobe/StellaOps.Concelier.Connector.Vndr.Adobe.csproj - MAINT |
|
| 589 | AUDIT-0197-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Adobe/StellaOps.Concelier.Connector.Vndr.Adobe.csproj - MAINT |
|
||||||
| 590 | AUDIT-0197-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Adobe/StellaOps.Concelier.Connector.Vndr.Adobe.csproj - TEST |
|
| 590 | AUDIT-0197-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Adobe/StellaOps.Concelier.Connector.Vndr.Adobe.csproj - TEST |
|
||||||
| 591 | AUDIT-0197-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Adobe/StellaOps.Concelier.Connector.Vndr.Adobe.csproj - APPLY |
|
| 591 | AUDIT-0197-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash+deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Adobe/StellaOps.Concelier.Connector.Vndr.Adobe.csproj - APPLY |
|
||||||
| 592 | AUDIT-0198-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests.csproj - MAINT |
|
| 592 | AUDIT-0198-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests.csproj - MAINT |
|
||||||
| 593 | AUDIT-0198-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests.csproj - TEST |
|
| 593 | AUDIT-0198-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests.csproj - TEST |
|
||||||
| 594 | AUDIT-0198-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests.csproj - APPLY |
|
| 594 | AUDIT-0198-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests/StellaOps.Concelier.Connector.Vndr.Adobe.Tests.csproj - APPLY |
|
||||||
| 595 | AUDIT-0199-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Apple/StellaOps.Concelier.Connector.Vndr.Apple.csproj - MAINT |
|
| 595 | AUDIT-0199-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Apple/StellaOps.Concelier.Connector.Vndr.Apple.csproj - MAINT |
|
||||||
| 596 | AUDIT-0199-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Apple/StellaOps.Concelier.Connector.Vndr.Apple.csproj - TEST |
|
| 596 | AUDIT-0199-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Apple/StellaOps.Concelier.Connector.Vndr.Apple.csproj - TEST |
|
||||||
| 597 | AUDIT-0199-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Apple/StellaOps.Concelier.Connector.Vndr.Apple.csproj - APPLY |
|
| 597 | AUDIT-0199-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash+deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Apple/StellaOps.Concelier.Connector.Vndr.Apple.csproj - APPLY |
|
||||||
| 598 | AUDIT-0200-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests.csproj - MAINT |
|
| 598 | AUDIT-0200-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests.csproj - MAINT |
|
||||||
| 599 | AUDIT-0200-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests.csproj - TEST |
|
| 599 | AUDIT-0200-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests.csproj - TEST |
|
||||||
| 600 | AUDIT-0200-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests.csproj - APPLY |
|
| 600 | AUDIT-0200-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests/StellaOps.Concelier.Connector.Vndr.Apple.Tests.csproj - APPLY |
|
||||||
| 601 | AUDIT-0201-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Chromium/StellaOps.Concelier.Connector.Vndr.Chromium.csproj - MAINT |
|
| 601 | AUDIT-0201-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Chromium/StellaOps.Concelier.Connector.Vndr.Chromium.csproj - MAINT |
|
||||||
| 602 | AUDIT-0201-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Chromium/StellaOps.Concelier.Connector.Vndr.Chromium.csproj - TEST |
|
| 602 | AUDIT-0201-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Chromium/StellaOps.Concelier.Connector.Vndr.Chromium.csproj - TEST |
|
||||||
| 603 | AUDIT-0201-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Chromium/StellaOps.Concelier.Connector.Vndr.Chromium.csproj - APPLY |
|
| 603 | AUDIT-0201-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash+deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Chromium/StellaOps.Concelier.Connector.Vndr.Chromium.csproj - APPLY |
|
||||||
| 604 | AUDIT-0202-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests.csproj - MAINT |
|
| 604 | AUDIT-0202-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests.csproj - MAINT |
|
||||||
| 605 | AUDIT-0202-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests.csproj - TEST |
|
| 605 | AUDIT-0202-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests.csproj - TEST |
|
||||||
| 606 | AUDIT-0202-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests.csproj - APPLY |
|
| 606 | AUDIT-0202-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests/StellaOps.Concelier.Connector.Vndr.Chromium.Tests.csproj - APPLY |
|
||||||
| 607 | AUDIT-0203-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Cisco/StellaOps.Concelier.Connector.Vndr.Cisco.csproj - MAINT |
|
| 607 | AUDIT-0203-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Cisco/StellaOps.Concelier.Connector.Vndr.Cisco.csproj - MAINT |
|
||||||
| 608 | AUDIT-0203-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Cisco/StellaOps.Concelier.Connector.Vndr.Cisco.csproj - TEST |
|
| 608 | AUDIT-0203-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Cisco/StellaOps.Concelier.Connector.Vndr.Cisco.csproj - TEST |
|
||||||
| 609 | AUDIT-0203-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Cisco/StellaOps.Concelier.Connector.Vndr.Cisco.csproj - APPLY |
|
| 609 | AUDIT-0203-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash+deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Cisco/StellaOps.Concelier.Connector.Vndr.Cisco.csproj - APPLY |
|
||||||
| 610 | AUDIT-0204-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests.csproj - MAINT |
|
| 610 | AUDIT-0204-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests.csproj - MAINT |
|
||||||
| 611 | AUDIT-0204-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests.csproj - TEST |
|
| 611 | AUDIT-0204-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests.csproj - TEST |
|
||||||
| 612 | AUDIT-0204-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests.csproj - APPLY |
|
| 612 | AUDIT-0204-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests/StellaOps.Concelier.Connector.Vndr.Cisco.Tests.csproj - APPLY |
|
||||||
| 613 | AUDIT-0205-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Msrc/StellaOps.Concelier.Connector.Vndr.Msrc.csproj - MAINT |
|
| 613 | AUDIT-0205-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Msrc/StellaOps.Concelier.Connector.Vndr.Msrc.csproj - MAINT |
|
||||||
| 614 | AUDIT-0205-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Msrc/StellaOps.Concelier.Connector.Vndr.Msrc.csproj - TEST |
|
| 614 | AUDIT-0205-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Msrc/StellaOps.Concelier.Connector.Vndr.Msrc.csproj - TEST |
|
||||||
| 615 | AUDIT-0205-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Msrc/StellaOps.Concelier.Connector.Vndr.Msrc.csproj - APPLY |
|
| 615 | AUDIT-0205-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash+deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Msrc/StellaOps.Concelier.Connector.Vndr.Msrc.csproj - APPLY |
|
||||||
| 616 | AUDIT-0206-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests.csproj - MAINT |
|
| 616 | AUDIT-0206-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests.csproj - MAINT |
|
||||||
| 617 | AUDIT-0206-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests.csproj - TEST |
|
| 617 | AUDIT-0206-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests.csproj - TEST |
|
||||||
| 618 | AUDIT-0206-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests.csproj - APPLY |
|
| 618 | AUDIT-0206-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests/StellaOps.Concelier.Connector.Vndr.Msrc.Tests.csproj - APPLY |
|
||||||
| 619 | AUDIT-0207-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Oracle/StellaOps.Concelier.Connector.Vndr.Oracle.csproj - MAINT |
|
| 619 | AUDIT-0207-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Oracle/StellaOps.Concelier.Connector.Vndr.Oracle.csproj - MAINT |
|
||||||
| 620 | AUDIT-0207-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Oracle/StellaOps.Concelier.Connector.Vndr.Oracle.csproj - TEST |
|
| 620 | AUDIT-0207-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Oracle/StellaOps.Concelier.Connector.Vndr.Oracle.csproj - TEST |
|
||||||
| 621 | AUDIT-0207-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Oracle/StellaOps.Concelier.Connector.Vndr.Oracle.csproj - APPLY |
|
| 621 | AUDIT-0207-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash+deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Oracle/StellaOps.Concelier.Connector.Vndr.Oracle.csproj - APPLY |
|
||||||
| 622 | AUDIT-0208-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests.csproj - MAINT |
|
| 622 | AUDIT-0208-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests.csproj - MAINT |
|
||||||
| 623 | AUDIT-0208-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests.csproj - TEST |
|
| 623 | AUDIT-0208-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests.csproj - TEST |
|
||||||
| 624 | AUDIT-0208-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests.csproj - APPLY |
|
| 624 | AUDIT-0208-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests/StellaOps.Concelier.Connector.Vndr.Oracle.Tests.csproj - APPLY |
|
||||||
| 625 | AUDIT-0209-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Vmware/StellaOps.Concelier.Connector.Vndr.Vmware.csproj - MAINT |
|
| 625 | AUDIT-0209-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Vmware/StellaOps.Concelier.Connector.Vndr.Vmware.csproj - MAINT |
|
||||||
| 626 | AUDIT-0209-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Vmware/StellaOps.Concelier.Connector.Vndr.Vmware.csproj - TEST |
|
| 626 | AUDIT-0209-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Vmware/StellaOps.Concelier.Connector.Vndr.Vmware.csproj - TEST |
|
||||||
| 627 | AUDIT-0209-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Vmware/StellaOps.Concelier.Connector.Vndr.Vmware.csproj - APPLY |
|
| 627 | AUDIT-0209-A | DONE | Enabled TreatWarningsAsErrors, added ICryptoHash+deterministic IDs, sorted cursor collections | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Connector.Vndr.Vmware/StellaOps.Concelier.Connector.Vndr.Vmware.csproj - APPLY |
|
||||||
| 628 | AUDIT-0210-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests.csproj - MAINT |
|
| 628 | AUDIT-0210-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests.csproj - MAINT |
|
||||||
| 629 | AUDIT-0210-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests.csproj - TEST |
|
| 629 | AUDIT-0210-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests.csproj - TEST |
|
||||||
| 630 | AUDIT-0210-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests.csproj - APPLY |
|
| 630 | AUDIT-0210-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests/StellaOps.Concelier.Connector.Vndr.Vmware.Tests.csproj - APPLY |
|
||||||
| 631 | AUDIT-0211-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj - MAINT |
|
| 631 | AUDIT-0211-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj - MAINT |
|
||||||
| 632 | AUDIT-0211-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj - TEST |
|
| 632 | AUDIT-0211-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj - TEST |
|
||||||
| 633 | AUDIT-0211-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj - APPLY |
|
| 633 | AUDIT-0211-A | DONE | Enabled TreatWarningsAsErrors, replaced Guid.NewGuid() with deterministic IDs | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj - APPLY |
|
||||||
| 634 | AUDIT-0212-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/StellaOps.Concelier.Core.Tests.csproj - MAINT |
|
| 634 | AUDIT-0212-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/StellaOps.Concelier.Core.Tests.csproj - MAINT |
|
||||||
| 635 | AUDIT-0212-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/StellaOps.Concelier.Core.Tests.csproj - TEST |
|
| 635 | AUDIT-0212-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/StellaOps.Concelier.Core.Tests.csproj - TEST |
|
||||||
| 636 | AUDIT-0212-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/StellaOps.Concelier.Core.Tests.csproj - APPLY |
|
| 636 | AUDIT-0212-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/StellaOps.Concelier.Core.Tests.csproj - APPLY |
|
||||||
| 637 | AUDIT-0213-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.Json/StellaOps.Concelier.Exporter.Json.csproj - MAINT |
|
| 637 | AUDIT-0213-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.Json/StellaOps.Concelier.Exporter.Json.csproj - MAINT |
|
||||||
| 638 | AUDIT-0213-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.Json/StellaOps.Concelier.Exporter.Json.csproj - TEST |
|
| 638 | AUDIT-0213-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.Json/StellaOps.Concelier.Exporter.Json.csproj - TEST |
|
||||||
| 639 | AUDIT-0213-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.Json/StellaOps.Concelier.Exporter.Json.csproj - APPLY |
|
| 639 | AUDIT-0213-A | DONE | Enabled TreatWarningsAsErrors (no Guid.NewGuid() patterns found) | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.Json/StellaOps.Concelier.Exporter.Json.csproj - APPLY |
|
||||||
| 640 | AUDIT-0214-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.Json.Tests/StellaOps.Concelier.Exporter.Json.Tests.csproj - MAINT |
|
| 640 | AUDIT-0214-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.Json.Tests/StellaOps.Concelier.Exporter.Json.Tests.csproj - MAINT |
|
||||||
| 641 | AUDIT-0214-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.Json.Tests/StellaOps.Concelier.Exporter.Json.Tests.csproj - TEST |
|
| 641 | AUDIT-0214-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.Json.Tests/StellaOps.Concelier.Exporter.Json.Tests.csproj - TEST |
|
||||||
| 642 | AUDIT-0214-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.Json.Tests/StellaOps.Concelier.Exporter.Json.Tests.csproj - APPLY |
|
| 642 | AUDIT-0214-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.Json.Tests/StellaOps.Concelier.Exporter.Json.Tests.csproj - APPLY |
|
||||||
| 643 | AUDIT-0215-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.TrivyDb/StellaOps.Concelier.Exporter.TrivyDb.csproj - MAINT |
|
| 643 | AUDIT-0215-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.TrivyDb/StellaOps.Concelier.Exporter.TrivyDb.csproj - MAINT |
|
||||||
| 644 | AUDIT-0215-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.TrivyDb/StellaOps.Concelier.Exporter.TrivyDb.csproj - TEST |
|
| 644 | AUDIT-0215-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.TrivyDb/StellaOps.Concelier.Exporter.TrivyDb.csproj - TEST |
|
||||||
| 645 | AUDIT-0215-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.TrivyDb/StellaOps.Concelier.Exporter.TrivyDb.csproj - APPLY |
|
| 645 | AUDIT-0215-A | DONE | Enabled TreatWarningsAsErrors, added StellaOps.Cryptography reference | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Exporter.TrivyDb/StellaOps.Concelier.Exporter.TrivyDb.csproj - APPLY |
|
||||||
| 646 | AUDIT-0216-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests.csproj - MAINT |
|
| 646 | AUDIT-0216-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests.csproj - MAINT |
|
||||||
| 647 | AUDIT-0216-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests.csproj - TEST |
|
| 647 | AUDIT-0216-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests.csproj - TEST |
|
||||||
| 648 | AUDIT-0216-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests.csproj - APPLY |
|
| 648 | AUDIT-0216-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests/StellaOps.Concelier.Exporter.TrivyDb.Tests.csproj - APPLY |
|
||||||
| 649 | AUDIT-0217-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Federation/StellaOps.Concelier.Federation.csproj - MAINT |
|
| 649 | AUDIT-0217-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Federation/StellaOps.Concelier.Federation.csproj - MAINT |
|
||||||
| 650 | AUDIT-0217-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Federation/StellaOps.Concelier.Federation.csproj - TEST |
|
| 650 | AUDIT-0217-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Federation/StellaOps.Concelier.Federation.csproj - TEST |
|
||||||
| 651 | AUDIT-0217-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Federation/StellaOps.Concelier.Federation.csproj - APPLY |
|
| 651 | AUDIT-0217-A | DONE | Enabled TreatWarningsAsErrors (no Guid.NewGuid() patterns found) | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Federation/StellaOps.Concelier.Federation.csproj - APPLY |
|
||||||
| 652 | AUDIT-0218-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Federation.Tests/StellaOps.Concelier.Federation.Tests.csproj - MAINT |
|
| 652 | AUDIT-0218-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Federation.Tests/StellaOps.Concelier.Federation.Tests.csproj - MAINT |
|
||||||
| 653 | AUDIT-0218-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Federation.Tests/StellaOps.Concelier.Federation.Tests.csproj - TEST |
|
| 653 | AUDIT-0218-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Federation.Tests/StellaOps.Concelier.Federation.Tests.csproj - TEST |
|
||||||
| 654 | AUDIT-0218-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Federation.Tests/StellaOps.Concelier.Federation.Tests.csproj - APPLY |
|
| 654 | AUDIT-0218-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Federation.Tests/StellaOps.Concelier.Federation.Tests.csproj - APPLY |
|
||||||
@@ -683,13 +683,13 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 657 | AUDIT-0219-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Integration.Tests/StellaOps.Concelier.Integration.Tests.csproj - APPLY |
|
| 657 | AUDIT-0219-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Integration.Tests/StellaOps.Concelier.Integration.Tests.csproj - APPLY |
|
||||||
| 658 | AUDIT-0220-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Interest/StellaOps.Concelier.Interest.csproj - MAINT |
|
| 658 | AUDIT-0220-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Interest/StellaOps.Concelier.Interest.csproj - MAINT |
|
||||||
| 659 | AUDIT-0220-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Interest/StellaOps.Concelier.Interest.csproj - TEST |
|
| 659 | AUDIT-0220-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Interest/StellaOps.Concelier.Interest.csproj - TEST |
|
||||||
| 660 | AUDIT-0220-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Interest/StellaOps.Concelier.Interest.csproj - APPLY |
|
| 660 | AUDIT-0220-A | DONE | Enabled TreatWarningsAsErrors (no Guid.NewGuid() patterns found) | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Interest/StellaOps.Concelier.Interest.csproj - APPLY |
|
||||||
| 661 | AUDIT-0221-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Interest.Tests/StellaOps.Concelier.Interest.Tests.csproj - MAINT |
|
| 661 | AUDIT-0221-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Interest.Tests/StellaOps.Concelier.Interest.Tests.csproj - MAINT |
|
||||||
| 662 | AUDIT-0221-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Interest.Tests/StellaOps.Concelier.Interest.Tests.csproj - TEST |
|
| 662 | AUDIT-0221-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Interest.Tests/StellaOps.Concelier.Interest.Tests.csproj - TEST |
|
||||||
| 663 | AUDIT-0221-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Interest.Tests/StellaOps.Concelier.Interest.Tests.csproj - APPLY |
|
| 663 | AUDIT-0221-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Interest.Tests/StellaOps.Concelier.Interest.Tests.csproj - APPLY |
|
||||||
| 664 | AUDIT-0222-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csproj - MAINT |
|
| 664 | AUDIT-0222-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csproj - MAINT |
|
||||||
| 665 | AUDIT-0222-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csproj - TEST |
|
| 665 | AUDIT-0222-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csproj - TEST |
|
||||||
| 666 | AUDIT-0222-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csproj - APPLY |
|
| 666 | AUDIT-0222-A | DONE | Enabled TreatWarningsAsErrors, replaced Guid.NewGuid() with deterministic IDs | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csproj - APPLY |
|
||||||
| 667 | AUDIT-0223-M | DONE | Report | Guild | src/Concelier/__Analyzers/StellaOps.Concelier.Merge.Analyzers/StellaOps.Concelier.Merge.Analyzers.csproj - MAINT |
|
| 667 | AUDIT-0223-M | DONE | Report | Guild | src/Concelier/__Analyzers/StellaOps.Concelier.Merge.Analyzers/StellaOps.Concelier.Merge.Analyzers.csproj - MAINT |
|
||||||
| 668 | AUDIT-0223-T | DONE | Report | Guild | src/Concelier/__Analyzers/StellaOps.Concelier.Merge.Analyzers/StellaOps.Concelier.Merge.Analyzers.csproj - TEST |
|
| 668 | AUDIT-0223-T | DONE | Report | Guild | src/Concelier/__Analyzers/StellaOps.Concelier.Merge.Analyzers/StellaOps.Concelier.Merge.Analyzers.csproj - TEST |
|
||||||
| 669 | AUDIT-0223-A | TODO | Approval | Guild | src/Concelier/__Analyzers/StellaOps.Concelier.Merge.Analyzers/StellaOps.Concelier.Merge.Analyzers.csproj - APPLY |
|
| 669 | AUDIT-0223-A | TODO | Approval | Guild | src/Concelier/__Analyzers/StellaOps.Concelier.Merge.Analyzers/StellaOps.Concelier.Merge.Analyzers.csproj - APPLY |
|
||||||
@@ -701,46 +701,46 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 675 | AUDIT-0225-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Merge.Tests/StellaOps.Concelier.Merge.Tests.csproj - APPLY |
|
| 675 | AUDIT-0225-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Merge.Tests/StellaOps.Concelier.Merge.Tests.csproj - APPLY |
|
||||||
| 676 | AUDIT-0226-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj - MAINT |
|
| 676 | AUDIT-0226-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj - MAINT |
|
||||||
| 677 | AUDIT-0226-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj - TEST |
|
| 677 | AUDIT-0226-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj - TEST |
|
||||||
| 678 | AUDIT-0226-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj - APPLY |
|
| 678 | AUDIT-0226-A | DONE | Enabled TreatWarningsAsErrors (entity ID fallbacks acceptable in model layer) | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj - APPLY |
|
||||||
| 679 | AUDIT-0227-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Models.Tests/StellaOps.Concelier.Models.Tests.csproj - MAINT |
|
| 679 | AUDIT-0227-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Models.Tests/StellaOps.Concelier.Models.Tests.csproj - MAINT |
|
||||||
| 680 | AUDIT-0227-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Models.Tests/StellaOps.Concelier.Models.Tests.csproj - TEST |
|
| 680 | AUDIT-0227-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Models.Tests/StellaOps.Concelier.Models.Tests.csproj - TEST |
|
||||||
| 681 | AUDIT-0227-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Models.Tests/StellaOps.Concelier.Models.Tests.csproj - APPLY |
|
| 681 | AUDIT-0227-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Models.Tests/StellaOps.Concelier.Models.Tests.csproj - APPLY |
|
||||||
| 682 | AUDIT-0228-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj - MAINT |
|
| 682 | AUDIT-0228-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj - MAINT |
|
||||||
| 683 | AUDIT-0228-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj - TEST |
|
| 683 | AUDIT-0228-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj - TEST |
|
||||||
| 684 | AUDIT-0228-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj - APPLY |
|
| 684 | AUDIT-0228-A | DONE | Added TreatWarningsAsErrors (no Guid.NewGuid() patterns found) | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj - APPLY |
|
||||||
| 685 | AUDIT-0229-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Normalization.Tests/StellaOps.Concelier.Normalization.Tests.csproj - MAINT |
|
| 685 | AUDIT-0229-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Normalization.Tests/StellaOps.Concelier.Normalization.Tests.csproj - MAINT |
|
||||||
| 686 | AUDIT-0229-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Normalization.Tests/StellaOps.Concelier.Normalization.Tests.csproj - TEST |
|
| 686 | AUDIT-0229-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Normalization.Tests/StellaOps.Concelier.Normalization.Tests.csproj - TEST |
|
||||||
| 687 | AUDIT-0229-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Normalization.Tests/StellaOps.Concelier.Normalization.Tests.csproj - APPLY |
|
| 687 | AUDIT-0229-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Normalization.Tests/StellaOps.Concelier.Normalization.Tests.csproj - APPLY |
|
||||||
| 688 | AUDIT-0230-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Persistence/StellaOps.Concelier.Persistence.csproj - MAINT |
|
| 688 | AUDIT-0230-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Persistence/StellaOps.Concelier.Persistence.csproj - MAINT |
|
||||||
| 689 | AUDIT-0230-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Persistence/StellaOps.Concelier.Persistence.csproj - TEST |
|
| 689 | AUDIT-0230-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Persistence/StellaOps.Concelier.Persistence.csproj - TEST |
|
||||||
| 690 | AUDIT-0230-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Persistence/StellaOps.Concelier.Persistence.csproj - APPLY |
|
| 690 | AUDIT-0230-A | DONE | Enabled TreatWarningsAsErrors (entity ID fallbacks acceptable in persistence layer) | Guild | src/Concelier/__Libraries/StellaOps.Concelier.Persistence/StellaOps.Concelier.Persistence.csproj - APPLY |
|
||||||
| 691 | AUDIT-0231-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Persistence.Tests/StellaOps.Concelier.Persistence.Tests.csproj - MAINT |
|
| 691 | AUDIT-0231-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Persistence.Tests/StellaOps.Concelier.Persistence.Tests.csproj - MAINT |
|
||||||
| 692 | AUDIT-0231-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Persistence.Tests/StellaOps.Concelier.Persistence.Tests.csproj - TEST |
|
| 692 | AUDIT-0231-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.Persistence.Tests/StellaOps.Concelier.Persistence.Tests.csproj - TEST |
|
||||||
| 693 | AUDIT-0231-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Persistence.Tests/StellaOps.Concelier.Persistence.Tests.csproj - APPLY |
|
| 693 | AUDIT-0231-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.Persistence.Tests/StellaOps.Concelier.Persistence.Tests.csproj - APPLY |
|
||||||
| 694 | AUDIT-0232-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService/StellaOps.Concelier.ProofService.csproj - MAINT |
|
| 694 | AUDIT-0232-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService/StellaOps.Concelier.ProofService.csproj - MAINT |
|
||||||
| 695 | AUDIT-0232-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService/StellaOps.Concelier.ProofService.csproj - TEST |
|
| 695 | AUDIT-0232-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService/StellaOps.Concelier.ProofService.csproj - TEST |
|
||||||
| 696 | AUDIT-0232-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService/StellaOps.Concelier.ProofService.csproj - APPLY |
|
| 696 | AUDIT-0232-A | DONE | Added TreatWarningsAsErrors (no Guid.NewGuid() patterns found) | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService/StellaOps.Concelier.ProofService.csproj - APPLY |
|
||||||
| 697 | AUDIT-0233-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService.Postgres/StellaOps.Concelier.ProofService.Postgres.csproj - MAINT |
|
| 697 | AUDIT-0233-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService.Postgres/StellaOps.Concelier.ProofService.Postgres.csproj - MAINT |
|
||||||
| 698 | AUDIT-0233-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService.Postgres/StellaOps.Concelier.ProofService.Postgres.csproj - TEST |
|
| 698 | AUDIT-0233-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService.Postgres/StellaOps.Concelier.ProofService.Postgres.csproj - TEST |
|
||||||
| 699 | AUDIT-0233-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService.Postgres/StellaOps.Concelier.ProofService.Postgres.csproj - APPLY |
|
| 699 | AUDIT-0233-A | DONE | Added TreatWarningsAsErrors (no Guid.NewGuid() patterns found) | Guild | src/Concelier/__Libraries/StellaOps.Concelier.ProofService.Postgres/StellaOps.Concelier.ProofService.Postgres.csproj - APPLY |
|
||||||
| 700 | AUDIT-0234-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.ProofService.Postgres.Tests/StellaOps.Concelier.ProofService.Postgres.Tests.csproj - MAINT |
|
| 700 | AUDIT-0234-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.ProofService.Postgres.Tests/StellaOps.Concelier.ProofService.Postgres.Tests.csproj - MAINT |
|
||||||
| 701 | AUDIT-0234-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.ProofService.Postgres.Tests/StellaOps.Concelier.ProofService.Postgres.Tests.csproj - TEST |
|
| 701 | AUDIT-0234-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.ProofService.Postgres.Tests/StellaOps.Concelier.ProofService.Postgres.Tests.csproj - TEST |
|
||||||
| 702 | AUDIT-0234-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.ProofService.Postgres.Tests/StellaOps.Concelier.ProofService.Postgres.Tests.csproj - APPLY |
|
| 702 | AUDIT-0234-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.ProofService.Postgres.Tests/StellaOps.Concelier.ProofService.Postgres.Tests.csproj - APPLY |
|
||||||
| 703 | AUDIT-0235-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.RawModels/StellaOps.Concelier.RawModels.csproj - MAINT |
|
| 703 | AUDIT-0235-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.RawModels/StellaOps.Concelier.RawModels.csproj - MAINT |
|
||||||
| 704 | AUDIT-0235-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.RawModels/StellaOps.Concelier.RawModels.csproj - TEST |
|
| 704 | AUDIT-0235-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.RawModels/StellaOps.Concelier.RawModels.csproj - TEST |
|
||||||
| 705 | AUDIT-0235-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.RawModels/StellaOps.Concelier.RawModels.csproj - APPLY |
|
| 705 | AUDIT-0235-A | DONE | Enabled TreatWarningsAsErrors (no Guid.NewGuid() patterns found) | Guild | src/Concelier/__Libraries/StellaOps.Concelier.RawModels/StellaOps.Concelier.RawModels.csproj - APPLY |
|
||||||
| 706 | AUDIT-0236-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.RawModels.Tests/StellaOps.Concelier.RawModels.Tests.csproj - MAINT |
|
| 706 | AUDIT-0236-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.RawModels.Tests/StellaOps.Concelier.RawModels.Tests.csproj - MAINT |
|
||||||
| 707 | AUDIT-0236-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.RawModels.Tests/StellaOps.Concelier.RawModels.Tests.csproj - TEST |
|
| 707 | AUDIT-0236-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.RawModels.Tests/StellaOps.Concelier.RawModels.Tests.csproj - TEST |
|
||||||
| 708 | AUDIT-0236-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.RawModels.Tests/StellaOps.Concelier.RawModels.Tests.csproj - APPLY |
|
| 708 | AUDIT-0236-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.RawModels.Tests/StellaOps.Concelier.RawModels.Tests.csproj - APPLY |
|
||||||
| 709 | AUDIT-0237-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SbomIntegration/StellaOps.Concelier.SbomIntegration.csproj - MAINT |
|
| 709 | AUDIT-0237-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SbomIntegration/StellaOps.Concelier.SbomIntegration.csproj - MAINT |
|
||||||
| 710 | AUDIT-0237-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SbomIntegration/StellaOps.Concelier.SbomIntegration.csproj - TEST |
|
| 710 | AUDIT-0237-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SbomIntegration/StellaOps.Concelier.SbomIntegration.csproj - TEST |
|
||||||
| 711 | AUDIT-0237-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SbomIntegration/StellaOps.Concelier.SbomIntegration.csproj - APPLY |
|
| 711 | AUDIT-0237-A | DONE | TreatWarningsAsErrors + deterministic match IDs | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SbomIntegration/StellaOps.Concelier.SbomIntegration.csproj - APPLY |
|
||||||
| 712 | AUDIT-0238-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.SbomIntegration.Tests/StellaOps.Concelier.SbomIntegration.Tests.csproj - MAINT |
|
| 712 | AUDIT-0238-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.SbomIntegration.Tests/StellaOps.Concelier.SbomIntegration.Tests.csproj - MAINT |
|
||||||
| 713 | AUDIT-0238-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.SbomIntegration.Tests/StellaOps.Concelier.SbomIntegration.Tests.csproj - TEST |
|
| 713 | AUDIT-0238-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.SbomIntegration.Tests/StellaOps.Concelier.SbomIntegration.Tests.csproj - TEST |
|
||||||
| 714 | AUDIT-0238-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.SbomIntegration.Tests/StellaOps.Concelier.SbomIntegration.Tests.csproj - APPLY |
|
| 714 | AUDIT-0238-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.SbomIntegration.Tests/StellaOps.Concelier.SbomIntegration.Tests.csproj - APPLY |
|
||||||
| 715 | AUDIT-0239-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SourceIntel/StellaOps.Concelier.SourceIntel.csproj - MAINT |
|
| 715 | AUDIT-0239-M | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SourceIntel/StellaOps.Concelier.SourceIntel.csproj - MAINT |
|
||||||
| 716 | AUDIT-0239-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SourceIntel/StellaOps.Concelier.SourceIntel.csproj - TEST |
|
| 716 | AUDIT-0239-T | DONE | Report | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SourceIntel/StellaOps.Concelier.SourceIntel.csproj - TEST |
|
||||||
| 717 | AUDIT-0239-A | TODO | Approval | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SourceIntel/StellaOps.Concelier.SourceIntel.csproj - APPLY |
|
| 717 | AUDIT-0239-A | DONE | TreatWarningsAsErrors added | Guild | src/Concelier/__Libraries/StellaOps.Concelier.SourceIntel/StellaOps.Concelier.SourceIntel.csproj - APPLY |
|
||||||
| 718 | AUDIT-0240-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.SourceIntel.Tests/StellaOps.Concelier.SourceIntel.Tests.csproj - MAINT |
|
| 718 | AUDIT-0240-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.SourceIntel.Tests/StellaOps.Concelier.SourceIntel.Tests.csproj - MAINT |
|
||||||
| 719 | AUDIT-0240-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.SourceIntel.Tests/StellaOps.Concelier.SourceIntel.Tests.csproj - TEST |
|
| 719 | AUDIT-0240-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.SourceIntel.Tests/StellaOps.Concelier.SourceIntel.Tests.csproj - TEST |
|
||||||
| 720 | AUDIT-0240-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.SourceIntel.Tests/StellaOps.Concelier.SourceIntel.Tests.csproj - APPLY |
|
| 720 | AUDIT-0240-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.SourceIntel.Tests/StellaOps.Concelier.SourceIntel.Tests.csproj - APPLY |
|
||||||
@@ -749,40 +749,40 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 723 | AUDIT-0241-A | TODO | Approval | Guild | src/__Tests/__Libraries/StellaOps.Concelier.Testing/StellaOps.Concelier.Testing.csproj - APPLY |
|
| 723 | AUDIT-0241-A | TODO | Approval | Guild | src/__Tests/__Libraries/StellaOps.Concelier.Testing/StellaOps.Concelier.Testing.csproj - APPLY |
|
||||||
| 724 | AUDIT-0242-M | DONE | Report | Guild | src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj - MAINT |
|
| 724 | AUDIT-0242-M | DONE | Report | Guild | src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj - MAINT |
|
||||||
| 725 | AUDIT-0242-T | DONE | Report | Guild | src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj - TEST |
|
| 725 | AUDIT-0242-T | DONE | Report | Guild | src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj - TEST |
|
||||||
| 726 | AUDIT-0242-A | TODO | Approval | Guild | src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj - APPLY |
|
| 726 | AUDIT-0242-A | DONE | TreatWarningsAsErrors enabled | Guild | src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj - APPLY |
|
||||||
| 727 | AUDIT-0243-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/StellaOps.Concelier.WebService.Tests.csproj - MAINT |
|
| 727 | AUDIT-0243-M | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/StellaOps.Concelier.WebService.Tests.csproj - MAINT |
|
||||||
| 728 | AUDIT-0243-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/StellaOps.Concelier.WebService.Tests.csproj - TEST |
|
| 728 | AUDIT-0243-T | DONE | Report | Guild | src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/StellaOps.Concelier.WebService.Tests.csproj - TEST |
|
||||||
| 729 | AUDIT-0243-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/StellaOps.Concelier.WebService.Tests.csproj - APPLY |
|
| 729 | AUDIT-0243-A | TODO | Approval | Guild | src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/StellaOps.Concelier.WebService.Tests.csproj - APPLY |
|
||||||
| 730 | AUDIT-0244-M | DONE | Report | Guild | src/__Libraries/StellaOps.Configuration/StellaOps.Configuration.csproj - MAINT |
|
| 730 | AUDIT-0244-M | DONE | Report | Guild | src/__Libraries/StellaOps.Configuration/StellaOps.Configuration.csproj - MAINT |
|
||||||
| 731 | AUDIT-0244-T | DONE | Report | Guild | src/__Libraries/StellaOps.Configuration/StellaOps.Configuration.csproj - TEST |
|
| 731 | AUDIT-0244-T | DONE | Report | Guild | src/__Libraries/StellaOps.Configuration/StellaOps.Configuration.csproj - TEST |
|
||||||
| 732 | AUDIT-0244-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Configuration/StellaOps.Configuration.csproj - APPLY |
|
| 732 | AUDIT-0244-A | DONE | TreatWarningsAsErrors added | Guild | src/__Libraries/StellaOps.Configuration/StellaOps.Configuration.csproj - APPLY |
|
||||||
| 733 | AUDIT-0245-M | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj - MAINT |
|
| 733 | AUDIT-0245-M | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj - MAINT |
|
||||||
| 734 | AUDIT-0245-T | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj - TEST |
|
| 734 | AUDIT-0245-T | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj - TEST |
|
||||||
| 735 | AUDIT-0245-A | TODO | Approval | Guild | src/__Libraries/__Tests/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj - APPLY |
|
| 735 | AUDIT-0245-A | TODO | Approval | Guild | src/__Libraries/__Tests/StellaOps.Configuration.Tests/StellaOps.Configuration.Tests.csproj - APPLY |
|
||||||
| 736 | AUDIT-0246-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj - MAINT |
|
| 736 | AUDIT-0246-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj - MAINT |
|
||||||
| 737 | AUDIT-0246-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj - TEST |
|
| 737 | AUDIT-0246-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj - TEST |
|
||||||
| 738 | AUDIT-0246-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj - APPLY |
|
| 738 | AUDIT-0246-A | DONE | TreatWarningsAsErrors enabled | Guild | src/__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj - APPLY |
|
||||||
| 739 | AUDIT-0247-M | DONE | Report | Guild | src/Cryptography/StellaOps.Cryptography/StellaOps.Cryptography.csproj - MAINT |
|
| 739 | AUDIT-0247-M | DONE | Report | Guild | src/Cryptography/StellaOps.Cryptography/StellaOps.Cryptography.csproj - MAINT |
|
||||||
| 740 | AUDIT-0247-T | DONE | Report | Guild | src/Cryptography/StellaOps.Cryptography/StellaOps.Cryptography.csproj - TEST |
|
| 740 | AUDIT-0247-T | DONE | Report | Guild | src/Cryptography/StellaOps.Cryptography/StellaOps.Cryptography.csproj - TEST |
|
||||||
| 741 | AUDIT-0247-A | TODO | Approval | Guild | src/Cryptography/StellaOps.Cryptography/StellaOps.Cryptography.csproj - APPLY |
|
| 741 | AUDIT-0247-A | TODO | Approval | Guild | src/Cryptography/StellaOps.Cryptography/StellaOps.Cryptography.csproj - APPLY |
|
||||||
| 742 | AUDIT-0248-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj - MAINT |
|
| 742 | AUDIT-0248-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj - MAINT |
|
||||||
| 743 | AUDIT-0248-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj - TEST |
|
| 743 | AUDIT-0248-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj - TEST |
|
||||||
| 744 | AUDIT-0248-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj - APPLY |
|
| 744 | AUDIT-0248-A | DONE | TreatWarningsAsErrors enabled | Guild | src/__Libraries/StellaOps.Cryptography.DependencyInjection/StellaOps.Cryptography.DependencyInjection.csproj - APPLY |
|
||||||
| 745 | AUDIT-0249-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Kms/StellaOps.Cryptography.Kms.csproj - MAINT |
|
| 745 | AUDIT-0249-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Kms/StellaOps.Cryptography.Kms.csproj - MAINT |
|
||||||
| 746 | AUDIT-0249-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Kms/StellaOps.Cryptography.Kms.csproj - TEST |
|
| 746 | AUDIT-0249-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Kms/StellaOps.Cryptography.Kms.csproj - TEST |
|
||||||
| 747 | AUDIT-0249-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Kms/StellaOps.Cryptography.Kms.csproj - APPLY |
|
| 747 | AUDIT-0249-A | DONE | TreatWarningsAsErrors added | Guild | src/__Libraries/StellaOps.Cryptography.Kms/StellaOps.Cryptography.Kms.csproj - APPLY |
|
||||||
| 748 | AUDIT-0250-M | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.Cryptography.Kms.Tests/StellaOps.Cryptography.Kms.Tests.csproj - MAINT |
|
| 748 | AUDIT-0250-M | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.Cryptography.Kms.Tests/StellaOps.Cryptography.Kms.Tests.csproj - MAINT |
|
||||||
| 749 | AUDIT-0250-T | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.Cryptography.Kms.Tests/StellaOps.Cryptography.Kms.Tests.csproj - TEST |
|
| 749 | AUDIT-0250-T | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.Cryptography.Kms.Tests/StellaOps.Cryptography.Kms.Tests.csproj - TEST |
|
||||||
| 750 | AUDIT-0250-A | TODO | Approval | Guild | src/__Libraries/__Tests/StellaOps.Cryptography.Kms.Tests/StellaOps.Cryptography.Kms.Tests.csproj - APPLY |
|
| 750 | AUDIT-0250-A | TODO | Approval | Guild | src/__Libraries/__Tests/StellaOps.Cryptography.Kms.Tests/StellaOps.Cryptography.Kms.Tests.csproj - APPLY |
|
||||||
| 751 | AUDIT-0251-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.BouncyCastle/StellaOps.Cryptography.Plugin.BouncyCastle.csproj - MAINT |
|
| 751 | AUDIT-0251-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.BouncyCastle/StellaOps.Cryptography.Plugin.BouncyCastle.csproj - MAINT |
|
||||||
| 752 | AUDIT-0251-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.BouncyCastle/StellaOps.Cryptography.Plugin.BouncyCastle.csproj - TEST |
|
| 752 | AUDIT-0251-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.BouncyCastle/StellaOps.Cryptography.Plugin.BouncyCastle.csproj - TEST |
|
||||||
| 753 | AUDIT-0251-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.BouncyCastle/StellaOps.Cryptography.Plugin.BouncyCastle.csproj - APPLY |
|
| 753 | AUDIT-0251-A | DONE | TreatWarningsAsErrors enabled | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.BouncyCastle/StellaOps.Cryptography.Plugin.BouncyCastle.csproj - APPLY |
|
||||||
| 754 | AUDIT-0252-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/StellaOps.Cryptography.Plugin.CryptoPro.csproj - MAINT |
|
| 754 | AUDIT-0252-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/StellaOps.Cryptography.Plugin.CryptoPro.csproj - MAINT |
|
||||||
| 755 | AUDIT-0252-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/StellaOps.Cryptography.Plugin.CryptoPro.csproj - TEST |
|
| 755 | AUDIT-0252-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/StellaOps.Cryptography.Plugin.CryptoPro.csproj - TEST |
|
||||||
| 756 | AUDIT-0252-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/StellaOps.Cryptography.Plugin.CryptoPro.csproj - APPLY |
|
| 756 | AUDIT-0252-A | DONE | TreatWarningsAsErrors enabled | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/StellaOps.Cryptography.Plugin.CryptoPro.csproj - APPLY |
|
||||||
| 757 | AUDIT-0253-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS/StellaOps.Cryptography.Plugin.EIDAS.csproj - MAINT |
|
| 757 | AUDIT-0253-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS/StellaOps.Cryptography.Plugin.EIDAS.csproj - MAINT |
|
||||||
| 758 | AUDIT-0253-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS/StellaOps.Cryptography.Plugin.EIDAS.csproj - TEST |
|
| 758 | AUDIT-0253-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS/StellaOps.Cryptography.Plugin.EIDAS.csproj - TEST |
|
||||||
| 759 | AUDIT-0253-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS/StellaOps.Cryptography.Plugin.EIDAS.csproj - APPLY |
|
| 759 | AUDIT-0253-A | DONE | TreatWarningsAsErrors added | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS/StellaOps.Cryptography.Plugin.EIDAS.csproj - APPLY |
|
||||||
| 760 | AUDIT-0254-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS.Tests/StellaOps.Cryptography.Plugin.EIDAS.Tests.csproj - MAINT |
|
| 760 | AUDIT-0254-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS.Tests/StellaOps.Cryptography.Plugin.EIDAS.Tests.csproj - MAINT |
|
||||||
| 761 | AUDIT-0254-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS.Tests/StellaOps.Cryptography.Plugin.EIDAS.Tests.csproj - TEST |
|
| 761 | AUDIT-0254-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS.Tests/StellaOps.Cryptography.Plugin.EIDAS.Tests.csproj - TEST |
|
||||||
| 762 | AUDIT-0254-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS.Tests/StellaOps.Cryptography.Plugin.EIDAS.Tests.csproj - APPLY |
|
| 762 | AUDIT-0254-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.EIDAS.Tests/StellaOps.Cryptography.Plugin.EIDAS.Tests.csproj - APPLY |
|
||||||
@@ -794,31 +794,31 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 768 | AUDIT-0256-A | TODO | Approval | Guild | src/__Libraries/__Tests/StellaOps.Cryptography.Plugin.OfflineVerification.Tests/StellaOps.Cryptography.Plugin.OfflineVerification.Tests.csproj - APPLY |
|
| 768 | AUDIT-0256-A | TODO | Approval | Guild | src/__Libraries/__Tests/StellaOps.Cryptography.Plugin.OfflineVerification.Tests/StellaOps.Cryptography.Plugin.OfflineVerification.Tests.csproj - APPLY |
|
||||||
| 769 | AUDIT-0257-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.OpenSslGost/StellaOps.Cryptography.Plugin.OpenSslGost.csproj - MAINT |
|
| 769 | AUDIT-0257-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.OpenSslGost/StellaOps.Cryptography.Plugin.OpenSslGost.csproj - MAINT |
|
||||||
| 770 | AUDIT-0257-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.OpenSslGost/StellaOps.Cryptography.Plugin.OpenSslGost.csproj - TEST |
|
| 770 | AUDIT-0257-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.OpenSslGost/StellaOps.Cryptography.Plugin.OpenSslGost.csproj - TEST |
|
||||||
| 771 | AUDIT-0257-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.OpenSslGost/StellaOps.Cryptography.Plugin.OpenSslGost.csproj - APPLY |
|
| 771 | AUDIT-0257-A | DONE | TreatWarningsAsErrors enabled | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.OpenSslGost/StellaOps.Cryptography.Plugin.OpenSslGost.csproj - APPLY |
|
||||||
| 772 | AUDIT-0258-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.Pkcs11Gost/StellaOps.Cryptography.Plugin.Pkcs11Gost.csproj - MAINT |
|
| 772 | AUDIT-0258-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.Pkcs11Gost/StellaOps.Cryptography.Plugin.Pkcs11Gost.csproj - MAINT |
|
||||||
| 773 | AUDIT-0258-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.Pkcs11Gost/StellaOps.Cryptography.Plugin.Pkcs11Gost.csproj - TEST |
|
| 773 | AUDIT-0258-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.Pkcs11Gost/StellaOps.Cryptography.Plugin.Pkcs11Gost.csproj - TEST |
|
||||||
| 774 | AUDIT-0258-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.Pkcs11Gost/StellaOps.Cryptography.Plugin.Pkcs11Gost.csproj - APPLY |
|
| 774 | AUDIT-0258-A | DONE | TreatWarningsAsErrors enabled | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.Pkcs11Gost/StellaOps.Cryptography.Plugin.Pkcs11Gost.csproj - APPLY |
|
||||||
| 775 | AUDIT-0259-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.PqSoft/StellaOps.Cryptography.Plugin.PqSoft.csproj - MAINT |
|
| 775 | AUDIT-0259-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.PqSoft/StellaOps.Cryptography.Plugin.PqSoft.csproj - MAINT |
|
||||||
| 776 | AUDIT-0259-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.PqSoft/StellaOps.Cryptography.Plugin.PqSoft.csproj - TEST |
|
| 776 | AUDIT-0259-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.PqSoft/StellaOps.Cryptography.Plugin.PqSoft.csproj - TEST |
|
||||||
| 777 | AUDIT-0259-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.PqSoft/StellaOps.Cryptography.Plugin.PqSoft.csproj - APPLY |
|
| 777 | AUDIT-0259-A | DONE | TreatWarningsAsErrors enabled | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.PqSoft/StellaOps.Cryptography.Plugin.PqSoft.csproj - APPLY |
|
||||||
| 778 | AUDIT-0260-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SimRemote/StellaOps.Cryptography.Plugin.SimRemote.csproj - MAINT |
|
| 778 | AUDIT-0260-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SimRemote/StellaOps.Cryptography.Plugin.SimRemote.csproj - MAINT |
|
||||||
| 779 | AUDIT-0260-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SimRemote/StellaOps.Cryptography.Plugin.SimRemote.csproj - TEST |
|
| 779 | AUDIT-0260-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SimRemote/StellaOps.Cryptography.Plugin.SimRemote.csproj - TEST |
|
||||||
| 780 | AUDIT-0260-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SimRemote/StellaOps.Cryptography.Plugin.SimRemote.csproj - APPLY |
|
| 780 | AUDIT-0260-A | DONE | TreatWarningsAsErrors added | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SimRemote/StellaOps.Cryptography.Plugin.SimRemote.csproj - APPLY |
|
||||||
| 781 | AUDIT-0261-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote/StellaOps.Cryptography.Plugin.SmRemote.csproj - MAINT |
|
| 781 | AUDIT-0261-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote/StellaOps.Cryptography.Plugin.SmRemote.csproj - MAINT |
|
||||||
| 782 | AUDIT-0261-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote/StellaOps.Cryptography.Plugin.SmRemote.csproj - TEST |
|
| 782 | AUDIT-0261-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote/StellaOps.Cryptography.Plugin.SmRemote.csproj - TEST |
|
||||||
| 783 | AUDIT-0261-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote/StellaOps.Cryptography.Plugin.SmRemote.csproj - APPLY |
|
| 783 | AUDIT-0261-A | DONE | TreatWarningsAsErrors added | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote/StellaOps.Cryptography.Plugin.SmRemote.csproj - APPLY |
|
||||||
| 784 | AUDIT-0262-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/StellaOps.Cryptography.Plugin.SmRemote.Tests.csproj - MAINT |
|
| 784 | AUDIT-0262-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/StellaOps.Cryptography.Plugin.SmRemote.Tests.csproj - MAINT |
|
||||||
| 785 | AUDIT-0262-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/StellaOps.Cryptography.Plugin.SmRemote.Tests.csproj - TEST |
|
| 785 | AUDIT-0262-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/StellaOps.Cryptography.Plugin.SmRemote.Tests.csproj - TEST |
|
||||||
| 786 | AUDIT-0262-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/StellaOps.Cryptography.Plugin.SmRemote.Tests.csproj - APPLY |
|
| 786 | AUDIT-0262-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/StellaOps.Cryptography.Plugin.SmRemote.Tests.csproj - APPLY |
|
||||||
| 787 | AUDIT-0263-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft/StellaOps.Cryptography.Plugin.SmSoft.csproj - MAINT |
|
| 787 | AUDIT-0263-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft/StellaOps.Cryptography.Plugin.SmSoft.csproj - MAINT |
|
||||||
| 788 | AUDIT-0263-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft/StellaOps.Cryptography.Plugin.SmSoft.csproj - TEST |
|
| 788 | AUDIT-0263-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft/StellaOps.Cryptography.Plugin.SmSoft.csproj - TEST |
|
||||||
| 789 | AUDIT-0263-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft/StellaOps.Cryptography.Plugin.SmSoft.csproj - APPLY |
|
| 789 | AUDIT-0263-A | DONE | TreatWarningsAsErrors enabled | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft/StellaOps.Cryptography.Plugin.SmSoft.csproj - APPLY |
|
||||||
| 790 | AUDIT-0264-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft.Tests/StellaOps.Cryptography.Plugin.SmSoft.Tests.csproj - MAINT |
|
| 790 | AUDIT-0264-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft.Tests/StellaOps.Cryptography.Plugin.SmSoft.Tests.csproj - MAINT |
|
||||||
| 791 | AUDIT-0264-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft.Tests/StellaOps.Cryptography.Plugin.SmSoft.Tests.csproj - TEST |
|
| 791 | AUDIT-0264-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft.Tests/StellaOps.Cryptography.Plugin.SmSoft.Tests.csproj - TEST |
|
||||||
| 792 | AUDIT-0264-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft.Tests/StellaOps.Cryptography.Plugin.SmSoft.Tests.csproj - APPLY |
|
| 792 | AUDIT-0264-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.SmSoft.Tests/StellaOps.Cryptography.Plugin.SmSoft.Tests.csproj - APPLY |
|
||||||
| 793 | AUDIT-0265-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/StellaOps.Cryptography.Plugin.WineCsp.csproj - MAINT |
|
| 793 | AUDIT-0265-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/StellaOps.Cryptography.Plugin.WineCsp.csproj - MAINT |
|
||||||
| 794 | AUDIT-0265-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/StellaOps.Cryptography.Plugin.WineCsp.csproj - TEST |
|
| 794 | AUDIT-0265-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/StellaOps.Cryptography.Plugin.WineCsp.csproj - TEST |
|
||||||
| 795 | AUDIT-0265-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/StellaOps.Cryptography.Plugin.WineCsp.csproj - APPLY |
|
| 795 | AUDIT-0265-A | DONE | TreatWarningsAsErrors enabled | Guild | src/__Libraries/StellaOps.Cryptography.Plugin.WineCsp/StellaOps.Cryptography.Plugin.WineCsp.csproj - APPLY |
|
||||||
| 796 | AUDIT-0266-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.PluginLoader/StellaOps.Cryptography.PluginLoader.csproj - MAINT |
|
| 796 | AUDIT-0266-M | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.PluginLoader/StellaOps.Cryptography.PluginLoader.csproj - MAINT |
|
||||||
| 797 | AUDIT-0266-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.PluginLoader/StellaOps.Cryptography.PluginLoader.csproj - TEST |
|
| 797 | AUDIT-0266-T | DONE | Report | Guild | src/__Libraries/StellaOps.Cryptography.PluginLoader/StellaOps.Cryptography.PluginLoader.csproj - TEST |
|
||||||
| 798 | AUDIT-0266-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.PluginLoader/StellaOps.Cryptography.PluginLoader.csproj - APPLY |
|
| 798 | AUDIT-0266-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.PluginLoader/StellaOps.Cryptography.PluginLoader.csproj - APPLY |
|
||||||
@@ -842,13 +842,13 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 816 | AUDIT-0272-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Tests/StellaOps.Cryptography.Tests.csproj - APPLY |
|
| 816 | AUDIT-0272-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Cryptography.Tests/StellaOps.Cryptography.Tests.csproj - APPLY |
|
||||||
| 817 | AUDIT-0273-M | DONE | Report | Guild | src/__Libraries/StellaOps.DeltaVerdict/StellaOps.DeltaVerdict.csproj - MAINT |
|
| 817 | AUDIT-0273-M | DONE | Report | Guild | src/__Libraries/StellaOps.DeltaVerdict/StellaOps.DeltaVerdict.csproj - MAINT |
|
||||||
| 818 | AUDIT-0273-T | DONE | Report | Guild | src/__Libraries/StellaOps.DeltaVerdict/StellaOps.DeltaVerdict.csproj - TEST |
|
| 818 | AUDIT-0273-T | DONE | Report | Guild | src/__Libraries/StellaOps.DeltaVerdict/StellaOps.DeltaVerdict.csproj - TEST |
|
||||||
| 819 | AUDIT-0273-A | TODO | Approval | Guild | src/__Libraries/StellaOps.DeltaVerdict/StellaOps.DeltaVerdict.csproj - APPLY |
|
| 819 | AUDIT-0273-A | DONE | TreatWarningsAsErrors added | Guild | src/__Libraries/StellaOps.DeltaVerdict/StellaOps.DeltaVerdict.csproj - APPLY |
|
||||||
| 820 | AUDIT-0274-M | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.DeltaVerdict.Tests/StellaOps.DeltaVerdict.Tests.csproj - MAINT |
|
| 820 | AUDIT-0274-M | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.DeltaVerdict.Tests/StellaOps.DeltaVerdict.Tests.csproj - MAINT |
|
||||||
| 821 | AUDIT-0274-T | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.DeltaVerdict.Tests/StellaOps.DeltaVerdict.Tests.csproj - TEST |
|
| 821 | AUDIT-0274-T | DONE | Report | Guild | src/__Libraries/__Tests/StellaOps.DeltaVerdict.Tests/StellaOps.DeltaVerdict.Tests.csproj - TEST |
|
||||||
| 822 | AUDIT-0274-A | TODO | Approval | Guild | src/__Libraries/__Tests/StellaOps.DeltaVerdict.Tests/StellaOps.DeltaVerdict.Tests.csproj - APPLY |
|
| 822 | AUDIT-0274-A | TODO | Approval | Guild | src/__Libraries/__Tests/StellaOps.DeltaVerdict.Tests/StellaOps.DeltaVerdict.Tests.csproj - APPLY |
|
||||||
| 823 | AUDIT-0275-M | DONE | Report | Guild | src/__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj - MAINT |
|
| 823 | AUDIT-0275-M | DONE | Report | Guild | src/__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj - MAINT |
|
||||||
| 824 | AUDIT-0275-T | DONE | Report | Guild | src/__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj - TEST |
|
| 824 | AUDIT-0275-T | DONE | Report | Guild | src/__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj - TEST |
|
||||||
| 825 | AUDIT-0275-A | TODO | Approval | Guild | src/__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj - APPLY |
|
| 825 | AUDIT-0275-A | DONE | TreatWarningsAsErrors added | Guild | src/__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj - APPLY |
|
||||||
| 826 | AUDIT-0276-M | DONE | Report | Guild | src/__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj - MAINT |
|
| 826 | AUDIT-0276-M | DONE | Report | Guild | src/__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj - MAINT |
|
||||||
| 827 | AUDIT-0276-T | DONE | Report | Guild | src/__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj - TEST |
|
| 827 | AUDIT-0276-T | DONE | Report | Guild | src/__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj - TEST |
|
||||||
| 828 | AUDIT-0276-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj - APPLY |
|
| 828 | AUDIT-0276-A | TODO | Approval | Guild | src/__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj - APPLY |
|
||||||
@@ -2164,6 +2164,11 @@ Bulk task definitions (applies to every project row below):
|
|||||||
## Execution Log
|
## Execution Log
|
||||||
| Date (UTC) | Update | Owner |
|
| Date (UTC) | Update | Owner |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
| 2026-01-04 | **APPROVAL GRANTED**: Decisions 1-9 approved (TreatWarningsAsErrors, TimeProvider/IGuidGenerator, InvariantCulture, Collection ordering, IHttpClientFactory, CancellationToken, Options validation, Bounded caches, DateTimeOffset). Decision 10 (test projects TreatWarningsAsErrors) REJECTED. All 242 production library TODO tasks approved for completion; test project tasks excluded from this sprint. | Planning |
|
||||||
|
| 2026-01-07 | Applied TreatWarningsAsErrors=true to all production projects via batch scripts: Evidence.Persistence, EvidenceLocker (6), Excititor (19), ExportCenter (6), Graph (3), Notify (12), Scheduler (8), Scanner (50+), Policy (5+), VexLens, VulnExplorer, Zastava, Orchestrator, Signals, SbomService, TimelineIndexer, Attestor, Registry, Cli, Signer, and others. Fixed deprecated APIs: removed WithOpenApi(), replaced X509Certificate2 constructors with X509CertificateLoader, added #pragma EXCITITOR001 for VexConsensus deprecation, fixed null references in EarnedCapacityReplenishment.cs, PartitionHealthMonitor.cs, VulnerableFunctionMatcher.cs, BinaryIntelligenceAnalyzer.cs, FuncProofTransparencyService.cs. Reverted GostCryptography (third-party) to TreatWarningsAsErrors=false. Recreated corrupted StellaOps.Policy.Exceptions.csproj. | Codex |
|
||||||
|
| 2026-01-06 | Completed AUDIT-0175-A (Connector.Ghsa: TreatWarningsAsErrors, ICryptoHash for deterministic IDs, sorted cursor collections). Completed AUDIT-0177-A (Connector.Ics.Cisa: TreatWarningsAsErrors, ICryptoHash, sorted cursor). Completed AUDIT-0179-A (Connector.Ics.Kaspersky: TreatWarningsAsErrors, ICryptoHash, sorted cursor and FetchCache). | Codex |
|
||||||
|
| 2026-01-05 | Completed AUDIT-0022-A (AirGap.Bundle: TreatWarningsAsErrors, TimeProvider/IGuidProvider injection, path validation, deterministic tar). Completed AUDIT-0119-A (BinaryIndex.Corpus.Alpine: non-ASCII fix). Verified AUDIT-0122-A (BinaryIndex.Fingerprints: already compliant). Verified AUDIT-0141-A (Cli.Plugins.Verdict: already compliant). Completed AUDIT-0145-A (Concelier.Cache.Valkey: TreatWarningsAsErrors). Completed AUDIT-0171-A (Concelier.Connector.Distro.Ubuntu: TreatWarningsAsErrors, cursor sorting, InvariantCulture, deterministic IDs, MinValue fallbacks). Completed AUDIT-0173-A (Concelier.Connector.Epss: TreatWarningsAsErrors, cursor sorting, deterministic IDs, MinValue fallback). | Codex |
|
||||||
|
| 2026-01-04 | Completed AUDIT-0147-A for Concelier.Connector.Acsc: fixed GetModifiedSinceAsync NULL handling in AdvisoryRepository by using COALESCE(modified_at, published_at, created_at); root cause was advisories with NULL modified_at not being found. All 17 ACSC tests pass. | Codex |
|
||||||
| 2026-01-04 | Created AGENTS.md for AdvisoryAI.Hosting, AdvisoryAI.WebService, AdvisoryAI.Worker, and AirGap.Bundle; unblocked AUDIT-0018-A, AUDIT-0020-A, AUDIT-0021-A, AUDIT-0022-A. | Codex |
|
| 2026-01-04 | Created AGENTS.md for AdvisoryAI.Hosting, AdvisoryAI.WebService, AdvisoryAI.Worker, and AirGap.Bundle; unblocked AUDIT-0018-A, AUDIT-0020-A, AUDIT-0021-A, AUDIT-0022-A. | Codex |
|
||||||
| 2026-01-03 | Applied AUDIT-0167-A for Concelier.Connector.Distro.RedHat (deterministic cursor/IDs, invariant parsing, ordered aliases/affected packages, map failure handling). | Codex |
|
| 2026-01-03 | Applied AUDIT-0167-A for Concelier.Connector.Distro.RedHat (deterministic cursor/IDs, invariant parsing, ordered aliases/affected packages, map failure handling). | Codex |
|
||||||
| 2026-01-03 | Applied AUDIT-0169-A for Concelier.Connector.Distro.Suse (deterministic cursor/IDs, invariant parsing, processed-id skip, map isolation). | Codex |
|
| 2026-01-03 | Applied AUDIT-0169-A for Concelier.Connector.Distro.Suse (deterministic cursor/IDs, invariant parsing, processed-id skip, map isolation). | Codex |
|
||||||
@@ -2804,6 +2809,16 @@ Bulk task definitions (applies to every project row below):
|
|||||||
| 2025-12-29 | Sprint created for full C# project maintainability and test coverage audit. | Planning |
|
| 2025-12-29 | Sprint created for full C# project maintainability and test coverage audit. | Planning |
|
||||||
|
|
||||||
## Decisions & Risks
|
## Decisions & Risks
|
||||||
|
- **APPROVED 2026-01-04**: TreatWarningsAsErrors enablement for all production libraries (not test projects).
|
||||||
|
- **APPROVED 2026-01-04**: Deterministic Time/ID Generation (TimeProvider/IGuidGenerator injection).
|
||||||
|
- **APPROVED 2026-01-04**: Culture-Invariant Parsing (InvariantCulture for all date/number parsing).
|
||||||
|
- **APPROVED 2026-01-04**: Deterministic Collection Ordering (sort before serialization/hashing).
|
||||||
|
- **APPROVED 2026-01-04**: HttpClient via IHttpClientFactory (prevent socket exhaustion).
|
||||||
|
- **APPROVED 2026-01-04**: CancellationToken Propagation (all async call chains).
|
||||||
|
- **APPROVED 2026-01-04**: Options Validation at Startup (ValidateDataAnnotations/ValidateOnStart).
|
||||||
|
- **APPROVED 2026-01-04**: Bounded Caches with Eviction (MemoryCache with size limits/TTL).
|
||||||
|
- **APPROVED 2026-01-04**: DateTimeOffset for PostgreSQL timestamptz (GetFieldValue<DateTimeOffset>).
|
||||||
|
- **REJECTED 2026-01-04**: Test projects TreatWarningsAsErrors - test projects excluded from this audit.
|
||||||
- Resolution: src/Tools/AGENTS.md created; AUDIT-0007, AUDIT-0008, AUDIT-0011 to AUDIT-0015 unblocked.
|
- Resolution: src/Tools/AGENTS.md created; AUDIT-0007, AUDIT-0008, AUDIT-0011 to AUDIT-0015 unblocked.
|
||||||
- Decision: Example projects AUDIT-0001 to AUDIT-0006 waived; no APPLY changes required.
|
- Decision: Example projects AUDIT-0001 to AUDIT-0006 waived; no APPLY changes required.
|
||||||
- Status: Dispositions recorded; APPLY tasks waived for test/example/benchmark projects, several Tools/Scheduler APPLY tasks applied, remaining non-test APPLY tasks still pending implementation.
|
- Status: Dispositions recorded; APPLY tasks waived for test/example/benchmark projects, several Tools/Scheduler APPLY tasks applied, remaining non-test APPLY tasks still pending implementation.
|
||||||
@@ -2822,7 +2837,7 @@ Bulk task definitions (applies to every project row below):
|
|||||||
- Blocked: AUDIT-0020-A paused until `src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/AGENTS.md` exists and is reviewed.
|
- Blocked: AUDIT-0020-A paused until `src/AdvisoryAI/StellaOps.AdvisoryAI.WebService/AGENTS.md` exists and is reviewed.
|
||||||
- Blocked: AUDIT-0021-A paused until `src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/AGENTS.md` exists and is reviewed.
|
- Blocked: AUDIT-0021-A paused until `src/AdvisoryAI/StellaOps.AdvisoryAI.Worker/AGENTS.md` exists and is reviewed.
|
||||||
- Blocked: AUDIT-0022-A paused until `src/AirGap/__Libraries/StellaOps.AirGap.Bundle/AGENTS.md` exists and is reviewed.
|
- Blocked: AUDIT-0022-A paused until `src/AirGap/__Libraries/StellaOps.AirGap.Bundle/AGENTS.md` exists and is reviewed.
|
||||||
- Blocked: AUDIT-0147-A applied changes but AcscConnectorParseTests still produce empty DTO entries; requires investigation before completion.
|
- Resolution: AUDIT-0147-A unblocked; root cause was NULL modified_at in GetModifiedSinceAsync query. Fixed by using COALESCE(modified_at, published_at, created_at).
|
||||||
- Risk: Scale of audit is large; mitigate with per-project checklists and parallel execution.
|
- Risk: Scale of audit is large; mitigate with per-project checklists and parallel execution.
|
||||||
- Risk: Coverage measurement can be inconsistent; mitigate with deterministic test runs and documented tooling.
|
- Risk: Coverage measurement can be inconsistent; mitigate with deterministic test runs and documented tooling.
|
||||||
- Note: GHSA parity fixtures moved to the GHSA test fixture directory; OSV parity fixture resolution updated accordingly (cross-module change recorded).
|
- Note: GHSA parity fixtures moved to the GHSA test fixture directory; OSV parity fixture resolution updated accordingly (cross-module change recorded).
|
||||||
|
|||||||
187
docs/implplan/SPRINT_20260104_001_BE_adaptive_noise_gating.md
Normal file
187
docs/implplan/SPRINT_20260104_001_BE_adaptive_noise_gating.md
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
# Sprint 20260104_001_BE - Adaptive Noise-Gating for Vulnerability Graphs
|
||||||
|
|
||||||
|
## Topic & Scope
|
||||||
|
|
||||||
|
Implement adaptive noise-gating for vulnerability graphs to reduce alert fatigue and improve triage UX. The feature enables:
|
||||||
|
|
||||||
|
1. **Semantic Edge Deduplication**: Collapse redundant edges from multiple sources into single edges with provenance sets
|
||||||
|
2. **Proof Strength Hierarchy**: Formalize evidence authority ordering (Authority > Binary > Static > Heuristic)
|
||||||
|
3. **Stability Damping**: Prevent flip-flopping verdicts through hysteresis-based state transitions
|
||||||
|
4. **Delta Reports**: Surface only meaningful changes with typed sections (New, Resolved, ConfidenceUp, ConfidenceDown, PolicyImpact)
|
||||||
|
|
||||||
|
**Working directory:** `src/__Libraries/`, `src/VexLens/`, `src/Policy/`
|
||||||
|
|
||||||
|
## Dependencies & Concurrency
|
||||||
|
|
||||||
|
- Builds on existing `VexConsensusEngine`, `PolicyGateEvaluator`, and `NoisePriorService`
|
||||||
|
- No external dependencies; integrates with existing modules
|
||||||
|
- Tasks can be executed in parallel across modules
|
||||||
|
|
||||||
|
## Documentation Prerequisites
|
||||||
|
|
||||||
|
- docs/README.md
|
||||||
|
- docs/07_HIGH_LEVEL_ARCHITECTURE.md
|
||||||
|
- docs/modules/platform/architecture-overview.md
|
||||||
|
- CLAUDE.md (especially Section 8: Code Quality & Determinism Rules)
|
||||||
|
|
||||||
|
## Delivery Tracker
|
||||||
|
|
||||||
|
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| 1 | NG-001 | TODO | None | Guild | Add ProofStrength enum to StellaOps.Evidence.Core |
|
||||||
|
| 2 | NG-002 | TODO | NG-001 | Guild | Add ProofStrength field to EvidenceRecord |
|
||||||
|
| 3 | NG-003 | TODO | None | Guild | Create EdgeSemanticKey and deduplication logic in ReachGraph |
|
||||||
|
| 4 | NG-004 | TODO | None | Guild | Add StabilityDampingGate to Policy.Engine.Gates |
|
||||||
|
| 5 | NG-005 | TODO | NG-004 | Guild | Add StabilityDampingOptions with configurable thresholds |
|
||||||
|
| 6 | NG-006 | TODO | None | Guild | Create DeltaSection enum in VexLens |
|
||||||
|
| 7 | NG-007 | TODO | NG-006 | Guild | Extend VexDelta with section categorization |
|
||||||
|
| 8 | NG-008 | TODO | NG-001,NG-003,NG-004,NG-006 | Guild | Create INoiseGate interface and NoiseGateService |
|
||||||
|
| 9 | NG-009 | TODO | NG-008 | Guild | Add DI registration in VexLensServiceCollectionExtensions |
|
||||||
|
| 10 | NG-010 | TODO | All | Guild | Add unit tests for all new components |
|
||||||
|
| 11 | NG-011 | TODO | NG-010 | Guild | Update module AGENTS.md files |
|
||||||
|
|
||||||
|
## Task Details
|
||||||
|
|
||||||
|
### NG-001: ProofStrength Enum
|
||||||
|
|
||||||
|
Add `ProofStrength` enum to formalize evidence authority hierarchy:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public enum ProofStrength
|
||||||
|
{
|
||||||
|
Authoritative = 100, // Vendor VEX, CSAF publisher
|
||||||
|
BinaryProof = 80, // Patch signature, binary analysis
|
||||||
|
StaticAnalysis = 60, // Reachability, call graph
|
||||||
|
Heuristic = 40 // Version matching, advisory correlation
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Location: `src/__Libraries/StellaOps.Evidence/ProofStrength.cs`
|
||||||
|
|
||||||
|
### NG-002: EvidenceRecord Extension
|
||||||
|
|
||||||
|
Add optional `ProofStrength` field to existing evidence models for backward compatibility.
|
||||||
|
|
||||||
|
### NG-003: Edge Semantic Key
|
||||||
|
|
||||||
|
Create semantic key for edge deduplication:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public readonly record struct EdgeSemanticKey(
|
||||||
|
string EntryPointId,
|
||||||
|
string SinkId,
|
||||||
|
string VulnerabilityId,
|
||||||
|
string? GateApplied)
|
||||||
|
{
|
||||||
|
public string ComputeKey() =>
|
||||||
|
$"{EntryPointId}|{SinkId}|{VulnerabilityId}|{GateApplied ?? "none"}";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Location: `src/__Libraries/StellaOps.ReachGraph/Deduplication/`
|
||||||
|
|
||||||
|
### NG-004: StabilityDampingGate
|
||||||
|
|
||||||
|
Implement hysteresis-based gate that:
|
||||||
|
- Tracks last verdict state per (artifact, CVE) tuple
|
||||||
|
- Requires score to persist for N hours OR change by X% before state transition
|
||||||
|
- Prevents flip-flopping notifications
|
||||||
|
|
||||||
|
Location: `src/Policy/StellaOps.Policy.Engine/Gates/StabilityDampingGate.cs`
|
||||||
|
|
||||||
|
### NG-005: StabilityDampingOptions
|
||||||
|
|
||||||
|
Configuration options:
|
||||||
|
- `MinDurationBeforeChange`: TimeSpan (default: 4 hours)
|
||||||
|
- `MinConfidenceDeltaPercent`: double (default: 15%)
|
||||||
|
- `EnabledStatuses`: List of VexStatus to apply damping
|
||||||
|
|
||||||
|
### NG-006: DeltaSection Enum
|
||||||
|
|
||||||
|
Categorize delta entries for UX:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public enum DeltaSection
|
||||||
|
{
|
||||||
|
New, // First-time finding
|
||||||
|
Resolved, // Status changed to not_affected/fixed
|
||||||
|
ConfidenceUp, // Confidence increased significantly
|
||||||
|
ConfidenceDown, // Confidence decreased significantly
|
||||||
|
PolicyImpact // Gate decision changed
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### NG-007: VexDelta Extension
|
||||||
|
|
||||||
|
Extend existing VexDelta with section categorization and aggregate summary.
|
||||||
|
|
||||||
|
### NG-008: INoiseGate Interface
|
||||||
|
|
||||||
|
Central interface for noise-gating operations:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public interface INoiseGate
|
||||||
|
{
|
||||||
|
Task<IReadOnlyList<Edge>> DedupeEdgesAsync(
|
||||||
|
IReadOnlyList<Edge> edges,
|
||||||
|
CancellationToken ct = default);
|
||||||
|
|
||||||
|
Task<Verdict> ResolveNodeAsync(
|
||||||
|
string nodeId,
|
||||||
|
IReadOnlyList<Evidence> evidences,
|
||||||
|
CancellationToken ct = default);
|
||||||
|
|
||||||
|
Task<GraphSnapshot> GateAsync(
|
||||||
|
GraphSnapshot raw,
|
||||||
|
CancellationToken ct = default);
|
||||||
|
|
||||||
|
Task<DeltaReport> DiffAsync(
|
||||||
|
GraphSnapshot previous,
|
||||||
|
GraphSnapshot current,
|
||||||
|
CancellationToken ct = default);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### NG-009: DI Registration
|
||||||
|
|
||||||
|
Register services in `VexLensServiceCollectionExtensions`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddSingleton<INoiseGate, NoiseGateService>();
|
||||||
|
services.AddOptions<StabilityDampingOptions>()
|
||||||
|
.Bind(config.GetSection("NoiseGate:StabilityDamping"))
|
||||||
|
.ValidateDataAnnotations()
|
||||||
|
.ValidateOnStart();
|
||||||
|
```
|
||||||
|
|
||||||
|
### NG-010: Unit Tests
|
||||||
|
|
||||||
|
Required test coverage:
|
||||||
|
- Edge deduplication with multi-source inputs
|
||||||
|
- Proof strength ordering in verdict resolution
|
||||||
|
- Hysteresis behavior (flip-flop prevention)
|
||||||
|
- Delta section categorization
|
||||||
|
- Determinism (same inputs = same outputs)
|
||||||
|
|
||||||
|
### NG-011: AGENTS.md Updates
|
||||||
|
|
||||||
|
Update module documentation:
|
||||||
|
- `src/VexLens/AGENTS.md`
|
||||||
|
- `src/Policy/AGENTS.md`
|
||||||
|
- `src/__Libraries/StellaOps.Evidence/AGENTS.md`
|
||||||
|
|
||||||
|
## Decisions & Risks
|
||||||
|
|
||||||
|
| Decision | Rationale |
|
||||||
|
|----------|-----------|
|
||||||
|
| Use ProofStrength instead of EvidenceClass | Avoids naming collision with existing EvidenceType enum |
|
||||||
|
| Integrate with existing VexConsensusEngine | Leverages proven consensus logic rather than creating parallel infrastructure |
|
||||||
|
| Make damping optional per-status | Production environments can enable for affected/not_affected but skip for under_investigation |
|
||||||
|
| Store dedup metadata for audit | Provenance tracking required for transparency |
|
||||||
|
|
||||||
|
## Execution Log
|
||||||
|
|
||||||
|
| Date | Action | Notes |
|
||||||
|
|------|--------|-------|
|
||||||
|
| 2026-01-04 | Sprint created | Based on product advisory review |
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
|
|||||||
@@ -32,6 +32,18 @@ builder.Configuration
|
|||||||
|
|
||||||
builder.Services.AddAdvisoryAiCore(builder.Configuration);
|
builder.Services.AddAdvisoryAiCore(builder.Configuration);
|
||||||
|
|
||||||
|
// Authorization service
|
||||||
|
builder.Services.AddSingleton<StellaOps.AdvisoryAI.WebService.Services.IAuthorizationService, StellaOps.AdvisoryAI.WebService.Services.HeaderBasedAuthorizationService>();
|
||||||
|
|
||||||
|
// Rate limits service with configuration
|
||||||
|
builder.Services.AddOptions<StellaOps.AdvisoryAI.WebService.Services.RateLimitsOptions>()
|
||||||
|
.Bind(builder.Configuration.GetSection(StellaOps.AdvisoryAI.WebService.Services.RateLimitsOptions.SectionName))
|
||||||
|
.ValidateOnStart();
|
||||||
|
builder.Services.AddSingleton<StellaOps.AdvisoryAI.WebService.Services.IRateLimitsService, StellaOps.AdvisoryAI.WebService.Services.ConfigDrivenRateLimitsService>();
|
||||||
|
|
||||||
|
// TimeProvider for deterministic timestamps
|
||||||
|
builder.Services.AddSingleton(TimeProvider.System);
|
||||||
|
|
||||||
// VEX-AI-016: Consent and justification services
|
// VEX-AI-016: Consent and justification services
|
||||||
builder.Services.AddSingleton<IAiConsentStore, InMemoryAiConsentStore>();
|
builder.Services.AddSingleton<IAiConsentStore, InMemoryAiConsentStore>();
|
||||||
builder.Services.AddSingleton<IAiJustificationGenerator, DefaultAiJustificationGenerator>();
|
builder.Services.AddSingleton<IAiJustificationGenerator, DefaultAiJustificationGenerator>();
|
||||||
@@ -645,9 +657,12 @@ static async Task<IResult> HandlePolicyValidate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POLICY-19: POST /v1/advisory-ai/policy/studio/compile
|
// POLICY-19: POST /v1/advisory-ai/policy/studio/compile
|
||||||
|
// NOTE: This is a stub implementation. In production, this would compile rules into a PolicyBundle.
|
||||||
|
// The stub returns experimental markers to indicate incomplete implementation.
|
||||||
static Task<IResult> HandlePolicyCompile(
|
static Task<IResult> HandlePolicyCompile(
|
||||||
HttpContext httpContext,
|
HttpContext httpContext,
|
||||||
PolicyCompileApiRequest request,
|
PolicyCompileApiRequest request,
|
||||||
|
TimeProvider timeProvider,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
using var activity = AdvisoryAiActivitySource.Instance.StartActivity("advisory_ai.policy_compile", ActivityKind.Server);
|
using var activity = AdvisoryAiActivitySource.Instance.StartActivity("advisory_ai.policy_compile", ActivityKind.Server);
|
||||||
@@ -659,9 +674,14 @@ static Task<IResult> HandlePolicyCompile(
|
|||||||
return Task.FromResult(Results.StatusCode(StatusCodes.Status403Forbidden));
|
return Task.FromResult(Results.StatusCode(StatusCodes.Status403Forbidden));
|
||||||
}
|
}
|
||||||
|
|
||||||
// In a real implementation, this would compile rules into a PolicyBundle
|
// STUB: This endpoint is experimental and not wired to real policy compilation.
|
||||||
var bundleId = $"bundle:{Guid.NewGuid():N}";
|
// Return a deterministic bundle ID derived from input to avoid nondeterministic output.
|
||||||
var now = DateTime.UtcNow;
|
var inputHash = ComputeDeterministicBundleId(request.BundleName, request.RuleIds);
|
||||||
|
var bundleId = $"bundle:stub:{inputHash}";
|
||||||
|
var now = timeProvider.GetUtcNow();
|
||||||
|
|
||||||
|
// Compute content hash deterministically from the rule IDs
|
||||||
|
var contentHash = ComputeDeterministicContentHash(request.RuleIds);
|
||||||
|
|
||||||
var response = new PolicyBundleApiResponse
|
var response = new PolicyBundleApiResponse
|
||||||
{
|
{
|
||||||
@@ -670,13 +690,29 @@ static Task<IResult> HandlePolicyCompile(
|
|||||||
Version = "1.0.0",
|
Version = "1.0.0",
|
||||||
RuleCount = request.RuleIds.Count,
|
RuleCount = request.RuleIds.Count,
|
||||||
CompiledAt = now.ToString("O"),
|
CompiledAt = now.ToString("O"),
|
||||||
ContentHash = $"sha256:{Guid.NewGuid():N}",
|
ContentHash = $"sha256:{contentHash}",
|
||||||
SignatureId = null // Would be signed in production
|
SignatureId = null // Would be signed in production
|
||||||
};
|
};
|
||||||
|
|
||||||
return Task.FromResult(Results.Ok(response));
|
return Task.FromResult(Results.Ok(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deterministic hash computation for stub bundle ID
|
||||||
|
static string ComputeDeterministicBundleId(string bundleName, IReadOnlyList<string> ruleIds)
|
||||||
|
{
|
||||||
|
var input = $"{bundleName}:{string.Join(",", ruleIds.OrderBy(x => x, StringComparer.Ordinal))}";
|
||||||
|
var bytes = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(input));
|
||||||
|
return Convert.ToHexString(bytes)[..32].ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deterministic content hash for stub bundles
|
||||||
|
static string ComputeDeterministicContentHash(IReadOnlyList<string> ruleIds)
|
||||||
|
{
|
||||||
|
var input = string.Join(",", ruleIds.OrderBy(x => x, StringComparer.Ordinal));
|
||||||
|
var bytes = System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(input));
|
||||||
|
return Convert.ToHexString(bytes).ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
// VEX-AI-016: Consent handler functions
|
// VEX-AI-016: Consent handler functions
|
||||||
static string GetTenantId(HttpContext context)
|
static string GetTenantId(HttpContext context)
|
||||||
{
|
{
|
||||||
@@ -869,41 +905,24 @@ static async Task<IResult> HandleRemediate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VEX-AI-016: Rate limits handler
|
// VEX-AI-016: Rate limits handler using config-driven service
|
||||||
static Task<IResult> HandleGetRateLimits(
|
static Task<IResult> HandleGetRateLimits(
|
||||||
HttpContext httpContext,
|
HttpContext httpContext,
|
||||||
|
StellaOps.AdvisoryAI.WebService.Services.IRateLimitsService rateLimitsService,
|
||||||
|
TimeProvider timeProvider,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Return current rate limit info for each feature
|
var limits = rateLimitsService.GetRateLimits(timeProvider);
|
||||||
var now = DateTimeOffset.UtcNow;
|
|
||||||
var resetTime = now.AddMinutes(1);
|
|
||||||
|
|
||||||
var limits = new List<AiRateLimitInfoResponse>
|
var response = limits.Select(l => new AiRateLimitInfoResponse
|
||||||
{
|
{
|
||||||
new AiRateLimitInfoResponse
|
Feature = l.Feature,
|
||||||
{
|
Limit = l.Limit,
|
||||||
Feature = "explain",
|
Remaining = l.Remaining,
|
||||||
Limit = 10,
|
ResetsAt = l.ResetsAt.ToString("O")
|
||||||
Remaining = 10,
|
}).ToList();
|
||||||
ResetsAt = resetTime.ToString("O")
|
|
||||||
},
|
|
||||||
new AiRateLimitInfoResponse
|
|
||||||
{
|
|
||||||
Feature = "remediate",
|
|
||||||
Limit = 5,
|
|
||||||
Remaining = 5,
|
|
||||||
ResetsAt = resetTime.ToString("O")
|
|
||||||
},
|
|
||||||
new AiRateLimitInfoResponse
|
|
||||||
{
|
|
||||||
Feature = "justify",
|
|
||||||
Limit = 3,
|
|
||||||
Remaining = 3,
|
|
||||||
ResetsAt = resetTime.ToString("O")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Task.FromResult(Results.Ok(limits));
|
return Task.FromResult(Results.Ok(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed record PipelinePlanRequest(
|
internal sealed record PipelinePlanRequest(
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using StellaOps.AdvisoryAI.Orchestration;
|
||||||
|
|
||||||
|
namespace StellaOps.AdvisoryAI.WebService.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Consolidated authorization service for advisory-ai endpoints.
|
||||||
|
/// Provides consistent scope-based authorization checks.
|
||||||
|
/// </summary>
|
||||||
|
public interface IAuthorizationService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the request is authorized for the given task type.
|
||||||
|
/// </summary>
|
||||||
|
bool IsAuthorized(HttpContext context, AdvisoryTaskType taskType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the request is authorized for explanation operations.
|
||||||
|
/// </summary>
|
||||||
|
bool IsExplainAuthorized(HttpContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the request is authorized for remediation operations.
|
||||||
|
/// </summary>
|
||||||
|
bool IsRemediationAuthorized(HttpContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the request is authorized for policy studio operations.
|
||||||
|
/// </summary>
|
||||||
|
bool IsPolicyAuthorized(HttpContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the request is authorized for justification operations.
|
||||||
|
/// </summary>
|
||||||
|
bool IsJustifyAuthorized(HttpContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the tenant ID from the request headers.
|
||||||
|
/// </summary>
|
||||||
|
string GetTenantId(HttpContext context);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the user ID from the request headers.
|
||||||
|
/// </summary>
|
||||||
|
string GetUserId(HttpContext context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default implementation of authorization service using header-based scopes.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class HeaderBasedAuthorizationService : IAuthorizationService
|
||||||
|
{
|
||||||
|
private const string ScopesHeader = "X-StellaOps-Scopes";
|
||||||
|
private const string TenantHeader = "X-StellaOps-Tenant";
|
||||||
|
private const string UserHeader = "X-StellaOps-User";
|
||||||
|
|
||||||
|
public bool IsAuthorized(HttpContext context, AdvisoryTaskType taskType)
|
||||||
|
{
|
||||||
|
var scopes = GetScopes(context);
|
||||||
|
if (scopes.Contains("advisory:run"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return scopes.Contains($"advisory:{taskType.ToString().ToLowerInvariant()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsExplainAuthorized(HttpContext context)
|
||||||
|
{
|
||||||
|
var scopes = GetScopes(context);
|
||||||
|
return scopes.Contains("advisory:run") || scopes.Contains("advisory:explain");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsRemediationAuthorized(HttpContext context)
|
||||||
|
{
|
||||||
|
var scopes = GetScopes(context);
|
||||||
|
return scopes.Contains("advisory:run") || scopes.Contains("advisory:remediate");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPolicyAuthorized(HttpContext context)
|
||||||
|
{
|
||||||
|
var scopes = GetScopes(context);
|
||||||
|
return scopes.Contains("advisory:run") || scopes.Contains("policy:write");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsJustifyAuthorized(HttpContext context)
|
||||||
|
{
|
||||||
|
var scopes = GetScopes(context);
|
||||||
|
return scopes.Contains("advisory:run") || scopes.Contains("advisory:justify");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTenantId(HttpContext context)
|
||||||
|
{
|
||||||
|
return context.Request.Headers.TryGetValue(TenantHeader, out var value)
|
||||||
|
? value.ToString()
|
||||||
|
: "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetUserId(HttpContext context)
|
||||||
|
{
|
||||||
|
return context.Request.Headers.TryGetValue(UserHeader, out var value)
|
||||||
|
? value.ToString()
|
||||||
|
: "anonymous";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashSet<string> GetScopes(HttpContext context)
|
||||||
|
{
|
||||||
|
if (!context.Request.Headers.TryGetValue(ScopesHeader, out var scopes))
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return scopes
|
||||||
|
.SelectMany(value => value?.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? [])
|
||||||
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace StellaOps.AdvisoryAI.WebService.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for feature-specific rate limits.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RateLimitsOptions
|
||||||
|
{
|
||||||
|
public const string SectionName = "AdvisoryAI:RateLimits";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rate limit for the explain feature.
|
||||||
|
/// </summary>
|
||||||
|
public FeatureRateLimitOptions Explain { get; set; } = new() { Limit = 10, PeriodMinutes = 1 };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rate limit for the remediate feature.
|
||||||
|
/// </summary>
|
||||||
|
public FeatureRateLimitOptions Remediate { get; set; } = new() { Limit = 5, PeriodMinutes = 1 };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rate limit for the justify feature.
|
||||||
|
/// </summary>
|
||||||
|
public FeatureRateLimitOptions Justify { get; set; } = new() { Limit = 3, PeriodMinutes = 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rate limit configuration for a single feature.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FeatureRateLimitOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum number of requests allowed per period.
|
||||||
|
/// </summary>
|
||||||
|
public int Limit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Period duration in minutes.
|
||||||
|
/// </summary>
|
||||||
|
public int PeriodMinutes { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents rate limit information for a feature.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RateLimitInfo
|
||||||
|
{
|
||||||
|
public required string Feature { get; init; }
|
||||||
|
public required int Limit { get; init; }
|
||||||
|
public required int Remaining { get; init; }
|
||||||
|
public required DateTimeOffset ResetsAt { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service for managing rate limit state and reporting.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRateLimitsService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current rate limit information for all features.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<RateLimitInfo> GetRateLimits(TimeProvider timeProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default implementation of rate limits service using configuration.
|
||||||
|
/// In production, this would integrate with the actual rate limiter state.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ConfigDrivenRateLimitsService : IRateLimitsService
|
||||||
|
{
|
||||||
|
private readonly RateLimitsOptions _options;
|
||||||
|
|
||||||
|
public ConfigDrivenRateLimitsService(IOptions<RateLimitsOptions> options)
|
||||||
|
{
|
||||||
|
_options = options.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<RateLimitInfo> GetRateLimits(TimeProvider timeProvider)
|
||||||
|
{
|
||||||
|
var now = timeProvider.GetUtcNow();
|
||||||
|
|
||||||
|
return
|
||||||
|
[
|
||||||
|
new RateLimitInfo
|
||||||
|
{
|
||||||
|
Feature = "explain",
|
||||||
|
Limit = _options.Explain.Limit,
|
||||||
|
Remaining = _options.Explain.Limit, // Would integrate with actual limiter state
|
||||||
|
ResetsAt = now.AddMinutes(_options.Explain.PeriodMinutes)
|
||||||
|
},
|
||||||
|
new RateLimitInfo
|
||||||
|
{
|
||||||
|
Feature = "remediate",
|
||||||
|
Limit = _options.Remediate.Limit,
|
||||||
|
Remaining = _options.Remediate.Limit, // Would integrate with actual limiter state
|
||||||
|
ResetsAt = now.AddMinutes(_options.Remediate.PeriodMinutes)
|
||||||
|
},
|
||||||
|
new RateLimitInfo
|
||||||
|
{
|
||||||
|
Feature = "justify",
|
||||||
|
Limit = _options.Justify.Limit,
|
||||||
|
Remaining = _options.Justify.Limit, // Would integrate with actual limiter state
|
||||||
|
ResetsAt = now.AddMinutes(_options.Justify.PeriodMinutes)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ namespace StellaOps.AdvisoryAI.Worker.Services;
|
|||||||
|
|
||||||
internal sealed class AdvisoryTaskWorker : BackgroundService
|
internal sealed class AdvisoryTaskWorker : BackgroundService
|
||||||
{
|
{
|
||||||
|
private const int MaxRetryDelaySeconds = 60;
|
||||||
|
private const int BaseRetryDelaySeconds = 2;
|
||||||
|
private const double JitterFactor = 0.2;
|
||||||
|
|
||||||
private readonly IAdvisoryTaskQueue _queue;
|
private readonly IAdvisoryTaskQueue _queue;
|
||||||
private readonly IAdvisoryPlanCache _cache;
|
private readonly IAdvisoryPlanCache _cache;
|
||||||
private readonly IAdvisoryPipelineOrchestrator _orchestrator;
|
private readonly IAdvisoryPipelineOrchestrator _orchestrator;
|
||||||
@@ -19,6 +23,7 @@ internal sealed class AdvisoryTaskWorker : BackgroundService
|
|||||||
private readonly IAdvisoryPipelineExecutor _executor;
|
private readonly IAdvisoryPipelineExecutor _executor;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly ILogger<AdvisoryTaskWorker> _logger;
|
private readonly ILogger<AdvisoryTaskWorker> _logger;
|
||||||
|
private int _consecutiveErrors;
|
||||||
|
|
||||||
public AdvisoryTaskWorker(
|
public AdvisoryTaskWorker(
|
||||||
IAdvisoryTaskQueue queue,
|
IAdvisoryTaskQueue queue,
|
||||||
@@ -61,11 +66,28 @@ internal sealed class AdvisoryTaskWorker : BackgroundService
|
|||||||
var fromCache = plan is not null && !message.Request.ForceRefresh;
|
var fromCache = plan is not null && !message.Request.ForceRefresh;
|
||||||
activity?.SetTag("advisory.plan_cache_hit", fromCache);
|
activity?.SetTag("advisory.plan_cache_hit", fromCache);
|
||||||
|
|
||||||
|
// When cache miss occurs, preserve the original plan cache key by storing
|
||||||
|
// under the message's key as an alias
|
||||||
|
string effectiveCacheKey = message.PlanCacheKey;
|
||||||
if (!fromCache)
|
if (!fromCache)
|
||||||
{
|
{
|
||||||
var start = _timeProvider.GetTimestamp();
|
var start = _timeProvider.GetTimestamp();
|
||||||
plan = await _orchestrator.CreatePlanAsync(message.Request, stoppingToken).ConfigureAwait(false);
|
plan = await _orchestrator.CreatePlanAsync(message.Request, stoppingToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Store under both the new cache key and the original message key
|
||||||
await _cache.SetAsync(plan.CacheKey, plan, stoppingToken).ConfigureAwait(false);
|
await _cache.SetAsync(plan.CacheKey, plan, stoppingToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// If the new plan's cache key differs from the original request,
|
||||||
|
// also store under the original key as an alias
|
||||||
|
if (!string.Equals(plan.CacheKey, message.PlanCacheKey, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
await _cache.SetAsync(message.PlanCacheKey, plan, stoppingToken).ConfigureAwait(false);
|
||||||
|
_logger.LogDebug(
|
||||||
|
"Plan cache key changed from {OriginalKey} to {NewKey}; stored alias",
|
||||||
|
message.PlanCacheKey,
|
||||||
|
plan.CacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
var elapsed = _timeProvider.GetElapsedTime(start);
|
var elapsed = _timeProvider.GetElapsedTime(start);
|
||||||
_metrics.RecordPlanCreated(elapsed.TotalSeconds, message.Request.TaskType);
|
_metrics.RecordPlanCreated(elapsed.TotalSeconds, message.Request.TaskType);
|
||||||
}
|
}
|
||||||
@@ -85,18 +107,48 @@ internal sealed class AdvisoryTaskWorker : BackgroundService
|
|||||||
var totalElapsed = _timeProvider.GetElapsedTime(processStart);
|
var totalElapsed = _timeProvider.GetElapsedTime(processStart);
|
||||||
_metrics.RecordPipelineLatency(message.Request.TaskType, totalElapsed.TotalSeconds, fromCache);
|
_metrics.RecordPipelineLatency(message.Request.TaskType, totalElapsed.TotalSeconds, fromCache);
|
||||||
activity?.SetTag("advisory.pipeline_latency_seconds", totalElapsed.TotalSeconds);
|
activity?.SetTag("advisory.pipeline_latency_seconds", totalElapsed.TotalSeconds);
|
||||||
|
|
||||||
|
// Reset consecutive error count on success
|
||||||
|
_consecutiveErrors = 0;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
// graceful shutdown
|
// Graceful shutdown - exit the loop cleanly
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error processing advisory task queue message");
|
_logger.LogError(ex, "Error processing advisory task queue message");
|
||||||
await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken).ConfigureAwait(false);
|
_consecutiveErrors++;
|
||||||
|
|
||||||
|
// Apply exponential backoff with jitter
|
||||||
|
var delaySeconds = ComputeRetryDelay(_consecutiveErrors);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(delaySeconds), stoppingToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// Graceful shutdown during delay - exit cleanly
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Advisory pipeline worker stopping");
|
_logger.LogInformation("Advisory pipeline worker stopping");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes retry delay with exponential backoff and jitter.
|
||||||
|
/// </summary>
|
||||||
|
private double ComputeRetryDelay(int errorCount)
|
||||||
|
{
|
||||||
|
// Exponential backoff: base * 2^(errorCount-1), capped at max
|
||||||
|
var backoff = Math.Min(BaseRetryDelaySeconds * Math.Pow(2, errorCount - 1), MaxRetryDelaySeconds);
|
||||||
|
|
||||||
|
// Add jitter (+/- JitterFactor percent)
|
||||||
|
var jitter = backoff * JitterFactor * (2 * Random.Shared.NextDouble() - 1);
|
||||||
|
|
||||||
|
return Math.Max(BaseRetryDelaySeconds, backoff + jitter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<RootNamespace>StellaOps.AirGap.Controller</RootNamespace>
|
<RootNamespace>StellaOps.AirGap.Controller</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<RootNamespace>StellaOps.AirGap.Importer</RootNamespace>
|
<RootNamespace>StellaOps.AirGap.Importer</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<RootNamespace>StellaOps.AirGap.Time</RootNamespace>
|
<RootNamespace>StellaOps.AirGap.Time</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// Description: Extracts advisory data from Concelier for knowledge snapshot bundles.
|
// Description: Extracts advisory data from Concelier for knowledge snapshot bundles.
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using StellaOps.AirGap.Bundle.Services;
|
using StellaOps.AirGap.Bundle.Services;
|
||||||
@@ -23,10 +24,17 @@ public sealed class AdvisorySnapshotExtractor : IAdvisorySnapshotExtractor
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly IAdvisoryDataSource _dataSource;
|
private readonly IAdvisoryDataSource _dataSource;
|
||||||
|
private readonly TimeProvider _timeProvider;
|
||||||
|
|
||||||
public AdvisorySnapshotExtractor(IAdvisoryDataSource dataSource)
|
public AdvisorySnapshotExtractor(IAdvisoryDataSource dataSource)
|
||||||
|
: this(dataSource, TimeProvider.System)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AdvisorySnapshotExtractor(IAdvisoryDataSource dataSource, TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
||||||
|
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -46,7 +54,10 @@ public sealed class AdvisorySnapshotExtractor : IAdvisorySnapshotExtractor
|
|||||||
{
|
{
|
||||||
var feeds = await _dataSource.GetAvailableFeedsAsync(cancellationToken);
|
var feeds = await _dataSource.GetAvailableFeedsAsync(cancellationToken);
|
||||||
|
|
||||||
foreach (var feed in feeds)
|
// Sort feeds for deterministic output
|
||||||
|
var sortedFeeds = feeds.OrderBy(f => f.FeedId, StringComparer.Ordinal).ToList();
|
||||||
|
|
||||||
|
foreach (var feed in sortedFeeds)
|
||||||
{
|
{
|
||||||
// Skip if specific feeds are requested and this isn't one of them
|
// Skip if specific feeds are requested and this isn't one of them
|
||||||
if (request.FeedIds is { Count: > 0 } && !request.FeedIds.Contains(feed.FeedId))
|
if (request.FeedIds is { Count: > 0 } && !request.FeedIds.Contains(feed.FeedId))
|
||||||
@@ -119,6 +130,8 @@ public sealed class AdvisorySnapshotExtractor : IAdvisorySnapshotExtractor
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var snapshotAt = _timeProvider.GetUtcNow();
|
||||||
|
|
||||||
// Serialize advisories to NDJSON format for deterministic output
|
// Serialize advisories to NDJSON format for deterministic output
|
||||||
var contentBuilder = new StringBuilder();
|
var contentBuilder = new StringBuilder();
|
||||||
foreach (var advisory in advisories.OrderBy(a => a.Id, StringComparer.Ordinal))
|
foreach (var advisory in advisories.OrderBy(a => a.Id, StringComparer.Ordinal))
|
||||||
@@ -128,7 +141,8 @@ public sealed class AdvisorySnapshotExtractor : IAdvisorySnapshotExtractor
|
|||||||
}
|
}
|
||||||
|
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(contentBuilder.ToString());
|
var contentBytes = Encoding.UTF8.GetBytes(contentBuilder.ToString());
|
||||||
var fileName = $"{feedId}-{DateTime.UtcNow:yyyyMMddHHmmss}.ndjson";
|
// Use invariant culture for deterministic filename formatting
|
||||||
|
var fileName = $"{feedId}-{snapshotAt.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture)}.ndjson";
|
||||||
|
|
||||||
return new FeedExtractionResult
|
return new FeedExtractionResult
|
||||||
{
|
{
|
||||||
@@ -139,7 +153,7 @@ public sealed class AdvisorySnapshotExtractor : IAdvisorySnapshotExtractor
|
|||||||
FeedId = feedId,
|
FeedId = feedId,
|
||||||
FileName = fileName,
|
FileName = fileName,
|
||||||
Content = contentBytes,
|
Content = contentBytes,
|
||||||
SnapshotAt = DateTimeOffset.UtcNow,
|
SnapshotAt = snapshotAt,
|
||||||
RecordCount = advisories.Count
|
RecordCount = advisories.Count
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,11 +23,23 @@ public sealed class PolicySnapshotExtractor : IPolicySnapshotExtractor
|
|||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fixed mtime for deterministic tar headers (2024-01-01 00:00:00 UTC).
|
||||||
|
/// </summary>
|
||||||
|
private const long DeterministicMtime = 1704067200;
|
||||||
|
|
||||||
private readonly IPolicyDataSource _dataSource;
|
private readonly IPolicyDataSource _dataSource;
|
||||||
|
private readonly TimeProvider _timeProvider;
|
||||||
|
|
||||||
public PolicySnapshotExtractor(IPolicyDataSource dataSource)
|
public PolicySnapshotExtractor(IPolicyDataSource dataSource)
|
||||||
|
: this(dataSource, TimeProvider.System)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PolicySnapshotExtractor(IPolicyDataSource dataSource, TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
||||||
|
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -46,7 +58,10 @@ public sealed class PolicySnapshotExtractor : IPolicySnapshotExtractor
|
|||||||
{
|
{
|
||||||
var policies = await _dataSource.GetAvailablePoliciesAsync(cancellationToken);
|
var policies = await _dataSource.GetAvailablePoliciesAsync(cancellationToken);
|
||||||
|
|
||||||
foreach (var policy in policies)
|
// Sort policies for deterministic output
|
||||||
|
var sortedPolicies = policies.OrderBy(p => p.PolicyId, StringComparer.Ordinal).ToList();
|
||||||
|
|
||||||
|
foreach (var policy in sortedPolicies)
|
||||||
{
|
{
|
||||||
// Skip if specific types are requested and this isn't one of them
|
// Skip if specific types are requested and this isn't one of them
|
||||||
if (request.Types is { Count: > 0 } && !request.Types.Contains(policy.Type))
|
if (request.Types is { Count: > 0 } && !request.Types.Contains(policy.Type))
|
||||||
@@ -247,9 +262,8 @@ public sealed class PolicySnapshotExtractor : IPolicySnapshotExtractor
|
|||||||
// File size in octal (124-135)
|
// File size in octal (124-135)
|
||||||
Encoding.ASCII.GetBytes(Convert.ToString(fileSize, 8).PadLeft(11, '0')).CopyTo(header, 124);
|
Encoding.ASCII.GetBytes(Convert.ToString(fileSize, 8).PadLeft(11, '0')).CopyTo(header, 124);
|
||||||
|
|
||||||
// Modification time (136-147)
|
// Modification time (136-147) - use deterministic mtime for reproducible output
|
||||||
var mtime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
Encoding.ASCII.GetBytes(Convert.ToString(DeterministicMtime, 8).PadLeft(11, '0')).CopyTo(header, 136);
|
||||||
Encoding.ASCII.GetBytes(Convert.ToString(mtime, 8).PadLeft(11, '0')).CopyTo(header, 136);
|
|
||||||
|
|
||||||
// Checksum placeholder (148-155) - spaces
|
// Checksum placeholder (148-155) - spaces
|
||||||
for (var i = 148; i < 156; i++)
|
for (var i = 148; i < 156; i++)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// Description: Extracts VEX statement data from Excititor for knowledge snapshot bundles.
|
// Description: Extracts VEX statement data from Excititor for knowledge snapshot bundles.
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using StellaOps.AirGap.Bundle.Services;
|
using StellaOps.AirGap.Bundle.Services;
|
||||||
@@ -24,10 +25,17 @@ public sealed class VexSnapshotExtractor : IVexSnapshotExtractor
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly IVexDataSource _dataSource;
|
private readonly IVexDataSource _dataSource;
|
||||||
|
private readonly TimeProvider _timeProvider;
|
||||||
|
|
||||||
public VexSnapshotExtractor(IVexDataSource dataSource)
|
public VexSnapshotExtractor(IVexDataSource dataSource)
|
||||||
|
: this(dataSource, TimeProvider.System)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public VexSnapshotExtractor(IVexDataSource dataSource, TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
||||||
|
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -47,7 +55,10 @@ public sealed class VexSnapshotExtractor : IVexSnapshotExtractor
|
|||||||
{
|
{
|
||||||
var sources = await _dataSource.GetAvailableSourcesAsync(cancellationToken);
|
var sources = await _dataSource.GetAvailableSourcesAsync(cancellationToken);
|
||||||
|
|
||||||
foreach (var source in sources)
|
// Sort sources for deterministic output
|
||||||
|
var sortedSources = sources.OrderBy(s => s.SourceId, StringComparer.Ordinal).ToList();
|
||||||
|
|
||||||
|
foreach (var source in sortedSources)
|
||||||
{
|
{
|
||||||
// Skip if specific sources are requested and this isn't one of them
|
// Skip if specific sources are requested and this isn't one of them
|
||||||
if (request.SourceIds is { Count: > 0 } && !request.SourceIds.Contains(source.SourceId))
|
if (request.SourceIds is { Count: > 0 } && !request.SourceIds.Contains(source.SourceId))
|
||||||
@@ -120,19 +131,22 @@ public sealed class VexSnapshotExtractor : IVexSnapshotExtractor
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var snapshotAt = _timeProvider.GetUtcNow();
|
||||||
|
var timestampStr = snapshotAt.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
// Serialize statements to OpenVEX format
|
// Serialize statements to OpenVEX format
|
||||||
var document = new OpenVexDocument
|
var document = new OpenVexDocument
|
||||||
{
|
{
|
||||||
Context = "https://openvex.dev/ns",
|
Context = "https://openvex.dev/ns",
|
||||||
Id = $"urn:stellaops:vex:{sourceId}:{DateTime.UtcNow:yyyyMMddHHmmss}",
|
Id = $"urn:stellaops:vex:{sourceId}:{timestampStr}",
|
||||||
Author = sourceId,
|
Author = sourceId,
|
||||||
Timestamp = DateTimeOffset.UtcNow,
|
Timestamp = snapshotAt,
|
||||||
Version = 1,
|
Version = 1,
|
||||||
Statements = statements.OrderBy(s => s.VulnerabilityId, StringComparer.Ordinal).ToList()
|
Statements = statements.OrderBy(s => s.VulnerabilityId, StringComparer.Ordinal).ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
var contentBytes = JsonSerializer.SerializeToUtf8Bytes(document, JsonOptions);
|
var contentBytes = JsonSerializer.SerializeToUtf8Bytes(document, JsonOptions);
|
||||||
var fileName = $"{sourceId}-{DateTime.UtcNow:yyyyMMddHHmmss}.json";
|
var fileName = $"{sourceId}-{timestampStr}.json";
|
||||||
|
|
||||||
return new VexSourceExtractionResult
|
return new VexSourceExtractionResult
|
||||||
{
|
{
|
||||||
@@ -143,7 +157,7 @@ public sealed class VexSnapshotExtractor : IVexSnapshotExtractor
|
|||||||
SourceId = sourceId,
|
SourceId = sourceId,
|
||||||
FileName = fileName,
|
FileName = fileName,
|
||||||
Content = contentBytes,
|
Content = contentBytes,
|
||||||
SnapshotAt = DateTimeOffset.UtcNow,
|
SnapshotAt = snapshotAt,
|
||||||
StatementCount = statements.Count
|
StatementCount = statements.Count
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Abstractions.cs
|
||||||
|
// Description: Abstractions for deterministic/testable time and ID generation.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace StellaOps.AirGap.Bundle.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides unique identifiers. Inject to enable deterministic testing.
|
||||||
|
/// </summary>
|
||||||
|
public interface IGuidProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new unique identifier.
|
||||||
|
/// </summary>
|
||||||
|
Guid NewGuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default GUID provider using system random GUIDs.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SystemGuidProvider : IGuidProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Singleton instance of the system GUID provider.
|
||||||
|
/// </summary>
|
||||||
|
public static SystemGuidProvider Instance { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Guid NewGuid() => Guid.NewGuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Options for configuring bundle validation behavior.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BundleValidationOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum age in days for feed snapshots before they are flagged as stale.
|
||||||
|
/// Default is 7 days.
|
||||||
|
/// </summary>
|
||||||
|
public int MaxFeedAgeDays { get; set; } = 7;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to fail validation on stale feeds or just warn.
|
||||||
|
/// </summary>
|
||||||
|
public bool FailOnStaleFeed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to validate policy digests.
|
||||||
|
/// </summary>
|
||||||
|
public bool ValidatePolicies { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to validate crypto material digests.
|
||||||
|
/// </summary>
|
||||||
|
public bool ValidateCryptoMaterials { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to validate catalog digests if present.
|
||||||
|
/// </summary>
|
||||||
|
public bool ValidateCatalogs { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to validate Rekor snapshot entries if present.
|
||||||
|
/// </summary>
|
||||||
|
public bool ValidateRekorSnapshots { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to validate crypto provider entries if present.
|
||||||
|
/// </summary>
|
||||||
|
public bool ValidateCryptoProviders { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Utility methods for path validation and security.
|
||||||
|
/// </summary>
|
||||||
|
public static class PathValidation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Validates that a relative path does not escape the bundle root.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relativePath">The relative path to validate.</param>
|
||||||
|
/// <returns>True if the path is safe; false if it contains traversal sequences or is absolute.</returns>
|
||||||
|
public static bool IsSafeRelativePath(string? relativePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(relativePath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for absolute paths
|
||||||
|
if (Path.IsPathRooted(relativePath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for path traversal sequences
|
||||||
|
var normalized = relativePath.Replace('\\', '/');
|
||||||
|
var segments = normalized.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
var depth = 0;
|
||||||
|
foreach (var segment in segments)
|
||||||
|
{
|
||||||
|
if (segment == "..")
|
||||||
|
{
|
||||||
|
depth--;
|
||||||
|
if (depth < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (segment != ".")
|
||||||
|
{
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check the raw string for null bytes or other dangerous chars
|
||||||
|
if (relativePath.Contains('\0'))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combines a root path with a relative path, validating that the result does not escape the root.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rootPath">The root directory path.</param>
|
||||||
|
/// <param name="relativePath">The relative path to combine.</param>
|
||||||
|
/// <returns>The combined path.</returns>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if the relative path would escape the root.</exception>
|
||||||
|
public static string SafeCombine(string rootPath, string relativePath)
|
||||||
|
{
|
||||||
|
if (!IsSafeRelativePath(relativePath))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"Invalid relative path: path traversal or absolute path detected in '{relativePath}'",
|
||||||
|
nameof(relativePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
var combined = Path.GetFullPath(Path.Combine(rootPath, relativePath));
|
||||||
|
var normalizedRoot = Path.GetFullPath(rootPath);
|
||||||
|
|
||||||
|
// Ensure the combined path starts with the root path
|
||||||
|
if (!combined.StartsWith(normalizedRoot, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"Path traversal detected: combined path escapes root directory",
|
||||||
|
nameof(relativePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,19 @@ namespace StellaOps.AirGap.Bundle.Services;
|
|||||||
|
|
||||||
public sealed class BundleBuilder : IBundleBuilder
|
public sealed class BundleBuilder : IBundleBuilder
|
||||||
{
|
{
|
||||||
|
private readonly TimeProvider _timeProvider;
|
||||||
|
private readonly IGuidProvider _guidProvider;
|
||||||
|
|
||||||
|
public BundleBuilder() : this(TimeProvider.System, SystemGuidProvider.Instance)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public BundleBuilder(TimeProvider timeProvider, IGuidProvider guidProvider)
|
||||||
|
{
|
||||||
|
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||||
|
_guidProvider = guidProvider ?? throw new ArgumentNullException(nameof(guidProvider));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<BundleManifest> BuildAsync(
|
public async Task<BundleManifest> BuildAsync(
|
||||||
BundleBuildRequest request,
|
BundleBuildRequest request,
|
||||||
string outputPath,
|
string outputPath,
|
||||||
@@ -21,7 +34,10 @@ public sealed class BundleBuilder : IBundleBuilder
|
|||||||
|
|
||||||
foreach (var feedConfig in request.Feeds)
|
foreach (var feedConfig in request.Feeds)
|
||||||
{
|
{
|
||||||
var component = await CopyComponentAsync(feedConfig, outputPath, ct).ConfigureAwait(false);
|
// Validate relative path before combining
|
||||||
|
var targetPath = PathValidation.SafeCombine(outputPath, feedConfig.RelativePath);
|
||||||
|
|
||||||
|
var component = await CopyComponentAsync(feedConfig, outputPath, targetPath, ct).ConfigureAwait(false);
|
||||||
feeds.Add(new FeedComponent(
|
feeds.Add(new FeedComponent(
|
||||||
feedConfig.FeedId,
|
feedConfig.FeedId,
|
||||||
feedConfig.Name,
|
feedConfig.Name,
|
||||||
@@ -35,7 +51,10 @@ public sealed class BundleBuilder : IBundleBuilder
|
|||||||
|
|
||||||
foreach (var policyConfig in request.Policies)
|
foreach (var policyConfig in request.Policies)
|
||||||
{
|
{
|
||||||
var component = await CopyComponentAsync(policyConfig, outputPath, ct).ConfigureAwait(false);
|
// Validate relative path before combining
|
||||||
|
var targetPath = PathValidation.SafeCombine(outputPath, policyConfig.RelativePath);
|
||||||
|
|
||||||
|
var component = await CopyComponentAsync(policyConfig, outputPath, targetPath, ct).ConfigureAwait(false);
|
||||||
policies.Add(new PolicyComponent(
|
policies.Add(new PolicyComponent(
|
||||||
policyConfig.PolicyId,
|
policyConfig.PolicyId,
|
||||||
policyConfig.Name,
|
policyConfig.Name,
|
||||||
@@ -48,7 +67,10 @@ public sealed class BundleBuilder : IBundleBuilder
|
|||||||
|
|
||||||
foreach (var cryptoConfig in request.CryptoMaterials)
|
foreach (var cryptoConfig in request.CryptoMaterials)
|
||||||
{
|
{
|
||||||
var component = await CopyComponentAsync(cryptoConfig, outputPath, ct).ConfigureAwait(false);
|
// Validate relative path before combining
|
||||||
|
var targetPath = PathValidation.SafeCombine(outputPath, cryptoConfig.RelativePath);
|
||||||
|
|
||||||
|
var component = await CopyComponentAsync(cryptoConfig, outputPath, targetPath, ct).ConfigureAwait(false);
|
||||||
cryptoMaterials.Add(new CryptoComponent(
|
cryptoMaterials.Add(new CryptoComponent(
|
||||||
cryptoConfig.ComponentId,
|
cryptoConfig.ComponentId,
|
||||||
cryptoConfig.Name,
|
cryptoConfig.Name,
|
||||||
@@ -65,11 +87,11 @@ public sealed class BundleBuilder : IBundleBuilder
|
|||||||
|
|
||||||
var manifest = new BundleManifest
|
var manifest = new BundleManifest
|
||||||
{
|
{
|
||||||
BundleId = Guid.NewGuid().ToString(),
|
BundleId = _guidProvider.NewGuid().ToString(),
|
||||||
SchemaVersion = "1.0.0",
|
SchemaVersion = "1.0.0",
|
||||||
Name = request.Name,
|
Name = request.Name,
|
||||||
Version = request.Version,
|
Version = request.Version,
|
||||||
CreatedAt = DateTimeOffset.UtcNow,
|
CreatedAt = _timeProvider.GetUtcNow(),
|
||||||
ExpiresAt = request.ExpiresAt,
|
ExpiresAt = request.ExpiresAt,
|
||||||
Feeds = feeds.ToImmutableArray(),
|
Feeds = feeds.ToImmutableArray(),
|
||||||
Policies = policies.ToImmutableArray(),
|
Policies = policies.ToImmutableArray(),
|
||||||
@@ -83,9 +105,9 @@ public sealed class BundleBuilder : IBundleBuilder
|
|||||||
private static async Task<CopiedComponent> CopyComponentAsync(
|
private static async Task<CopiedComponent> CopyComponentAsync(
|
||||||
BundleComponentSource source,
|
BundleComponentSource source,
|
||||||
string outputPath,
|
string outputPath,
|
||||||
|
string targetPath,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var targetPath = Path.Combine(outputPath, source.RelativePath);
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath) ?? outputPath);
|
Directory.CreateDirectory(Path.GetDirectoryName(targetPath) ?? outputPath);
|
||||||
|
|
||||||
await using (var input = File.OpenRead(source.SourcePath))
|
await using (var input = File.OpenRead(source.SourcePath))
|
||||||
|
|||||||
@@ -25,6 +25,19 @@ public sealed class SnapshotBundleReader : ISnapshotBundleReader
|
|||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly TimeProvider _timeProvider;
|
||||||
|
private readonly IGuidProvider _guidProvider;
|
||||||
|
|
||||||
|
public SnapshotBundleReader() : this(TimeProvider.System, SystemGuidProvider.Instance)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SnapshotBundleReader(TimeProvider timeProvider, IGuidProvider guidProvider)
|
||||||
|
{
|
||||||
|
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||||
|
_guidProvider = guidProvider ?? throw new ArgumentNullException(nameof(guidProvider));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads and verifies a snapshot bundle.
|
/// Reads and verifies a snapshot bundle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -40,12 +53,12 @@ public sealed class SnapshotBundleReader : ISnapshotBundleReader
|
|||||||
return SnapshotBundleReadResult.Failed("Bundle file not found");
|
return SnapshotBundleReadResult.Failed("Bundle file not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
var tempDir = Path.Combine(Path.GetTempPath(), $"bundle-read-{Guid.NewGuid():N}");
|
var tempDir = Path.Combine(Path.GetTempPath(), $"bundle-read-{_guidProvider.NewGuid():N}");
|
||||||
Directory.CreateDirectory(tempDir);
|
Directory.CreateDirectory(tempDir);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Extract the bundle
|
// Extract the bundle with path validation
|
||||||
await ExtractBundleAsync(request.BundlePath, tempDir, cancellationToken);
|
await ExtractBundleAsync(request.BundlePath, tempDir, cancellationToken);
|
||||||
|
|
||||||
// Read manifest
|
// Read manifest
|
||||||
@@ -124,7 +137,7 @@ public sealed class SnapshotBundleReader : ISnapshotBundleReader
|
|||||||
// Verify time anchor if present
|
// Verify time anchor if present
|
||||||
if (request.VerifyTimeAnchor && manifest.TimeAnchor is not null)
|
if (request.VerifyTimeAnchor && manifest.TimeAnchor is not null)
|
||||||
{
|
{
|
||||||
var timeAnchorService = new TimeAnchorService();
|
var timeAnchorService = new TimeAnchorService(_timeProvider, _guidProvider);
|
||||||
var timeAnchorContent = new TimeAnchorContent
|
var timeAnchorContent = new TimeAnchorContent
|
||||||
{
|
{
|
||||||
AnchorTime = manifest.TimeAnchor.AnchorTime,
|
AnchorTime = manifest.TimeAnchor.AnchorTime,
|
||||||
@@ -185,7 +198,34 @@ public sealed class SnapshotBundleReader : ISnapshotBundleReader
|
|||||||
{
|
{
|
||||||
await using var fileStream = File.OpenRead(bundlePath);
|
await using var fileStream = File.OpenRead(bundlePath);
|
||||||
await using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress);
|
await using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress);
|
||||||
await TarFile.ExtractToDirectoryAsync(gzipStream, targetDir, overwriteFiles: true, ct);
|
await using var tarReader = new TarReader(gzipStream);
|
||||||
|
|
||||||
|
TarEntry? entry;
|
||||||
|
while ((entry = await tarReader.GetNextEntryAsync(copyData: false, ct)) is not null)
|
||||||
|
{
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
// Validate entry name to prevent path traversal
|
||||||
|
if (!PathValidation.IsSafeRelativePath(entry.Name))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Unsafe path detected in bundle: '{entry.Name}'. Path traversal or absolute paths are not allowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate safe target path
|
||||||
|
var targetPath = PathValidation.SafeCombine(targetDir, entry.Name);
|
||||||
|
var targetEntryDir = Path.GetDirectoryName(targetPath);
|
||||||
|
if (!string.IsNullOrEmpty(targetEntryDir) && !Directory.Exists(targetEntryDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(targetEntryDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.EntryType == TarEntryType.RegularFile && entry.DataStream is not null)
|
||||||
|
{
|
||||||
|
await using var outputStream = File.Create(targetPath);
|
||||||
|
await entry.DataStream.CopyToAsync(outputStream, ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> ComputeFileDigestAsync(string filePath, CancellationToken ct)
|
private static async Task<string> ComputeFileDigestAsync(string filePath, CancellationToken ct)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Formats.Tar;
|
using System.Formats.Tar;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -26,6 +27,24 @@ public sealed class SnapshotBundleWriter : ISnapshotBundleWriter
|
|||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fixed mtime for deterministic tar headers (2024-01-01 00:00:00 UTC).
|
||||||
|
/// </summary>
|
||||||
|
private static readonly DateTimeOffset DeterministicMtime = new(2024, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||||
|
|
||||||
|
private readonly TimeProvider _timeProvider;
|
||||||
|
private readonly IGuidProvider _guidProvider;
|
||||||
|
|
||||||
|
public SnapshotBundleWriter() : this(TimeProvider.System, SystemGuidProvider.Instance)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SnapshotBundleWriter(TimeProvider timeProvider, IGuidProvider guidProvider)
|
||||||
|
{
|
||||||
|
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||||
|
_guidProvider = guidProvider ?? throw new ArgumentNullException(nameof(guidProvider));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a knowledge snapshot bundle from the specified contents.
|
/// Creates a knowledge snapshot bundle from the specified contents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -36,18 +55,19 @@ public sealed class SnapshotBundleWriter : ISnapshotBundleWriter
|
|||||||
ArgumentNullException.ThrowIfNull(request);
|
ArgumentNullException.ThrowIfNull(request);
|
||||||
ArgumentException.ThrowIfNullOrWhiteSpace(request.OutputPath);
|
ArgumentException.ThrowIfNullOrWhiteSpace(request.OutputPath);
|
||||||
|
|
||||||
var tempDir = Path.Combine(Path.GetTempPath(), $"snapshot-{Guid.NewGuid():N}");
|
var tempDir = Path.Combine(Path.GetTempPath(), $"snapshot-{_guidProvider.NewGuid():N}");
|
||||||
Directory.CreateDirectory(tempDir);
|
Directory.CreateDirectory(tempDir);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var entries = new List<BundleEntry>();
|
var entries = new List<BundleEntry>();
|
||||||
|
var createdAt = _timeProvider.GetUtcNow();
|
||||||
var manifest = new KnowledgeSnapshotManifest
|
var manifest = new KnowledgeSnapshotManifest
|
||||||
{
|
{
|
||||||
BundleId = request.BundleId ?? Guid.NewGuid().ToString("N"),
|
BundleId = request.BundleId ?? _guidProvider.NewGuid().ToString("N"),
|
||||||
Name = request.Name ?? $"knowledge-{DateTime.UtcNow:yyyy-MM-dd}",
|
Name = request.Name ?? $"knowledge-{createdAt.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}",
|
||||||
Version = request.Version ?? "1.0.0",
|
Version = request.Version ?? "1.0.0",
|
||||||
CreatedAt = DateTimeOffset.UtcNow,
|
CreatedAt = createdAt,
|
||||||
SchemaVersion = "1.0.0"
|
SchemaVersion = "1.0.0"
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -75,7 +95,7 @@ public sealed class SnapshotBundleWriter : ISnapshotBundleWriter
|
|||||||
RelativePath = relativePath,
|
RelativePath = relativePath,
|
||||||
Digest = digest,
|
Digest = digest,
|
||||||
SizeBytes = advisory.Content.Length,
|
SizeBytes = advisory.Content.Length,
|
||||||
SnapshotAt = advisory.SnapshotAt ?? DateTimeOffset.UtcNow,
|
SnapshotAt = advisory.SnapshotAt ?? createdAt,
|
||||||
RecordCount = advisory.RecordCount
|
RecordCount = advisory.RecordCount
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -105,7 +125,7 @@ public sealed class SnapshotBundleWriter : ISnapshotBundleWriter
|
|||||||
RelativePath = relativePath,
|
RelativePath = relativePath,
|
||||||
Digest = digest,
|
Digest = digest,
|
||||||
SizeBytes = vex.Content.Length,
|
SizeBytes = vex.Content.Length,
|
||||||
SnapshotAt = vex.SnapshotAt ?? DateTimeOffset.UtcNow,
|
SnapshotAt = vex.SnapshotAt ?? createdAt,
|
||||||
StatementCount = vex.StatementCount
|
StatementCount = vex.StatementCount
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -321,7 +341,24 @@ public sealed class SnapshotBundleWriter : ISnapshotBundleWriter
|
|||||||
|
|
||||||
await using var fileStream = File.Create(outputPath);
|
await using var fileStream = File.Create(outputPath);
|
||||||
await using var gzipStream = new GZipStream(fileStream, CompressionLevel.Optimal);
|
await using var gzipStream = new GZipStream(fileStream, CompressionLevel.Optimal);
|
||||||
await TarFile.CreateFromDirectoryAsync(sourceDir, gzipStream, includeBaseDirectory: false, ct);
|
await using var tarWriter = new TarWriter(gzipStream, TarEntryFormat.Pax);
|
||||||
|
|
||||||
|
// Collect all files and sort for deterministic ordering
|
||||||
|
var files = Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories)
|
||||||
|
.Select(f => (FullPath: f, RelativePath: Path.GetRelativePath(sourceDir, f).Replace('\\', '/')))
|
||||||
|
.OrderBy(f => f.RelativePath, StringComparer.Ordinal)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var (fullPath, relativePath) in files)
|
||||||
|
{
|
||||||
|
var entry = new PaxTarEntry(TarEntryType.RegularFile, relativePath)
|
||||||
|
{
|
||||||
|
DataStream = File.OpenRead(fullPath),
|
||||||
|
ModificationTime = DeterministicMtime,
|
||||||
|
Mode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.GroupRead | UnixFileMode.OtherRead
|
||||||
|
};
|
||||||
|
await tarWriter.WriteEntryAsync(entry, ct);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed record BundleEntry(string Path, string Digest, long SizeBytes);
|
private sealed record BundleEntry(string Path, string Digest, long SizeBytes);
|
||||||
|
|||||||
@@ -23,6 +23,19 @@ public sealed class TimeAnchorService : ITimeAnchorService
|
|||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly TimeProvider _timeProvider;
|
||||||
|
private readonly IGuidProvider _guidProvider;
|
||||||
|
|
||||||
|
public TimeAnchorService() : this(TimeProvider.System, SystemGuidProvider.Instance)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeAnchorService(TimeProvider timeProvider, IGuidProvider guidProvider)
|
||||||
|
{
|
||||||
|
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||||
|
_guidProvider = guidProvider ?? throw new ArgumentNullException(nameof(guidProvider));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a time anchor token for a snapshot.
|
/// Creates a time anchor token for a snapshot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -39,8 +52,8 @@ public sealed class TimeAnchorService : ITimeAnchorService
|
|||||||
return source switch
|
return source switch
|
||||||
{
|
{
|
||||||
"local" => await CreateLocalAnchorAsync(request, cancellationToken),
|
"local" => await CreateLocalAnchorAsync(request, cancellationToken),
|
||||||
var s when s.StartsWith("roughtime:") => await CreateRoughtimeAnchorAsync(request, cancellationToken),
|
var s when s.StartsWith("roughtime:", StringComparison.Ordinal) => await CreateRoughtimeAnchorAsync(request, cancellationToken),
|
||||||
var s when s.StartsWith("rfc3161:") => await CreateRfc3161AnchorAsync(request, cancellationToken),
|
var s when s.StartsWith("rfc3161:", StringComparison.Ordinal) => await CreateRfc3161AnchorAsync(request, cancellationToken),
|
||||||
_ => await CreateLocalAnchorAsync(request, cancellationToken)
|
_ => await CreateLocalAnchorAsync(request, cancellationToken)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -64,7 +77,7 @@ public sealed class TimeAnchorService : ITimeAnchorService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Validate timestamp is within acceptable range
|
// Validate timestamp is within acceptable range
|
||||||
var now = DateTimeOffset.UtcNow;
|
var now = _timeProvider.GetUtcNow();
|
||||||
var anchorAge = now - anchor.AnchorTime;
|
var anchorAge = now - anchor.AnchorTime;
|
||||||
|
|
||||||
if (request.MaxAgeHours.HasValue && anchorAge.TotalHours > request.MaxAgeHours.Value)
|
if (request.MaxAgeHours.HasValue && anchorAge.TotalHours > request.MaxAgeHours.Value)
|
||||||
@@ -127,19 +140,19 @@ public sealed class TimeAnchorService : ITimeAnchorService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<TimeAnchorResult> CreateLocalAnchorAsync(
|
private async Task<TimeAnchorResult> CreateLocalAnchorAsync(
|
||||||
TimeAnchorRequest request,
|
TimeAnchorRequest request,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
|
|
||||||
var anchorTime = DateTimeOffset.UtcNow;
|
var anchorTime = _timeProvider.GetUtcNow();
|
||||||
|
|
||||||
// Create a local anchor with a signed timestamp
|
// Create a local anchor with a signed timestamp
|
||||||
var anchorData = new LocalAnchorData
|
var anchorData = new LocalAnchorData
|
||||||
{
|
{
|
||||||
Timestamp = anchorTime,
|
Timestamp = anchorTime,
|
||||||
Nonce = Guid.NewGuid().ToString("N"),
|
Nonce = _guidProvider.NewGuid().ToString("N"),
|
||||||
MerkleRoot = request.MerkleRoot
|
MerkleRoot = request.MerkleRoot
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -160,7 +173,7 @@ public sealed class TimeAnchorService : ITimeAnchorService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<TimeAnchorResult> CreateRoughtimeAnchorAsync(
|
private async Task<TimeAnchorResult> CreateRoughtimeAnchorAsync(
|
||||||
TimeAnchorRequest request,
|
TimeAnchorRequest request,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -169,14 +182,14 @@ public sealed class TimeAnchorService : ITimeAnchorService
|
|||||||
var serverUrl = request.Source?["roughtime:".Length..] ?? "roughtime.cloudflare.com:2003";
|
var serverUrl = request.Source?["roughtime:".Length..] ?? "roughtime.cloudflare.com:2003";
|
||||||
|
|
||||||
// For now, fallback to local with indication of intended source
|
// For now, fallback to local with indication of intended source
|
||||||
var anchorTime = DateTimeOffset.UtcNow;
|
var anchorTime = _timeProvider.GetUtcNow();
|
||||||
var anchorData = new RoughtimeAnchorData
|
var anchorData = new RoughtimeAnchorData
|
||||||
{
|
{
|
||||||
Timestamp = anchorTime,
|
Timestamp = anchorTime,
|
||||||
Server = serverUrl,
|
Server = serverUrl,
|
||||||
Midpoint = anchorTime.ToUnixTimeSeconds(),
|
Midpoint = anchorTime.ToUnixTimeSeconds(),
|
||||||
Radius = 1000000, // 1 second radius in microseconds
|
Radius = 1000000, // 1 second radius in microseconds
|
||||||
Nonce = Guid.NewGuid().ToString("N"),
|
Nonce = _guidProvider.NewGuid().ToString("N"),
|
||||||
MerkleRoot = request.MerkleRoot
|
MerkleRoot = request.MerkleRoot
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -200,7 +213,7 @@ public sealed class TimeAnchorService : ITimeAnchorService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<TimeAnchorResult> CreateRfc3161AnchorAsync(
|
private async Task<TimeAnchorResult> CreateRfc3161AnchorAsync(
|
||||||
TimeAnchorRequest request,
|
TimeAnchorRequest request,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -208,12 +221,12 @@ public sealed class TimeAnchorService : ITimeAnchorService
|
|||||||
// This is a placeholder implementation - full implementation would use a TSA client
|
// This is a placeholder implementation - full implementation would use a TSA client
|
||||||
var tsaUrl = request.Source?["rfc3161:".Length..] ?? "http://timestamp.digicert.com";
|
var tsaUrl = request.Source?["rfc3161:".Length..] ?? "http://timestamp.digicert.com";
|
||||||
|
|
||||||
var anchorTime = DateTimeOffset.UtcNow;
|
var anchorTime = _timeProvider.GetUtcNow();
|
||||||
var anchorData = new Rfc3161AnchorData
|
var anchorData = new Rfc3161AnchorData
|
||||||
{
|
{
|
||||||
Timestamp = anchorTime,
|
Timestamp = anchorTime,
|
||||||
TsaUrl = tsaUrl,
|
TsaUrl = tsaUrl,
|
||||||
SerialNumber = Guid.NewGuid().ToString("N"),
|
SerialNumber = _guidProvider.NewGuid().ToString("N"),
|
||||||
PolicyOid = "2.16.840.1.114412.2.1", // DigiCert timestamp policy
|
PolicyOid = "2.16.840.1.114412.2.1", // DigiCert timestamp policy
|
||||||
MerkleRoot = request.MerkleRoot
|
MerkleRoot = request.MerkleRoot
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -2,11 +2,25 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using StellaOps.AirGap.Bundle.Models;
|
using StellaOps.AirGap.Bundle.Models;
|
||||||
using StellaOps.AirGap.Bundle.Serialization;
|
using StellaOps.AirGap.Bundle.Serialization;
|
||||||
|
using StellaOps.AirGap.Bundle.Services;
|
||||||
|
|
||||||
namespace StellaOps.AirGap.Bundle.Validation;
|
namespace StellaOps.AirGap.Bundle.Validation;
|
||||||
|
|
||||||
public sealed class BundleValidator : IBundleValidator
|
public sealed class BundleValidator : IBundleValidator
|
||||||
{
|
{
|
||||||
|
private readonly TimeProvider _timeProvider;
|
||||||
|
private readonly BundleValidationOptions _options;
|
||||||
|
|
||||||
|
public BundleValidator() : this(TimeProvider.System, new BundleValidationOptions())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public BundleValidator(TimeProvider timeProvider, BundleValidationOptions options)
|
||||||
|
{
|
||||||
|
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||||
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<BundleValidationResult> ValidateAsync(
|
public async Task<BundleValidationResult> ValidateAsync(
|
||||||
BundleManifest manifest,
|
BundleManifest manifest,
|
||||||
string bundlePath,
|
string bundlePath,
|
||||||
@@ -14,6 +28,7 @@ public sealed class BundleValidator : IBundleValidator
|
|||||||
{
|
{
|
||||||
var errors = new List<BundleValidationError>();
|
var errors = new List<BundleValidationError>();
|
||||||
var warnings = new List<BundleValidationWarning>();
|
var warnings = new List<BundleValidationWarning>();
|
||||||
|
var now = _timeProvider.GetUtcNow();
|
||||||
|
|
||||||
if (manifest.Feeds.Length == 0)
|
if (manifest.Feeds.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -25,9 +40,18 @@ public sealed class BundleValidator : IBundleValidator
|
|||||||
errors.Add(new BundleValidationError("CryptoMaterials", "Trust roots required"));
|
errors.Add(new BundleValidationError("CryptoMaterials", "Trust roots required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate feed digests and paths
|
||||||
foreach (var feed in manifest.Feeds)
|
foreach (var feed in manifest.Feeds)
|
||||||
{
|
{
|
||||||
var filePath = Path.Combine(bundlePath, feed.RelativePath);
|
// Validate path safety
|
||||||
|
if (!PathValidation.IsSafeRelativePath(feed.RelativePath))
|
||||||
|
{
|
||||||
|
errors.Add(new BundleValidationError("Feeds",
|
||||||
|
$"Feed {feed.FeedId} has unsafe relative path: {feed.RelativePath}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filePath = PathValidation.SafeCombine(bundlePath, feed.RelativePath);
|
||||||
var result = await VerifyFileDigestAsync(filePath, feed.Digest, ct).ConfigureAwait(false);
|
var result = await VerifyFileDigestAsync(filePath, feed.Digest, ct).ConfigureAwait(false);
|
||||||
if (!result.IsValid)
|
if (!result.IsValid)
|
||||||
{
|
{
|
||||||
@@ -36,21 +60,75 @@ public sealed class BundleValidator : IBundleValidator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manifest.ExpiresAt.HasValue && manifest.ExpiresAt.Value < DateTimeOffset.UtcNow)
|
// Validate policy digests if enabled
|
||||||
|
if (_options.ValidatePolicies)
|
||||||
|
{
|
||||||
|
foreach (var policy in manifest.Policies)
|
||||||
|
{
|
||||||
|
if (!PathValidation.IsSafeRelativePath(policy.RelativePath))
|
||||||
|
{
|
||||||
|
errors.Add(new BundleValidationError("Policies",
|
||||||
|
$"Policy {policy.PolicyId} has unsafe relative path: {policy.RelativePath}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filePath = PathValidation.SafeCombine(bundlePath, policy.RelativePath);
|
||||||
|
var result = await VerifyFileDigestAsync(filePath, policy.Digest, ct).ConfigureAwait(false);
|
||||||
|
if (!result.IsValid)
|
||||||
|
{
|
||||||
|
errors.Add(new BundleValidationError("Policies",
|
||||||
|
$"Policy {policy.PolicyId} digest mismatch: expected {policy.Digest}, got {result.ActualDigest}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate crypto material digests if enabled
|
||||||
|
if (_options.ValidateCryptoMaterials)
|
||||||
|
{
|
||||||
|
foreach (var crypto in manifest.CryptoMaterials)
|
||||||
|
{
|
||||||
|
if (!PathValidation.IsSafeRelativePath(crypto.RelativePath))
|
||||||
|
{
|
||||||
|
errors.Add(new BundleValidationError("CryptoMaterials",
|
||||||
|
$"Crypto material {crypto.ComponentId} has unsafe relative path: {crypto.RelativePath}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filePath = PathValidation.SafeCombine(bundlePath, crypto.RelativePath);
|
||||||
|
var result = await VerifyFileDigestAsync(filePath, crypto.Digest, ct).ConfigureAwait(false);
|
||||||
|
if (!result.IsValid)
|
||||||
|
{
|
||||||
|
errors.Add(new BundleValidationError("CryptoMaterials",
|
||||||
|
$"Crypto material {crypto.ComponentId} digest mismatch: expected {crypto.Digest}, got {result.ActualDigest}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check bundle expiration
|
||||||
|
if (manifest.ExpiresAt.HasValue && manifest.ExpiresAt.Value < now)
|
||||||
{
|
{
|
||||||
warnings.Add(new BundleValidationWarning("ExpiresAt", "Bundle has expired"));
|
warnings.Add(new BundleValidationWarning("ExpiresAt", "Bundle has expired"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check feed staleness using configurable threshold
|
||||||
foreach (var feed in manifest.Feeds)
|
foreach (var feed in manifest.Feeds)
|
||||||
{
|
{
|
||||||
var age = DateTimeOffset.UtcNow - feed.SnapshotAt;
|
var age = now - feed.SnapshotAt;
|
||||||
if (age.TotalDays > 7)
|
if (age.TotalDays > _options.MaxFeedAgeDays)
|
||||||
{
|
{
|
||||||
warnings.Add(new BundleValidationWarning("Feeds",
|
var message = $"Feed {feed.FeedId} is {age.TotalDays:F0} days old (threshold: {_options.MaxFeedAgeDays} days)";
|
||||||
$"Feed {feed.FeedId} is {age.TotalDays:F0} days old"));
|
if (_options.FailOnStaleFeed)
|
||||||
|
{
|
||||||
|
errors.Add(new BundleValidationError("Feeds", message));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
warnings.Add(new BundleValidationWarning("Feeds", message));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify bundle digest if present
|
||||||
if (manifest.BundleDigest is not null)
|
if (manifest.BundleDigest is not null)
|
||||||
{
|
{
|
||||||
var computed = ComputeBundleDigest(manifest);
|
var computed = ComputeBundleDigest(manifest);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<RootNamespace>StellaOps.AirGap.Persistence</RootNamespace>
|
<RootNamespace>StellaOps.AirGap.Persistence</RootNamespace>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
<AnalysisLevel>latest</AnalysisLevel>
|
<AnalysisLevel>latest</AnalysisLevel>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<RootNamespace>StellaOps.Attestor.Bundle</RootNamespace>
|
<RootNamespace>StellaOps.Attestor.Bundle</RootNamespace>
|
||||||
<Description>Sigstore Bundle v0.3 implementation for DSSE envelope packaging and offline verification.</Description>
|
<Description>Sigstore Bundle v0.3 implementation for DSSE envelope packaging and offline verification.</Description>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<RootNamespace>StellaOps.Attestor.TrustVerdict</RootNamespace>
|
<RootNamespace>StellaOps.Attestor.TrustVerdict</RootNamespace>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Description>TrustVerdict attestation library for signed VEX trust evaluations</Description>
|
<Description>TrustVerdict attestation library for signed VEX trust evaluations</Description>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<LangVersion>preview</LangVersion>
|
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="../../../../Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/StellaOps.Scanner.Analyzers.Lang.csproj" />
|
|
||||||
<ProjectReference Include="../../../../Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Bun/StellaOps.Scanner.Analyzers.Lang.Bun.csproj" />
|
|
||||||
<ProjectReference Include="../../../../Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Go/StellaOps.Scanner.Analyzers.Lang.Go.csproj" />
|
|
||||||
<ProjectReference Include="../../../../Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Node/StellaOps.Scanner.Analyzers.Lang.Node.csproj" />
|
|
||||||
<ProjectReference Include="../../../../Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Java/StellaOps.Scanner.Analyzers.Lang.Java.csproj" />
|
|
||||||
<ProjectReference Include="../../../../Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/StellaOps.Scanner.Analyzers.Lang.DotNet.csproj" />
|
|
||||||
<ProjectReference Include="../../../../Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Python/StellaOps.Scanner.Analyzers.Lang.Python.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<InternalsVisibleTo Include="StellaOps.Bench.ScannerAnalyzers.Tests" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ApkBuildSecfixesExtractor.cs
|
// ApkBuildSecfixesExtractor.cs
|
||||||
// Sprint: SPRINT_20251226_012_BINIDX_backport_handling
|
// Sprint: SPRINT_20251226_012_BINIDX_backport_handling
|
||||||
// Task: BACKPORT-17 — Implement APKBUILD secfixes extraction
|
// Task: BACKPORT-17 - Implement APKBUILD secfixes extraction
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<RootNamespace>StellaOps.Concelier.WebService</RootNamespace>
|
<RootNamespace>StellaOps.Concelier.WebService</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
<AnalysisLevel>latest</AnalysisLevel>
|
<AnalysisLevel>latest</AnalysisLevel>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<RootNamespace>StellaOps.Concelier.Connector.Astra</RootNamespace>
|
<RootNamespace>StellaOps.Concelier.Connector.Astra</RootNamespace>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<RootNamespace>StellaOps.Concelier.BackportProof</RootNamespace>
|
<RootNamespace>StellaOps.Concelier.BackportProof</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<RootNamespace>StellaOps.Concelier.Cache.Valkey</RootNamespace>
|
<RootNamespace>StellaOps.Concelier.Cache.Valkey</RootNamespace>
|
||||||
<AssemblyName>StellaOps.Concelier.Cache.Valkey</AssemblyName>
|
<AssemblyName>StellaOps.Concelier.Cache.Valkey</AssemblyName>
|
||||||
<Description>Valkey/Redis caching for Concelier canonical advisories</Description>
|
<Description>Valkey/Redis caching for Concelier canonical advisories</Description>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using StellaOps.Concelier.Documents;
|
using StellaOps.Concelier.Documents;
|
||||||
|
|
||||||
@@ -32,7 +33,11 @@ internal sealed record UbuntuCursor(
|
|||||||
lastPublished = value.DocumentType switch
|
lastPublished = value.DocumentType switch
|
||||||
{
|
{
|
||||||
DocumentType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
DocumentType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
|
||||||
DocumentType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
|
DocumentType.String when DateTimeOffset.TryParse(
|
||||||
|
value.AsString,
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
|
||||||
|
out var parsed) => parsed.ToUniversalTime(),
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -47,10 +52,14 @@ internal sealed record UbuntuCursor(
|
|||||||
|
|
||||||
public DocumentObject ToDocumentObject()
|
public DocumentObject ToDocumentObject()
|
||||||
{
|
{
|
||||||
|
// Sort collections for deterministic serialization
|
||||||
|
var sortedPendingDocs = PendingDocuments.OrderBy(id => id).Select(id => id.ToString());
|
||||||
|
var sortedPendingMaps = PendingMappings.OrderBy(id => id).Select(id => id.ToString());
|
||||||
|
|
||||||
var doc = new DocumentObject
|
var doc = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(sortedPendingDocs),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString()))
|
["pendingMappings"] = new DocumentArray(sortedPendingMaps)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastPublished.HasValue)
|
if (LastPublished.HasValue)
|
||||||
@@ -60,13 +69,16 @@ internal sealed record UbuntuCursor(
|
|||||||
|
|
||||||
if (ProcessedNoticeIds.Count > 0)
|
if (ProcessedNoticeIds.Count > 0)
|
||||||
{
|
{
|
||||||
doc["processedIds"] = new DocumentArray(ProcessedNoticeIds);
|
// Sort processed IDs for deterministic output
|
||||||
|
var sortedProcessedIds = ProcessedNoticeIds.OrderBy(id => id, StringComparer.Ordinal);
|
||||||
|
doc["processedIds"] = new DocumentArray(sortedProcessedIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FetchCache.Count > 0)
|
if (FetchCache.Count > 0)
|
||||||
{
|
{
|
||||||
var cacheDoc = new DocumentObject();
|
var cacheDoc = new DocumentObject();
|
||||||
foreach (var (key, entry) in FetchCache)
|
// Sort fetch cache keys for deterministic output
|
||||||
|
foreach (var (key, entry) in FetchCache.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
cacheDoc[key] = entry.ToDocumentObject();
|
cacheDoc[key] = entry.ToDocumentObject();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using StellaOps.Concelier.Documents;
|
using StellaOps.Concelier.Documents;
|
||||||
using StorageContracts = StellaOps.Concelier.Storage.Contracts;
|
using StorageContracts = StellaOps.Concelier.Storage.Contracts;
|
||||||
|
|
||||||
@@ -31,7 +32,11 @@ internal sealed record UbuntuFetchCacheEntry(string? ETag, DateTimeOffset? LastM
|
|||||||
lastModified = modifiedValue.DocumentType switch
|
lastModified = modifiedValue.DocumentType switch
|
||||||
{
|
{
|
||||||
DocumentType.DateTime => DateTime.SpecifyKind(modifiedValue.ToUniversalTime(), DateTimeKind.Utc),
|
DocumentType.DateTime => DateTime.SpecifyKind(modifiedValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||||
DocumentType.String when DateTimeOffset.TryParse(modifiedValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
DocumentType.String when DateTimeOffset.TryParse(
|
||||||
|
modifiedValue.AsString,
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
|
||||||
|
out var parsed) => parsed.ToUniversalTime(),
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ internal static class UbuntuNoticeParser
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var published = ParseDate(noticeElement, "published") ?? DateTimeOffset.UtcNow;
|
// Use MinValue instead of UtcNow for deterministic fallback on invalid/missing dates
|
||||||
|
var published = ParseDate(noticeElement, "published") ?? DateTimeOffset.MinValue;
|
||||||
var title = noticeElement.TryGetProperty("title", out var titleElement)
|
var title = noticeElement.TryGetProperty("title", out var titleElement)
|
||||||
? titleElement.GetString() ?? noticeId
|
? titleElement.GetString() ?? noticeId
|
||||||
: noticeId;
|
: noticeId;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -150,7 +150,8 @@ public sealed class UbuntuConnector : IFeedConnector
|
|||||||
var dtoDocument = ToDocument(notice);
|
var dtoDocument = ToDocument(notice);
|
||||||
var sha256 = ComputeNoticeHash(dtoDocument);
|
var sha256 = ComputeNoticeHash(dtoDocument);
|
||||||
|
|
||||||
var documentId = existing?.Id ?? Guid.NewGuid();
|
// Use existing ID or derive deterministic ID from source + uri hash
|
||||||
|
var documentId = existing?.Id ?? ComputeDeterministicId(SourceName, detailUri.AbsoluteUri);
|
||||||
var record = new DocumentRecord(
|
var record = new DocumentRecord(
|
||||||
documentId,
|
documentId,
|
||||||
SourceName,
|
SourceName,
|
||||||
@@ -167,7 +168,9 @@ public sealed class UbuntuConnector : IFeedConnector
|
|||||||
|
|
||||||
await _documentStore.UpsertAsync(record, cancellationToken).ConfigureAwait(false);
|
await _documentStore.UpsertAsync(record, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), record.Id, SourceName, "ubuntu.notice.v1", dtoDocument, now);
|
// Derive deterministic DTO ID from document ID + schema
|
||||||
|
var dtoId = ComputeDeterministicId(record.Id.ToString(), "ubuntu.notice.v1");
|
||||||
|
var dtoRecord = new DtoRecord(dtoId, record.Id, SourceName, "ubuntu.notice.v1", dtoDocument, now);
|
||||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
pendingMappings.Add(record.Id);
|
pendingMappings.Add(record.Id);
|
||||||
@@ -435,6 +438,15 @@ public sealed class UbuntuConnector : IFeedConnector
|
|||||||
return Convert.ToHexString(hash).ToLowerInvariant();
|
return Convert.ToHexString(hash).ToLowerInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
// Deterministic GUID based on SHA-256 hash of source + identifier
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
// Use first 16 bytes of hash as GUID
|
||||||
|
return new Guid(hash.AsSpan(0, 16));
|
||||||
|
}
|
||||||
|
|
||||||
private static DocumentObject ToDocument(UbuntuNoticeDto notice)
|
private static DocumentObject ToDocument(UbuntuNoticeDto notice)
|
||||||
{
|
{
|
||||||
var packages = new DocumentArray();
|
var packages = new DocumentArray();
|
||||||
@@ -486,14 +498,19 @@ public sealed class UbuntuConnector : IFeedConnector
|
|||||||
private static UbuntuNoticeDto FromDocument(DocumentObject document)
|
private static UbuntuNoticeDto FromDocument(DocumentObject document)
|
||||||
{
|
{
|
||||||
var noticeId = document.GetValue("noticeId", string.Empty).AsString;
|
var noticeId = document.GetValue("noticeId", string.Empty).AsString;
|
||||||
|
// Use MinValue instead of UtcNow for deterministic fallback on invalid/missing dates
|
||||||
var published = document.TryGetValue("published", out var publishedValue)
|
var published = document.TryGetValue("published", out var publishedValue)
|
||||||
? publishedValue.DocumentType switch
|
? publishedValue.DocumentType switch
|
||||||
{
|
{
|
||||||
DocumentType.DateTime => DateTime.SpecifyKind(publishedValue.ToUniversalTime(), DateTimeKind.Utc),
|
DocumentType.DateTime => DateTime.SpecifyKind(publishedValue.ToUniversalTime(), DateTimeKind.Utc),
|
||||||
DocumentType.String when DateTimeOffset.TryParse(publishedValue.AsString, out var parsed) => parsed.ToUniversalTime(),
|
DocumentType.String when DateTimeOffset.TryParse(
|
||||||
_ => DateTimeOffset.UtcNow
|
publishedValue.AsString,
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
|
||||||
|
out var parsed) => parsed.ToUniversalTime(),
|
||||||
|
_ => DateTimeOffset.MinValue
|
||||||
}
|
}
|
||||||
: DateTimeOffset.UtcNow;
|
: DateTimeOffset.MinValue;
|
||||||
|
|
||||||
var title = document.GetValue("title", noticeId).AsString;
|
var title = document.GetValue("title", noticeId).AsString;
|
||||||
var summary = document.GetValue("summary", string.Empty).AsString;
|
var summary = document.GetValue("summary", string.Empty).AsString;
|
||||||
|
|||||||
@@ -223,7 +223,8 @@ public sealed class EpssConnector : IFeedConnector
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var publishedDate = session.PublishedDate ?? TryParseDateFromMetadata(document.Metadata) ?? DateOnly.FromDateTime(document.CreatedAt.UtcDateTime);
|
// Use MinValue as deterministic fallback when published date cannot be determined
|
||||||
|
var publishedDate = session.PublishedDate ?? TryParseDateFromMetadata(document.Metadata) ?? DateOnly.MinValue;
|
||||||
var modelVersion = string.IsNullOrWhiteSpace(session.ModelVersionTag) ? "unknown" : session.ModelVersionTag!;
|
var modelVersion = string.IsNullOrWhiteSpace(session.ModelVersionTag) ? "unknown" : session.ModelVersionTag!;
|
||||||
var contentHash = session.DecompressedSha256 ?? string.Empty;
|
var contentHash = session.DecompressedSha256 ?? string.Empty;
|
||||||
|
|
||||||
@@ -235,8 +236,10 @@ public sealed class EpssConnector : IFeedConnector
|
|||||||
["contentHash"] = contentHash
|
["contentHash"] = contentHash
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Derive deterministic DTO ID from document ID + schema
|
||||||
|
var dtoId = ComputeDeterministicId(document.Id.ToString(), DtoSchemaVersion);
|
||||||
var dtoRecord = new DtoRecord(
|
var dtoRecord = new DtoRecord(
|
||||||
Guid.NewGuid(),
|
dtoId,
|
||||||
document.Id,
|
document.Id,
|
||||||
SourceName,
|
SourceName,
|
||||||
DtoSchemaVersion,
|
DtoSchemaVersion,
|
||||||
@@ -467,7 +470,8 @@ public sealed class EpssConnector : IFeedConnector
|
|||||||
}
|
}
|
||||||
|
|
||||||
var existing = await _documentStore.FindBySourceAndUriAsync(SourceName, fetchResult.SourceUri, cancellationToken).ConfigureAwait(false);
|
var existing = await _documentStore.FindBySourceAndUriAsync(SourceName, fetchResult.SourceUri, cancellationToken).ConfigureAwait(false);
|
||||||
var recordId = existing?.Id ?? Guid.NewGuid();
|
// Use existing ID or derive deterministic ID from source + uri
|
||||||
|
var recordId = existing?.Id ?? ComputeDeterministicId(SourceName, fetchResult.SourceUri);
|
||||||
|
|
||||||
await _rawDocumentStorage.UploadAsync(
|
await _rawDocumentStorage.UploadAsync(
|
||||||
SourceName,
|
SourceName,
|
||||||
@@ -760,6 +764,15 @@ public sealed class EpssConnector : IFeedConnector
|
|||||||
return _stateRepository.UpdateCursorAsync(SourceName, document, _timeProvider.GetUtcNow(), cancellationToken);
|
return _stateRepository.UpdateCursorAsync(SourceName, document, _timeProvider.GetUtcNow(), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
// Deterministic GUID based on SHA-256 hash of source + identifier
|
||||||
|
var input = System.Text.Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
// Use first 16 bytes of hash as GUID
|
||||||
|
return new Guid(hash.AsSpan(0, 16));
|
||||||
|
}
|
||||||
|
|
||||||
private sealed record EpssFetchResult(
|
private sealed record EpssFetchResult(
|
||||||
DateOnly SnapshotDate,
|
DateOnly SnapshotDate,
|
||||||
string SourceUri,
|
string SourceUri,
|
||||||
|
|||||||
@@ -27,10 +27,14 @@ internal sealed record EpssCursor(
|
|||||||
|
|
||||||
public DocumentObject ToDocumentObject()
|
public DocumentObject ToDocumentObject()
|
||||||
{
|
{
|
||||||
|
// Sort collections for deterministic serialization
|
||||||
|
var sortedPendingDocs = PendingDocuments.OrderBy(id => id).Select(id => id.ToString());
|
||||||
|
var sortedPendingMaps = PendingMappings.OrderBy(id => id).Select(id => id.ToString());
|
||||||
|
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(sortedPendingDocs),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString()))
|
["pendingMappings"] = new DocumentArray(sortedPendingMaps)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(ModelVersion))
|
if (!string.IsNullOrWhiteSpace(ModelVersion))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@@ -14,6 +15,7 @@ using StellaOps.Concelier.Connector.Ghsa.Internal;
|
|||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage.Advisories;
|
using StellaOps.Concelier.Storage.Advisories;
|
||||||
using StellaOps.Concelier.Core.Canonical;
|
using StellaOps.Concelier.Core.Canonical;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Ghsa;
|
namespace StellaOps.Concelier.Connector.Ghsa;
|
||||||
@@ -36,6 +38,7 @@ public sealed class GhsaConnector : IFeedConnector
|
|||||||
private readonly GhsaDiagnostics _diagnostics;
|
private readonly GhsaDiagnostics _diagnostics;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly ILogger<GhsaConnector> _logger;
|
private readonly ILogger<GhsaConnector> _logger;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly ICanonicalAdvisoryService? _canonicalService;
|
private readonly ICanonicalAdvisoryService? _canonicalService;
|
||||||
private readonly object _rateLimitWarningLock = new();
|
private readonly object _rateLimitWarningLock = new();
|
||||||
private readonly Dictionary<(string Phase, string Resource), bool> _rateLimitWarnings = new();
|
private readonly Dictionary<(string Phase, string Resource), bool> _rateLimitWarnings = new();
|
||||||
@@ -51,6 +54,7 @@ public sealed class GhsaConnector : IFeedConnector
|
|||||||
GhsaDiagnostics diagnostics,
|
GhsaDiagnostics diagnostics,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
ILogger<GhsaConnector> logger,
|
ILogger<GhsaConnector> logger,
|
||||||
|
ICryptoHash cryptoHash,
|
||||||
ICanonicalAdvisoryService? canonicalService = null)
|
ICanonicalAdvisoryService? canonicalService = null)
|
||||||
{
|
{
|
||||||
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
|
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
|
||||||
@@ -64,6 +68,7 @@ public sealed class GhsaConnector : IFeedConnector
|
|||||||
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_hash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
|
||||||
_canonicalService = canonicalService; // Optional - canonical ingest
|
_canonicalService = canonicalService; // Optional - canonical ingest
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,8 +327,9 @@ public sealed class GhsaConnector : IFeedConnector
|
|||||||
}
|
}
|
||||||
|
|
||||||
var payload = DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
var payload = DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||||
|
var dtoId = ComputeDeterministicId(document.Id.ToString(), "ghsa/1.0");
|
||||||
var dtoRecord = new DtoRecord(
|
var dtoRecord = new DtoRecord(
|
||||||
Guid.NewGuid(),
|
dtoId,
|
||||||
document.Id,
|
document.Id,
|
||||||
SourceName,
|
SourceName,
|
||||||
"ghsa/1.0",
|
"ghsa/1.0",
|
||||||
@@ -640,4 +646,15 @@ public sealed class GhsaConnector : IFeedConnector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a deterministic GUID from source and identifier using SHA-256 hash.
|
||||||
|
/// </summary>
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
// Use first 16 bytes of hash as GUID
|
||||||
|
return new Guid(hash.AsSpan(0, 16));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ internal sealed record GhsaCursor(
|
|||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["nextPage"] = NextPage,
|
["nextPage"] = NextPage,
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastUpdatedExclusive.HasValue)
|
if (LastUpdatedExclusive.HasValue)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ using StellaOps.Concelier.Storage.Advisories;
|
|||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Normalization.SemVer;
|
using StellaOps.Concelier.Normalization.SemVer;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Ics.Cisa;
|
namespace StellaOps.Concelier.Connector.Ics.Cisa;
|
||||||
@@ -52,6 +53,7 @@ public sealed class IcsCisaConnector : IFeedConnector
|
|||||||
private readonly IcsCisaDiagnostics _diagnostics;
|
private readonly IcsCisaDiagnostics _diagnostics;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly ILogger<IcsCisaConnector> _logger;
|
private readonly ILogger<IcsCisaConnector> _logger;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly HtmlContentSanitizer _htmlSanitizer = new();
|
private readonly HtmlContentSanitizer _htmlSanitizer = new();
|
||||||
private readonly HtmlParser _htmlParser = new();
|
private readonly HtmlParser _htmlParser = new();
|
||||||
|
|
||||||
@@ -66,7 +68,8 @@ public sealed class IcsCisaConnector : IFeedConnector
|
|||||||
IcsCisaFeedParser parser,
|
IcsCisaFeedParser parser,
|
||||||
IcsCisaDiagnostics diagnostics,
|
IcsCisaDiagnostics diagnostics,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
ILogger<IcsCisaConnector> logger)
|
ILogger<IcsCisaConnector> logger,
|
||||||
|
ICryptoHash cryptoHash)
|
||||||
{
|
{
|
||||||
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
|
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
|
||||||
_rawDocumentStorage = rawDocumentStorage ?? throw new ArgumentNullException(nameof(rawDocumentStorage));
|
_rawDocumentStorage = rawDocumentStorage ?? throw new ArgumentNullException(nameof(rawDocumentStorage));
|
||||||
@@ -80,6 +83,7 @@ public sealed class IcsCisaConnector : IFeedConnector
|
|||||||
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_hash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SourceName => IcsCisaConnectorPlugin.SourceName;
|
public string SourceName => IcsCisaConnectorPlugin.SourceName;
|
||||||
@@ -323,8 +327,9 @@ public sealed class IcsCisaConnector : IFeedConnector
|
|||||||
WriteIndented = false,
|
WriteIndented = false,
|
||||||
});
|
});
|
||||||
var doc = DocumentObject.Parse(json);
|
var doc = DocumentObject.Parse(json);
|
||||||
|
var dtoId = ComputeDeterministicId(document.Id.ToString(), SchemaVersion);
|
||||||
var dtoRecord = new DtoRecord(
|
var dtoRecord = new DtoRecord(
|
||||||
Guid.NewGuid(),
|
dtoId,
|
||||||
document.Id,
|
document.Id,
|
||||||
SourceName,
|
SourceName,
|
||||||
SchemaVersion,
|
SchemaVersion,
|
||||||
@@ -1416,4 +1421,15 @@ public sealed class IcsCisaConnector : IFeedConnector
|
|||||||
|
|
||||||
private Task UpdateCursorAsync(IcsCisaCursor cursor, CancellationToken cancellationToken)
|
private Task UpdateCursorAsync(IcsCisaCursor cursor, CancellationToken cancellationToken)
|
||||||
=> _stateRepository.UpdateCursorAsync(SourceName, cursor.ToDocumentObject(), _timeProvider.GetUtcNow(), cancellationToken);
|
=> _stateRepository.UpdateCursorAsync(SourceName, cursor.ToDocumentObject(), _timeProvider.GetUtcNow(), cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a deterministic GUID from source and identifier using SHA-256 hash.
|
||||||
|
/// </summary>
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
// Use first 16 bytes of hash as GUID
|
||||||
|
return new Guid(hash.AsSpan(0, 16));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ internal sealed record IcsCisaCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(static id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(static id => id).Select(static id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(static id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(static id => id).Select(static id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastPublished.HasValue)
|
if (LastPublished.HasValue)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ internal sealed record KasperskyCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastPublished.HasValue)
|
if (LastPublished.HasValue)
|
||||||
@@ -33,7 +33,7 @@ internal sealed record KasperskyCursor(
|
|||||||
if (FetchCache.Count > 0)
|
if (FetchCache.Count > 0)
|
||||||
{
|
{
|
||||||
var cacheArray = new DocumentArray();
|
var cacheArray = new DocumentArray();
|
||||||
foreach (var (uri, metadata) in FetchCache)
|
foreach (var (uri, metadata) in FetchCache.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
var cacheDocument = new DocumentObject
|
var cacheDocument = new DocumentObject
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -16,6 +17,7 @@ using StellaOps.Concelier.Storage;
|
|||||||
using StellaOps.Concelier.Storage.Advisories;
|
using StellaOps.Concelier.Storage.Advisories;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Ics.Kaspersky;
|
namespace StellaOps.Concelier.Connector.Ics.Kaspersky;
|
||||||
@@ -39,6 +41,7 @@ public sealed class KasperskyConnector : IFeedConnector
|
|||||||
private readonly KasperskyOptions _options;
|
private readonly KasperskyOptions _options;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly ILogger<KasperskyConnector> _logger;
|
private readonly ILogger<KasperskyConnector> _logger;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
|
|
||||||
public KasperskyConnector(
|
public KasperskyConnector(
|
||||||
KasperskyFeedClient feedClient,
|
KasperskyFeedClient feedClient,
|
||||||
@@ -50,7 +53,8 @@ public sealed class KasperskyConnector : IFeedConnector
|
|||||||
ISourceStateRepository stateRepository,
|
ISourceStateRepository stateRepository,
|
||||||
IOptions<KasperskyOptions> options,
|
IOptions<KasperskyOptions> options,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
ILogger<KasperskyConnector> logger)
|
ILogger<KasperskyConnector> logger,
|
||||||
|
ICryptoHash cryptoHash)
|
||||||
{
|
{
|
||||||
_feedClient = feedClient ?? throw new ArgumentNullException(nameof(feedClient));
|
_feedClient = feedClient ?? throw new ArgumentNullException(nameof(feedClient));
|
||||||
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
|
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
|
||||||
@@ -63,6 +67,7 @@ public sealed class KasperskyConnector : IFeedConnector
|
|||||||
_options.Validate();
|
_options.Validate();
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_hash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SourceName => KasperskyConnectorPlugin.SourceName;
|
public string SourceName => KasperskyConnectorPlugin.SourceName;
|
||||||
@@ -255,7 +260,7 @@ public sealed class KasperskyConnector : IFeedConnector
|
|||||||
: document.FetchedAt;
|
: document.FetchedAt;
|
||||||
var summary = metadata.TryGetValue("kaspersky.summary", out var summaryValue) ? summaryValue : null;
|
var summary = metadata.TryGetValue("kaspersky.summary", out var summaryValue) ? summaryValue : null;
|
||||||
var slug = metadata.TryGetValue("kaspersky.slug", out var slugValue) ? slugValue : ExtractSlug(new Uri(link, UriKind.Absolute));
|
var slug = metadata.TryGetValue("kaspersky.slug", out var slugValue) ? slugValue : ExtractSlug(new Uri(link, UriKind.Absolute));
|
||||||
var advisoryKey = string.IsNullOrWhiteSpace(slug) ? Guid.NewGuid().ToString("N") : slug;
|
var advisoryKey = string.IsNullOrWhiteSpace(slug) ? ComputeDeterministicId(document.Id.ToString(), "kaspersky.advisory").ToString("N") : slug;
|
||||||
|
|
||||||
byte[] rawBytes;
|
byte[] rawBytes;
|
||||||
try
|
try
|
||||||
@@ -270,7 +275,8 @@ public sealed class KasperskyConnector : IFeedConnector
|
|||||||
|
|
||||||
var dto = KasperskyAdvisoryParser.Parse(advisoryKey, title, link, published, summary, rawBytes);
|
var dto = KasperskyAdvisoryParser.Parse(advisoryKey, title, link, published, summary, rawBytes);
|
||||||
var payload = DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
var payload = DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "ics.kaspersky/1", payload, _timeProvider.GetUtcNow());
|
var dtoId = ComputeDeterministicId(document.Id.ToString(), "ics.kaspersky/1");
|
||||||
|
var dtoRecord = new DtoRecord(dtoId, document.Id, SourceName, "ics.kaspersky/1", payload, _timeProvider.GetUtcNow());
|
||||||
|
|
||||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
||||||
@@ -461,4 +467,15 @@ public sealed class KasperskyConnector : IFeedConnector
|
|||||||
var last = segments[^1].Trim('/');
|
var last = segments[^1].Trim('/');
|
||||||
return string.IsNullOrWhiteSpace(last) && segments.Length > 1 ? segments[^2].Trim('/') : last;
|
return string.IsNullOrWhiteSpace(last) && segments.Length > 1 ? segments[^2].Trim('/') : last;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a deterministic GUID from source and identifier using SHA-256 hash.
|
||||||
|
/// </summary>
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
// Use first 16 bytes of hash as GUID
|
||||||
|
return new Guid(hash.AsSpan(0, 16));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ internal sealed record JvnCursor(
|
|||||||
document["lastCompletedWindowEnd"] = LastCompletedWindowEnd.Value.UtcDateTime;
|
document["lastCompletedWindowEnd"] = LastCompletedWindowEnd.Value.UtcDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
document["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(static id => id.ToString()));
|
document["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(static id => id.ToString()));
|
||||||
document["pendingMappings"] = new DocumentArray(PendingMappings.Select(static id => id.ToString()));
|
document["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(static id => id.ToString()));
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@@ -14,6 +15,7 @@ using StellaOps.Concelier.Storage.Advisories;
|
|||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage.JpFlags;
|
using StellaOps.Concelier.Storage.JpFlags;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Jvn;
|
namespace StellaOps.Concelier.Connector.Jvn;
|
||||||
@@ -35,6 +37,7 @@ public sealed class JvnConnector : IFeedConnector
|
|||||||
private readonly IAdvisoryStore _advisoryStore;
|
private readonly IAdvisoryStore _advisoryStore;
|
||||||
private readonly IJpFlagStore _jpFlagStore;
|
private readonly IJpFlagStore _jpFlagStore;
|
||||||
private readonly ISourceStateRepository _stateRepository;
|
private readonly ISourceStateRepository _stateRepository;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly JvnOptions _options;
|
private readonly JvnOptions _options;
|
||||||
private readonly ILogger<JvnConnector> _logger;
|
private readonly ILogger<JvnConnector> _logger;
|
||||||
@@ -48,6 +51,7 @@ public sealed class JvnConnector : IFeedConnector
|
|||||||
IAdvisoryStore advisoryStore,
|
IAdvisoryStore advisoryStore,
|
||||||
IJpFlagStore jpFlagStore,
|
IJpFlagStore jpFlagStore,
|
||||||
ISourceStateRepository stateRepository,
|
ISourceStateRepository stateRepository,
|
||||||
|
ICryptoHash cryptoHash,
|
||||||
IOptions<JvnOptions> options,
|
IOptions<JvnOptions> options,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
ILogger<JvnConnector> logger)
|
ILogger<JvnConnector> logger)
|
||||||
@@ -60,6 +64,7 @@ public sealed class JvnConnector : IFeedConnector
|
|||||||
_advisoryStore = advisoryStore ?? throw new ArgumentNullException(nameof(advisoryStore));
|
_advisoryStore = advisoryStore ?? throw new ArgumentNullException(nameof(advisoryStore));
|
||||||
_jpFlagStore = jpFlagStore ?? throw new ArgumentNullException(nameof(jpFlagStore));
|
_jpFlagStore = jpFlagStore ?? throw new ArgumentNullException(nameof(jpFlagStore));
|
||||||
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
||||||
|
_hash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
|
||||||
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_options.Validate();
|
_options.Validate();
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
@@ -225,7 +230,7 @@ public sealed class JvnConnector : IFeedConnector
|
|||||||
var sanitizedJson = JsonSerializer.Serialize(detail, SerializerOptions);
|
var sanitizedJson = JsonSerializer.Serialize(detail, SerializerOptions);
|
||||||
var payload = DocumentObject.Parse(sanitizedJson);
|
var payload = DocumentObject.Parse(sanitizedJson);
|
||||||
var dtoRecord = new DtoRecord(
|
var dtoRecord = new DtoRecord(
|
||||||
Guid.NewGuid(),
|
ComputeDeterministicId(document.Id.ToString(), "jvn/1.0"),
|
||||||
document.Id,
|
document.Id,
|
||||||
SourceName,
|
SourceName,
|
||||||
JvnConstants.DtoSchemaVersion,
|
JvnConstants.DtoSchemaVersion,
|
||||||
@@ -322,4 +327,11 @@ public sealed class JvnConnector : IFeedConnector
|
|||||||
var cursorDocument = cursor.ToDocumentObject();
|
var cursorDocument = cursor.ToDocumentObject();
|
||||||
await _stateRepository.UpdateCursorAsync(SourceName, cursorDocument, _timeProvider.GetUtcNow(), cancellationToken).ConfigureAwait(false);
|
await _stateRepository.UpdateCursorAsync(SourceName, cursorDocument, _timeProvider.GetUtcNow(), cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hash.AsSpan()[..16]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Schemas\*.xsd" />
|
<EmbeddedResource Include="Schemas\*.xsd" />
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ internal sealed record KevCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(static id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(static id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(static id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(static id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(CatalogVersion))
|
if (!string.IsNullOrEmpty(CatalogVersion))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -18,6 +19,7 @@ using StellaOps.Concelier.Storage;
|
|||||||
using StellaOps.Concelier.Storage.Advisories;
|
using StellaOps.Concelier.Storage.Advisories;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Kev;
|
namespace StellaOps.Concelier.Connector.Kev;
|
||||||
@@ -40,6 +42,7 @@ public sealed class KevConnector : IFeedConnector
|
|||||||
private readonly ISourceStateRepository _stateRepository;
|
private readonly ISourceStateRepository _stateRepository;
|
||||||
private readonly KevOptions _options;
|
private readonly KevOptions _options;
|
||||||
private readonly IJsonSchemaValidator _schemaValidator;
|
private readonly IJsonSchemaValidator _schemaValidator;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly ILogger<KevConnector> _logger;
|
private readonly ILogger<KevConnector> _logger;
|
||||||
private readonly KevDiagnostics _diagnostics;
|
private readonly KevDiagnostics _diagnostics;
|
||||||
@@ -53,6 +56,7 @@ public sealed class KevConnector : IFeedConnector
|
|||||||
ISourceStateRepository stateRepository,
|
ISourceStateRepository stateRepository,
|
||||||
IOptions<KevOptions> options,
|
IOptions<KevOptions> options,
|
||||||
IJsonSchemaValidator schemaValidator,
|
IJsonSchemaValidator schemaValidator,
|
||||||
|
ICryptoHash cryptoHash,
|
||||||
KevDiagnostics diagnostics,
|
KevDiagnostics diagnostics,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
ILogger<KevConnector> logger)
|
ILogger<KevConnector> logger)
|
||||||
@@ -66,6 +70,7 @@ public sealed class KevConnector : IFeedConnector
|
|||||||
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
|
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_options.Validate();
|
_options.Validate();
|
||||||
_schemaValidator = schemaValidator ?? throw new ArgumentNullException(nameof(schemaValidator));
|
_schemaValidator = schemaValidator ?? throw new ArgumentNullException(nameof(schemaValidator));
|
||||||
|
_hash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
|
||||||
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
@@ -273,7 +278,7 @@ public sealed class KevConnector : IFeedConnector
|
|||||||
_diagnostics.CatalogParsed(catalog.CatalogVersion, entryCount);
|
_diagnostics.CatalogParsed(catalog.CatalogVersion, entryCount);
|
||||||
|
|
||||||
var dtoRecord = new DtoRecord(
|
var dtoRecord = new DtoRecord(
|
||||||
Guid.NewGuid(),
|
ComputeDeterministicId(document.Id.ToString(), "kev/1.0"),
|
||||||
document.Id,
|
document.Id,
|
||||||
SourceName,
|
SourceName,
|
||||||
SchemaVersion,
|
SchemaVersion,
|
||||||
@@ -438,4 +443,11 @@ public sealed class KevConnector : IFeedConnector
|
|||||||
|
|
||||||
private static Uri? TryParseUri(string? value)
|
private static Uri? TryParseUri(string? value)
|
||||||
=> Uri.TryCreate(value, UriKind.Absolute, out var uri) ? uri : null;
|
=> Uri.TryCreate(value, UriKind.Absolute, out var uri) ? uri : null;
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hash.AsSpan()[..16]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ internal sealed record KisaCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["knownIds"] = new DocumentArray(KnownIds),
|
["knownIds"] = new DocumentArray(KnownIds.OrderBy(id => id, StringComparer.Ordinal)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastPublished.HasValue)
|
if (LastPublished.HasValue)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -15,6 +16,7 @@ using StellaOps.Concelier.Storage;
|
|||||||
using StellaOps.Concelier.Storage.Advisories;
|
using StellaOps.Concelier.Storage.Advisories;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Kisa;
|
namespace StellaOps.Concelier.Connector.Kisa;
|
||||||
@@ -37,6 +39,7 @@ public sealed class KisaConnector : IFeedConnector
|
|||||||
private readonly ISourceStateRepository _stateRepository;
|
private readonly ISourceStateRepository _stateRepository;
|
||||||
private readonly KisaOptions _options;
|
private readonly KisaOptions _options;
|
||||||
private readonly KisaDiagnostics _diagnostics;
|
private readonly KisaDiagnostics _diagnostics;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly ILogger<KisaConnector> _logger;
|
private readonly ILogger<KisaConnector> _logger;
|
||||||
|
|
||||||
@@ -51,6 +54,7 @@ public sealed class KisaConnector : IFeedConnector
|
|||||||
ISourceStateRepository stateRepository,
|
ISourceStateRepository stateRepository,
|
||||||
IOptions<KisaOptions> options,
|
IOptions<KisaOptions> options,
|
||||||
KisaDiagnostics diagnostics,
|
KisaDiagnostics diagnostics,
|
||||||
|
ICryptoHash cryptoHash,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
ILogger<KisaConnector> logger)
|
ILogger<KisaConnector> logger)
|
||||||
{
|
{
|
||||||
@@ -65,6 +69,7 @@ public sealed class KisaConnector : IFeedConnector
|
|||||||
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_options.Validate();
|
_options.Validate();
|
||||||
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
||||||
|
_hash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
@@ -288,7 +293,7 @@ public sealed class KisaConnector : IFeedConnector
|
|||||||
_logger.LogDebug("KISA parsed detail for {DocumentId} ({Category})", document.Id, category ?? "unknown");
|
_logger.LogDebug("KISA parsed detail for {DocumentId} ({Category})", document.Id, category ?? "unknown");
|
||||||
|
|
||||||
var dtoDoc = DocumentObject.Parse(JsonSerializer.Serialize(parsed, SerializerOptions));
|
var dtoDoc = DocumentObject.Parse(JsonSerializer.Serialize(parsed, SerializerOptions));
|
||||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "kisa.detail.v1", dtoDoc, now);
|
var dtoRecord = new DtoRecord(ComputeDeterministicId(document.Id.ToString(), "kisa/1.0"), document.Id, SourceName, "kisa.detail.v1", dtoDoc, now);
|
||||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -421,4 +426,11 @@ public sealed class KisaConnector : IFeedConnector
|
|||||||
var completedAt = cursor.LastFetchAt ?? _timeProvider.GetUtcNow();
|
var completedAt = cursor.LastFetchAt ?? _timeProvider.GetUtcNow();
|
||||||
return _stateRepository.UpdateCursorAsync(SourceName, document, completedAt, cancellationToken);
|
return _stateRepository.UpdateCursorAsync(SourceName, document, completedAt, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hash.AsSpan()[..16]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ internal sealed record NvdCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject();
|
var document = new DocumentObject();
|
||||||
Window.WriteTo(document);
|
Window.WriteTo(document);
|
||||||
document["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString()));
|
document["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString()));
|
||||||
document["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString()));
|
document["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString()));
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ public sealed class NvdConnector : IFeedConnector
|
|||||||
var payload = DocumentObject.Parse(sanitized);
|
var payload = DocumentObject.Parse(sanitized);
|
||||||
|
|
||||||
var dtoRecord = new DtoRecord(
|
var dtoRecord = new DtoRecord(
|
||||||
Guid.NewGuid(),
|
ComputeDeterministicId(document.Id.ToString(), "nvd/1.0"),
|
||||||
document.Id,
|
document.Id,
|
||||||
SourceName,
|
SourceName,
|
||||||
"nvd.cve.v2",
|
"nvd.cve.v2",
|
||||||
@@ -473,7 +473,7 @@ public sealed class NvdConnector : IFeedConnector
|
|||||||
: document.Sha256;
|
: document.Sha256;
|
||||||
|
|
||||||
var record = new ChangeHistoryRecord(
|
var record = new ChangeHistoryRecord(
|
||||||
Guid.NewGuid(),
|
ComputeDeterministicId($"{current.AdvisoryKey}:{document.Id}", "nvd-change/1.0"),
|
||||||
SourceName,
|
SourceName,
|
||||||
current.AdvisoryKey,
|
current.AdvisoryKey,
|
||||||
document.Id,
|
document.Id,
|
||||||
@@ -544,6 +544,13 @@ public sealed class NvdConnector : IFeedConnector
|
|||||||
return $"sha256:{hex}";
|
return $"sha256:{hex}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hash.AsSpan()[..16]);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<NvdCursor> GetCursorAsync(CancellationToken cancellationToken)
|
private async Task<NvdCursor> GetCursorAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var record = await _stateRepository.TryGetAsync(SourceName, cancellationToken).ConfigureAwait(false);
|
var record = await _stateRepository.TryGetAsync(SourceName, cancellationToken).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Schemas\nvd-vulnerability.schema.json" />
|
<EmbeddedResource Include="Schemas\nvd-vulnerability.schema.json" />
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ internal sealed record OsvCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastModifiedByEcosystem.Count > 0)
|
if (LastModifiedByEcosystem.Count > 0)
|
||||||
{
|
{
|
||||||
var lastModifiedDoc = new DocumentObject();
|
var lastModifiedDoc = new DocumentObject();
|
||||||
foreach (var (ecosystem, timestamp) in LastModifiedByEcosystem)
|
foreach (var (ecosystem, timestamp) in LastModifiedByEcosystem.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
lastModifiedDoc[ecosystem] = timestamp.HasValue ? DocumentValue.Create(timestamp.Value.UtcDateTime) : DocumentNull.Value;
|
lastModifiedDoc[ecosystem] = timestamp.HasValue ? DocumentValue.Create(timestamp.Value.UtcDateTime) : DocumentNull.Value;
|
||||||
}
|
}
|
||||||
@@ -45,9 +45,9 @@ internal sealed record OsvCursor(
|
|||||||
if (ProcessedIdsByEcosystem.Count > 0)
|
if (ProcessedIdsByEcosystem.Count > 0)
|
||||||
{
|
{
|
||||||
var processedDoc = new DocumentObject();
|
var processedDoc = new DocumentObject();
|
||||||
foreach (var (ecosystem, ids) in ProcessedIdsByEcosystem)
|
foreach (var (ecosystem, ids) in ProcessedIdsByEcosystem.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
processedDoc[ecosystem] = new DocumentArray(ids.Select(id => id));
|
processedDoc[ecosystem] = new DocumentArray(ids.OrderBy(id => id, StringComparer.Ordinal).Select(id => id));
|
||||||
}
|
}
|
||||||
|
|
||||||
document["processed"] = processedDoc;
|
document["processed"] = processedDoc;
|
||||||
@@ -56,7 +56,7 @@ internal sealed record OsvCursor(
|
|||||||
if (ArchiveMetadataByEcosystem.Count > 0)
|
if (ArchiveMetadataByEcosystem.Count > 0)
|
||||||
{
|
{
|
||||||
var metadataDoc = new DocumentObject();
|
var metadataDoc = new DocumentObject();
|
||||||
foreach (var (ecosystem, metadata) in ArchiveMetadataByEcosystem)
|
foreach (var (ecosystem, metadata) in ArchiveMetadataByEcosystem.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
var element = new DocumentObject();
|
var element = new DocumentObject();
|
||||||
if (!string.IsNullOrWhiteSpace(metadata.ETag))
|
if (!string.IsNullOrWhiteSpace(metadata.ETag))
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ public sealed class OsvConnector : IFeedConnector
|
|||||||
var sanitized = JsonSerializer.Serialize(dto, SerializerOptions);
|
var sanitized = JsonSerializer.Serialize(dto, SerializerOptions);
|
||||||
var payload = StellaOps.Concelier.Documents.DocumentObject.Parse(sanitized);
|
var payload = StellaOps.Concelier.Documents.DocumentObject.Parse(sanitized);
|
||||||
var dtoRecord = new DtoRecord(
|
var dtoRecord = new DtoRecord(
|
||||||
Guid.NewGuid(),
|
ComputeDeterministicId(document.Id.ToString(), "osv/1.0"),
|
||||||
document.Id,
|
document.Id,
|
||||||
SourceName,
|
SourceName,
|
||||||
"osv.v1",
|
"osv.v1",
|
||||||
@@ -434,7 +434,7 @@ public sealed class OsvConnector : IFeedConnector
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordId = existing?.Id ?? Guid.NewGuid();
|
var recordId = existing?.Id ?? ComputeDeterministicId(documentUri, "osv-doc/1.0");
|
||||||
_ = await _rawDocumentStorage.UploadAsync(SourceName, documentUri, bytes, "application/json", null, cancellationToken, recordId).ConfigureAwait(false);
|
_ = await _rawDocumentStorage.UploadAsync(SourceName, documentUri, bytes, "application/json", null, cancellationToken, recordId).ConfigureAwait(false);
|
||||||
var metadata = new Dictionary<string, string>(StringComparer.Ordinal)
|
var metadata = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
@@ -613,4 +613,11 @@ public sealed class OsvConnector : IFeedConnector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = System.Text.Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hash.AsSpan()[..16]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ internal sealed record RuBduCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastSuccessfulFetch.HasValue)
|
if (LastSuccessfulFetch.HasValue)
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ public sealed class RuBduConnector : IFeedConnector
|
|||||||
}
|
}
|
||||||
|
|
||||||
var doc = StellaOps.Concelier.Documents.DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
var doc = StellaOps.Concelier.Documents.DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "ru-bdu.v1", doc, _timeProvider.GetUtcNow());
|
var dtoRecord = new DtoRecord(ComputeDeterministicId(document.Id.ToString(), "ru-bdu/1.0"), document.Id, SourceName, "ru-bdu.v1", doc, _timeProvider.GetUtcNow());
|
||||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
||||||
_diagnostics.ParseSuccess(
|
_diagnostics.ParseSuccess(
|
||||||
@@ -411,7 +411,7 @@ public sealed class RuBduConnector : IFeedConnector
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordId = existing?.Id ?? Guid.NewGuid();
|
var recordId = existing?.Id ?? ComputeDeterministicId(documentUri, "ru-bdu-doc/1.0");
|
||||||
_ = await _rawDocumentStorage.UploadAsync(SourceName, documentUri, payload, "application/json", null, cancellationToken, recordId).ConfigureAwait(false);
|
_ = await _rawDocumentStorage.UploadAsync(SourceName, documentUri, payload, "application/json", null, cancellationToken, recordId).ConfigureAwait(false);
|
||||||
|
|
||||||
var metadata = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
var metadata = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||||
@@ -530,4 +530,11 @@ public sealed class RuBduConnector : IFeedConnector
|
|||||||
var completedAt = cursor.LastSuccessfulFetch ?? _timeProvider.GetUtcNow();
|
var completedAt = cursor.LastSuccessfulFetch ?? _timeProvider.GetUtcNow();
|
||||||
return _stateRepository.UpdateCursorAsync(SourceName, document, completedAt, cancellationToken);
|
return _stateRepository.UpdateCursorAsync(SourceName, document, completedAt, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = System.Text.Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hash.AsSpan()[..16]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ internal sealed record RuNkckiCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["knownBulletins"] = new DocumentArray(KnownBulletins),
|
["knownBulletins"] = new DocumentArray(KnownBulletins.OrderBy(id => id, StringComparer.Ordinal)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastListingFetchAt.HasValue)
|
if (LastListingFetchAt.HasValue)
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ public sealed class RuNkckiConnector : IFeedConnector
|
|||||||
}
|
}
|
||||||
|
|
||||||
var doc = StellaOps.Concelier.Documents.DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
var doc = StellaOps.Concelier.Documents.DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "ru-nkcki.v1", doc, _timeProvider.GetUtcNow());
|
var dtoRecord = new DtoRecord(ComputeDeterministicId(document.Id.ToString(), "ru-nkcki/1.0"), document.Id, SourceName, "ru-nkcki.v1", doc, _timeProvider.GetUtcNow());
|
||||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -609,7 +609,7 @@ public sealed class RuNkckiConnector : IFeedConnector
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordId = existing?.Id ?? Guid.NewGuid();
|
var recordId = existing?.Id ?? ComputeDeterministicId(documentUri, "ru-nkcki-doc/1.0");
|
||||||
_ = await _rawDocumentStorage.UploadAsync(SourceName, documentUri, payload, "application/json", null, cancellationToken, recordId).ConfigureAwait(false);
|
_ = await _rawDocumentStorage.UploadAsync(SourceName, documentUri, payload, "application/json", null, cancellationToken, recordId).ConfigureAwait(false);
|
||||||
|
|
||||||
var metadata = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
var metadata = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||||
@@ -725,12 +725,12 @@ public sealed class RuNkckiConnector : IFeedConnector
|
|||||||
return new ListingPageResult(attachments, uniquePagination);
|
return new ListingPageResult(attachments, uniquePagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string DeriveBulletinId(Uri uri)
|
private string DeriveBulletinId(Uri uri)
|
||||||
{
|
{
|
||||||
var fileName = Path.GetFileName(uri.AbsolutePath);
|
var fileName = Path.GetFileName(uri.AbsolutePath);
|
||||||
if (string.IsNullOrWhiteSpace(fileName))
|
if (string.IsNullOrWhiteSpace(fileName))
|
||||||
{
|
{
|
||||||
return Guid.NewGuid().ToString("N");
|
return ComputeDeterministicSlug(uri.AbsoluteUri, "bulletin-id");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
if (fileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -746,7 +746,7 @@ public sealed class RuNkckiConnector : IFeedConnector
|
|||||||
return fileName.Replace('_', '-');
|
return fileName.Replace('_', '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string BuildDocumentUri(RuNkckiVulnerabilityDto dto)
|
private string BuildDocumentUri(RuNkckiVulnerabilityDto dto)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(dto.FstecId))
|
if (!string.IsNullOrWhiteSpace(dto.FstecId))
|
||||||
{
|
{
|
||||||
@@ -761,7 +761,10 @@ public sealed class RuNkckiConnector : IFeedConnector
|
|||||||
return $"https://nvd.nist.gov/vuln/detail/{dto.MitreId}";
|
return $"https://nvd.nist.gov/vuln/detail/{dto.MitreId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"https://cert.gov.ru/materialy/uyazvimosti/{Guid.NewGuid():N}";
|
// Fallback: deterministic slug based on dto content
|
||||||
|
var dtoJson = JsonSerializer.Serialize(dto, SerializerOptions);
|
||||||
|
var slug2 = ComputeDeterministicSlug(dtoJson, "nkcki-doc");
|
||||||
|
return $"https://cert.gov.ru/materialy/uyazvimosti/{slug2}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveCacheDirectory(string? configuredPath)
|
private string ResolveCacheDirectory(string? configuredPath)
|
||||||
@@ -791,7 +794,7 @@ public sealed class RuNkckiConnector : IFeedConnector
|
|||||||
private string GetBulletinCachePath(string bulletinId)
|
private string GetBulletinCachePath(string bulletinId)
|
||||||
{
|
{
|
||||||
var fileStem = string.IsNullOrWhiteSpace(bulletinId)
|
var fileStem = string.IsNullOrWhiteSpace(bulletinId)
|
||||||
? Guid.NewGuid().ToString("N")
|
? ComputeDeterministicSlug("unknown-bulletin", _timeProvider.GetUtcNow().ToString("O"))
|
||||||
: Uri.EscapeDataString(bulletinId);
|
: Uri.EscapeDataString(bulletinId);
|
||||||
return Path.Combine(_cacheDirectory, $"{fileStem}.json.zip");
|
return Path.Combine(_cacheDirectory, $"{fileStem}.json.zip");
|
||||||
}
|
}
|
||||||
@@ -947,4 +950,17 @@ public sealed class RuNkckiConnector : IFeedConnector
|
|||||||
|
|
||||||
return new ListingFetchSummary(attachments, visited);
|
return new ListingFetchSummary(attachments, visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hash.AsSpan()[..16]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ComputeDeterministicSlug(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
return _hash.ComputeHashHex(input, HashAlgorithms.Sha256)[..32];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ internal sealed record StellaOpsMirrorCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(ExportId))
|
if (!string.IsNullOrWhiteSpace(ExportId))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ public sealed class StellaOpsMirrorConnector : IFeedConnector
|
|||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordId = existing?.Id ?? Guid.NewGuid();
|
var recordId = existing?.Id ?? ComputeDeterministicId(absolute, "mirror-doc/1.0");
|
||||||
_ = await _rawDocumentStorage.UploadAsync(Source, absolute, payload, contentType, ExpiresAt: null, cancellationToken, recordId).ConfigureAwait(false);
|
_ = await _rawDocumentStorage.UploadAsync(Source, absolute, payload, contentType, ExpiresAt: null, cancellationToken, recordId).ConfigureAwait(false);
|
||||||
var now = _timeProvider.GetUtcNow();
|
var now = _timeProvider.GetUtcNow();
|
||||||
var sha = ComputeSha256(payload);
|
var sha = ComputeSha256(payload);
|
||||||
@@ -423,7 +423,7 @@ public sealed class StellaOpsMirrorConnector : IFeedConnector
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dtoDoc = DocumentObject.Parse(json);
|
var dtoDoc = DocumentObject.Parse(json);
|
||||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, Source, BundleDtoSchemaVersion, dtoDoc, now);
|
var dtoRecord = new DtoRecord(ComputeDeterministicId(document.Id.ToString(), "mirror/1.0"), document.Id, Source, BundleDtoSchemaVersion, dtoDoc, now);
|
||||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -566,6 +566,13 @@ public sealed class StellaOpsMirrorConnector : IFeedConnector
|
|||||||
pendingMappings.Count);
|
pendingMappings.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hash.AsSpan()[..16]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file static class UriExtensions
|
file static class UriExtensions
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ using StellaOps.Concelier.Storage;
|
|||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage.PsirtFlags;
|
using StellaOps.Concelier.Storage.PsirtFlags;
|
||||||
using StellaOps.Concelier.Models;
|
using StellaOps.Concelier.Models;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Vndr.Adobe;
|
namespace StellaOps.Concelier.Connector.Vndr.Adobe;
|
||||||
@@ -39,6 +40,7 @@ public sealed class AdobeConnector : IFeedConnector
|
|||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly AdobeDiagnostics _diagnostics;
|
private readonly AdobeDiagnostics _diagnostics;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly ILogger<AdobeConnector> _logger;
|
private readonly ILogger<AdobeConnector> _logger;
|
||||||
|
|
||||||
private static readonly JsonSchema Schema = AdobeSchemaProvider.Schema;
|
private static readonly JsonSchema Schema = AdobeSchemaProvider.Schema;
|
||||||
@@ -61,6 +63,7 @@ public sealed class AdobeConnector : IFeedConnector
|
|||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
AdobeDiagnostics diagnostics,
|
AdobeDiagnostics diagnostics,
|
||||||
|
ICryptoHash cryptoHash,
|
||||||
ILogger<AdobeConnector> logger)
|
ILogger<AdobeConnector> logger)
|
||||||
{
|
{
|
||||||
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
|
_fetchService = fetchService ?? throw new ArgumentNullException(nameof(fetchService));
|
||||||
@@ -76,6 +79,7 @@ public sealed class AdobeConnector : IFeedConnector
|
|||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
|
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
|
||||||
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
||||||
|
_hash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,7 +501,7 @@ public sealed class AdobeConnector : IFeedConnector
|
|||||||
|
|
||||||
var payload = StellaOps.Concelier.Documents.DocumentObject.Parse(json);
|
var payload = StellaOps.Concelier.Documents.DocumentObject.Parse(json);
|
||||||
var dtoRecord = new DtoRecord(
|
var dtoRecord = new DtoRecord(
|
||||||
Guid.NewGuid(),
|
ComputeDeterministicId(document.Id.ToString(), "adobe/1.0"),
|
||||||
document.Id,
|
document.Id,
|
||||||
SourceName,
|
SourceName,
|
||||||
"adobe.bulletin.v1",
|
"adobe.bulletin.v1",
|
||||||
@@ -754,4 +758,11 @@ public sealed class AdobeConnector : IFeedConnector
|
|||||||
|
|
||||||
return rules.Count == 0 ? Array.Empty<NormalizedVersionRule>() : rules.ToArray();
|
return rules.Count == 0 ? Array.Empty<NormalizedVersionRule>() : rules.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Guid ComputeDeterministicId(string source, string identifier)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{source}:{identifier}");
|
||||||
|
var hash = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hash.AsSpan()[..16]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ internal sealed record AdobeCursor(
|
|||||||
document["lastPublished"] = LastPublished.Value.UtcDateTime;
|
document["lastPublished"] = LastPublished.Value.UtcDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
document["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString()));
|
document["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString()));
|
||||||
document["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString()));
|
document["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString()));
|
||||||
|
|
||||||
if (FetchCache is { Count: > 0 })
|
if (FetchCache is { Count: > 0 })
|
||||||
{
|
{
|
||||||
var cacheDocument = new DocumentObject();
|
var cacheDocument = new DocumentObject();
|
||||||
foreach (var (key, entry) in FetchCache)
|
foreach (var (key, entry) in FetchCache.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
cacheDocument[key] = entry.ToDocument();
|
cacheDocument[key] = entry.ToDocument();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -17,7 +18,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||||
|
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -17,6 +18,7 @@ using StellaOps.Concelier.Storage.Advisories;
|
|||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage.PsirtFlags;
|
using StellaOps.Concelier.Storage.PsirtFlags;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Vndr.Apple;
|
namespace StellaOps.Concelier.Connector.Vndr.Apple;
|
||||||
@@ -36,6 +38,7 @@ public sealed class AppleConnector : IFeedConnector
|
|||||||
private readonly IAdvisoryStore _advisoryStore;
|
private readonly IAdvisoryStore _advisoryStore;
|
||||||
private readonly IPsirtFlagStore _psirtFlagStore;
|
private readonly IPsirtFlagStore _psirtFlagStore;
|
||||||
private readonly ISourceStateRepository _stateRepository;
|
private readonly ISourceStateRepository _stateRepository;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly AppleOptions _options;
|
private readonly AppleOptions _options;
|
||||||
private readonly AppleDiagnostics _diagnostics;
|
private readonly AppleDiagnostics _diagnostics;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
@@ -49,6 +52,7 @@ public sealed class AppleConnector : IFeedConnector
|
|||||||
IAdvisoryStore advisoryStore,
|
IAdvisoryStore advisoryStore,
|
||||||
IPsirtFlagStore psirtFlagStore,
|
IPsirtFlagStore psirtFlagStore,
|
||||||
ISourceStateRepository stateRepository,
|
ISourceStateRepository stateRepository,
|
||||||
|
ICryptoHash hash,
|
||||||
AppleDiagnostics diagnostics,
|
AppleDiagnostics diagnostics,
|
||||||
IOptions<AppleOptions> options,
|
IOptions<AppleOptions> options,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
@@ -61,6 +65,7 @@ public sealed class AppleConnector : IFeedConnector
|
|||||||
_advisoryStore = advisoryStore ?? throw new ArgumentNullException(nameof(advisoryStore));
|
_advisoryStore = advisoryStore ?? throw new ArgumentNullException(nameof(advisoryStore));
|
||||||
_psirtFlagStore = psirtFlagStore ?? throw new ArgumentNullException(nameof(psirtFlagStore));
|
_psirtFlagStore = psirtFlagStore ?? throw new ArgumentNullException(nameof(psirtFlagStore));
|
||||||
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
||||||
|
_hash = hash ?? throw new ArgumentNullException(nameof(hash));
|
||||||
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
||||||
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_options.Validate();
|
_options.Validate();
|
||||||
@@ -70,6 +75,16 @@ public sealed class AppleConnector : IFeedConnector
|
|||||||
|
|
||||||
public string SourceName => VndrAppleConnectorPlugin.SourceName;
|
public string SourceName => VndrAppleConnectorPlugin.SourceName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a deterministic GUID from the source namespace and identifier using SHA-256.
|
||||||
|
/// </summary>
|
||||||
|
private Guid ComputeDeterministicId(string identifier, string sourceNamespace)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{sourceNamespace}:{identifier}");
|
||||||
|
var hashBytes = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hashBytes[..16]);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(services);
|
ArgumentNullException.ThrowIfNull(services);
|
||||||
@@ -259,7 +274,7 @@ public sealed class AppleConnector : IFeedConnector
|
|||||||
|
|
||||||
var existingDto = await _dtoStore.FindByDocumentIdAsync(document.Id, cancellationToken).ConfigureAwait(false);
|
var existingDto = await _dtoStore.FindByDocumentIdAsync(document.Id, cancellationToken).ConfigureAwait(false);
|
||||||
var dtoRecord = existingDto is null
|
var dtoRecord = existingDto is null
|
||||||
? new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "apple.security.update.v1", payload, validatedAt)
|
? new DtoRecord(ComputeDeterministicId(document.Id.ToString(), "apple/1.0"), document.Id, SourceName, "apple.security.update.v1", payload, validatedAt)
|
||||||
: existingDto with
|
: existingDto with
|
||||||
{
|
{
|
||||||
Payload = payload,
|
Payload = payload,
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ internal sealed record AppleCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastPosted.HasValue)
|
if (LastPosted.HasValue)
|
||||||
@@ -31,7 +31,7 @@ internal sealed record AppleCursor(
|
|||||||
|
|
||||||
if (ProcessedIds.Count > 0)
|
if (ProcessedIds.Count > 0)
|
||||||
{
|
{
|
||||||
document["processedIds"] = new DocumentArray(ProcessedIds);
|
document["processedIds"] = new DocumentArray(ProcessedIds.OrderBy(id => id, StringComparer.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
return document;
|
return document;
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||||
|
|
||||||
<ProjectReference Include="../StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
|
<ProjectReference Include="../StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using StellaOps.Concelier.Storage.Advisories;
|
|||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage.PsirtFlags;
|
using StellaOps.Concelier.Storage.PsirtFlags;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
using Json.Schema;
|
using Json.Schema;
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ public sealed class ChromiumConnector : IFeedConnector
|
|||||||
private readonly IPsirtFlagStore _psirtFlagStore;
|
private readonly IPsirtFlagStore _psirtFlagStore;
|
||||||
private readonly ISourceStateRepository _stateRepository;
|
private readonly ISourceStateRepository _stateRepository;
|
||||||
private readonly IJsonSchemaValidator _schemaValidator;
|
private readonly IJsonSchemaValidator _schemaValidator;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly ChromiumOptions _options;
|
private readonly ChromiumOptions _options;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly ChromiumDiagnostics _diagnostics;
|
private readonly ChromiumDiagnostics _diagnostics;
|
||||||
@@ -55,6 +57,7 @@ public sealed class ChromiumConnector : IFeedConnector
|
|||||||
IPsirtFlagStore psirtFlagStore,
|
IPsirtFlagStore psirtFlagStore,
|
||||||
ISourceStateRepository stateRepository,
|
ISourceStateRepository stateRepository,
|
||||||
IJsonSchemaValidator schemaValidator,
|
IJsonSchemaValidator schemaValidator,
|
||||||
|
ICryptoHash hash,
|
||||||
IOptions<ChromiumOptions> options,
|
IOptions<ChromiumOptions> options,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
ChromiumDiagnostics diagnostics,
|
ChromiumDiagnostics diagnostics,
|
||||||
@@ -69,6 +72,7 @@ public sealed class ChromiumConnector : IFeedConnector
|
|||||||
_psirtFlagStore = psirtFlagStore ?? throw new ArgumentNullException(nameof(psirtFlagStore));
|
_psirtFlagStore = psirtFlagStore ?? throw new ArgumentNullException(nameof(psirtFlagStore));
|
||||||
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
||||||
_schemaValidator = schemaValidator ?? throw new ArgumentNullException(nameof(schemaValidator));
|
_schemaValidator = schemaValidator ?? throw new ArgumentNullException(nameof(schemaValidator));
|
||||||
|
_hash = hash ?? throw new ArgumentNullException(nameof(hash));
|
||||||
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
|
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_options.Validate();
|
_options.Validate();
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
@@ -78,6 +82,16 @@ public sealed class ChromiumConnector : IFeedConnector
|
|||||||
|
|
||||||
public string SourceName => VndrChromiumConnectorPlugin.SourceName;
|
public string SourceName => VndrChromiumConnectorPlugin.SourceName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a deterministic GUID from the source namespace and identifier using SHA-256.
|
||||||
|
/// </summary>
|
||||||
|
private Guid ComputeDeterministicId(string identifier, string sourceNamespace)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{sourceNamespace}:{identifier}");
|
||||||
|
var hashBytes = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hashBytes[..16]);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var cursor = await GetCursorAsync(cancellationToken).ConfigureAwait(false);
|
var cursor = await GetCursorAsync(cancellationToken).ConfigureAwait(false);
|
||||||
@@ -261,7 +275,7 @@ public sealed class ChromiumConnector : IFeedConnector
|
|||||||
var validatedAt = _timeProvider.GetUtcNow();
|
var validatedAt = _timeProvider.GetUtcNow();
|
||||||
|
|
||||||
var dtoRecord = existingDto is null
|
var dtoRecord = existingDto is null
|
||||||
? new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "chromium.post.v1", payload, validatedAt)
|
? new DtoRecord(ComputeDeterministicId(document.Id.ToString(), "chromium/1.0"), document.Id, SourceName, "chromium.post.v1", payload, validatedAt)
|
||||||
: existingDto with
|
: existingDto with
|
||||||
{
|
{
|
||||||
Payload = payload,
|
Payload = payload,
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ internal sealed record ChromiumCursor(
|
|||||||
document["lastPublished"] = LastPublished.Value.UtcDateTime;
|
document["lastPublished"] = LastPublished.Value.UtcDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
document["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString()));
|
document["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString()));
|
||||||
document["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString()));
|
document["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString()));
|
||||||
|
|
||||||
if (FetchCache.Count > 0)
|
if (FetchCache.Count > 0)
|
||||||
{
|
{
|
||||||
var cacheDocument = new DocumentObject();
|
var cacheDocument = new DocumentObject();
|
||||||
foreach (var (key, entry) in FetchCache)
|
foreach (var (key, entry) in FetchCache.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
cacheDocument[key] = entry.ToDocument();
|
cacheDocument[key] = entry.ToDocument();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||||
|
|
||||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -14,6 +14,7 @@ using StellaOps.Concelier.Storage;
|
|||||||
using StellaOps.Concelier.Storage.Advisories;
|
using StellaOps.Concelier.Storage.Advisories;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Vndr.Cisco;
|
namespace StellaOps.Concelier.Connector.Vndr.Cisco;
|
||||||
@@ -45,6 +46,7 @@ public sealed class CiscoConnector : IFeedConnector
|
|||||||
private readonly IAdvisoryStore _advisoryStore;
|
private readonly IAdvisoryStore _advisoryStore;
|
||||||
private readonly ISourceStateRepository _stateRepository;
|
private readonly ISourceStateRepository _stateRepository;
|
||||||
private readonly CiscoDtoFactory _dtoFactory;
|
private readonly CiscoDtoFactory _dtoFactory;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly CiscoDiagnostics _diagnostics;
|
private readonly CiscoDiagnostics _diagnostics;
|
||||||
private readonly IOptions<CiscoOptions> _options;
|
private readonly IOptions<CiscoOptions> _options;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
@@ -58,6 +60,7 @@ public sealed class CiscoConnector : IFeedConnector
|
|||||||
IAdvisoryStore advisoryStore,
|
IAdvisoryStore advisoryStore,
|
||||||
ISourceStateRepository stateRepository,
|
ISourceStateRepository stateRepository,
|
||||||
CiscoDtoFactory dtoFactory,
|
CiscoDtoFactory dtoFactory,
|
||||||
|
ICryptoHash hash,
|
||||||
CiscoDiagnostics diagnostics,
|
CiscoDiagnostics diagnostics,
|
||||||
IOptions<CiscoOptions> options,
|
IOptions<CiscoOptions> options,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
@@ -70,6 +73,7 @@ public sealed class CiscoConnector : IFeedConnector
|
|||||||
_advisoryStore = advisoryStore ?? throw new ArgumentNullException(nameof(advisoryStore));
|
_advisoryStore = advisoryStore ?? throw new ArgumentNullException(nameof(advisoryStore));
|
||||||
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
||||||
_dtoFactory = dtoFactory ?? throw new ArgumentNullException(nameof(dtoFactory));
|
_dtoFactory = dtoFactory ?? throw new ArgumentNullException(nameof(dtoFactory));
|
||||||
|
_hash = hash ?? throw new ArgumentNullException(nameof(hash));
|
||||||
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
_diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
@@ -78,6 +82,25 @@ public sealed class CiscoConnector : IFeedConnector
|
|||||||
|
|
||||||
public string SourceName => VndrCiscoConnectorPlugin.SourceName;
|
public string SourceName => VndrCiscoConnectorPlugin.SourceName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a deterministic GUID from the source namespace and identifier using SHA-256.
|
||||||
|
/// </summary>
|
||||||
|
private Guid ComputeDeterministicId(string identifier, string sourceNamespace)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{sourceNamespace}:{identifier}");
|
||||||
|
var hashBytes = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hashBytes[..16]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a SHA-256 hash of the payload and returns it as a lowercase hex string.
|
||||||
|
/// </summary>
|
||||||
|
private string ComputeSha256(byte[] payload)
|
||||||
|
{
|
||||||
|
var hashBytes = _hash.ComputeHash(payload, HashAlgorithms.Sha256);
|
||||||
|
return Convert.ToHexString(hashBytes).ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(services);
|
ArgumentNullException.ThrowIfNull(services);
|
||||||
@@ -137,7 +160,7 @@ public sealed class CiscoConnector : IFeedConnector
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordId = existing?.Id ?? Guid.NewGuid();
|
var recordId = existing?.Id ?? ComputeDeterministicId(documentUri, "cisco-doc/1.0");
|
||||||
_ = await _rawDocumentStorage.UploadAsync(
|
_ = await _rawDocumentStorage.UploadAsync(
|
||||||
SourceName,
|
SourceName,
|
||||||
documentUri,
|
documentUri,
|
||||||
@@ -326,7 +349,7 @@ public sealed class CiscoConnector : IFeedConnector
|
|||||||
{
|
{
|
||||||
var dtoJson = JsonSerializer.Serialize(dto, DtoSerializerOptions);
|
var dtoJson = JsonSerializer.Serialize(dto, DtoSerializerOptions);
|
||||||
var dtoDoc = DocumentObject.Parse(dtoJson);
|
var dtoDoc = DocumentObject.Parse(dtoJson);
|
||||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, SourceName, DtoSchemaVersion, dtoDoc, _timeProvider.GetUtcNow());
|
var dtoRecord = new DtoRecord(ComputeDeterministicId(document.Id.ToString(), "cisco/1.0"), document.Id, SourceName, DtoSchemaVersion, dtoDoc, _timeProvider.GetUtcNow());
|
||||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
||||||
pendingDocuments.Remove(documentId);
|
pendingDocuments.Remove(documentId);
|
||||||
@@ -463,13 +486,6 @@ public sealed class CiscoConnector : IFeedConnector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ComputeSha256(byte[] payload)
|
|
||||||
{
|
|
||||||
Span<byte> hash = stackalloc byte[32];
|
|
||||||
SHA256.HashData(payload, hash);
|
|
||||||
return Convert.ToHexString(hash).ToLowerInvariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ShouldProcess(CiscoAdvisoryItem advisory, DateTimeOffset? checkpoint, string? checkpointId)
|
private static bool ShouldProcess(CiscoAdvisoryItem advisory, DateTimeOffset? checkpoint, string? checkpointId)
|
||||||
{
|
{
|
||||||
if (checkpoint is null || advisory.LastUpdated is null)
|
if (checkpoint is null || advisory.LastUpdated is null)
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ internal sealed record CiscoCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastModified.HasValue)
|
if (LastModified.HasValue)
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||||
<ProjectReference Include="../StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
|
<ProjectReference Include="../StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
|
||||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ internal sealed record MsrcCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastModifiedCursor.HasValue)
|
if (LastModifiedCursor.HasValue)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Text;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -18,6 +18,7 @@ using StellaOps.Concelier.Storage;
|
|||||||
using StellaOps.Concelier.Storage.Advisories;
|
using StellaOps.Concelier.Storage.Advisories;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
using StellaOps.Concelier.Storage;
|
using StellaOps.Concelier.Storage;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Vndr.Msrc;
|
namespace StellaOps.Concelier.Connector.Vndr.Msrc;
|
||||||
@@ -39,6 +40,7 @@ public sealed class MsrcConnector : IFeedConnector
|
|||||||
private readonly IAdvisoryStore _advisoryStore;
|
private readonly IAdvisoryStore _advisoryStore;
|
||||||
private readonly ISourceStateRepository _stateRepository;
|
private readonly ISourceStateRepository _stateRepository;
|
||||||
private readonly MsrcOptions _options;
|
private readonly MsrcOptions _options;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly ILogger<MsrcConnector> _logger;
|
private readonly ILogger<MsrcConnector> _logger;
|
||||||
private readonly MsrcDiagnostics _diagnostics;
|
private readonly MsrcDiagnostics _diagnostics;
|
||||||
@@ -52,6 +54,7 @@ public sealed class MsrcConnector : IFeedConnector
|
|||||||
IDtoStore dtoStore,
|
IDtoStore dtoStore,
|
||||||
IAdvisoryStore advisoryStore,
|
IAdvisoryStore advisoryStore,
|
||||||
ISourceStateRepository stateRepository,
|
ISourceStateRepository stateRepository,
|
||||||
|
ICryptoHash hash,
|
||||||
IOptions<MsrcOptions> options,
|
IOptions<MsrcOptions> options,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
MsrcDiagnostics diagnostics,
|
MsrcDiagnostics diagnostics,
|
||||||
@@ -65,6 +68,7 @@ public sealed class MsrcConnector : IFeedConnector
|
|||||||
_dtoStore = dtoStore ?? throw new ArgumentNullException(nameof(dtoStore));
|
_dtoStore = dtoStore ?? throw new ArgumentNullException(nameof(dtoStore));
|
||||||
_advisoryStore = advisoryStore ?? throw new ArgumentNullException(nameof(advisoryStore));
|
_advisoryStore = advisoryStore ?? throw new ArgumentNullException(nameof(advisoryStore));
|
||||||
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
||||||
|
_hash = hash ?? throw new ArgumentNullException(nameof(hash));
|
||||||
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_options.Validate();
|
_options.Validate();
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
@@ -74,6 +78,25 @@ public sealed class MsrcConnector : IFeedConnector
|
|||||||
|
|
||||||
public string SourceName => MsrcConnectorPlugin.SourceName;
|
public string SourceName => MsrcConnectorPlugin.SourceName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a deterministic GUID from the source namespace and identifier using SHA-256.
|
||||||
|
/// </summary>
|
||||||
|
private Guid ComputeDeterministicId(string identifier, string sourceNamespace)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{sourceNamespace}:{identifier}");
|
||||||
|
var hashBytes = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hashBytes[..16]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a SHA-256 hash of the payload and returns it as a lowercase hex string.
|
||||||
|
/// </summary>
|
||||||
|
private string ComputeSha256(byte[] payload)
|
||||||
|
{
|
||||||
|
var hashBytes = _hash.ComputeHash(payload, HashAlgorithms.Sha256);
|
||||||
|
return Convert.ToHexString(hashBytes).ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(services);
|
ArgumentNullException.ThrowIfNull(services);
|
||||||
@@ -139,9 +162,9 @@ public sealed class MsrcConnector : IFeedConnector
|
|||||||
}
|
}
|
||||||
|
|
||||||
var bytes = await _apiClient.FetchDetailAsync(vulnerabilityId, cancellationToken).ConfigureAwait(false);
|
var bytes = await _apiClient.FetchDetailAsync(vulnerabilityId, cancellationToken).ConfigureAwait(false);
|
||||||
var sha = Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant();
|
var sha = ComputeSha256(bytes);
|
||||||
|
|
||||||
var documentId = existing?.Id ?? Guid.NewGuid();
|
var documentId = existing?.Id ?? ComputeDeterministicId(detailUri, "msrc-doc/1.0");
|
||||||
|
|
||||||
_ = await _rawDocumentStorage.UploadAsync(
|
_ = await _rawDocumentStorage.UploadAsync(
|
||||||
SourceName,
|
SourceName,
|
||||||
@@ -294,7 +317,7 @@ public sealed class MsrcConnector : IFeedConnector
|
|||||||
|
|
||||||
var dto = _detailParser.Parse(detail);
|
var dto = _detailParser.Parse(detail);
|
||||||
var doc = DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
var doc = DocumentObject.Parse(JsonSerializer.Serialize(dto, SerializerOptions));
|
||||||
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "msrc.detail.v1", doc, now);
|
var dtoRecord = new DtoRecord(ComputeDeterministicId(document.Id.ToString(), "msrc/1.0"), document.Id, SourceName, "msrc.detail.v1", doc, now);
|
||||||
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
await _dtoStore.UpsertAsync(dtoRecord, cancellationToken).ConfigureAwait(false);
|
||||||
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
await _documentStore.UpdateStatusAsync(document.Id, DocumentStatuses.PendingMap, cancellationToken).ConfigureAwait(false);
|
||||||
remainingDocuments.Remove(documentId);
|
remainingDocuments.Remove(documentId);
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||||
|
|
||||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ internal sealed record OracleCursor(
|
|||||||
{
|
{
|
||||||
var document = new DocumentObject
|
var document = new DocumentObject
|
||||||
{
|
{
|
||||||
["pendingDocuments"] = new DocumentArray(PendingDocuments.Select(id => id.ToString())),
|
["pendingDocuments"] = new DocumentArray(PendingDocuments.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
["pendingMappings"] = new DocumentArray(PendingMappings.Select(id => id.ToString())),
|
["pendingMappings"] = new DocumentArray(PendingMappings.OrderBy(id => id).Select(id => id.ToString())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (LastProcessed.HasValue)
|
if (LastProcessed.HasValue)
|
||||||
@@ -35,7 +35,7 @@ internal sealed record OracleCursor(
|
|||||||
if (FetchCache.Count > 0)
|
if (FetchCache.Count > 0)
|
||||||
{
|
{
|
||||||
var cacheDocument = new DocumentObject();
|
var cacheDocument = new DocumentObject();
|
||||||
foreach (var (key, entry) in FetchCache)
|
foreach (var (key, entry) in FetchCache.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
cacheDocument[key] = entry.ToDocumentObject();
|
cacheDocument[key] = entry.ToDocumentObject();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -15,6 +16,7 @@ using StellaOps.Concelier.Storage;
|
|||||||
using StellaOps.Concelier.Storage.Advisories;
|
using StellaOps.Concelier.Storage.Advisories;
|
||||||
using StellaOps.Concelier.Storage.Contracts;
|
using StellaOps.Concelier.Storage.Contracts;
|
||||||
using StellaOps.Concelier.Storage.PsirtFlags;
|
using StellaOps.Concelier.Storage.PsirtFlags;
|
||||||
|
using StellaOps.Cryptography;
|
||||||
using StellaOps.Plugin;
|
using StellaOps.Plugin;
|
||||||
|
|
||||||
namespace StellaOps.Concelier.Connector.Vndr.Oracle;
|
namespace StellaOps.Concelier.Connector.Vndr.Oracle;
|
||||||
@@ -35,6 +37,7 @@ public sealed class OracleConnector : IFeedConnector
|
|||||||
private readonly IPsirtFlagStore _psirtFlagStore;
|
private readonly IPsirtFlagStore _psirtFlagStore;
|
||||||
private readonly ISourceStateRepository _stateRepository;
|
private readonly ISourceStateRepository _stateRepository;
|
||||||
private readonly OracleCalendarFetcher _calendarFetcher;
|
private readonly OracleCalendarFetcher _calendarFetcher;
|
||||||
|
private readonly ICryptoHash _hash;
|
||||||
private readonly OracleOptions _options;
|
private readonly OracleOptions _options;
|
||||||
private readonly TimeProvider _timeProvider;
|
private readonly TimeProvider _timeProvider;
|
||||||
private readonly ILogger<OracleConnector> _logger;
|
private readonly ILogger<OracleConnector> _logger;
|
||||||
@@ -48,6 +51,7 @@ public sealed class OracleConnector : IFeedConnector
|
|||||||
IPsirtFlagStore psirtFlagStore,
|
IPsirtFlagStore psirtFlagStore,
|
||||||
ISourceStateRepository stateRepository,
|
ISourceStateRepository stateRepository,
|
||||||
OracleCalendarFetcher calendarFetcher,
|
OracleCalendarFetcher calendarFetcher,
|
||||||
|
ICryptoHash hash,
|
||||||
IOptions<OracleOptions> options,
|
IOptions<OracleOptions> options,
|
||||||
TimeProvider? timeProvider,
|
TimeProvider? timeProvider,
|
||||||
ILogger<OracleConnector> logger)
|
ILogger<OracleConnector> logger)
|
||||||
@@ -60,6 +64,7 @@ public sealed class OracleConnector : IFeedConnector
|
|||||||
_psirtFlagStore = psirtFlagStore ?? throw new ArgumentNullException(nameof(psirtFlagStore));
|
_psirtFlagStore = psirtFlagStore ?? throw new ArgumentNullException(nameof(psirtFlagStore));
|
||||||
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
_stateRepository = stateRepository ?? throw new ArgumentNullException(nameof(stateRepository));
|
||||||
_calendarFetcher = calendarFetcher ?? throw new ArgumentNullException(nameof(calendarFetcher));
|
_calendarFetcher = calendarFetcher ?? throw new ArgumentNullException(nameof(calendarFetcher));
|
||||||
|
_hash = hash ?? throw new ArgumentNullException(nameof(hash));
|
||||||
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
_options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_options.Validate();
|
_options.Validate();
|
||||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||||
@@ -68,6 +73,16 @@ public sealed class OracleConnector : IFeedConnector
|
|||||||
|
|
||||||
public string SourceName => VndrOracleConnectorPlugin.SourceName;
|
public string SourceName => VndrOracleConnectorPlugin.SourceName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a deterministic GUID from the source namespace and identifier using SHA-256.
|
||||||
|
/// </summary>
|
||||||
|
private Guid ComputeDeterministicId(string identifier, string sourceNamespace)
|
||||||
|
{
|
||||||
|
var input = Encoding.UTF8.GetBytes($"{sourceNamespace}:{identifier}");
|
||||||
|
var hashBytes = _hash.ComputeHash(input, HashAlgorithms.Sha256);
|
||||||
|
return new Guid(hashBytes[..16]);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
public async Task FetchAsync(IServiceProvider services, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var cursor = await GetCursorAsync(cancellationToken).ConfigureAwait(false);
|
var cursor = await GetCursorAsync(cancellationToken).ConfigureAwait(false);
|
||||||
@@ -227,7 +242,7 @@ public sealed class OracleConnector : IFeedConnector
|
|||||||
|
|
||||||
var existingDto = await _dtoStore.FindByDocumentIdAsync(document.Id, cancellationToken).ConfigureAwait(false);
|
var existingDto = await _dtoStore.FindByDocumentIdAsync(document.Id, cancellationToken).ConfigureAwait(false);
|
||||||
var dtoRecord = existingDto is null
|
var dtoRecord = existingDto is null
|
||||||
? new DtoRecord(Guid.NewGuid(), document.Id, SourceName, "oracle.advisory.v1", payload, validatedAt)
|
? new DtoRecord(ComputeDeterministicId(document.Id.ToString(), "oracle/1.0"), document.Id, SourceName, "oracle.advisory.v1", payload, validatedAt)
|
||||||
: existingDto with
|
: existingDto with
|
||||||
{
|
{
|
||||||
Payload = payload,
|
Payload = payload,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user