fix(tess): redact sensitive runtime content

This commit is contained in:
Jarvis
2026-07-12 17:00:52 -05:00
parent e92186d768
commit 5f9067cf57
5 changed files with 71 additions and 16 deletions

View File

@@ -106,8 +106,8 @@ describe('CommandExecutorService — P8-012 commands', () => {
expect(result.command).toBe('provider');
});
// /provider login anthropic — success with URL containing poll token
it('/provider login <name> returns success with URL and poll token', async () => {
// /provider login anthropic — no bearer token or auth URL reaches chat output
it('/provider login <name> keeps its one-time token out of chat output', async () => {
const payload: SlashCommandPayload = {
command: 'provider',
args: 'login anthropic',
@@ -117,14 +117,9 @@ describe('CommandExecutorService — P8-012 commands', () => {
expect(result.success).toBe(true);
expect(result.command).toBe('provider');
expect(result.message).toContain('anthropic');
expect(result.message).toContain('http');
// data should contain loginUrl and pollToken
expect(result.data).toBeDefined();
const data = result.data as Record<string, unknown>;
expect(typeof data['loginUrl']).toBe('string');
expect(typeof data['pollToken']).toBe('string');
expect(data['loginUrl'] as string).toContain('anthropic');
expect(data['loginUrl'] as string).toContain(data['pollToken'] as string);
expect(result.message).not.toContain('http');
expect(result.message).not.toContain('token=');
expect(result.data).toEqual({ provider: 'anthropic' });
// Verify Valkey was called
expect(mockRedis.set).toHaveBeenCalledOnce();
const [key, value, , ttl] = mockRedis.set.mock.calls[0] as [string, string, string, number];

View File

@@ -436,22 +436,28 @@ export class CommandExecutorService {
};
}
const pollToken = crypto.randomUUID();
const key = `mosaic:auth:poll:${pollToken}`;
// Store pending state in Valkey (TTL 5 minutes)
const tokenDigest = await crypto.subtle.digest(
'SHA-256',
new TextEncoder().encode(pollToken),
);
const tokenHash = Array.from(new Uint8Array(tokenDigest), (byte: number): string =>
byte.toString(16).padStart(2, '0'),
).join('');
const key = `mosaic:auth:poll:${tokenHash}`;
// Persist only a short-lived token digest. The raw token is delivered only by
// the authenticated dashboard flow, never in chat output or command metadata.
await this.redis.set(
key,
JSON.stringify({ status: 'pending', provider: providerName, userId }),
'EX',
300,
);
// In production this would construct an OAuth URL
const loginUrl = `${process.env['MOSAIC_BASE_URL'] ?? 'http://localhost:3000'}/auth/provider/${providerName}?token=${pollToken}`;
return {
command: 'provider',
success: true,
message: `Open this URL to authenticate with ${providerName}:\n${loginUrl}`,
message: `Provider login for ${providerName} is ready. Continue in the authenticated dashboard.`,
conversationId,
data: { loginUrl, pollToken, provider: providerName },
data: { provider: providerName },
};
}