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
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:
@@ -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`:
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -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' });
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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[] = [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user