fix(web,gateway): close P3 re-review#3 findings — sanitize command errors, harden turn-lock & caps

This commit is contained in:
shaggy (mosaic-dev box)
2026-08-10 14:21:52 -05:00
parent 48bb19310d
commit d46a2d675a
7 changed files with 350 additions and 166 deletions
+13 -2
View File
@@ -1,5 +1,6 @@
import { useState, type ReactElement } from 'react';
import type { PendingApproval } from './use-chat-connection';
import { MAX_COMMAND_MESSAGE_CHARS } from './limits';
import { asNonEmptyString, asString } from './runtime-guards';
import type {
CommandManifest,
@@ -13,6 +14,16 @@ import type {
* directly, never a raw thrown exception, stack trace, or object value. */
const COMMAND_FAILURE_COPY = 'Command failed.';
/** Render-site defense-in-depth: `use-chat-connection.ts` already bounds a
* stored command:result message at ingestion, but this component must never
* assume every caller went through that path — bounding again here means a
* hostile/oversized message can never force an unbounded render. */
function boundMessage(value: string): string {
return value.length > MAX_COMMAND_MESSAGE_CHARS
? value.slice(0, MAX_COMMAND_MESSAGE_CHARS)
: value;
}
interface CommandsPanelProps {
manifest: CommandManifest | null;
results: SlashCommandResultPayload[];
@@ -141,9 +152,9 @@ export function CommandsPanel({
/{asString(result.command)}: {result.success ? 'success' : 'failed'}
{result.success
? typeof result.message === 'string' && result.message
? `${result.message}`
? `${boundMessage(result.message)}`
: ''
: `${asNonEmptyString(result.message, COMMAND_FAILURE_COPY)}`}
: `${boundMessage(asNonEmptyString(result.message, COMMAND_FAILURE_COPY))}`}
</li>
))}
</ul>