feat(api): Implement Console Export Client and Models
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
mock-dev-release / package-mock-release (push) Has been cancelled

- Added ConsoleExportClient for managing export requests and responses.
- Introduced ConsoleExportRequest and ConsoleExportResponse models.
- Implemented methods for creating and retrieving exports with appropriate headers.

feat(crypto): Add Software SM2/SM3 Cryptography Provider

- Implemented SmSoftCryptoProvider for software-only SM2/SM3 cryptography.
- Added support for signing and verification using SM2 algorithm.
- Included hashing functionality with SM3 algorithm.
- Configured options for loading keys from files and environment gate checks.

test(crypto): Add unit tests for SmSoftCryptoProvider

- Created comprehensive tests for signing, verifying, and hashing functionalities.
- Ensured correct behavior for key management and error handling.

feat(api): Enhance Console Export Models

- Expanded ConsoleExport models to include detailed status and event types.
- Added support for various export formats and notification options.

test(time): Implement TimeAnchorPolicyService tests

- Developed tests for TimeAnchorPolicyService to validate time anchors.
- Covered scenarios for anchor validation, drift calculation, and policy enforcement.
This commit is contained in:
StellaOps Bot
2025-12-07 00:27:33 +02:00
parent 9bd6a73926
commit 0de92144d2
229 changed files with 32351 additions and 1481 deletions

View File

@@ -0,0 +1,120 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AuthSessionStore } from '../auth/auth-session.store';
import {
CONSOLE_API_BASE_URL,
DEFAULT_EVENT_SOURCE_FACTORY,
EVENT_SOURCE_FACTORY,
EventSourceFactory,
} from './console-status.client';
import {
ConsoleExportEvent,
ConsoleExportRequest,
ConsoleExportStatusDto,
} from './console-export.models';
import { generateTraceId } from './trace.util';
@Injectable({
providedIn: 'root',
})
export class ConsoleExportClient {
constructor(
private readonly http: HttpClient,
private readonly authSession: AuthSessionStore,
@Inject(CONSOLE_API_BASE_URL) private readonly baseUrl: string,
@Inject(EVENT_SOURCE_FACTORY)
private readonly eventSourceFactory: EventSourceFactory = DEFAULT_EVENT_SOURCE_FACTORY
) {}
createExport(
request: ConsoleExportRequest,
options?: { tenantId?: string; traceId?: string; idempotencyKey?: string }
): Observable<ConsoleExportStatusDto> {
const trace = options?.traceId ?? generateTraceId();
const tenant = this.resolveTenant(options?.tenantId);
const headers = new HttpHeaders({
'X-StellaOps-Tenant': tenant,
'X-Stella-Trace-Id': trace,
'X-Stella-Request-Id': trace,
...(options?.idempotencyKey
? { 'Idempotency-Key': options.idempotencyKey }
: undefined),
});
return this.http
.post<ConsoleExportStatusDto>(`${this.baseUrl}/exports`, request, { headers })
.pipe(map(this.normalizeStatus));
}
getExport(
exportId: string,
options?: { tenantId?: string; traceId?: string }
): Observable<ConsoleExportStatusDto> {
const trace = options?.traceId ?? generateTraceId();
const tenant = this.resolveTenant(options?.tenantId);
const headers = new HttpHeaders({
'X-StellaOps-Tenant': tenant,
'X-Stella-Trace-Id': trace,
'X-Stella-Request-Id': trace,
});
return this.http
.get<ConsoleExportStatusDto>(`${this.baseUrl}/exports/${encodeURIComponent(exportId)}`, {
headers,
})
.pipe(map(this.normalizeStatus));
}
streamExport(
exportId: string,
options?: { tenantId?: string; traceId?: string }
): Observable<ConsoleExportEvent> {
const trace = options?.traceId ?? generateTraceId();
const tenant = this.resolveTenant(options?.tenantId);
const url = `${this.baseUrl}/exports/${encodeURIComponent(
exportId
)}/events?tenant=${encodeURIComponent(tenant)}&traceId=${encodeURIComponent(trace)}`;
return new Observable<ConsoleExportEvent>((observer) => {
const source = this.eventSourceFactory(url);
source.onmessage = (event) => {
try {
const parsed = JSON.parse(event.data) as ConsoleExportEvent;
observer.next(parsed);
} catch (err) {
observer.error(err);
}
};
source.onerror = (err) => {
observer.error(err);
source.close();
};
return () => source.close();
});
}
private resolveTenant(tenantId?: string): string {
const tenant = (tenantId && tenantId.trim()) || this.authSession.getActiveTenantId();
if (!tenant) {
throw new Error('ConsoleExportClient requires an active tenant identifier.');
}
return tenant;
}
private readonly normalizeStatus = (dto: ConsoleExportStatusDto): ConsoleExportStatusDto => ({
...dto,
estimateSeconds: dto.estimateSeconds ?? null,
retryAfter: dto.retryAfter ?? null,
createdAt: dto.createdAt ?? null,
updatedAt: dto.updatedAt ?? null,
outputs: dto.outputs ?? [],
progress: dto.progress ?? null,
errors: dto.errors ?? [],
});
}

