save progress

This commit is contained in:
StellaOps Bot
2026-01-04 19:08:47 +02:00
parent f7d27c6fda
commit 75611a505f
97 changed files with 4531 additions and 293 deletions

View File

@@ -19,6 +19,7 @@ completely isolated network:
| **Delta patches** | Daily diff bundles keep size \<350MB |
| **Scanner plug-ins** | OS analyzers plus the Node.js, Go, .NET, Python, Ruby, Rust, and PHP language analyzers packaged under `plugins/scanner/analyzers/**` with manifests so Workers load deterministically offline. |
| **Debug store** | `.debug` artefacts laid out under `debug/.build-id/<aa>/<rest>.debug` with `debug/debug-manifest.json` mapping build-ids to originating images for symbol retrieval. |
| **Secret Detection Rules** | DSSE-signed rule bundles under `rules/secrets/<version>/` with manifest, JSONL rules, and signature envelope for air-gapped secret leak detection. |
| **Telemetry collector bundle** | `telemetry/telemetry-offline-bundle.tar.gz` plus `.sha256`, containing OTLP collector config, Helm/Compose overlays, and operator instructions. |
| **CLI + Task Packs** | `cli/` binaries from `release/cli`, Task Runner bootstrap (`bootstrap/task-runner/task-runner.yaml.sample`), and task-pack docs under `docs/task-packs/**` + `docs/modules/taskrunner/**`. |
| **Orchestrator/Export/Notifier kits** | Orchestrator service, worker SDK, Postgres snapshot, dashboards (`orchestrator/**`), Export Center bundles (`export-center/**`), Notifier offline packs (`notifier/**`). |
@@ -41,6 +42,68 @@ completely isolated network:
The PHP analyzer parses `composer.lock` for Composer dependencies and supports optional runtime evidence via the `stella-trace.php` shim; set `STELLA_PHP_OPCACHE=1` to enable opcache statistics collection.
**Secret Detection Rules:**
The Offline Kit includes DSSE-signed rule bundles for secret leak detection, enabling fully offline scanning for exposed credentials, API keys, and other sensitive data.
**Bundle Structure:**
```
rules/secrets/<version>/
secrets.ruleset.manifest.json # Bundle metadata (version, rule count, signer)
secrets.ruleset.rules.jsonl # Rule definitions (one JSON per line)
secrets.ruleset.dsse.json # DSSE signature envelope
SHA256SUMS # File checksums
```
**Manifest Format:**
```json
{
"bundleId": "secrets.ruleset",
"bundleType": "secrets",
"version": "2026.01",
"ruleCount": 150,
"signerKeyId": "stellaops-secrets-signer",
"signedAt": "2026-01-04T00:00:00Z",
"files": [
{
"name": "secrets.ruleset.rules.jsonl",
"digest": "sha256:...",
"sizeBytes": 45678
}
]
}
```
**Installation:**
```bash
# Verify bundle signature using local attestor mirror
export STELLA_ATTESTOR_URL="file:///mnt/offline-kit/attestor-mirror"
devops/offline/scripts/install-secrets-bundle.sh \
/mnt/offline-kit/rules/secrets/2026.01 \
/opt/stellaops/plugins/scanner/analyzers/secrets
```
**Bundle Rotation:**
```bash
# Upgrade to new version with automatic backup
devops/offline/scripts/rotate-secrets-bundle.sh \
/mnt/offline-kit/rules/secrets/2026.02
```
**Enable Feature:**
```yaml
scanner:
features:
experimental:
secret-leak-detection: true
```
**Verify Bundle is Loaded:**
```bash
kubectl logs -l app=scanner-worker --tail=100 | grep SecretsAnalyzerHost
# Expected: SecretsAnalyzerHost: Loaded bundle 2026.01 with 150 rules
```
**Python analyzer features:**
- **Wheel/sdist/editable** parsing with dependency edges from `METADATA`, `PKG-INFO`, `requirements.txt`, and `pyproject.toml`
- **Virtual environment** support for virtualenv, venv, and conda prefix layouts

View File

