up
This commit is contained in:
136
src/Unknowns/AGENTS.md
Normal file
136
src/Unknowns/AGENTS.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# Unknowns Module - Agent Guidelines
|
||||
|
||||
## Module Overview
|
||||
|
||||
The Unknowns module tracks ambiguities discovered during vulnerability scanning that cannot be definitively resolved. It uses **bitemporal semantics** to enable point-in-time compliance queries and full audit trails.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Bitemporal Data Model
|
||||
|
||||
The module uses two time dimensions:
|
||||
- **Valid time** (`valid_from`, `valid_to`): When the unknown was relevant in the real world
|
||||
- **System time** (`sys_from`, `sys_to`): When the system recorded/knew about the unknown
|
||||
|
||||
This enables queries like:
|
||||
- "What unknowns existed on January 1st?" (valid time query)
|
||||
- "What did we know about unknowns on January 1st, as of January 15th?" (bitemporal query)
|
||||
|
||||
### Unknown Types
|
||||
|
||||
| Kind | Description |
|
||||
|------|-------------|
|
||||
| `MissingSbom` | SBOM is missing for a component |
|
||||
| `AmbiguousPackage` | Package reference is ambiguous |
|
||||
| `MissingFeed` | No vulnerability feed covers this component |
|
||||
| `UnresolvedEdge` | Dependency edge cannot be resolved |
|
||||
| `NoVersionInfo` | No version information available |
|
||||
| `UnknownEcosystem` | Ecosystem is not recognized |
|
||||
| `PartialMatch` | Only partial match found |
|
||||
| `VersionRangeUnbounded` | Version range is unbounded |
|
||||
| `UnsupportedFormat` | Format is not supported |
|
||||
| `TransitiveGap` | Gap in transitive dependency chain |
|
||||
|
||||
### Resolution Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `FeedUpdated` | Vulnerability feed was updated to cover the component |
|
||||
| `SbomProvided` | SBOM was provided for the component |
|
||||
| `ManualMapping` | Manual mapping was created |
|
||||
| `Superseded` | Superseded by another unknown or finding |
|
||||
| `FalsePositive` | Determined to be a false positive |
|
||||
| `WontFix` | Acknowledged but not going to be fixed |
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
src/Unknowns/
|
||||
├── __Libraries/
|
||||
│ ├── StellaOps.Unknowns.Core/
|
||||
│ │ ├── Models/
|
||||
│ │ │ └── Unknown.cs # Domain model and enums
|
||||
│ │ └── Repositories/
|
||||
│ │ └── IUnknownRepository.cs # Repository interface
|
||||
│ └── StellaOps.Unknowns.Storage.Postgres/
|
||||
│ ├── Migrations/
|
||||
│ │ └── 001_initial_schema.sql # Bitemporal schema
|
||||
│ └── Repositories/
|
||||
│ └── PostgresUnknownRepository.cs
|
||||
└── __Tests/
|
||||
└── StellaOps.Unknowns.Storage.Postgres.Tests/
|
||||
└── PostgresUnknownRepositoryTests.cs
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
### Key Tables
|
||||
|
||||
- `unknowns.unknown` - Main bitemporal table with RLS
|
||||
- Views: `unknowns.current`, `unknowns.resolved`
|
||||
- Functions: `unknowns.as_of()`, `unknowns.count_by_kind()`, `unknowns.count_by_severity()`
|
||||
|
||||
### Row-Level Security
|
||||
|
||||
The module uses RLS for tenant isolation:
|
||||
```sql
|
||||
SELECT set_config('app.tenant_id', 'my-tenant', false);
|
||||
SELECT * FROM unknowns.unknown; -- Only returns rows for my-tenant
|
||||
```
|
||||
|
||||
## Coding Guidelines
|
||||
|
||||
### Creating Unknowns
|
||||
|
||||
```csharp
|
||||
var unknown = await repository.CreateAsync(
|
||||
tenantId: "my-tenant",
|
||||
subjectType: UnknownSubjectType.Package,
|
||||
subjectRef: "pkg:npm/lodash@4.17.21",
|
||||
kind: UnknownKind.MissingFeed,
|
||||
severity: UnknownSeverity.Medium,
|
||||
context: """{"ecosystem": "npm"}""",
|
||||
sourceScanId: scanId,
|
||||
sourceGraphId: null,
|
||||
sourceSbomDigest: null,
|
||||
createdBy: "scanner",
|
||||
cancellationToken);
|
||||
```
|
||||
|
||||
### Resolving Unknowns
|
||||
|
||||
```csharp
|
||||
var resolved = await repository.ResolveAsync(
|
||||
tenantId,
|
||||
unknownId,
|
||||
ResolutionType.FeedUpdated,
|
||||
resolutionRef: "nvd-feed-2025-01",
|
||||
resolutionNotes: "NVD now covers this package",
|
||||
resolvedBy: "feed-sync",
|
||||
cancellationToken);
|
||||
```
|
||||
|
||||
### Point-in-Time Queries
|
||||
|
||||
```csharp
|
||||
// What unknowns existed on a specific date?
|
||||
var historicalUnknowns = await repository.AsOfAsync(
|
||||
tenantId,
|
||||
validAt: new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
||||
cancellationToken: ct);
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Run tests with:
|
||||
```bash
|
||||
dotnet test src/Unknowns/__Tests/StellaOps.Unknowns.Storage.Postgres.Tests
|
||||
```
|
||||
|
||||
Tests use Testcontainers for PostgreSQL integration testing.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- `docs/operations/postgresql-patterns-runbook.md` - Operational guide
|
||||
- `docs/implplan/SPRINT_3420_0001_0001_bitemporal_unknowns_schema.md` - Sprint spec
|
||||
- `deploy/postgres-validation/001_validate_rls.sql` - RLS validation
|
||||
Reference in New Issue
Block a user