View File

@@ -0,0 +1,96 @@
export type ConsoleExportStatus =
| 'queued'
| 'running'
| 'succeeded'
| 'failed'
| 'expired';
export type ConsoleExportFormat = 'json' | 'csv' | 'ndjson' | 'pdf';
export interface ConsoleExportScope {
readonly tenantId: string;
readonly projectId?: string | null;
}
export type ConsoleExportSourceType = 'advisory' | 'vex' | 'policy' | 'scan';
export interface ConsoleExportSource {
readonly type: ConsoleExportSourceType;
readonly ids: readonly string[];
}
export interface ConsoleExportAttestations {
readonly include: boolean;
readonly sigstoreBundle?: boolean;
}
export interface ConsoleExportNotify {
readonly webhooks?: readonly string[];
readonly email?: readonly string[];
}
export type ConsoleExportPriority = 'low' | 'normal' | 'high';
export interface ConsoleExportRequest {
readonly scope: ConsoleExportScope;
readonly sources: readonly ConsoleExportSource[];
readonly formats: readonly ConsoleExportFormat[];
readonly attestations?: ConsoleExportAttestations;
readonly notify?: ConsoleExportNotify;
readonly priority?: ConsoleExportPriority;
}
export interface ConsoleExportOutput {
readonly type: string;
readonly format: ConsoleExportFormat | string;
readonly url: string;
readonly sha256?: string;
readonly expiresAt?: string | null;
}
export interface ConsoleExportProgress {
readonly percent: number;
readonly itemsCompleted?: number;
readonly itemsTotal?: number;
readonly assetsReady?: number;
}
export interface ConsoleExportError {
readonly code: string;
readonly message: string;
}
export interface ConsoleExportStatusDto {
readonly exportId: string;
readonly status: ConsoleExportStatus;
readonly estimateSeconds?: number | null;
readonly retryAfter?: number | null;
readonly createdAt?: string | null;
readonly updatedAt?: string | null;
readonly outputs?: readonly ConsoleExportOutput[];
readonly progress?: ConsoleExportProgress | null;
readonly errors?: readonly ConsoleExportError[];
}
export type ConsoleExportEventType =
| 'started'
| 'progress'
| 'asset_ready'
| 'completed'
| 'failed';
export interface ConsoleExportEvent {
readonly event: ConsoleExportEventType;
readonly exportId: string;
readonly percent?: number;
readonly itemsCompleted?: number;
readonly itemsTotal?: number;
readonly type?: string;
readonly id?: string;
readonly url?: string;
readonly sha256?: string;
readonly status?: ConsoleExportStatus;
readonly manifestUrl?: string;
readonly code?: string;
readonly message?: string;
}