@@ -13,35 +13,35 @@ This documentation set is internal and does not keep compatibility stubs for old
| Goal | Open this |
| --- | --- |
| Understand the product in 2 minutes | `overview.md` |
| Run a first scan (CLI) | `quickstart.md` |
| Browse capabilities | `key-features.md` |
| Roadmap (priorities + definition of "done") | `05_ROADMAP.md` |
| Architecture: high-level overview | `40_ARCHITECTURE_OVERVIEW.md` |
| Architecture: full reference map | `07_HIGH_LEVEL_ARCHITECTURE.md` |
| Architecture: user flows (UML) | `technical/architecture/user-flows.md` |
| Architecture: module matrix (46 modules) | `technical/architecture/module-matrix.md` |
| Architecture: data flows | `technical/architecture/data-flows.md` |
| Architecture: schema mapping | `technical/architecture/schema-mapping.md` |
| Offline / air-gap operations | `24_OFFLINE_KIT.md` |
| Security deployment hardening | `17_SECURITY_HARDENING_GUIDE.md` |
| Ingest advisories (Concelier + CLI) | `10_CONCELIER_CLI_QUICKSTART.md` |
| Develop plugins/connectors | `10_PLUGIN_SDK_GUIDE.md` |
| Console (Web UI) operator guide | `15_UI_GUIDE.md` |
| VEX consensus and issuer trust | `16_VEX_CONSENSUS_GUIDE.md` |
| Vulnerability Explorer guide | `20_VULNERABILITY_EXPLORER_GUIDE.md` |
| Understand the product in 2 minutes | [overview.md](/docs/overview/) |
| Run a first scan (CLI) | [quickstart.md](/docs/quickstart/) |
| Browse capabilities | [key-features.md](/docs/key-features/) |
| Roadmap (priorities + definition of "done") | [05_ROADMAP.md](/docs/05_roadmap/) |
| Architecture: high-level overview | [40_ARCHITECTURE_OVERVIEW.md](/docs/40_architecture_overview/) |
| Architecture: full reference map | [07_HIGH_LEVEL_ARCHITECTURE.md](/docs/07_high_level_architecture/) |
| Architecture: user flows (UML) | [technical/architecture/user-flows.md](/docs/technical/architecture/user-flows/) |
| Architecture: module matrix (46 modules) | [technical/architecture/module-matrix.md](/docs/technical/architecture/module-matrix/) |
| Architecture: data flows | [technical/architecture/data-flows.md](/docs/technical/architecture/data-flows/) |
| Architecture: schema mapping | [technical/architecture/schema-mapping.md](/docs/technical/architecture/schema-mapping/) |
| Offline / air-gap operations | [24_OFFLINE_KIT.md](/docs/24_offline_kit/) |
| Security deployment hardening | [17_SECURITY_HARDENING_GUIDE.md](/docs/17_security_hardening_guide/) |
| Ingest advisories (Concelier + CLI) | [10_CONCELIER_CLI_QUICKSTART.md](/docs/10_concelier_cli_quickstart/) |
| Develop plugins/connectors | [10_PLUGIN_SDK_GUIDE.md](/docs/10_plugin_sdk_guide/) |
| Console (Web UI) operator guide | [15_UI_GUIDE.md](/docs/15_ui_guide/) |
| VEX consensus and issuer trust | [16_VEX_CONSENSUS_GUIDE.md](/docs/16_vex_consensus_guide/) |
| Vulnerability Explorer guide | [20_VULNERABILITY_EXPLORER_GUIDE.md](/docs/20_vulnerability_explorer_guide/) |
## Detailed Indexes
- **Technical index (everything):** `docs/technical/README.md`
- **End-to-end workflow flows:** `docs/flows/` (16 detailed flow documents)
- **Module dossiers:** `docs/modules/`
- **API contracts and samples:** `docs/api/`
- **Architecture notes / ADRs:** `docs/architecture/`, `docs/adr/`
- **Operations and deployment:** `docs/operations/`, `docs/deploy/`, `docs/deployment/`
- **Air-gap workflows:** `docs/airgap/`
- **Security deep dives:** `docs/security/`
- **Benchmarks and fixtures:** `docs/benchmarks/`, `docs/assets/`
- **Technical index (everything):** [docs/technical/README.md](/docs/technical/)
- **End-to-end workflow flows:** [docs/flows/](/docs/flows/) (16 detailed flow documents)
- **Module dossiers:** [docs/modules/](/docs/modules/)
- **API contracts and samples:** [docs/api/](/docs/api/)
- **Architecture notes / ADRs:** [docs/architecture/](/docs/architecture/), [docs/adr/](/docs/adr/)
- **Operations and deployment:** [docs/operations/](/docs/operations/), [docs/deploy/](/docs/deploy/), [docs/deployment/](/docs/deployment/)
- **Air-gap workflows:** [docs/airgap/](/docs/airgap/)
- **Security deep dives:** [docs/security/](/docs/security/)
- **Benchmarks and fixtures:** [docs/benchmarks/](/docs/benchmarks/), [docs/assets/](/docs/assets/)
## Notes

View File

@@ -64,13 +64,13 @@
| 8 | DET-008 | DONE | DET-002, DET-003 | Guild | Refactor Registry module (1 file: RegistryTokenIssuer) |
| 9 | DET-009 | DONE | DET-002, DET-003 | Guild | Refactor Replay module (6 files: ReplayEngine, ReplayModels, ReplayExportModels, ReplayManifestExporter, FeedSnapshotCoordinatorService, PolicySimulationInputLock) |
| 10 | DET-010 | DONE | DET-002, DET-003 | Guild | Refactor RiskEngine module (skipped - no determinism issues found) |
| 11 | DET-011 | TODO | DET-002, DET-003 | Guild | Refactor Scanner module (~45+ matches remaining) |
| 11 | DET-011 | DOING | DET-002, DET-003 | Guild | Refactor Scanner module - Explainability (2 files: RiskReport, FalsifiabilityGenerator), Sources (5 files: ConnectionTesters, SourceConnectionTester, SourceTriggerDispatcher), VulnSurfaces (1 file: PostgresVulnSurfaceRepository), Storage (5 files: PostgresProofSpineRepository, PostgresScanMetricsRepository, RuntimeEventRepository, PostgresFuncProofRepository, PostgresIdempotencyKeyRepository), Storage.Oci (1 file: SlicePullService) |
| 12 | DET-012 | DONE | DET-002, DET-003 | Guild | Refactor Scheduler module (WebService, Persistence, Worker projects - 30+ files updated, tests migrated to FakeTimeProvider) |
| 13 | DET-013 | TODO | DET-002, DET-003 | Guild | Refactor Signer module (~89 matches remaining) |
| 13 | DET-013 | DONE | DET-002, DET-003 | Guild | Refactor Signer module (16 production files refactored: AmbientOidcTokenProvider, EphemeralKeyPair, IOidcTokenProvider, IFulcioClient, TrustAnchorManager, KeyRotationService, DefaultSigningKeyResolver, SigstoreSigningService, InMemorySignerAuditSink, KeyRotationEndpoints, Program.cs) |
| 14 | DET-014 | DONE | DET-002, DET-003 | Guild | Refactor Unknowns module (skipped - no determinism issues found) |
| 15 | DET-015 | TODO | DET-002, DET-003 | Guild | Refactor VexLens module (~76 matches remaining) |
| 15 | DET-015 | DONE | DET-002, DET-003 | Guild | Refactor VexLens module (production files: IConsensusRationaleCache, InMemorySourceTrustScoreCache, ISourceTrustScoreCalculator, InMemoryIssuerDirectory, InMemoryConsensusProjectionStore, OpenVexNormalizer, CycloneDxVexNormalizer, CsafVexNormalizer, IConsensusJobService, VexProofBuilder, IConsensusExportService, IVexLensApiService, TrustScorecardApiModels, OrchestratorLedgerEventEmitter, PostgresConsensusProjectionStore, PostgresConsensusProjectionStoreProxy, ProvenanceChainValidator, VexConsensusEngine, IConsensusRationaleService, VexLensEndpointExtensions) |
| 16 | DET-016 | DONE | DET-002, DET-003 | Guild | Refactor VulnExplorer module (1 file: VexDecisionStore) |
| 17 | DET-017 | TODO | DET-002, DET-003 | Guild | Refactor Zastava module (~48 matches remaining) |
| 17 | DET-017 | DONE | DET-002, DET-003 | Guild | Refactor Zastava module (~48 matches remaining) |
| 18 | DET-018 | TODO | DET-004 to DET-017 | Guild | Final audit: verify zero direct DateTime/Guid/Random calls in production code |
## Implementation Pattern
@@ -123,6 +123,12 @@ services.AddSingleton<IGuidProvider, SystemGuidProvider>();
| 2026-01-05 | DET-005: Provcache module refactored - 8 files (EvidenceChunker, LazyFetchOrchestrator, MinimalProofExporter, FeedEpochAdvancedEvent, SignerRevokedEvent, Postgres repos, ValkeyProvcacheStore) | Agent |
| 2026-01-05 | DET-006 to DET-010: Batch completed - ReachGraph (1 file), Registry (1 file), Replay (6 files); Provenance, RiskEngine, Unknowns already clean | Agent |
| 2026-01-05 | Remaining modules assessed: Scanner (~45), Scheduler (~20), Signer (~89), VexLens (~76), VulnExplorer (3), Zastava (~48) matches | Agent |
| 2026-01-05 | DET-012 complete: Scheduler module refactored - WebService, Persistence, Worker projects (30+ files) | Agent |
| 2026-01-05 | DET-013 complete: Signer module refactored - Keyless (4 files: AmbientOidcTokenProvider, EphemeralKeyPair, IOidcTokenProvider, IFulcioClient with IsExpiredAt/IsValidAt methods), KeyManagement (2 files: TrustAnchorManager, KeyRotationService), Infrastructure (3 files: DefaultSigningKeyResolver, SigstoreSigningService, InMemorySignerAuditSink), WebService (2 files: Program.cs, KeyRotationEndpoints) | Agent |
| 2026-01-05 | DET-015 complete: VexLens module refactored - 20 production files (caching, storage, normalization, orchestration, API, consensus, trust, persistence) with TimeProvider and IGuidProvider injection. Note: Pre-existing build errors in NoiseGateService.cs and NoiseGatingApiModels.cs unrelated to determinism changes. | Agent |
| 2026-01-05 | DET-017 complete: Zastava module refactored - Agent (RuntimeEventsClient, HealthCheckHostedService, RuntimeEventDispatchService, RuntimeEventBuffer), Observer (RuntimeEventDispatchService, RuntimeEventBuffer, ProcSnapshotCollector, EbpfProbeManager), Webhook (WebhookCertificateHealthCheck) with TimeProvider and IGuidProvider injection. | Agent |
| 2026-01-05 | DET-011 in progress: Scanner module refactoring - 14 production files refactored (RiskReport.cs, FalsifiabilityGenerator.cs, SourceConnectionTester.cs, SourceTriggerDispatcher.cs, DockerConnectionTester.cs, ZastavaConnectionTester.cs, GitConnectionTester.cs, PostgresVulnSurfaceRepository.cs, PostgresProofSpineRepository.cs, PostgresScanMetricsRepository.cs, RuntimeEventRepository.cs, PostgresFuncProofRepository.cs, PostgresIdempotencyKeyRepository.cs, SlicePullService.cs). Added Determinism.Abstractions references to 4 Scanner sub-projects. | Agent |
## Decisions & Risks
- **Decision:** Defer determinism refactoring from MAINT audit to dedicated sprint for focused, systematic approach.

