feat(tess): add safe runtime observability (#726)
This commit was merged in pull request #726.
This commit is contained in:
@@ -15,6 +15,7 @@ import type {
|
||||
import { AgentRuntimeProviderRegistry } from '@mosaicstack/agent';
|
||||
import type { ActorTenantScope } from '../../auth/session-scope.js';
|
||||
import {
|
||||
RuntimeProviderAuditService,
|
||||
RuntimeProviderService,
|
||||
type RuntimeAuditEvent,
|
||||
type RuntimeAuditSink,
|
||||
@@ -159,20 +160,47 @@ describe('RuntimeProviderService security boundary', (): void => {
|
||||
correlationId: CONTEXT.correlationId,
|
||||
});
|
||||
expect(Object.isFrozen(providerScope)).toBe(true);
|
||||
expect(audit.events).toContainEqual({
|
||||
providerId: 'fleet',
|
||||
operation: 'session.send',
|
||||
outcome: 'succeeded',
|
||||
actorId: OWNER_SCOPE.userId,
|
||||
tenantId: OWNER_SCOPE.tenantId,
|
||||
channelId: CONTEXT.channelId,
|
||||
correlationId: CONTEXT.correlationId,
|
||||
resourceId: 'session-1',
|
||||
});
|
||||
expect(audit.events).toContainEqual(
|
||||
expect.objectContaining({
|
||||
providerId: 'fleet',
|
||||
operation: 'session.send',
|
||||
outcome: 'succeeded',
|
||||
actorId: OWNER_SCOPE.userId,
|
||||
tenantId: OWNER_SCOPE.tenantId,
|
||||
channelId: CONTEXT.channelId,
|
||||
correlationId: CONTEXT.correlationId,
|
||||
resourceId: 'session-1',
|
||||
durationMs: expect.any(Number),
|
||||
}),
|
||||
);
|
||||
expect(JSON.stringify(audit.events)).not.toContain('hello');
|
||||
expect(JSON.stringify(audit.events)).not.toContain('key-1');
|
||||
});
|
||||
|
||||
it('does not block a provider operation when an unsafe resource ID is redacted in durable audit', async (): Promise<void> => {
|
||||
const provider = new RecordingRuntimeProvider(['session.send']);
|
||||
let persisted: unknown;
|
||||
const durableAudit = new RuntimeProviderAuditService({
|
||||
logs: {
|
||||
ingest: async (entry: unknown): Promise<unknown> => {
|
||||
persisted = entry;
|
||||
return entry;
|
||||
},
|
||||
},
|
||||
} as never);
|
||||
const service = makeService(provider, durableAudit);
|
||||
|
||||
await service.sendMessage(
|
||||
'fleet',
|
||||
'session/credential-canary=secret-value',
|
||||
{ content: 'safe message', idempotencyKey: 'key-1' },
|
||||
CONTEXT,
|
||||
);
|
||||
|
||||
expect(provider.sentMessages).toHaveLength(1);
|
||||
expect(JSON.stringify(persisted)).not.toContain('secret-value');
|
||||
});
|
||||
|
||||
it('fails closed before a provider side effect when a capability is missing', async (): Promise<void> => {
|
||||
const provider = new RecordingRuntimeProvider([]);
|
||||
const service = makeService(provider);
|
||||
@@ -191,12 +219,14 @@ describe('RuntimeProviderService security boundary', (): void => {
|
||||
it('requires a consumed exact-action approval before termination', async (): Promise<void> => {
|
||||
const provider = new RecordingRuntimeProvider(['session.terminate']);
|
||||
const approval = new DenyingApprovalVerifier();
|
||||
const service = makeService(provider, new RecordingAuditSink(), approval);
|
||||
const audit = new RecordingAuditSink();
|
||||
const service = makeService(provider, audit, approval);
|
||||
|
||||
await expect(
|
||||
service.terminate('fleet', 'session-1', 'forged-approval', CONTEXT),
|
||||
).rejects.toThrow(/approval denied/);
|
||||
expect(provider.terminateCalls).toBe(0);
|
||||
expect(audit.events.at(-1)).toMatchObject({ outcome: 'denied', errorCode: 'policy_denied' });
|
||||
});
|
||||
|
||||
it('binds an accepted termination approval to provider, session, immutable scope, and correlation', async (): Promise<void> => {
|
||||
@@ -256,6 +286,37 @@ describe('RuntimeProviderService security boundary', (): void => {
|
||||
'requested',
|
||||
'failed',
|
||||
]);
|
||||
expect(audit.events.at(-1)).toMatchObject({
|
||||
errorCode: 'provider_error',
|
||||
durationMs: expect.any(Number),
|
||||
});
|
||||
});
|
||||
|
||||
it('persists only metadata-only runtime audit fields', async (): Promise<void> => {
|
||||
let persisted: unknown;
|
||||
const ingest = async (entry: unknown): Promise<unknown> => {
|
||||
persisted = entry;
|
||||
return entry;
|
||||
};
|
||||
const service = new RuntimeProviderAuditService({ logs: { ingest } } as never);
|
||||
|
||||
await service.record({
|
||||
providerId: 'fleet',
|
||||
operation: 'session.send',
|
||||
outcome: 'succeeded',
|
||||
actorId: 'owner-1',
|
||||
tenantId: 'tenant-1',
|
||||
channelId: 'cli',
|
||||
correlationId: 'correlation-1',
|
||||
resourceId: 'session-1',
|
||||
durationMs: 12,
|
||||
});
|
||||
|
||||
expect(persisted).toMatchObject({
|
||||
content: 'runtime.provider.audit',
|
||||
metadata: expect.objectContaining({ correlationId: 'correlation-1', durationMs: 12 }),
|
||||
});
|
||||
expect(JSON.stringify(persisted)).not.toContain('approval');
|
||||
});
|
||||
|
||||
it('does not misreport a completed provider side effect when completion auditing fails', async (): Promise<void> => {
|
||||
|
||||
Reference in New Issue
Block a user