feat: Enhance traceability and logging in Risk and Vulnerability clients
- Implemented shared trace ID generation utility for Risk and Vulnerability clients, ensuring consistent trace headers across API calls. - Updated RiskHttpClient and VulnerabilityHttpClient to utilize the new trace ID generation method. - Added validation for artifact metadata in PackRun endpoints, ensuring all artifacts include a digest and positive size. - Enhanced logging payloads in PackRun to include artifact digests and sizes. - Created a utility for generating trace IDs, preferring crypto.randomUUID when available, with a fallback to a ULID-style string. - Added unit tests to verify the presence of trace IDs in HTTP requests for VulnerabilityHttpClient. - Documented query-hash metrics for Vuln Explorer, detailing hashing rules and logging filters to ensure compliance with privacy standards. - Consolidated findings from late-November reviews into a comprehensive advisory for Scanner and SBOM/VEX areas, outlining remediation tracks and gaps.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
| WEB-AOC-19-002 | DONE (2025-11-30) | Added provenance builder, checksum utilities, and DSSE/CMS signature verification helpers with unit tests. |
|
||||
| WEB-AOC-19-003 | DONE (2025-11-30) | Added client-side guard validator (forbidden/derived/unknown fields, provenance/signature checks) with unit fixtures. |
|
||||
| WEB-CONSOLE-23-002 | DOING (2025-12-01) | Console status polling + SSE run stream client/store/UI added; tests pending once env fixed. |
|
||||
| WEB-RISK-66-001 | DOING (2025-12-02) | Added risk gateway HTTP client (trace-id headers), store, `/risk` dashboard with filters, empty state, vuln link, auth guard; added `/vulnerabilities/:vulnId` detail + specs; risk/vuln providers switch via quickstart; awaiting gateway endpoints/test harness. |
|
||||
| WEB-RISK-66-001 | DOING (2025-12-02) | Added risk + vuln gateway HTTP clients (shared trace util), store, `/risk` dashboard with filters/empty state/vuln link, auth guard; added `/vulnerabilities/:vulnId` detail + specs; providers switch via quickstart; awaiting gateway endpoints/test harness. |
|
||||
| WEB-EXC-25-001 | TODO | Exceptions workflow CRUD pending policy scopes. |
|
||||
| WEB-TEN-47-CONTRACT | DONE (2025-12-01) | Gateway tenant auth/ABAC contract doc v1.0 published (`docs/api/gateway/tenant-auth.md`). |
|
||||
| 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`). |
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Observable, map } from 'rxjs';
|
||||
import { AuthSessionStore } from '../auth/auth-session.store';
|
||||
import { RiskApi } from './risk.client';
|
||||
import { RiskQueryOptions, RiskResultPage, RiskStats } from './risk.models';
|
||||
import { generateTraceId } from './trace.util';
|
||||
|
||||
export const RISK_API_BASE_URL = new InjectionToken<string>('RISK_API_BASE_URL');
|
||||
|
||||
@@ -18,7 +19,7 @@ export class RiskHttpClient implements RiskApi {
|
||||
|
||||
list(options: RiskQueryOptions): Observable<RiskResultPage> {
|
||||
const tenant = this.resolveTenant(options.tenantId);
|
||||
const traceId = options.traceId ?? crypto.randomUUID?.() ?? this.generateTraceId();
|
||||
const traceId = options.traceId ?? generateTraceId();
|
||||
const headers = this.buildHeaders(tenant, options.projectId, traceId);
|
||||
|
||||
let params = new HttpParams();
|
||||
@@ -40,7 +41,7 @@ export class RiskHttpClient implements RiskApi {
|
||||
|
||||
stats(options: Pick<RiskQueryOptions, 'tenantId' | 'projectId' | 'traceId'>): Observable<RiskStats> {
|
||||
const tenant = this.resolveTenant(options.tenantId);
|
||||
const traceId = options.traceId ?? crypto.randomUUID?.() ?? this.generateTraceId();
|
||||
const traceId = options.traceId ?? generateTraceId();
|
||||
const headers = this.buildHeaders(tenant, options.projectId, traceId);
|
||||
|
||||
return this.http
|
||||
@@ -60,13 +61,6 @@ export class RiskHttpClient implements RiskApi {
|
||||
return headers;
|
||||
}
|
||||
|
||||
private generateTraceId(): string {
|
||||
// Lightweight ULID-like generator (time + random) for trace correlation.
|
||||
const time = Date.now().toString(36);
|
||||
const rand = crypto.getRandomValues(new Uint32Array(1))[0].toString(36).padStart(6, '0');
|
||||
return `${time}-${rand}`;
|
||||
}
|
||||
|
||||
private resolveTenant(tenantId?: string): string {
|
||||
const tenant = (tenantId && tenantId.trim()) || this.authSession.getActiveTenantId();
|
||||
if (!tenant) {
|
||||
|
||||
13
src/Web/StellaOps.Web/src/app/core/api/trace.util.ts
Normal file
13
src/Web/StellaOps.Web/src/app/core/api/trace.util.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Generate a correlation/trace identifier.
|
||||
* Prefers crypto.randomUUID when available; falls back to a lightweight ULID-style string.
|
||||
*/
|
||||
export function generateTraceId(): string {
|
||||
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
|
||||
const time = Date.now().toString(36);
|
||||
const rand = crypto.getRandomValues(new Uint32Array(1))[0].toString(36).padStart(6, '0');
|
||||
return `${time}-${rand}`;
|
||||
}
|
||||
@@ -40,6 +40,7 @@ describe('VulnerabilityHttpClient', () => {
|
||||
|
||||
const req = httpMock.expectOne('https://api.example.local/vuln?page=1&pageSize=5');
|
||||
expect(req.request.headers.get('X-Stella-Tenant')).toBe('tenant-dev');
|
||||
expect(req.request.headers.has('X-Stella-Trace-Id')).toBeTrue();
|
||||
req.flush(stub);
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Vulnerability,
|
||||
VulnerabilityStats,
|
||||
} from './vulnerability.models';
|
||||
import { generateTraceId } from './trace.util';
|
||||
import { VulnerabilityApi } from './vulnerability.client';
|
||||
|
||||
export const VULNERABILITY_API_BASE_URL = new InjectionToken<string>('VULNERABILITY_API_BASE_URL');
|
||||
@@ -23,7 +24,8 @@ export class VulnerabilityHttpClient implements VulnerabilityApi {
|
||||
|
||||
listVulnerabilities(options?: VulnerabilitiesQueryOptions): Observable<VulnerabilitiesResponse> {
|
||||
const tenant = this.resolveTenant(options?.tenantId);
|
||||
const headers = this.buildHeaders(tenant, options?.projectId, options?.traceId);
|
||||
const traceId = options?.traceId ?? generateTraceId();
|
||||
const headers = this.buildHeaders(tenant, options?.projectId, traceId);
|
||||
|
||||
let params = new HttpParams();
|
||||
if (options?.page) params = params.set('page', options.page);
|
||||
@@ -39,13 +41,15 @@ export class VulnerabilityHttpClient implements VulnerabilityApi {
|
||||
|
||||
getVulnerability(vulnId: string): Observable<Vulnerability> {
|
||||
const tenant = this.resolveTenant();
|
||||
const headers = this.buildHeaders(tenant, undefined, undefined);
|
||||
const traceId = generateTraceId();
|
||||
const headers = this.buildHeaders(tenant, undefined, traceId);
|
||||
return this.http.get<Vulnerability>(`${this.baseUrl}/vuln/${encodeURIComponent(vulnId)}`, { headers });
|
||||
}
|
||||
|
||||
getStats(): Observable<VulnerabilityStats> {
|
||||
const tenant = this.resolveTenant();
|
||||
const headers = this.buildHeaders(tenant, undefined, undefined);
|
||||
const traceId = generateTraceId();
|
||||
const headers = this.buildHeaders(tenant, undefined, traceId);
|
||||
return this.http.get<VulnerabilityStats>(`${this.baseUrl}/vuln/status`, { headers });
|
||||
}
|
||||
|
||||
@@ -63,4 +67,5 @@ export class VulnerabilityHttpClient implements VulnerabilityApi {
|
||||
}
|
||||
return tenant;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user