View File

@@ -30,16 +30,16 @@ Integrate secret detection rule bundles with the Offline Kit infrastructure for
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
| --- | --- | --- | --- | --- | --- |
| 1 | OKS-001 | TODO | None | AirGap Guild | Update Offline Kit manifest schema for rules |
| 2 | OKS-002 | TODO | OKS-001 | AirGap Guild | Add secrets bundle to BundleBuilder |
| 3 | OKS-003 | TODO | OKS-002 | AirGap Guild | Create bundle verification in Importer |
| 4 | OKS-004 | TODO | None | AirGap Guild | Add Attestor mirror support for bundle verification |
| 5 | OKS-005 | TODO | OKS-003 | AirGap Guild | Create bundle installation script |
| 6 | OKS-006 | TODO | OKS-005 | AirGap Guild | Add bundle rotation/upgrade workflow |
| 7 | OKS-007 | TODO | None | CI/CD Guild | Add bundle to release workflow |
| 8 | OKS-008 | TODO | All | AirGap Guild | Add integration tests for offline flow |
| 9 | OKS-009 | TODO | All | Docs Guild | Update offline kit documentation |
| 10 | OKS-010 | TODO | All | DevOps Guild | Update Helm charts for bundle mounting |
| 1 | OKS-001 | DONE | None | AirGap Guild | Update Offline Kit manifest schema for rules |
| 2 | OKS-002 | DONE | OKS-001 | AirGap Guild | Add secrets bundle to BundleBuilder |
| 3 | OKS-003 | DONE | OKS-002 | AirGap Guild | Create bundle verification in Importer |
| 4 | OKS-004 | DONE | None | AirGap Guild | Add Attestor mirror support for bundle verification |
| 5 | OKS-005 | DONE | OKS-003 | AirGap Guild | Create bundle installation script |
| 6 | OKS-006 | DONE | OKS-005 | AirGap Guild | Add bundle rotation/upgrade workflow |
| 7 | OKS-007 | DONE | None | CI/CD Guild | Add bundle to release workflow |
| 8 | OKS-008 | DONE | All | AirGap Guild | Add integration tests for offline flow |
| 9 | OKS-009 | DONE | All | Docs Guild | Update offline kit documentation |
| 10 | OKS-010 | DONE | All | DevOps Guild | Update Helm charts for bundle mounting |
## Task Details
@@ -588,4 +588,17 @@ devops/offline/
| Date | Action | Notes |
|------|--------|-------|
| 2026-01-04 | Sprint created | Part of secret leak detection implementation |
| 2026-01-04 | OKS-001 DONE | Added RuleBundleComponent to OfflineKitManifest.cs with rules schema |
| 2026-01-04 | OKS-002 DONE | Extended SnapshotBundleWriter/Reader, added RulesSnapshotExtractor |
| 2026-01-04 | OKS-003 DONE | Created RuleBundleValidator with digest/signature/monotonicity checks |
| 2026-01-04 | OKS-004 DONE | Added RuleBundleSigningPath to FileSystemRootStore, DsseVerifier support |
| 2026-01-04 | OKS-005 DONE | Created devops/offline/scripts/install-secrets-bundle.sh |
| 2026-01-04 | OKS-006 DONE | Created devops/offline/scripts/rotate-secrets-bundle.sh with rollback |
| 2026-01-04 | OKS-007 DONE | Created .gitea/workflows/secrets-bundle-release.yml CI/CD workflow |
| 2026-01-04 | OKS-008 DONE | Added RuleBundleValidatorTests.cs with 8 test cases |
| 2026-01-04 | OKS-009 DONE | Updated docs/24_OFFLINE_KIT.md with secrets bundle documentation |
| 2026-01-04 | OKS-010 DONE | Updated values-airgap.yaml with secrets-rules volume mount and PVC |
| 2026-01-04 | Fix build errors | Fixed 4 nullability errors in OfflineVerificationPolicy.cs, JsonNormalizer.cs, SbomNormalizer.cs |
| 2026-01-04 | Fix test versions | Updated RuleBundleValidatorTests to use 3-part semver (2026.1.0) instead of CalVer |
| 2026-01-04 | Sprint complete | All 10 tasks completed, build passes, tests pass (9/9) |

