Files
safetynet/packages/core/evidence-payload.cjs
T

12 lines
1.7 KiB
JavaScript
Raw Normal View History

'use strict';
const {z}=require('zod');
const InteractionEvidenceSchema=z.object({
pageUrl:z.string().max(4096).refine(value=>{try{const u=new URL(value);return ['http:','https:'].includes(u.protocol)&&!u.username&&!u.password&&!u.search&&!u.hash;}catch{return false;}},'Evidence URL must exclude credentials, query and fragment'),
action:z.enum(['click','copy','cut','paste','submit']),sessionId:z.string().uuid(),outcome:z.enum(['completed','observed']),captureSource:z.enum(['guarded-clipboard','agent-adapter','human-observer']),bytes:z.number().int().min(0).max(16777216).optional(),
target:z.object({tag:z.enum(['A','BUTTON','INPUT','TEXTAREA','SELECT','TEXT','OTHER','DIV','SPAN','P','BODY','UNKNOWN']),role:z.enum(['link','button','checkbox','radio','textbox','combobox','text','other','menuitem','option']).optional(),x:z.number().finite().min(0).max(10000000).optional(),y:z.number().finite().min(0).max(10000000).optional()}).strict().optional(),sensitiveField:z.literal(false)
}).strict().superRefine((value,context)=>{
const human=value.captureSource==='human-observer';
if(human){try{if(value.pageUrl!==new URL(value.pageUrl).origin+'/')context.addIssue({code:'custom',message:'Human metadata must contain origin only'});}catch{context.addIssue({code:'custom',message:'Invalid human metadata origin'});}}
if(human?(value.outcome!=='observed'||!['click','submit'].includes(value.action)||value.bytes!==undefined):(value.outcome!=='completed'||value.captureSource==='guarded-clipboard'&&!['copy','cut','paste'].includes(value.action)||value.captureSource==='agent-adapter'&&value.action!=='click'))context.addIssue({code:'custom',message:'Interaction source, action and outcome must agree'});
});
module.exports={InteractionEvidenceSchema};