Harden Windows DLP enforcement and Electron runtime packaging

This commit is contained in:
Karl committed 2026-10-13 23:19:22 +02:00
1 parent e7d778aeac
commit 8a253eda89
120 files changed
+3569 -135

No files matched your search

+5 -1
View File
@@ -7,6 +7,7 @@ const APP_ACTIONS=Object.freeze(['vault:prepare-submit','sessions:save','session
const SCOPES = Object.freeze({ screenshot:'browser:screenshot', sessions: 'sessions:list', navigate: 'browser:navigate', back:'browser:navigate',forward:'browser:navigate',reload:'browser:navigate', read: 'browser:read', inspect:'browser:read', scroll:'browser:interact', click: 'browser:interact', type: 'browser:interact', select:'browser:interact', check:'browser:interact', ai: 'ai:request',...Object.fromEntries(APP_ACTIONS.map(action=>[action,action])) });
const fail = (status, message) => Object.assign(new Error(message), { status });
const bounded = (value, max, name) => {
// eslint-disable-next-line no-control-regex -- Explicitly reject control characters in security-sensitive input.
if (typeof value !== 'string' || !value.length || value.length > max || /\u0000/.test(value)) throw fail(400, `Invalid ${name}`);
return value;
};
@@ -48,8 +49,9 @@ function validate(body) {
if (['click', 'type','select','check'].includes(action)) {
clean.selector = bounded(args.selector, 512, 'selector');
// A small CSS subset prevents pseudo-elements, escapes and control characters.
if (!/^[a-zA-Z0-9_#.*\[\]="' >+~-]+$/.test(clean.selector) || /password|autocomplete/i.test(clean.selector)) throw fail(400, 'Unsupported selector');
if (!/^[a-zA-Z0-9_#.*[\]="' >+~-]+$/.test(clean.selector) || /password|autocomplete/i.test(clean.selector)) throw fail(400, 'Unsupported selector');
}
// eslint-disable-next-line no-control-regex -- Explicitly reject control characters in security-sensitive input.
if(action==='select'){if(typeof args.value!=='string'||args.value.length>1024||/\u0000/.test(args.value))throw fail(400,'Invalid option value');clean.value=args.value;}
if(action==='check'){if(typeof args.checked!=='boolean')throw fail(400,'Invalid checked state');clean.checked=args.checked;}
if(action==='scroll'){for(const key of ['x','y']){if(!Number.isSafeInteger(args[key])||Math.abs(args[key])>100000)throw fail(400,'Invalid scroll');clean[key]=args[key];}}
@@ -57,12 +59,14 @@ function validate(body) {
if (action === 'ai') clean.prompt = bounded(args.prompt, 16384, 'prompt');
if(action.startsWith('tabs:')&&action!=='tabs:create')clean.sessionId=bounded(args.sessionId,128,'sessionId');
if(action==='tabs:create'&&args.url!==undefined){let url;try{url=new URL(bounded(args.url,4096,'url'));}catch{throw fail(400,'Invalid URL');}if(!['http:','https:'].includes(url.protocol)||url.username||url.password)throw fail(400,'Unsupported URL');clean.url=url.href;}
// eslint-disable-next-line no-control-regex -- Explicitly reject control characters in security-sensitive input.
if(['tabs:create','tabs:group'].includes(action)&&args.group!==undefined){if(typeof args.group!=='string'||args.group.length>40||/[\u0000-\u001f\u007f]/.test(args.group))throw fail(400,'Invalid group');clean.group=args.group;}
if(action==='tabs:group'&&args.group===undefined)throw fail(400,'Invalid group');
if(action==='settings:update'){try{clean.appearance=require('./managed-settings.cjs').validateAppearance(args.appearance);}catch{throw fail(400,'Invalid appearance settings');}}
if(action==='bookmarks:mutate'){
if(!['add','update','delete'].includes(args.action)||!Number.isSafeInteger(args.expectedRevision)||args.expectedRevision<0||!Number.isSafeInteger(args.policyVersion)||args.policyVersion<1)throw fail(400,'Invalid bookmark operation');
clean.action=args.action;clean.expectedRevision=args.expectedRevision;clean.policyVersion=args.policyVersion;
// eslint-disable-next-line no-control-regex -- Explicitly reject control characters in security-sensitive input.
if(args.action==='delete')clean.id=bounded(args.id,80,'bookmark id');else{const item=args.bookmark;if(!item||typeof item!=='object'||Array.isArray(item)||Object.keys(item).some(key=>!['id','title','url','folder'].includes(key)))throw fail(400,'Invalid bookmark');const folder=item.folder===undefined?'':item.folder;if(typeof folder!=='string'||folder.length>80||/[\u0000-\u001f\u007f]/.test(folder))throw fail(400,'Invalid bookmark folder');clean.bookmark={id:bounded(item.id,80,'bookmark id'),title:bounded(item.title,120,'bookmark title'),url:bounded(item.url,2048,'bookmark URL'),folder};}
}
if(action==='library:organize-apply'){clean.proposalId=bounded(args.proposalId,128,'proposal id');if(args.confirm!==undefined&&typeof args.confirm!=='boolean'||args.autoApply!==undefined&&typeof args.autoApply!=='boolean')throw fail(400,'Invalid organization confirmation');if(args.confirm!==undefined)clean.confirm=args.confirm;if(args.autoApply!==undefined)clean.autoApply=args.autoApply;}