View File

@@ -0,0 +1,213 @@
# Sprint 20260104_006_BE - Secret Detection Configuration API
## Topic & Scope
Backend APIs and data models for configuring secret detection behavior per tenant. This sprint provides the foundation for UI configuration of secret leak detection.
**Key deliverables:**
1. **Tenant Settings Model**: Per-tenant secret detection configuration
2. **Revelation Policy**: Control how detected secrets are displayed/masked
3. **Exception Management**: Allowlist patterns for false positives
4. **Configuration API**: CRUD endpoints for settings
**Working directory:** `src/Scanner/`, `src/Platform/`
## Dependencies & Concurrency
- **Depends on**: Sprint 20260104_001 (Core Analyzer), Sprint 20260104_002 (Rule Bundles)
- **Parallel with**: Sprint 20260104_007 (Alert Integration)
- **Blocks**: Sprint 20260104_008 (UI)
## Documentation Prerequisites
- docs/modules/scanner/operations/secret-leak-detection.md
- CLAUDE.md Section 8 (Determinism)
## Delivery Tracker
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
| --- | --- | --- | --- | --- | --- |
| 1 | SDC-001 | TODO | None | Scanner Guild | Define SecretDetectionSettings domain model |
| 2 | SDC-002 | TODO | SDC-001 | Scanner Guild | Create SecretRevelationPolicy enum and config |
| 3 | SDC-003 | TODO | SDC-001 | Scanner Guild | Create SecretExceptionPattern model for allowlists |
| 4 | SDC-004 | TODO | SDC-001 | Platform Guild | Add persistence (EF Core migrations) |
| 5 | SDC-005 | TODO | SDC-004 | Platform Guild | Create Settings CRUD API endpoints |
| 6 | SDC-006 | TODO | SDC-005 | Platform Guild | Add OpenAPI spec for settings endpoints |
| 7 | SDC-007 | TODO | SDC-003 | Scanner Guild | Integrate exception patterns into SecretsAnalyzerHost |
| 8 | SDC-008 | TODO | SDC-002 | Scanner Guild | Implement revelation policy in findings output |
| 9 | SDC-009 | TODO | All | Scanner Guild | Add unit and integration tests |
## Task Details
### SDC-001: SecretDetectionSettings Domain Model
```csharp
public sealed record SecretDetectionSettings
{
public required Guid TenantId { get; init; }
public required bool Enabled { get; init; }
public required SecretRevelationPolicy RevelationPolicy { get; init; }
public required IReadOnlyList<string> EnabledRuleCategories { get; init; }
public required IReadOnlyList<SecretExceptionPattern> Exceptions { get; init; }
public required SecretAlertSettings AlertSettings { get; init; }
public required DateTimeOffset UpdatedAt { get; init; }
public required string UpdatedBy { get; init; }
}
```
Location: `src/Scanner/__Libraries/StellaOps.Scanner.Core/Secrets/Configuration/`
### SDC-002: SecretRevelationPolicy
Control how detected secrets appear in different contexts:
```csharp
public enum SecretRevelationPolicy
{
/// <summary>
/// Show only that a secret was detected, no value shown.
/// Example: [SECRET_DETECTED: aws_access_key_id]
/// </summary>
FullMask = 0,
/// <summary>
/// Show first and last 4 characters.
/// Example: AKIA****WXYZ
/// </summary>
PartialReveal = 1,
/// <summary>
/// Show full value (requires elevated permissions).
/// Use only for debugging/incident response.
/// </summary>
FullReveal = 2
}
public sealed record RevelationPolicyConfig
{
/// <summary>Default policy for UI/API responses.</summary>
public SecretRevelationPolicy DefaultPolicy { get; init; } = SecretRevelationPolicy.PartialReveal;
/// <summary>Policy for exported reports (PDF, JSON).</summary>
public SecretRevelationPolicy ExportPolicy { get; init; } = SecretRevelationPolicy.FullMask;
/// <summary>Policy for logs and telemetry.</summary>
public SecretRevelationPolicy LogPolicy { get; init; } = SecretRevelationPolicy.FullMask;
/// <summary>Roles allowed to use FullReveal.</summary>
public IReadOnlyList<string> FullRevealRoles { get; init; } = ["security-admin", "incident-responder"];
/// <summary>Number of characters to show at start/end for PartialReveal.</summary>
public int PartialRevealChars { get; init; } = 4;
}
```
### SDC-003: SecretExceptionPattern (Allowlist)
```csharp
public sealed record SecretExceptionPattern
{
public required Guid Id { get; init; }
public required string Name { get; init; }
public required string Description { get; init; }
/// <summary>Regex pattern to match against detected secret value.</summary>
public required string Pattern { get; init; }
/// <summary>Optional: Only apply to specific rule IDs.</summary>
public IReadOnlyList<string>? ApplicableRuleIds { get; init; }
/// <summary>Optional: Only apply to specific file paths.</summary>
public string? FilePathGlob { get; init; }
/// <summary>Reason for exception (audit trail).</summary>
public required string Justification { get; init; }
/// <summary>Expiration date (null = permanent).</summary>
public DateTimeOffset? ExpiresAt { get; init; }
public required DateTimeOffset CreatedAt { get; init; }
public required string CreatedBy { get; init; }
}
```
### SDC-005: Settings API Endpoints
```
GET /api/v1/tenants/{tenantId}/settings/secret-detection
PUT /api/v1/tenants/{tenantId}/settings/secret-detection
PATCH /api/v1/tenants/{tenantId}/settings/secret-detection
GET /api/v1/tenants/{tenantId}/settings/secret-detection/exceptions
POST /api/v1/tenants/{tenantId}/settings/secret-detection/exceptions
DELETE /api/v1/tenants/{tenantId}/settings/secret-detection/exceptions/{exceptionId}
GET /api/v1/tenants/{tenantId}/settings/secret-detection/rule-categories
```
### SDC-008: Revelation Policy Implementation
```csharp
public static class SecretMasker
{
public static string Mask(string secretValue, SecretRevelationPolicy policy, int partialChars = 4)
{
return policy switch
{
SecretRevelationPolicy.FullMask => "[REDACTED]",
SecretRevelationPolicy.PartialReveal => MaskPartial(secretValue, partialChars),
SecretRevelationPolicy.FullReveal => secretValue,
_ => "[REDACTED]"
};
}
private static string MaskPartial(string value, int chars)
{
if (value.Length <= chars * 2)
return new string('*', value.Length);
var prefix = value[..chars];
var suffix = value[^chars..];
var masked = new string('*', Math.Min(value.Length - chars * 2, 8));
return $"{prefix}{masked}{suffix}";
}
}
```
## Directory Structure
```
src/Scanner/__Libraries/StellaOps.Scanner.Core/
├── Secrets/
│ ├── Configuration/
│ │ ├── SecretDetectionSettings.cs
│ │ ├── SecretRevelationPolicy.cs
│ │ ├── RevelationPolicyConfig.cs
│ │ ├── SecretExceptionPattern.cs
│ │ └── SecretAlertSettings.cs
│ └── Masking/
│ └── SecretMasker.cs
src/Platform/StellaOps.Platform.WebService/
├── Endpoints/
│ └── SecretDetectionSettingsEndpoints.cs
└── Persistence/
└── Migrations/
└── AddSecretDetectionSettings.cs
```
## Decisions & Risks
| Decision | Rationale |
|----------|-----------|
| Per-tenant settings | Multi-tenant isolation requirement |
| Role-based full reveal | Security: prevent accidental exposure |
| Exception expiration | Force periodic review of allowlists |
| Separate export/log policies | Defense in depth for sensitive data |
## Execution Log
| Date | Action | Notes |
|------|--------|-------|
| 2026-01-04 | Sprint created | Gap identified in secret detection feature |

