blob: 6b342e96a7c24442b3a1c2241be583ea91545cd3 [file]
const SAFE_TRACE_KEYS = new Set([
'type',
'actionType',
'path',
'effect',
'verified',
'supported',
'ok',
'durationMs',
'at',
'toolCallId',
'pid',
'windowId',
'expectedPid',
'expectedWindowId',
'winnerPid',
'winnerWindowId',
'winnerZIndex',
'address',
'tool',
]);
const SAFE_TRACE_TYPES = new Set([
'dispatch',
'fallback',
'occlusion',
'outcome',
'semantic_result',
'snapshot',
'target',
'tool_start',
'tool_result',
'complete',
'abort',
'error',
]);
const SAFE_ACTION_TYPES = new Set([
'list_apps',
'observe',
'click_element',
'set_value',
'select_text',
'secondary_action',
'press_key',
'screenshot',
'cursor_position',
'mouse_move',
'left_click',
'right_click',
'middle_click',
'double_click',
'triple_click',
'left_mouse_down',
'left_mouse_up',
'left_click_drag',
'type',
'key',
'hold_key',
'scroll',
'wait',
'zoom',
'unknown',
]);
const SAFE_TRACE_PATHS = new Set(['ax', 'cdp', 'cgevent', 'screenshot-detail']);
const SAFE_TRACE_EFFECTS = new Set(['confirmed', 'unverifiable']);
const SAFE_DISPATCH_ADDRESSES = new Set(['ax', 'px', 'semantic', 'none']);
const SAFE_DISPATCH_TOOLS = new Set([
'click',
'set_value',
'page',
'press_key',
'scroll',
'drag',
'zoom',
]);
function safeUrlOrigin(value) {
try {
return new URL(value).origin;
} catch {
return undefined;
}
}
function actionType(action) {
return typeof action?.type === 'string'
? action.type
: typeof action?.action === 'string'
? action.action
: 'unknown';
}
function safeToken(value, allowed) {
return typeof value === 'string' && allowed.has(value) ? value : undefined;
}
function resultCode(text) {
if (typeof text !== 'string') return undefined;
const failed = text.match(/\bfailed:\s*([a-z][a-z0-9_]{1,63})\b/i);
if (failed) return failed[1];
const status = text.match(/\b(?:ok|error)=([a-z][a-z0-9_]{1,63})\b/i);
return status?.[1];
}
export function sanitizeCuActionRecord(record) {
const type = actionType(record?.action ?? record);
return {
type: SAFE_ACTION_TYPES.has(type) ? type : 'unknown',
...(safeId(record?.toolCallId) ? { toolCallId: safeId(record.toolCallId) } : {}),
...(safeId(record?.sourceObservationId)
? { sourceObservationId: safeId(record.sourceObservationId) }
: {}),
...(type !== 'screenshot' && safeId(record?.resultObservationId)
? { resultObservationId: safeId(record.resultObservationId) }
: {}),
...(Number.isInteger(record?.targetPid) && record.targetPid > 0
? { targetPid: record.targetPid }
: {}),
...(Number.isInteger(record?.targetWindowId) && record.targetWindowId > 0
? { targetWindowId: record.targetWindowId }
: {}),
...(typeof record?.success === 'boolean' ? { success: record.success } : {}),
...(typeof record?.targetOwned === 'boolean' ? { targetOwned: record.targetOwned } : {}),
...(record?.expectedFailure === true ? { expectedFailure: true } : {}),
...(Number.isFinite(record?.durationMs) ? { durationMs: record.durationMs } : {}),
...(Number.isFinite(record?.modelLatencyMs) ? { modelLatencyMs: record.modelLatencyMs } : {}),
...(Number.isFinite(record?.toolLatencyMs) ? { toolLatencyMs: record.toolLatencyMs } : {}),
...(Number.isFinite(record?.displayLagMs) ? { displayLagMs: record.displayLagMs } : {}),
...(resultCode(record?.text) ? { resultCode: resultCode(record.text) } : {}),
};
}
export function sanitizeCuTrace(trace) {
if (!trace || typeof trace !== 'object') return null;
if (!SAFE_TRACE_TYPES.has(trace.type)) return null;
const sanitized = {};
for (const [key, value] of Object.entries(trace)) {
if (!SAFE_TRACE_KEYS.has(key)) continue;
if (key === 'type') {
sanitized.type = trace.type;
continue;
}
if (key === 'actionType') {
const action = safeToken(value, SAFE_ACTION_TYPES);
if (action) sanitized.actionType = action;
continue;
}
if (key === 'path') {
const path = safeToken(value, SAFE_TRACE_PATHS);
if (path) sanitized.path = path;
continue;
}
if (key === 'effect') {
const effect = safeToken(value, SAFE_TRACE_EFFECTS);
if (effect) sanitized.effect = effect;
continue;
}
if (key === 'address') {
const address = safeToken(value, SAFE_DISPATCH_ADDRESSES);
if (address) sanitized.address = address;
continue;
}
if (key === 'tool') {
const tool = safeToken(value, SAFE_DISPATCH_TOOLS);
if (tool) sanitized.tool = tool;
continue;
}
if (typeof value === 'string' || typeof value === 'boolean' || Number.isFinite(value)) {
sanitized[key] = value;
}
}
return Object.keys(sanitized).length > 0 ? sanitized : null;
}
export function sanitizeCuModelPlans(plans) {
return Array.isArray(plans)
? plans.map((plan) => ({
turn: plan?.turn,
actionTypes: Array.isArray(plan?.actions)
? plan.actions.map((action) => actionType(action))
: [],
}))
: [];
}
export function sanitizeCuDirectReport(report) {
return sanitizeCuReport({
...report,
endpointOrigin: safeUrlOrigin(report?.endpointOrigin ?? report?.baseUrl),
});
}
export function sanitizeCuReport(report) {
const terminal = report?.terminal;
const fixtureState = report?.fixtureState;
const expectedState = Array.isArray(report?.expectedState)
? report.expectedState.map(sanitizeAssertionResult).filter(Boolean)
: [];
const violations = Array.isArray(report?.forbiddenEffects?.violations)
? report.forbiddenEffects.violations.map(sanitizeAssertionResult).filter(Boolean)
: [];
return {
schemaVersion: report?.schemaVersion === 1 ? 1 : undefined,
runId: safeId(report?.runId),
gitRevision: safeGitRevision(report?.gitRevision),
generatedAt: safeTimestamp(report?.generatedAt),
contentLineage: sanitizeContentLineage(report?.contentLineage),
evidenceClass: safeToken(
report?.evidenceClass,
new Set(['real-runtime', 'fault-injection', 'hermetic-protocol', 'static-contract']),
),
scenarioId: safeId(report?.scenarioId),
producer: safeId(report?.producer),
transportClass: safeToken(
report?.transportClass,
new Set(['live-network', 'hermetic', 'static']),
),
policyMode: safeToken(report?.policyMode, new Set(['enforced', 'bypassed'])),
qualificationEligible: report?.qualificationEligible === true,
deprecated: report?.deprecated === true,
toolExposure: safeToken(report?.toolExposure, new Set(['direct-e2e', 'deferred'])),
provider: safeId(report?.provider),
model: safeId(report?.model),
endpointOrigin: safeUrlOrigin(report.baseUrl),
status: safeToken(report?.status, new Set(['pass', 'fail', 'inconclusive'])),
terminal:
terminal && typeof terminal === 'object'
? {
type: safeToken(terminal.type, new Set(['complete', 'abort', 'error'])),
stopReason: safeToken(
terminal.stopReason,
new Set([
'end_turn',
'max_tokens',
'step_limit',
'error',
'user_stop',
'permission_handoff',
]),
),
}
: undefined,
fixtureIdentity: sanitizeFixtureIdentity(report?.fixtureIdentity),
faultInjection: sanitizeFaultInjection(report?.faultInjection),
run:
report?.run && typeof report.run === 'object'
? {
status: safeToken(
report.run.status,
new Set([
'created',
'running',
'waiting_for_user',
'completed',
'failed',
'cancelled',
]),
),
failureClass: safeId(report.run.failureClass),
...(Number.isFinite(report.run.durationMs)
? { durationMs: report.run.durationMs }
: {}),
}
: undefined,
...(Number.isFinite(report?.totalLatencyMs) ? { totalLatencyMs: report.totalLatencyMs } : {}),
actionAttempts: Number.isInteger(report?.actionAttempts) ? report.actionAttempts : undefined,
actionCount: Number.isInteger(report?.actionCount) ? report.actionCount : undefined,
actionCounts: sanitizeCountMap(report?.actionCounts),
minimumActionsPassed: report?.minimumActionsPassed === true,
actionsWithinBudget: report?.actionsWithinBudget === true,
dispatchPathPassed: report?.dispatchPathPassed === true,
actions: Array.isArray(report?.actions) ? report.actions.map(sanitizeCuActionRecord) : [],
fixtureState: sanitizeFixtureState(fixtureState),
expectedState,
forbiddenEffects: {
status: safeToken(report?.forbiddenEffects?.status, new Set(['pass', 'fail'])),
violations,
},
traces: Array.isArray(report?.traces) ? report.traces.map(sanitizeCuTrace).filter(Boolean) : [],
driverTraces: Array.isArray(report?.driverTraces)
? report.driverTraces.map(sanitizeCuTrace).filter(Boolean)
: [],
};
}
function sanitizeContentLineage(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
const generator = safeScriptPath(value.generator);
const gitRevision = safeGitRevision(value.gitRevision);
const generatedAt = safeTimestamp(value.generatedAt);
if (!generator || !gitRevision || !generatedAt) return undefined;
return { generator, gitRevision, generatedAt };
}
function safeScriptPath(value) {
return typeof value === 'string' && /^scripts\/[A-Za-z0-9._/-]{1,192}$/.test(value)
? value
: undefined;
}
function safeGitRevision(value) {
return typeof value === 'string' && /^[a-f0-9]{40}$/.test(value) ? value : undefined;
}
function safeTimestamp(value) {
if (typeof value !== 'string') return undefined;
const timestamp = Date.parse(value);
return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value
? value
: undefined;
}
function sanitizeFixtureIdentity(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
const candidates = Array.isArray(value.instances) ? value.instances : [value];
const instances = candidates.flatMap((candidate) => {
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) return [];
const pid = Number.isInteger(candidate.pid) && candidate.pid > 0 ? candidate.pid : undefined;
const windowIds = Array.isArray(candidate.windowIds)
? [...new Set(candidate.windowIds.filter((entry) => Number.isInteger(entry) && entry > 0))]
: [];
return pid && windowIds.length > 0 ? [{ pid, windowIds }] : [];
});
if (instances.length === 0) return undefined;
return {
instances: instances.filter(
(candidate, index) =>
instances.findIndex(
(entry) =>
entry.pid === candidate.pid &&
JSON.stringify(entry.windowIds) === JSON.stringify(candidate.windowIds),
) === index,
),
};
}
function sanitizeFaultInjection(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
const layer = safeToken(value.layer, new Set(['runtime']));
const kind = safeId(value.kind);
return layer && kind ? { layer, kind } : undefined;
}
function safeId(value) {
return typeof value === 'string' && /^[A-Za-z0-9._-]{1,128}$/.test(value) ? value : undefined;
}
function sanitizeCountMap(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
return Object.fromEntries(
Object.entries(value).flatMap(([key, count]) =>
SAFE_ACTION_TYPES.has(key) && Number.isInteger(count) && count >= 0 ? [[key, count]] : [],
),
);
}
function sanitizeFixtureState(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
return Object.fromEntries(
Object.entries(value).flatMap(([windowId, state]) => {
if (!safeId(windowId) || !state || typeof state !== 'object' || Array.isArray(state)) {
return [];
}
const safeState = Object.fromEntries(
Object.entries(state).flatMap(([key, field]) =>
safeId(key) &&
(typeof field === 'boolean' ||
typeof field === 'number' ||
(typeof field === 'string' && /^[A-Za-z0-9 .:_-]{0,128}$/.test(field)))
? [[key, field]]
: [],
),
);
return [[windowId, safeState]];
}),
);
}
function sanitizeAssertionResult(value) {
if (!value || typeof value !== 'object') return null;
const windowId = safeId(value.windowId);
const path = safeId(value.path);
if (!windowId || !path || typeof value.pass !== 'boolean') return null;
return {
windowId,
path,
pass: value.pass,
...(typeof value.actual === 'boolean' ||
typeof value.actual === 'number' ||
typeof value.actual === 'string'
? { actual: value.actual }
: {}),
};
}