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

@@ -34,6 +34,23 @@ Run `ng build` to build the project. The build artifacts will be stored in the `
`verify:chromium` prints every location inspected (environment overrides, system paths, `.cache/chromium/`). Set `CHROME_BIN` or `STELLAOPS_CHROMIUM_BIN` if you host the binary in a non-standard path.
### Headless Karma recipe (offline-friendly)
For local, deterministic Karma runs without system Chrome:
```bash
cd src/Web/StellaOps.Web
CHROME_BIN=$(pwd)/node_modules/playwright/.local-browsers/chromium-1140/chrome-linux/chrome \
LD_LIBRARY_PATH=$(pwd)/.deps/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH \
npx ng test --watch=false --browsers=ChromeHeadless --progress=false \
--include src/app/features/policy-studio/editor/policy-editor.component.spec.ts \
--source-map=false
```
- The `.deps` folder carries the minimal NSS/GTK libs we vendor for air-gapped nodes.
- Use one `--include` per invocation; Angular CLI rejects multiple `--include` flags.
- Monaco is file-replaced with a lightweight test stub during Karma runs; production builds are unaffected.
## Runtime configuration
The SPA loads environment details from `/config.json` at startup. During development we ship a stub configuration under `src/config/config.json`; adjust the issuer, client ID, and API base URLs to match your Authority instance. To reset, copy `src/config/config.sample.json` back to `src/config/config.json`:

View File

@@ -11,7 +11,7 @@
| WEB-VULN-29-LEDGER-DOC | DONE (2025-12-01) | Findings Ledger proxy contract doc v1.0 with idempotency + retries (`docs/api/gateway/findings-ledger-proxy.md`). |
| WEB-RISK-68-NOTIFY-DOC | DONE (2025-12-01) | Notifications severity transition event schema v1.0 published (`docs/api/gateway/notifications-severity.md`). |
| UI-MICRO-GAPS-0209-011 | BLOCKED (2025-12-06) | Motion token catalog + Storybook/Playwright a11y harness added; remaining work paused pending SIG-26 reachability fixtures and final token mapping approvals. |
| UI-POLICY-20-001 | DONE (2025-12-05) | Policy Studio Monaco editor with DSL highlighting, lint markers, and compliance checklist shipped. |
| UI-POLICY-20-001 | DONE (2025-12-05) | Policy Studio Monaco editor with DSL highlighting, lint markers, and compliance checklist shipped; Karma spec now passes locally via Monaco loader file-replacement stub + Playwright Chromium/.deps NSS libs. |
| UI-POLICY-20-002 | DONE (2025-12-05) | Simulation panel with deterministic diff rendering shipped (`/policy-studio/packs/:packId/simulate`). |
| UI-POLICY-20-003 | DONE (2025-12-05) | Approvals workflow UI delivered with submit/review actions, two-person badge, and deterministic log. |
| UI-POLICY-20-004 | DONE (2025-12-05) | Policy run dashboards delivered with filters, exports, heatmap, and daily deltas. |

View File

@@ -0,0 +1,69 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { AuthSessionStore } from '../auth/auth-session.store';
import { ConsoleExportClient } from './console-export.client';
import {
CONSOLE_API_BASE_URL,
DEFAULT_EVENT_SOURCE_FACTORY,
EVENT_SOURCE_FACTORY,
} from './console-status.client';
import { ConsoleExportRequest } from './console-export.models';
describe('ConsoleExportClient', () => {
let client: ConsoleExportClient;
let httpMock: HttpTestingController;
const baseUrl = '/console';
const exportRequest: ConsoleExportRequest = {
scope: { tenantId: 'tenant-default', projectId: 'proj-1' },
sources: [{ type: 'advisory', ids: ['CVE-2024-12345'] }],
formats: ['json'],
attestations: { include: true, sigstoreBundle: true },
notify: { webhooks: ['https://hooks.local/export'] },
priority: 'normal',
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
ConsoleExportClient,
{ provide: CONSOLE_API_BASE_URL, useValue: baseUrl },
{ provide: EVENT_SOURCE_FACTORY, useValue: DEFAULT_EVENT_SOURCE_FACTORY },
{
provide: AuthSessionStore,
useValue: {
getActiveTenantId: () => 'tenant-default',
} satisfies Partial<AuthSessionStore>,
},
],
});
client = TestBed.inject(ConsoleExportClient);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => httpMock.verify());
it('posts export request with tenant and trace headers', () => {
client.createExport(exportRequest, { traceId: 'trace-1', idempotencyKey: 'abc' }).subscribe();
const req = httpMock.expectOne('/console/exports');
expect(req.request.method).toBe('POST');
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-default');
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-1');
expect(req.request.headers.get('Idempotency-Key')).toBe('abc');
req.flush({ exportId: 'exp-1', status: 'queued' });
});
it('gets export status with tenant header', () => {
client.getExport('exp-1', { traceId: 'trace-2', tenantId: 'tenant-xyz' }).subscribe();
const req = httpMock.expectOne('/console/exports/exp-1');
expect(req.request.method).toBe('GET');
expect(req.request.headers.get('X-StellaOps-Tenant')).toBe('tenant-xyz');
expect(req.request.headers.get('X-Stella-Trace-Id')).toBe('trace-2');
req.flush({ exportId: 'exp-1', status: 'running' });
});
});

View File

@@ -0,0 +1,67 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthSessionStore } from '../auth/auth-session.store';
import { CONSOLE_API_BASE_URL } from './console-status.client';
import {
ConsoleExportRequest,
ConsoleExportResponse,
} from './console-export.models';
import { generateTraceId } from './trace.util';
interface ExportRequestOptions {
tenantId?: string;
traceId?: string;
idempotencyKey?: string;
}
interface ExportGetOptions {
tenantId?: string;
traceId?: string;
}
@Injectable({
providedIn: 'root',
})
export class ConsoleExportClient {
constructor(
private readonly http: HttpClient,
private readonly authSession: AuthSessionStore,
@Inject(CONSOLE_API_BASE_URL) private readonly baseUrl: string
) {}
createExport(
request: ConsoleExportRequest,
options: ExportRequestOptions = {}
): Observable<ConsoleExportResponse> {
const headers = options.idempotencyKey
? this.buildHeaders(options).set('Idempotency-Key', options.idempotencyKey)
: this.buildHeaders(options);
return this.http.post<ConsoleExportResponse>(`${this.baseUrl}/exports`, request, { headers });
}
getExport(exportId: string, options: ExportGetOptions = {}): Observable<ConsoleExportResponse> {
const headers = this.buildHeaders(options);
return this.http.get<ConsoleExportResponse>(
`${this.baseUrl}/exports/${encodeURIComponent(exportId)}`,
{ headers }
);
}
private buildHeaders(opts: { tenantId?: string; traceId?: string }): HttpHeaders {
const tenant = (opts.tenantId && opts.tenantId.trim()) || this.authSession.getActiveTenantId();
if (!tenant) {
throw new Error('ConsoleExportClient requires an active tenant identifier.');
}
const trace = opts.traceId ?? generateTraceId();
return new HttpHeaders({
'X-StellaOps-Tenant': tenant,
'X-Stella-Trace-Id': trace,
'X-Stella-Request-Id': trace,
});
}
}

View File

@@ -0,0 +1,38 @@
export interface ConsoleExportScope {
tenantId: string;
projectId?: string;
}
export interface ConsoleExportSource {
type: string;
ids: string[];
}
export interface ConsoleExportFormats {
formats: string[];
}
export interface ConsoleExportAttestations {
include: boolean;
sigstoreBundle?: boolean;
}
export interface ConsoleExportNotify {
webhooks?: string[];
}
export type ConsoleExportPriority = 'low' | 'normal' | 'high' | string;
export interface ConsoleExportRequest {
scope: ConsoleExportScope;
sources: ConsoleExportSource[];
formats: string[];
attestations?: ConsoleExportAttestations;
notify?: ConsoleExportNotify;
priority?: ConsoleExportPriority;
}
export interface ConsoleExportResponse {
exportId: string;
status: string;
}

View File

@@ -13,9 +13,11 @@ class MonacoLoaderStub {
value: '',
getValue: () => this.model.value,
setValue: (v: string) => (this.model.value = v),
dispose: () => undefined,
} as any;
editor = {
onDidChangeModelContent: () => ({ dispose: () => undefined }),
dispose: () => undefined,
} as any;
lastMarkers: any[] = [];