View File

@@ -0,0 +1,290 @@
# Sprint 20260104_007_BE - Secret Detection Alert Integration
## Topic & Scope
Integration between secret detection findings and the Notify service for real-time alerting when secrets are discovered in scans.
**Key deliverables:**
1. **Alert Routing**: Route secret findings to configured channels
2. **Alert Templates**: Formatted notifications for different channels
3. **Rate Limiting**: Prevent alert fatigue from mass findings
4. **Severity Mapping**: Map rule severity to alert priority
**Working directory:** `src/Scanner/`, `src/Notify/`
## Dependencies & Concurrency
- **Depends on**: Sprint 20260104_001 (Core Analyzer), Sprint 20260104_006 (Config API)
- **Parallel with**: Sprint 20260104_008 (UI)
- **Blocks**: Production deployment with alerting
## Documentation Prerequisites
- docs/modules/notify/architecture.md
- docs/modules/scanner/operations/secret-leak-detection.md
## Delivery Tracker
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
| --- | --- | --- | --- | --- | --- |
| 1 | SDA-001 | TODO | None | Scanner Guild | Define SecretAlertSettings model |
| 2 | SDA-002 | TODO | SDA-001 | Scanner Guild | Create SecretFindingAlertEvent |
| 3 | SDA-003 | TODO | SDA-002 | Notify Guild | Add secret-finding alert template |
| 4 | SDA-004 | TODO | SDA-003 | Notify Guild | Implement Slack/Teams formatters |
| 5 | SDA-005 | TODO | SDA-002 | Scanner Guild | Add alert emission to SecretsAnalyzerHost |
| 6 | SDA-006 | TODO | SDA-005 | Scanner Guild | Implement rate limiting / deduplication |
| 7 | SDA-007 | TODO | SDA-006 | Scanner Guild | Add severity-based routing |
| 8 | SDA-008 | TODO | SDA-001 | Platform Guild | Add alert settings to config API |
| 9 | SDA-009 | TODO | All | Scanner Guild | Add integration tests |
## Task Details
### SDA-001: SecretAlertSettings Model
```csharp
public sealed record SecretAlertSettings
{
/// <summary>Enable/disable alerting for this tenant.</summary>
public bool Enabled { get; init; } = true;
/// <summary>Minimum severity to trigger alert (Critical, High, Medium, Low).</summary>
public SecretSeverity MinimumAlertSeverity { get; init; } = SecretSeverity.High;
/// <summary>Alert destinations by channel type.</summary>
public IReadOnlyList<SecretAlertDestination> Destinations { get; init; } = [];
/// <summary>Rate limit: max alerts per scan.</summary>
public int MaxAlertsPerScan { get; init; } = 10;
/// <summary>Deduplication window: don't re-alert same secret within this period.</summary>
public TimeSpan DeduplicationWindow { get; init; } = TimeSpan.FromHours(24);
/// <summary>Include file path in alert (may reveal repo structure).</summary>
public bool IncludeFilePath { get; init; } = true;
/// <summary>Include masked secret value in alert.</summary>
public bool IncludeMaskedValue { get; init; } = true;
}
public sealed record SecretAlertDestination
{
public required Guid Id { get; init; }
public required AlertChannelType ChannelType { get; init; }
public required string ChannelId { get; init; } // Slack channel ID, email, webhook URL
public IReadOnlyList<SecretSeverity>? SeverityFilter { get; init; }
public IReadOnlyList<string>? RuleCategoryFilter { get; init; }
}
public enum AlertChannelType
{
Slack,
Teams,
Email,
Webhook,
PagerDuty
}
```
### SDA-002: SecretFindingAlertEvent
```csharp
public sealed record SecretFindingAlertEvent
{
public required Guid EventId { get; init; }
public required Guid TenantId { get; init; }
public required Guid ScanId { get; init; }
public required string ImageRef { get; init; }
public required SecretSeverity Severity { get; init; }
public required string RuleId { get; init; }
public required string RuleName { get; init; }
public required string RuleCategory { get; init; }
public required string FilePath { get; init; }
public required int LineNumber { get; init; }
public required string MaskedValue { get; init; }
public required DateTimeOffset DetectedAt { get; init; }
public required string ScanTriggeredBy { get; init; }
/// <summary>Deduplication key for rate limiting.</summary>
public string DeduplicationKey => $"{TenantId}:{RuleId}:{FilePath}:{LineNumber}";
}
```
### SDA-003: Alert Templates
**Slack Template:**
```json
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "🚨 Secret Detected in Container Scan"
}
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": "*Severity:*\n{{severity}}" },
{ "type": "mrkdwn", "text": "*Rule:*\n{{ruleName}}" },
{ "type": "mrkdwn", "text": "*Image:*\n`{{imageRef}}`" },
{ "type": "mrkdwn", "text": "*File:*\n`{{filePath}}:{{lineNumber}}`" }
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Detected Value:*\n```{{maskedValue}}```"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": { "type": "plain_text", "text": "View in StellaOps" },
"url": "{{findingUrl}}"
},
{
"type": "button",
"text": { "type": "plain_text", "text": "Add Exception" },
"url": "{{exceptionUrl}}"
}
]
}
]
}
```
### SDA-005: Alert Emission in SecretsAnalyzerHost
```csharp
public sealed class SecretsAnalyzerHost
{
private readonly ISecretAlertEmitter _alertEmitter;
private readonly ISecretAlertDeduplicator _deduplicator;
public async Task OnSecretFoundAsync(
SecretFinding finding,
ScanContext context,
CancellationToken ct)
{
var settings = await _settingsProvider.GetAlertSettingsAsync(context.TenantId, ct);
if (!settings.Enabled)
return;
if (finding.Severity < settings.MinimumAlertSeverity)
return;
var alertEvent = MapToAlertEvent(finding, context);
// Check deduplication
if (await _deduplicator.IsDuplicateAsync(alertEvent, settings.DeduplicationWindow, ct))
{
_logger.LogDebug("secret.alert.deduplicated key={key}", alertEvent.DeduplicationKey);
return;
}
// Rate limiting
var alertCount = await _alertEmitter.GetAlertCountForScanAsync(context.ScanId, ct);
if (alertCount >= settings.MaxAlertsPerScan)
{
_logger.LogWarning("secret.alert.rate_limited scan_id={scan_id} count={count}",
context.ScanId, alertCount);
return;
}
// Emit to configured destinations
await _alertEmitter.EmitAsync(alertEvent, settings.Destinations, ct);
}
}
```
### SDA-006: Rate Limiting & Deduplication
```csharp
public interface ISecretAlertDeduplicator
{
Task<bool> IsDuplicateAsync(
SecretFindingAlertEvent alert,
TimeSpan window,
CancellationToken ct);
Task RecordAlertAsync(
SecretFindingAlertEvent alert,
CancellationToken ct);
}
public sealed class ValkeySecretAlertDeduplicator : ISecretAlertDeduplicator
{
private readonly IValkeyConnection _valkey;
public async Task<bool> IsDuplicateAsync(
SecretFindingAlertEvent alert,
TimeSpan window,
CancellationToken ct)
{
var key = $"secret:alert:dedup:{alert.DeduplicationKey}";
var exists = await _valkey.ExistsAsync(key);
return exists;
}
public async Task RecordAlertAsync(
SecretFindingAlertEvent alert,
CancellationToken ct)
{
var key = $"secret:alert:dedup:{alert.DeduplicationKey}";
await _valkey.SetAsync(key, alert.EventId.ToString(), expiry: TimeSpan.FromHours(24));
}
}
```
## Severity Mapping
| Rule Severity | Alert Priority | Default Behavior |
|---------------|----------------|------------------|
| Critical | P1 / Immediate | Always alert, page on-call |
| High | P2 / Urgent | Alert to security channel |
| Medium | P3 / Normal | Alert if configured |
| Low | P4 / Info | No alert by default |
## Directory Structure
```
src/Scanner/__Libraries/StellaOps.Scanner.Core/
├── Secrets/
│ ├── Alerts/
│ │ ├── SecretAlertSettings.cs
│ │ ├── SecretFindingAlertEvent.cs
│ │ ├── ISecretAlertEmitter.cs
│ │ ├── ISecretAlertDeduplicator.cs
│ │ └── ValkeySecretAlertDeduplicator.cs
src/Notify/__Libraries/StellaOps.Notify.Engine/
├── Templates/
│ └── SecretFindingAlertTemplate.cs
├── Formatters/
│ ├── SlackSecretAlertFormatter.cs
│ └── TeamsSecretAlertFormatter.cs
```
## Decisions & Risks
| Decision | Rationale |
|----------|-----------|
| Valkey for deduplication | Fast, distributed, TTL support |
| Per-scan rate limit | Prevent alert storms on large findings |
| Masked values in alerts | Balance security awareness vs exposure |
| Severity-based routing | Different channels for different priorities |
## Execution Log
| Date | Action | Notes |
|------|--------|-------|
| 2026-01-04 | Sprint created | Alert integration for secret detection |

View File

@@ -0,0 +1,499 @@
# Sprint 20260104_008_FE - Secret Detection UI
## Topic & Scope
Frontend components for configuring and viewing secret detection findings. Provides tenant administrators with tools to manage detection settings, view findings, and configure alerts.
**Key deliverables:**
1. **Settings Page**: Configure secret detection for tenant
2. **Findings Viewer**: View detected secrets with proper masking
3. **Exception Manager**: Add/remove allowlist patterns
4. **Alert Configuration**: Set up notification channels
**Working directory:** `src/Web/StellaOps.Web/`
## Dependencies & Concurrency
- **Depends on**: Sprint 20260104_006 (Config API), Sprint 20260104_007 (Alerts)
- **Parallel with**: None (final UI sprint)
- **Blocks**: Feature release
## Documentation Prerequisites
- docs/modules/web/architecture.md
- Angular v17 component patterns
## Delivery Tracker
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
| --- | --- | --- | --- | --- | --- |
| 1 | SDU-001 | TODO | None | Frontend Guild | Create secret-detection feature module |
| 2 | SDU-002 | TODO | SDU-001 | Frontend Guild | Build settings page component |
| 3 | SDU-003 | TODO | SDU-002 | Frontend Guild | Add revelation policy selector |
| 4 | SDU-004 | TODO | SDU-002 | Frontend Guild | Build rule category toggles |
| 5 | SDU-005 | TODO | SDU-001 | Frontend Guild | Create findings list component |
| 6 | SDU-006 | TODO | SDU-005 | Frontend Guild | Implement masked value display |
| 7 | SDU-007 | TODO | SDU-005 | Frontend Guild | Add finding detail drawer |
| 8 | SDU-008 | TODO | SDU-001 | Frontend Guild | Build exception manager component |
| 9 | SDU-009 | TODO | SDU-008 | Frontend Guild | Create exception form with validation |
| 10 | SDU-010 | TODO | SDU-001 | Frontend Guild | Build alert destination config |
| 11 | SDU-011 | TODO | SDU-010 | Frontend Guild | Add channel test functionality |
| 12 | SDU-012 | TODO | All | Frontend Guild | Add E2E tests |
## Task Details
### SDU-002: Settings Page Component
```typescript
// secret-detection-settings.component.ts
@Component({
selector: 'app-secret-detection-settings',
template: `
<div class="settings-container">
<header class="settings-header">
<h1>Secret Detection</h1>
<mat-slide-toggle
[checked]="settings()?.enabled"
(change)="onEnabledChange($event)"
color="primary">
{{ settings()?.enabled ? 'Enabled' : 'Disabled' }}
</mat-slide-toggle>
</header>
<mat-tab-group>
<mat-tab label="General">
<app-revelation-policy-config
[policy]="settings()?.revelationPolicy"
(policyChange)="onPolicyChange($event)" />
<app-rule-category-selector
[categories]="availableCategories()"
[selected]="settings()?.enabledRuleCategories"
(selectionChange)="onCategoriesChange($event)" />
</mat-tab>
<mat-tab label="Exceptions">
<app-exception-manager
[exceptions]="settings()?.exceptions"
(add)="onAddException($event)"
(remove)="onRemoveException($event)" />
</mat-tab>
<mat-tab label="Alerts">
<app-alert-destination-config
[settings]="settings()?.alertSettings"
(settingsChange)="onAlertSettingsChange($event)" />
</mat-tab>
</mat-tab-group>
</div>
`
})
export class SecretDetectionSettingsComponent {
private settingsService = inject(SecretDetectionSettingsService);
settings = this.settingsService.settings;
availableCategories = this.settingsService.availableCategories;
// ... handlers
}
```
### SDU-003: Revelation Policy Selector
```typescript
// revelation-policy-config.component.ts
@Component({
selector: 'app-revelation-policy-config',
template: `
<mat-card>
<mat-card-header>
<mat-card-title>Secret Revelation Policy</mat-card-title>
<mat-card-subtitle>
Control how detected secrets are displayed
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<mat-radio-group
[value]="policy()?.defaultPolicy"
(change)="onDefaultPolicyChange($event)">
<mat-radio-button value="FullMask">
<div class="policy-option">
<strong>Full Mask</strong>
<span class="example">[REDACTED]</span>
<p>No secret value shown. Safest option.</p>
</div>
</mat-radio-button>
<mat-radio-button value="PartialReveal">
<div class="policy-option">
<strong>Partial Reveal</strong>
<span class="example">AKIA****WXYZ</span>
<p>Show first/last 4 characters. Helps identify specific secrets.</p>
</div>
</mat-radio-button>
<mat-radio-button value="FullReveal" [disabled]="!canFullReveal()">
<div class="policy-option">
<strong>Full Reveal</strong>
<span class="example">AKIAIOSFODNN7EXAMPLE</span>
<p>Show complete value. Requires security-admin role.</p>
</div>
</mat-radio-button>
</mat-radio-group>
<mat-divider />
<h4>Context-Specific Policies</h4>
<div class="context-policies">
<mat-form-field>
<mat-label>Export Reports</mat-label>
<mat-select [value]="policy()?.exportPolicy">
<mat-option value="FullMask">Full Mask</mat-option>
<mat-option value="PartialReveal">Partial Reveal</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>Logs & Telemetry</mat-label>
<mat-select [value]="policy()?.logPolicy" disabled>
<mat-option value="FullMask">Full Mask (Enforced)</mat-option>
</mat-select>
<mat-hint>Secrets are never logged in full</mat-hint>
</mat-form-field>
</div>
</mat-card-content>
</mat-card>
`
})
```
### SDU-005: Findings List Component
```typescript
// secret-findings-list.component.ts
@Component({
selector: 'app-secret-findings-list',
template: `
<div class="findings-container">
<header class="findings-header">
<h2>Secret Findings</h2>
<div class="filters">
<mat-form-field>
<mat-label>Severity</mat-label>
<mat-select multiple [(value)]="severityFilter">
<mat-option value="Critical">Critical</mat-option>
<mat-option value="High">High</mat-option>
<mat-option value="Medium">Medium</mat-option>
<mat-option value="Low">Low</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>Status</mat-label>
<mat-select [(value)]="statusFilter">
<mat-option value="Open">Open</mat-option>
<mat-option value="Dismissed">Dismissed</mat-option>
<mat-option value="Excepted">Excepted</mat-option>
</mat-select>
</mat-form-field>
</div>
</header>
<table mat-table [dataSource]="findings()">
<ng-container matColumnDef="severity">
<th mat-header-cell *matHeaderCellDef>Severity</th>
<td mat-cell *matCellDef="let finding">
<app-severity-badge [severity]="finding.severity" />
</td>
</ng-container>
<ng-container matColumnDef="rule">
<th mat-header-cell *matHeaderCellDef>Rule</th>
<td mat-cell *matCellDef="let finding">
<div class="rule-info">
<span class="rule-name">{{ finding.ruleName }}</span>
<span class="rule-category">{{ finding.ruleCategory }}</span>
</div>
</td>
</ng-container>
<ng-container matColumnDef="location">
<th mat-header-cell *matHeaderCellDef>Location</th>
<td mat-cell *matCellDef="let finding">
<code class="file-path">{{ finding.filePath }}:{{ finding.lineNumber }}</code>
</td>
</ng-container>
<ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef>Detected Value</th>
<td mat-cell *matCellDef="let finding">
<app-masked-secret-value
[value]="finding.value"
[policy]="revelationPolicy()"
[canReveal]="canRevealSecrets()" />
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let finding">
<button mat-icon-button [matMenuTriggerFor]="actionMenu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #actionMenu>
<button mat-menu-item (click)="viewDetails(finding)">
<mat-icon>visibility</mat-icon>
View Details
</button>
<button mat-menu-item (click)="dismiss(finding)">
<mat-icon>cancel</mat-icon>
Dismiss
</button>
<button mat-menu-item (click)="addException(finding)">
<mat-icon>playlist_add</mat-icon>
Add Exception
</button>
</mat-menu>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"
(click)="viewDetails(row)"></tr>
</table>
</div>
`
})
```
### SDU-006: Masked Value Display
```typescript
// masked-secret-value.component.ts
@Component({
selector: 'app-masked-secret-value',
template: `
<div class="masked-value" [class.revealed]="isRevealed()">
<code>{{ displayValue() }}</code>
@if (canReveal() && !isRevealed()) {
<button
mat-icon-button
matTooltip="Reveal value (logged)"
(click)="reveal()">
<mat-icon>visibility</mat-icon>
</button>
}
@if (isRevealed()) {
<button
mat-icon-button
matTooltip="Hide value"
(click)="hide()">
<mat-icon>visibility_off</mat-icon>
</button>
<button
mat-icon-button
matTooltip="Copy to clipboard"
(click)="copy()">
<mat-icon>content_copy</mat-icon>
</button>
}
</div>
`,
styles: [`
.masked-value {
font-family: monospace;
display: flex;
align-items: center;
gap: 8px;
}
.revealed code {
background: #fff3cd;
padding: 4px 8px;
border-radius: 4px;
}
`]
})
export class MaskedSecretValueComponent {
value = input.required<string>();
policy = input.required<SecretRevelationPolicy>();
canReveal = input<boolean>(false);
private revealed = signal(false);
isRevealed = computed(() => this.revealed() && this.canReveal());
displayValue = computed(() => {
if (this.isRevealed()) {
return this.value();
}
return this.maskValue(this.value(), this.policy());
});
reveal() {
// Log reveal action for audit
this.auditService.logSecretReveal(this.value());
this.revealed.set(true);
}
hide() {
this.revealed.set(false);
}
private maskValue(value: string, policy: SecretRevelationPolicy): string {
switch (policy) {
case 'FullMask':
return '[REDACTED]';
case 'PartialReveal':
if (value.length <= 8) return '*'.repeat(value.length);
return `${value.slice(0, 4)}${'*'.repeat(Math.min(8, value.length - 8))}${value.slice(-4)}`;
default:
return '[REDACTED]';
}
}
}
```
### SDU-010: Alert Destination Configuration
```typescript
// alert-destination-config.component.ts
@Component({
selector: 'app-alert-destination-config',
template: `
<mat-card>
<mat-card-header>
<mat-card-title>Alert Destinations</mat-card-title>
</mat-card-header>
<mat-card-content>
<div class="alert-settings">
<mat-slide-toggle [(ngModel)]="settings().enabled">
Enable Alerts
</mat-slide-toggle>
<mat-form-field>
<mat-label>Minimum Severity</mat-label>
<mat-select [(value)]="settings().minimumAlertSeverity">
<mat-option value="Critical">Critical only</mat-option>
<mat-option value="High">High and above</mat-option>
<mat-option value="Medium">Medium and above</mat-option>
<mat-option value="Low">All findings</mat-option>
</mat-select>
</mat-form-field>
</div>
<mat-divider />
<h4>Configured Channels</h4>
<div class="destinations-list">
@for (dest of settings().destinations; track dest.id) {
<mat-card class="destination-card">
<div class="destination-info">
<mat-icon>{{ getChannelIcon(dest.channelType) }}</mat-icon>
<span>{{ dest.channelType }}</span>
<code>{{ dest.channelId }}</code>
</div>
<div class="destination-actions">
<button mat-icon-button (click)="testChannel(dest)">
<mat-icon>send</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="removeDestination(dest)">
<mat-icon>delete</mat-icon>
</button>
</div>
</mat-card>
}
</div>
<button mat-stroked-button (click)="addDestination()">
<mat-icon>add</mat-icon>
Add Destination
</button>
</mat-card-content>
</mat-card>
`
})
```
## UI Mockups
### Settings Page Layout
```
┌─────────────────────────────────────────────────────────────┐
│ Secret Detection [Enabled ●] │
├─────────────────────────────────────────────────────────────┤
│ [General] [Exceptions] [Alerts] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─ Revelation Policy ─────────────────────────────────┐ │
│ │ ○ Full Mask [REDACTED] │ │
│ │ ● Partial Reveal AKIA****WXYZ │ │
│ │ ○ Full Reveal (requires security-admin) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Rule Categories ───────────────────────────────────┐ │
│ │ ☑ AWS Credentials ☑ GCP Service Accounts │ │
│ │ ☑ Generic API Keys ☑ Private Keys │ │
│ │ ☐ Internal Tokens ☑ Database Credentials │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
### Findings List
```
┌─────────────────────────────────────────────────────────────┐
│ Secret Findings │
│ Severity: [All ▼] Status: [Open ▼] Image: [All ▼] │
├─────────────────────────────────────────────────────────────┤
│ SEV │ RULE │ LOCATION │ VALUE │
├─────────────────────────────────────────────────────────────┤
│ 🔴 │ AWS Access Key │ config.yaml:42 │ AKIA****XYZ │
│ 🟠 │ Generic API Key │ .env:15 │ sk_l****abc │
│ 🟡 │ Private Key │ certs/server.key │ [REDACTED] │
└─────────────────────────────────────────────────────────────┘
```
## Directory Structure
```
src/Web/StellaOps.Web/src/app/
├── features/
│ └── secret-detection/
│ ├── secret-detection.module.ts
│ ├── secret-detection.routes.ts
│ ├── pages/
│ │ ├── settings/
│ │ │ └── secret-detection-settings.component.ts
│ │ └── findings/
│ │ └── secret-findings-list.component.ts
│ ├── components/
│ │ ├── revelation-policy-config/
│ │ ├── rule-category-selector/
│ │ ├── exception-manager/
│ │ ├── alert-destination-config/
│ │ ├── masked-secret-value/
│ │ └── finding-detail-drawer/
│ └── services/
│ ├── secret-detection-settings.service.ts
│ └── secret-findings.service.ts
```
## Decisions & Risks
| Decision | Rationale |
|----------|-----------|
| Angular Material | Consistent with existing UI |
| Signal-based state | Modern Angular patterns |
| Audit logging on reveal | Compliance requirement |
| Lazy-loaded module | Performance optimization |
## Execution Log
| Date | Action | Notes |
|------|--------|-------|
| 2026-01-04 | Sprint created | UI components for secret detection |