@xenosystem/agent-sdk 0.9.0 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifacts/index.cjs +1 -1
- package/dist/artifacts/index.js +1 -1
- package/dist/artifacts/metafile-cjs.json +1 -1
- package/dist/artifacts/metafile-esm.json +1 -1
- package/dist/automation/index.cjs +478 -17
- package/dist/automation/index.js +478 -17
- package/dist/automation/metafile-cjs.json +1 -1
- package/dist/automation/metafile-esm.json +1 -1
- package/dist/control-plane/index.cjs +1 -1
- package/dist/control-plane/index.js +1 -1
- package/dist/control-plane/metafile-cjs.json +1 -1
- package/dist/control-plane/metafile-esm.json +1 -1
- package/dist/electron/index.cjs +614 -153
- package/dist/electron/index.d.cts +0 -1
- package/dist/electron/index.d.ts +0 -1
- package/dist/electron/index.js +617 -156
- package/dist/electron/metafile-cjs.json +1 -1
- package/dist/electron/metafile-esm.json +1 -1
- package/dist/index.cjs +787 -316
- package/dist/index.d.cts +15 -4
- package/dist/index.d.ts +15 -4
- package/dist/index.js +788 -317
- package/dist/metafile-cjs.json +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/providers/index.cjs +8 -8
- package/dist/providers/index.js +7 -7
- package/dist/providers/metafile-cjs.json +1 -1
- package/dist/providers/metafile-esm.json +1 -1
- package/dist/recipes/metafile-cjs.json +1 -1
- package/dist/recipes/metafile-esm.json +1 -1
- package/dist/research/metafile-cjs.json +1 -1
- package/dist/research/metafile-esm.json +1 -1
- package/dist/session/index.cjs +6 -6
- package/dist/session/index.js +10 -10
- package/dist/session/metafile-cjs.json +1 -1
- package/dist/session/metafile-esm.json +1 -1
- package/dist/ui/index.cjs +3 -3
- package/dist/ui/index.js +3 -3
- package/dist/ui/metafile-cjs.json +1 -1
- package/dist/ui/metafile-esm.json +1 -1
- package/ownership/audit.mjs +758 -727
- package/ownership/ownership-policy.json +273 -273
- package/package.json +4 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var V=Object.defineProperty;var xe=Object.getOwnPropertyDescriptor;var Pe=Object.getOwnPropertyNames;var Xe=Object.prototype.hasOwnProperty;var Le=(e,t)=>{for(var r in t)V(e,r,{get:t[r],enumerable:!0})},De=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Pe(t))!Xe.call(e,o)&&o!==r&&V(e,o,{get:()=>t[o],enumerable:!(n=xe(t,o))||n.enumerable});return e};var ke=e=>De(V({},"__esModule",{value:!0}),e);var kt={};Le(kt,{XENO_AUTOMATION_OPERATIONS:()=>R,XENO_AUTOMATION_PROTOCOL_VERSION:()=>T,XenoAutomationError:()=>i,XenoGovernedAutomationExecutor:()=>te,XenoLoopbackAutomationAdapter:()=>ne,assertRequiredXenoAutomationEvidence:()=>C,assertValidXenoAutomationAdapterManifest:()=>S,assertValidXenoAutomationRequest:()=>J,assertXenoAutomationAdapterConformant:()=>ct,assertXenoAutomationAuthority:()=>x,buildXenoAutomationCapabilityUse:()=>q,createXenoAutomationConformanceReport:()=>_e,createXenoGovernedAutomationTools:()=>bt,describeXenoAutomationOperation:()=>h,isXenoAutomationOperation:()=>O,materializeXenoAutomationEvidence:()=>L,persistXenoAutomationEvidence:()=>D,resolveXenoAutomationEvidencePolicy:()=>K});module.exports=ke(kt);var T=1;var $=new Set(["browser.navigate","browser.back","browser.forward","browser.reload","browser.wait","browser.snapshot","browser.screenshot","browser.locate","browser.tabs.list","browser.tabs.open","browser.console.read","browser.network.read","browser.storage.read","browser.page-errors.read"]),F=new Set(["browser.click","browser.type","browser.key","browser.select","browser.scroll","browser.tabs.close","browser.upload","browser.download","browser.record.start","browser.record.stop"]),H=new Set(["computer.observe","computer.screenshot","computer.window-state","computer.wait"]),j=new Set(["computer.point","computer.click","computer.double-click","computer.right-click","computer.drag","computer.type","computer.key","computer.scroll","computer.launch"]),R=[...$,...F,...H,...j];function h(e){if($.has(e))return{operation:e,surface:"browser",effect:"observe",evidence:{before:"disabled",after:e==="browser.wait"||e==="browser.tabs.list"?"optional":"required",recording:"optional"}};if(F.has(e)){let t=e==="browser.record.start"||e==="browser.record.stop";return{operation:e,surface:"browser",effect:"act",evidence:{before:"required",after:"required",recording:e==="browser.record.stop"?"required":"optional"}}}if(H.has(e))return{operation:e,surface:"computer",effect:"observe",evidence:{before:"disabled",after:e==="computer.wait"?"optional":"required",recording:"optional"}};if(j.has(e))return{operation:e,surface:"computer",effect:"act",evidence:{before:"required",after:"required",recording:"optional"}};throw new TypeError(`Unsupported XENO automation operation: ${e}`)}function O(e){return $.has(e)||F.has(e)||H.has(e)||j.has(e)}var i=class extends Error{constructor(r,n,o,a){super(n,a);this.code=r;this.detail=o;this.name="XenoAutomationError"}code;detail};var fe=require("node:crypto");function Ue(e,t){let r=0;for(let n=0;n<t;n+=1)r=r<<1|e>>>n&1;return r}function de(e,t=!1){let r=e.reduce((s,u)=>Math.max(s,u),0);if(r===0){if(t)return{tree:null,codes:e.map(()=>({bits:0,length:0}))};throw new Error("DEFLATE Huffman tree has no symbols")}if(r>15||e.some(s=>!Number.isInteger(s)||s<0||s>15))throw new Error("Invalid DEFLATE Huffman code length");let n=new Uint16Array(r+1);for(let s of e)s>0&&(n[s]+=1);let o=1;for(let s=1;s<=r;s+=1)if(o=(o<<1)-n[s],o<0)throw new Error("Oversubscribed DEFLATE Huffman tree");let a=new Uint32Array(r+1),d=0;for(let s=1;s<=r;s+=1)d=d+n[s-1]<<1,a[s]=d;let c=new Array(r+1),l=e.map(()=>({bits:0,length:0}));for(let s=0;s<e.length;s+=1){let u=e[s];if(u===0)continue;let p=a[u]++,v=Ue(p,u),f=c[u]??new Map;f.set(v,s),c[u]=f,l[s]={bits:v,length:u}}return{tree:{maxBits:r,byLength:c},codes:l}}function Me(){let e=new Array(288).fill(0);for(let t=0;t<=143;t+=1)e[t]=8;for(let t=144;t<=255;t+=1)e[t]=9;for(let t=256;t<=279;t+=1)e[t]=7;for(let t=280;t<=287;t+=1)e[t]=8;return e}var Ft=de(Me()),Ht=de(new Array(32).fill(5));var zt=String.raw`
|
|
2
2
|
using System;
|
|
3
3
|
using System.ComponentModel;
|
|
4
4
|
using System.Runtime.InteropServices;
|
|
5
|
+
using System.Security.AccessControl;
|
|
6
|
+
using System.Security.Principal;
|
|
5
7
|
using System.Text;
|
|
6
8
|
|
|
7
9
|
namespace Xeno.Security
|
|
@@ -15,6 +17,16 @@ namespace Xeno.Security
|
|
|
15
17
|
private const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100;
|
|
16
18
|
private const UInt32 DISABLE_MAX_PRIVILEGE = 0x0001;
|
|
17
19
|
private const UInt32 LUA_TOKEN = 0x0004;
|
|
20
|
+
// TOKEN_INFORMATION_CLASS members used by the owner/default-DACL diagnostic.
|
|
21
|
+
private const Int32 TokenUser = 1;
|
|
22
|
+
private const Int32 TokenOwner = 4;
|
|
23
|
+
private const Int32 TokenDefaultDacl = 6;
|
|
24
|
+
private const UInt32 GenericAll = 0x10000000;
|
|
25
|
+
private const Byte GenericAclRevision = 2;
|
|
26
|
+
// Set when a diagnostic probe could not APPLY its change. Reported separately from the
|
|
27
|
+
// launch result, because "the fix did not help" and "the fix never went on" are different
|
|
28
|
+
// answers and only one of them is about the product.
|
|
29
|
+
private static string ownerDaclError = "";
|
|
18
30
|
private const UInt32 SE_GROUP_INTEGRITY = 0x00000020;
|
|
19
31
|
private const UInt32 CREATE_SUSPENDED = 0x00000004;
|
|
20
32
|
private const UInt32 EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
|
|
@@ -135,6 +147,25 @@ namespace Xeno.Security
|
|
|
135
147
|
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
136
148
|
private static extern bool ConvertStringSidToSid(string StringSid, out IntPtr Sid);
|
|
137
149
|
|
|
150
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
151
|
+
private static extern bool GetTokenInformation(
|
|
152
|
+
IntPtr TokenHandle,
|
|
153
|
+
Int32 TokenInformationClass,
|
|
154
|
+
IntPtr TokenInformation,
|
|
155
|
+
UInt32 TokenInformationLength,
|
|
156
|
+
out UInt32 ReturnLength);
|
|
157
|
+
|
|
158
|
+
// A second, POINTER-typed declaration of the same export. The one below is typed
|
|
159
|
+
// 'ref SID_AND_ATTRIBUTES' for its original caller; the owner/default-DACL diagnostic
|
|
160
|
+
// passes a TOKEN_OWNER / TOKEN_DEFAULT_DACL buffer instead, and C# resolves overloads
|
|
161
|
+
// before the marshaller ever sees them.
|
|
162
|
+
[DllImport("advapi32.dll", SetLastError = true, EntryPoint = "SetTokenInformation")]
|
|
163
|
+
private static extern bool SetTokenInformationPtr(
|
|
164
|
+
IntPtr TokenHandle,
|
|
165
|
+
Int32 TokenInformationClass,
|
|
166
|
+
IntPtr TokenInformation,
|
|
167
|
+
UInt32 TokenInformationLength);
|
|
168
|
+
|
|
138
169
|
[DllImport("advapi32.dll", SetLastError = true)]
|
|
139
170
|
private static extern bool SetTokenInformation(
|
|
140
171
|
IntPtr TokenHandle,
|
|
@@ -229,6 +260,264 @@ namespace Xeno.Security
|
|
|
229
260
|
[DllImport("kernel32.dll")]
|
|
230
261
|
private static extern IntPtr LocalFree(IntPtr hMem);
|
|
231
262
|
|
|
263
|
+
// ---- Window station / desktop access for the restricted token -------------------
|
|
264
|
+
//
|
|
265
|
+
// MSDN, CreateProcessAsUser: "If lpDesktop is NULL, the new process inherits the desktop
|
|
266
|
+
// and window station of its parent process. You must add permission for the specified
|
|
267
|
+
// account to the specified window station and desktop, EVEN FOR WinSta0\Default."
|
|
268
|
+
//
|
|
269
|
+
// That requirement was unmet, and it is why a hardened launch from an ELEVATED parent died
|
|
270
|
+
// with 0xC0000142 (STATUS_DLL_INIT_FAILED) while the identical code from an unelevated
|
|
271
|
+
// parent worked: the interactive window station's DACL is written at logon for the
|
|
272
|
+
// FILTERED token's SIDs, so the unelevated case was being granted by accident.
|
|
273
|
+
// What the last grant actually DID, per object, so one diagnostic line distinguishes
|
|
274
|
+
// "never ran", "already present" and "added" — three states that look identical from
|
|
275
|
+
// outside and imply completely different next steps.
|
|
276
|
+
private static string lastGrantReport = "not-run";
|
|
277
|
+
private const UInt32 DACL_SECURITY_INFORMATION = 0x00000004;
|
|
278
|
+
private const UInt32 WINSTA_ALL_ACCESS = 0x0000037F;
|
|
279
|
+
private const UInt32 DESKTOP_ALL_ACCESS = 0x000001FF;
|
|
280
|
+
private const UInt32 GENERIC_ACCESS_ALL = 0xF0000000;
|
|
281
|
+
private const UInt32 ACL_REVISION_2 = 2;
|
|
282
|
+
private const UInt32 CONTAINER_INHERIT_ACE = 0x02;
|
|
283
|
+
private const UInt32 INHERIT_ONLY_ACE = 0x08;
|
|
284
|
+
private const UInt32 OBJECT_INHERIT_ACE = 0x01;
|
|
285
|
+
private const UInt32 NO_PROPAGATE_INHERIT_ACE = 0x04;
|
|
286
|
+
private const UInt32 SECURITY_DESCRIPTOR_REVISION = 1;
|
|
287
|
+
private const UInt32 TokenUserClass = 1;
|
|
288
|
+
private const UInt32 AclSizeInformationClass = 2;
|
|
289
|
+
private const Byte ACCESS_ALLOWED_ACE_TYPE = 0;
|
|
290
|
+
|
|
291
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
292
|
+
private struct ACL_SIZE_INFORMATION
|
|
293
|
+
{
|
|
294
|
+
public UInt32 AceCount;
|
|
295
|
+
public UInt32 AclBytesInUse;
|
|
296
|
+
public UInt32 AclBytesFree;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
300
|
+
private struct ACE_HEADER
|
|
301
|
+
{
|
|
302
|
+
public Byte AceType;
|
|
303
|
+
public Byte AceFlags;
|
|
304
|
+
public UInt16 AceSize;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
308
|
+
private static extern IntPtr GetProcessWindowStation();
|
|
309
|
+
|
|
310
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
311
|
+
private static extern IntPtr GetThreadDesktop(UInt32 dwThreadId);
|
|
312
|
+
|
|
313
|
+
[DllImport("kernel32.dll")]
|
|
314
|
+
private static extern UInt32 GetCurrentThreadId();
|
|
315
|
+
|
|
316
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
317
|
+
private static extern bool GetUserObjectSecurity(IntPtr hObj, ref UInt32 pSIRequested, IntPtr pSid, UInt32 nLength, out UInt32 lpnLengthNeeded);
|
|
318
|
+
|
|
319
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
320
|
+
private static extern bool SetUserObjectSecurity(IntPtr hObj, ref UInt32 pSIRequested, IntPtr pSid);
|
|
321
|
+
|
|
322
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
323
|
+
private static extern bool GetTokenInformation(IntPtr TokenHandle, UInt32 TokenInformationClass, IntPtr TokenInformation, UInt32 TokenInformationLength, out UInt32 ReturnLength);
|
|
324
|
+
|
|
325
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
326
|
+
private static extern bool GetSecurityDescriptorDacl(IntPtr pSecurityDescriptor, out bool lpbDaclPresent, out IntPtr pDacl, out bool lpbDaclDefaulted);
|
|
327
|
+
|
|
328
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
329
|
+
private static extern bool InitializeSecurityDescriptor(IntPtr pSecurityDescriptor, UInt32 dwRevision);
|
|
330
|
+
|
|
331
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
332
|
+
private static extern bool SetSecurityDescriptorDacl(IntPtr pSecurityDescriptor, bool bDaclPresent, IntPtr pDacl, bool bDaclDefaulted);
|
|
333
|
+
|
|
334
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
335
|
+
private static extern bool GetAclInformation(IntPtr pAcl, ref ACL_SIZE_INFORMATION pAclInformation, UInt32 nAclInformationLength, UInt32 dwAclInformationClass);
|
|
336
|
+
|
|
337
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
338
|
+
private static extern bool InitializeAcl(IntPtr pAcl, UInt32 nAclLength, UInt32 dwAclRevision);
|
|
339
|
+
|
|
340
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
341
|
+
private static extern bool GetAce(IntPtr pAcl, UInt32 dwAceIndex, out IntPtr pAce);
|
|
342
|
+
|
|
343
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
344
|
+
private static extern bool AddAce(IntPtr pAcl, UInt32 dwAceRevision, UInt32 dwStartingAceIndex, IntPtr pAceList, UInt32 nAceListLength);
|
|
345
|
+
|
|
346
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
347
|
+
private static extern bool AddAccessAllowedAceEx(IntPtr pAcl, UInt32 dwAceRevision, UInt32 AceFlags, UInt32 AccessMask, IntPtr pSid);
|
|
348
|
+
|
|
349
|
+
[DllImport("advapi32.dll", SetLastError = true)]
|
|
350
|
+
private static extern bool EqualSid(IntPtr pSid1, IntPtr pSid2);
|
|
351
|
+
|
|
352
|
+
// Copy the token's user SID into unmanaged memory the caller frees with FreeHGlobal.
|
|
353
|
+
private static IntPtr CopyTokenUserSid(IntPtr token)
|
|
354
|
+
{
|
|
355
|
+
UInt32 needed = 0;
|
|
356
|
+
GetTokenInformation(token, TokenUserClass, IntPtr.Zero, 0, out needed);
|
|
357
|
+
if (needed == 0) ThrowLastError("GetTokenInformation(TokenUser size)");
|
|
358
|
+
IntPtr buffer = Marshal.AllocHGlobal(checked((int)needed));
|
|
359
|
+
try
|
|
360
|
+
{
|
|
361
|
+
if (!GetTokenInformation(token, TokenUserClass, buffer, needed, out needed))
|
|
362
|
+
ThrowLastError("GetTokenInformation(TokenUser)");
|
|
363
|
+
// TOKEN_USER's first field is a POINTER into this buffer, so the SID has to be
|
|
364
|
+
// copied out before the buffer is freed.
|
|
365
|
+
IntPtr sidPtr = Marshal.ReadIntPtr(buffer);
|
|
366
|
+
UInt32 sidLength = GetLengthSid(sidPtr);
|
|
367
|
+
IntPtr copy = Marshal.AllocHGlobal(checked((int)sidLength));
|
|
368
|
+
for (UInt32 i = 0; i < sidLength; i++)
|
|
369
|
+
Marshal.WriteByte(copy, checked((int)i), Marshal.ReadByte(sidPtr, checked((int)i)));
|
|
370
|
+
return copy;
|
|
371
|
+
}
|
|
372
|
+
finally
|
|
373
|
+
{
|
|
374
|
+
Marshal.FreeHGlobal(buffer);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Add an access-allowed ACE for the given SID to a USER OBJECT's DACL, preserving every existing
|
|
379
|
+
// ACE. Returns without writing when the SID is already present: a window station lives for
|
|
380
|
+
// the whole logon session, so a launch-time mutation that appended unconditionally would
|
|
381
|
+
// grow a shared descriptor without bound.
|
|
382
|
+
private static string GrantUserObjectAccess(IntPtr userObject, IntPtr sid, UInt32 accessMask, UInt32 inheritFlags, string label)
|
|
383
|
+
{
|
|
384
|
+
UInt32 requested = DACL_SECURITY_INFORMATION;
|
|
385
|
+
UInt32 needed = 0;
|
|
386
|
+
GetUserObjectSecurity(userObject, ref requested, IntPtr.Zero, 0, out needed);
|
|
387
|
+
if (needed == 0) ThrowLastError("GetUserObjectSecurity(" + label + " size)");
|
|
388
|
+
IntPtr sd = Marshal.AllocHGlobal(checked((int)needed));
|
|
389
|
+
IntPtr newSd = IntPtr.Zero;
|
|
390
|
+
IntPtr newAcl = IntPtr.Zero;
|
|
391
|
+
try
|
|
392
|
+
{
|
|
393
|
+
UInt32 ignored;
|
|
394
|
+
if (!GetUserObjectSecurity(userObject, ref requested, sd, needed, out ignored))
|
|
395
|
+
ThrowLastError("GetUserObjectSecurity(" + label + ")");
|
|
396
|
+
|
|
397
|
+
bool present, defaulted;
|
|
398
|
+
IntPtr dacl;
|
|
399
|
+
if (!GetSecurityDescriptorDacl(sd, out present, out dacl, out defaulted))
|
|
400
|
+
ThrowLastError("GetSecurityDescriptorDacl(" + label + ")");
|
|
401
|
+
|
|
402
|
+
var info = new ACL_SIZE_INFORMATION();
|
|
403
|
+
UInt32 existingBytes = 8;
|
|
404
|
+
UInt32 aceCount = 0;
|
|
405
|
+
if (present && dacl != IntPtr.Zero)
|
|
406
|
+
{
|
|
407
|
+
if (!GetAclInformation(dacl, ref info, checked((UInt32)Marshal.SizeOf(typeof(ACL_SIZE_INFORMATION))), AclSizeInformationClass))
|
|
408
|
+
ThrowLastError("GetAclInformation(" + label + ")");
|
|
409
|
+
existingBytes = info.AclBytesInUse;
|
|
410
|
+
aceCount = info.AceCount;
|
|
411
|
+
for (UInt32 i = 0; i < aceCount; i++)
|
|
412
|
+
{
|
|
413
|
+
IntPtr existing;
|
|
414
|
+
if (!GetAce(dacl, i, out existing)) continue;
|
|
415
|
+
var header = (ACE_HEADER)Marshal.PtrToStructure(existing, typeof(ACE_HEADER));
|
|
416
|
+
if (header.AceType != ACCESS_ALLOWED_ACE_TYPE) continue;
|
|
417
|
+
if (header.AceFlags != (Byte)inheritFlags) continue;
|
|
418
|
+
// ACCESS_ALLOWED_ACE is header(4) + mask(4) + SID.
|
|
419
|
+
UInt32 existingMask = (UInt32)Marshal.ReadInt32(existing, 4);
|
|
420
|
+
IntPtr aceSid = new IntPtr(existing.ToInt64() + 8);
|
|
421
|
+
if (!EqualSid(aceSid, sid)) continue;
|
|
422
|
+
// 🔴 The MASK is part of the question, and leaving it out was a real bug.
|
|
423
|
+
// This window station already grants the logged-in user
|
|
424
|
+
// mask=0x24 flags=0x04 (clipboard + global atoms) at logon. Matching on
|
|
425
|
+
// SID and flags alone treated that as "already granted" and skipped
|
|
426
|
+
// WINSTA_ALL_ACCESS entirely — the grant silently did nothing, and the
|
|
427
|
+
// only reason it was caught is that the DACL was read back and compared.
|
|
428
|
+
if ((existingMask & accessMask) == accessMask)
|
|
429
|
+
return label + "=present(0x" + existingMask.ToString("x") + ")";
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
UInt32 sidLength = GetLengthSid(sid);
|
|
434
|
+
UInt32 newSize = existingBytes + sidLength + 64;
|
|
435
|
+
newAcl = Marshal.AllocHGlobal(checked((int)newSize));
|
|
436
|
+
if (!InitializeAcl(newAcl, newSize, ACL_REVISION_2))
|
|
437
|
+
ThrowLastError("InitializeAcl(" + label + ")");
|
|
438
|
+
|
|
439
|
+
for (UInt32 i = 0; i < aceCount; i++)
|
|
440
|
+
{
|
|
441
|
+
IntPtr existing;
|
|
442
|
+
if (!GetAce(dacl, i, out existing)) ThrowLastError("GetAce(" + label + ")");
|
|
443
|
+
var header = (ACE_HEADER)Marshal.PtrToStructure(existing, typeof(ACE_HEADER));
|
|
444
|
+
if (!AddAce(newAcl, ACL_REVISION_2, UInt32.MaxValue, existing, header.AceSize))
|
|
445
|
+
ThrowLastError("AddAce(" + label + ")");
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (!AddAccessAllowedAceEx(newAcl, ACL_REVISION_2, inheritFlags, accessMask, sid))
|
|
449
|
+
ThrowLastError("AddAccessAllowedAceEx(" + label + ")");
|
|
450
|
+
|
|
451
|
+
newSd = Marshal.AllocHGlobal(128);
|
|
452
|
+
if (!InitializeSecurityDescriptor(newSd, SECURITY_DESCRIPTOR_REVISION))
|
|
453
|
+
ThrowLastError("InitializeSecurityDescriptor(" + label + ")");
|
|
454
|
+
if (!SetSecurityDescriptorDacl(newSd, true, newAcl, false))
|
|
455
|
+
ThrowLastError("SetSecurityDescriptorDacl(" + label + ")");
|
|
456
|
+
if (!SetUserObjectSecurity(userObject, ref requested, newSd))
|
|
457
|
+
ThrowLastError("SetUserObjectSecurity(" + label + ")");
|
|
458
|
+
return label + "=added";
|
|
459
|
+
}
|
|
460
|
+
finally
|
|
461
|
+
{
|
|
462
|
+
if (newAcl != IntPtr.Zero) Marshal.FreeHGlobal(newAcl);
|
|
463
|
+
if (newSd != IntPtr.Zero) Marshal.FreeHGlobal(newSd);
|
|
464
|
+
Marshal.FreeHGlobal(sd);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Grant the restricted token's own user SID access to this process's window station and
|
|
469
|
+
// desktop. Returns null on success, or a description of what failed.
|
|
470
|
+
//
|
|
471
|
+
// Best-effort BY DESIGN, and this is deliberately NOT the usual fail-closed case. A
|
|
472
|
+
// hardened launch from an unelevated parent already works with no grant at all, so
|
|
473
|
+
// treating a grant failure as fatal would convert a working configuration into a broken
|
|
474
|
+
// one. The real gate is whether the child starts; when it does not, this reason is
|
|
475
|
+
// reported alongside, which is what turns an opaque NTSTATUS into something actionable.
|
|
476
|
+
private static string GrantDesktopAccessForToken(IntPtr token, string tokenProbe)
|
|
477
|
+
{
|
|
478
|
+
IntPtr sid = IntPtr.Zero;
|
|
479
|
+
try
|
|
480
|
+
{
|
|
481
|
+
sid = CopyTokenUserSid(token);
|
|
482
|
+
IntPtr winsta = GetProcessWindowStation();
|
|
483
|
+
if (winsta == IntPtr.Zero) return "GetProcessWindowStation returned NULL";
|
|
484
|
+
IntPtr desktop = GetThreadDesktop(GetCurrentThreadId());
|
|
485
|
+
if (desktop == IntPtr.Zero) return "GetThreadDesktop returned NULL";
|
|
486
|
+
// Two ACEs on the window station, as the documented sequence requires: an
|
|
487
|
+
// inherit-only one so desktops created later in this session carry the grant, and
|
|
488
|
+
// a non-propagating one for the station itself.
|
|
489
|
+
lastGrantReport =
|
|
490
|
+
GrantUserObjectAccess(winsta, sid, GENERIC_ACCESS_ALL, CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE, "winsta-inherit")
|
|
491
|
+
+ " " + GrantUserObjectAccess(winsta, sid, WINSTA_ALL_ACCESS, NO_PROPAGATE_INHERIT_ACE, "winsta")
|
|
492
|
+
+ " " + GrantUserObjectAccess(desktop, sid, DESKTOP_ALL_ACCESS, 0, "desktop");
|
|
493
|
+
if (tokenProbe == "grant-everyone")
|
|
494
|
+
{
|
|
495
|
+
// DIAGNOSTIC ONLY, and never a fix: this asks whether the denied object is
|
|
496
|
+
// the window station/desktop at all, by granting a SID no access check can
|
|
497
|
+
// miss. A pass here means the real fix is naming the right SID.
|
|
498
|
+
IntPtr everyone = IntPtr.Zero;
|
|
499
|
+
if (ConvertStringSidToSid("S-1-1-0", out everyone))
|
|
500
|
+
{
|
|
501
|
+
try
|
|
502
|
+
{
|
|
503
|
+
lastGrantReport += " " + GrantUserObjectAccess(winsta, everyone, WINSTA_ALL_ACCESS, NO_PROPAGATE_INHERIT_ACE, "winsta-everyone");
|
|
504
|
+
lastGrantReport += " " + GrantUserObjectAccess(desktop, everyone, DESKTOP_ALL_ACCESS, 0, "desktop-everyone");
|
|
505
|
+
}
|
|
506
|
+
finally { LocalFree(everyone); }
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
catch (Exception error)
|
|
512
|
+
{
|
|
513
|
+
return error.Message;
|
|
514
|
+
}
|
|
515
|
+
finally
|
|
516
|
+
{
|
|
517
|
+
if (sid != IntPtr.Zero) Marshal.FreeHGlobal(sid);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
232
521
|
private static void ThrowLastError(string operation)
|
|
233
522
|
{
|
|
234
523
|
throw new Win32Exception(Marshal.GetLastWin32Error(), operation);
|
|
@@ -267,25 +556,123 @@ namespace Xeno.Security
|
|
|
267
556
|
ThrowLastError("UpdateProcThreadAttribute(HANDLE_LIST)");
|
|
268
557
|
}
|
|
269
558
|
|
|
270
|
-
private static void
|
|
559
|
+
private static void ApplyIntegrity(IntPtr token, string integritySid, string label_)
|
|
271
560
|
{
|
|
272
|
-
IntPtr
|
|
561
|
+
IntPtr sid = IntPtr.Zero;
|
|
273
562
|
try
|
|
274
563
|
{
|
|
275
|
-
if (!ConvertStringSidToSid(
|
|
276
|
-
ThrowLastError("ConvertStringSidToSid(
|
|
277
|
-
var label = new SID_AND_ATTRIBUTES { Sid =
|
|
278
|
-
UInt32 size = checked((UInt32)Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES)) + GetLengthSid(
|
|
564
|
+
if (!ConvertStringSidToSid(integritySid, out sid))
|
|
565
|
+
ThrowLastError("ConvertStringSidToSid(" + label_ + " integrity)");
|
|
566
|
+
var label = new SID_AND_ATTRIBUTES { Sid = sid, Attributes = SE_GROUP_INTEGRITY };
|
|
567
|
+
UInt32 size = checked((UInt32)Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES)) + GetLengthSid(sid));
|
|
279
568
|
if (!SetTokenInformation(token, TokenIntegrityLevel, ref label, size))
|
|
280
|
-
ThrowLastError("SetTokenInformation(TokenIntegrityLevel)");
|
|
569
|
+
ThrowLastError("SetTokenInformation(TokenIntegrityLevel/" + label_ + ")");
|
|
570
|
+
}
|
|
571
|
+
finally
|
|
572
|
+
{
|
|
573
|
+
if (sid != IntPtr.Zero) LocalFree(sid);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
private static void ApplyLowIntegrity(IntPtr token)
|
|
578
|
+
{
|
|
579
|
+
ApplyIntegrity(token, "S-1-16-4096", "low");
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/// Read the token's own user SID.
|
|
583
|
+
///
|
|
584
|
+
/// Needed because the fix under test has to name the USER, and on an elevated process the
|
|
585
|
+
/// user is exactly what the owner and default DACL do NOT name.
|
|
586
|
+
private static SecurityIdentifier TokenUserSid(IntPtr token)
|
|
587
|
+
{
|
|
588
|
+
UInt32 needed = 0;
|
|
589
|
+
GetTokenInformation(token, TokenUser, IntPtr.Zero, 0, out needed);
|
|
590
|
+
IntPtr buffer = Marshal.AllocHGlobal(checked((int)needed));
|
|
591
|
+
try
|
|
592
|
+
{
|
|
593
|
+
if (!GetTokenInformation(token, TokenUser, buffer, needed, out needed))
|
|
594
|
+
ThrowLastError("GetTokenInformation(TokenUser)");
|
|
595
|
+
var user = (SID_AND_ATTRIBUTES)Marshal.PtrToStructure(buffer, typeof(SID_AND_ATTRIBUTES));
|
|
596
|
+
return new SecurityIdentifier(user.Sid);
|
|
281
597
|
}
|
|
282
598
|
finally
|
|
283
599
|
{
|
|
284
|
-
|
|
600
|
+
Marshal.FreeHGlobal(buffer);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/// DIAGNOSTIC: give the restricted token an owner and a default DACL that name its own USER.
|
|
605
|
+
///
|
|
606
|
+
/// A token created from an ELEVATED parent carries an owner and a default DACL that name
|
|
607
|
+
/// BUILTIN\\Administrators. Every kernel object the child creates during startup inherits
|
|
608
|
+
/// that DACL — and in a token where Administrators is deny-only, the child cannot open what
|
|
609
|
+
/// it just created. That is a textbook STATUS_DLL_INIT_FAILED, and it explains the whole
|
|
610
|
+
/// measurement table: the two sufficient single factors both remove Administrators, and
|
|
611
|
+
/// forcing the integrity level back to High changes nothing.
|
|
612
|
+
///
|
|
613
|
+
/// Returns an empty string on success so the caller can report WHY it could not apply it,
|
|
614
|
+
/// rather than reporting a launch failure that is really a probe failure.
|
|
615
|
+
private static string ApplyOwnerAndDefaultDacl(IntPtr token, bool setOwner, bool setDacl)
|
|
616
|
+
{
|
|
617
|
+
try
|
|
618
|
+
{
|
|
619
|
+
SecurityIdentifier user = TokenUserSid(token);
|
|
620
|
+
if (setDacl)
|
|
621
|
+
{
|
|
622
|
+
var acl = new RawAcl(GenericAclRevision, 2);
|
|
623
|
+
acl.InsertAce(0, new CommonAce(
|
|
624
|
+
AceFlags.None, AceQualifier.AccessAllowed, unchecked((int)GenericAll), user, false, null));
|
|
625
|
+
acl.InsertAce(1, new CommonAce(
|
|
626
|
+
AceFlags.None, AceQualifier.AccessAllowed, unchecked((int)GenericAll),
|
|
627
|
+
new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), false, null));
|
|
628
|
+
var raw = new byte[acl.BinaryLength];
|
|
629
|
+
acl.GetBinaryForm(raw, 0);
|
|
630
|
+
IntPtr aclBuffer = Marshal.AllocHGlobal(raw.Length);
|
|
631
|
+
IntPtr daclBuffer = Marshal.AllocHGlobal(IntPtr.Size);
|
|
632
|
+
try
|
|
633
|
+
{
|
|
634
|
+
Marshal.Copy(raw, 0, aclBuffer, raw.Length);
|
|
635
|
+
Marshal.WriteIntPtr(daclBuffer, aclBuffer);
|
|
636
|
+
if (!SetTokenInformationPtr(token, TokenDefaultDacl, daclBuffer, (UInt32)IntPtr.Size))
|
|
637
|
+
return "set-default-dacl-failed:" + Marshal.GetLastWin32Error();
|
|
638
|
+
}
|
|
639
|
+
finally
|
|
640
|
+
{
|
|
641
|
+
Marshal.FreeHGlobal(daclBuffer);
|
|
642
|
+
Marshal.FreeHGlobal(aclBuffer);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (setOwner)
|
|
646
|
+
{
|
|
647
|
+
var sidBytes = new byte[user.BinaryLength];
|
|
648
|
+
user.GetBinaryForm(sidBytes, 0);
|
|
649
|
+
IntPtr sidBuffer = Marshal.AllocHGlobal(sidBytes.Length);
|
|
650
|
+
IntPtr ownerBuffer = Marshal.AllocHGlobal(IntPtr.Size);
|
|
651
|
+
try
|
|
652
|
+
{
|
|
653
|
+
Marshal.Copy(sidBytes, 0, sidBuffer, sidBytes.Length);
|
|
654
|
+
Marshal.WriteIntPtr(ownerBuffer, sidBuffer);
|
|
655
|
+
if (!SetTokenInformationPtr(token, TokenOwner, ownerBuffer, (UInt32)IntPtr.Size))
|
|
656
|
+
return "set-owner-failed:" + Marshal.GetLastWin32Error();
|
|
657
|
+
}
|
|
658
|
+
finally
|
|
659
|
+
{
|
|
660
|
+
Marshal.FreeHGlobal(ownerBuffer);
|
|
661
|
+
Marshal.FreeHGlobal(sidBuffer);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return "";
|
|
665
|
+
}
|
|
666
|
+
catch (Exception error)
|
|
667
|
+
{
|
|
668
|
+
return "owner-dacl-threw:" + error.Message;
|
|
285
669
|
}
|
|
286
670
|
}
|
|
287
671
|
|
|
288
|
-
|
|
672
|
+
/// The tokenProbe argument drops ONE restriction so a failure can be attributed to it. Diagnostic
|
|
673
|
+
/// only — the caller passes "" unless XENO_CONTAINMENT_DIAGNOSTICS=1, so a normal launch
|
|
674
|
+
/// is unaffected and cannot be weakened by an environment variable alone.
|
|
675
|
+
private static IntPtr CreateHardenedToken(bool lowIntegrity, string tokenProbe)
|
|
289
676
|
{
|
|
290
677
|
IntPtr sourceToken = IntPtr.Zero;
|
|
291
678
|
IntPtr restrictedToken = IntPtr.Zero;
|
|
@@ -302,18 +689,67 @@ namespace Xeno.Security
|
|
|
302
689
|
var disabledSid = new SID_AND_ATTRIBUTES { Sid = adminSid, Attributes = 0 };
|
|
303
690
|
disabledSidBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES)));
|
|
304
691
|
Marshal.StructureToPtr(disabledSid, disabledSidBuffer, false);
|
|
692
|
+
UInt32 probeFlags = DISABLE_MAX_PRIVILEGE | LUA_TOKEN;
|
|
693
|
+
UInt32 disabledSidCount = 1;
|
|
694
|
+
// "plain" keeps CreateProcessAsUser but removes EVERY restriction, so a failure
|
|
695
|
+
// here means the restrictions are innocent and the call itself is implicated.
|
|
696
|
+
if (tokenProbe == "plain") { probeFlags = 0; disabledSidCount = 0; }
|
|
697
|
+
// Singles complete the table. Every PAIR fails and the empty set succeeds, so
|
|
698
|
+
// more than one restriction is independently sufficient; these say which.
|
|
699
|
+
else if (tokenProbe == "only-lua") { probeFlags = LUA_TOKEN; disabledSidCount = 0; }
|
|
700
|
+
else if (tokenProbe == "only-privilege-drop") { probeFlags = DISABLE_MAX_PRIVILEGE; disabledSidCount = 0; }
|
|
701
|
+
else if (tokenProbe == "only-disable-admin") { probeFlags = 0; disabledSidCount = 1; }
|
|
702
|
+
else if (tokenProbe == "no-lua") probeFlags = DISABLE_MAX_PRIVILEGE;
|
|
703
|
+
else if (tokenProbe == "no-privilege-drop") probeFlags = LUA_TOKEN;
|
|
704
|
+
else if (tokenProbe == "no-disable-admin") disabledSidCount = 0;
|
|
705
|
+
// The owner/default-DACL probes sit on top of the FAILING 'only-disable-admin'
|
|
706
|
+
// baseline, so a success is attributable to the fix and to nothing else.
|
|
707
|
+
else if (tokenProbe == "set-default-dacl" || tokenProbe == "set-owner" || tokenProbe == "set-both")
|
|
708
|
+
{ probeFlags = 0; disabledSidCount = 1; }
|
|
305
709
|
if (!CreateRestrictedToken(
|
|
306
710
|
sourceToken,
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
disabledSidBuffer,
|
|
711
|
+
probeFlags,
|
|
712
|
+
disabledSidCount,
|
|
713
|
+
disabledSidCount == 0 ? IntPtr.Zero : disabledSidBuffer,
|
|
310
714
|
0,
|
|
311
715
|
IntPtr.Zero,
|
|
312
716
|
0,
|
|
313
717
|
IntPtr.Zero,
|
|
314
718
|
out restrictedToken))
|
|
315
719
|
ThrowLastError("CreateRestrictedToken");
|
|
720
|
+
// The restricted token gets a default DACL naming its OWN user.
|
|
721
|
+
//
|
|
722
|
+
// 🔴 Without this, a hardened launch from an ELEVATED parent cannot start ANY
|
|
723
|
+
// process. A token created by an elevated process carries a default DACL naming
|
|
724
|
+
// BUILTIN\Administrators; every kernel object the child creates during startup
|
|
725
|
+
// inherits it, and in a token where that SID is deny-only the child cannot open
|
|
726
|
+
// what it just created. The loader reports STATUS_DLL_INIT_FAILED (0xC0000142),
|
|
727
|
+
// which reads as a missing or blocked DLL and is nothing of the kind.
|
|
728
|
+
//
|
|
729
|
+
// Proven on a hosted Windows runner, not reasoned about: with Administrators
|
|
730
|
+
// deny-only the launch failed 0xC0000142; adding ONLY this made it succeed;
|
|
731
|
+
// setting the token OWNER instead did not. The measurement is in the diagnostic
|
|
732
|
+
// table below, and 'fix-candidate' confirmed it with every restriction still on.
|
|
733
|
+
//
|
|
734
|
+
// ⚠️ Throws rather than continuing. A silent skip leaves a non-elevated parent
|
|
735
|
+
// working and an elevated one failing with an unexplained loader error — exactly
|
|
736
|
+
// the environment-dependent divergence that made this take four rounds to find.
|
|
737
|
+
string defaultDaclError = ApplyOwnerAndDefaultDacl(restrictedToken, false, true);
|
|
738
|
+
if (defaultDaclError != "")
|
|
739
|
+
throw new InvalidOperationException(
|
|
740
|
+
"Could not give the restricted token a default DACL naming its own user: " + defaultDaclError);
|
|
316
741
|
if (lowIntegrity) ApplyLowIntegrity(restrictedToken);
|
|
742
|
+
// DIAGNOSTIC: keep the restrictions, put the integrity level back to High. The
|
|
743
|
+
// group change and the integrity drop have co-varied in every measurement so
|
|
744
|
+
// far; this is the only way to tell which one the child actually needs.
|
|
745
|
+
if (tokenProbe == "force-high-il") ApplyIntegrity(restrictedToken, "S-1-16-12288", "high");
|
|
746
|
+
// DIAGNOSTIC: the last standing hypothesis. 'fix-candidate' keeps every restriction
|
|
747
|
+
// and applies both, because that is the shape a real fix would ship — a probe that
|
|
748
|
+
// only works with the restrictions removed would prove nothing about the product.
|
|
749
|
+
if (tokenProbe == "set-default-dacl") ownerDaclError = ApplyOwnerAndDefaultDacl(restrictedToken, false, true);
|
|
750
|
+
else if (tokenProbe == "set-owner") ownerDaclError = ApplyOwnerAndDefaultDacl(restrictedToken, true, false);
|
|
751
|
+
else if (tokenProbe == "set-both" || tokenProbe == "fix-candidate")
|
|
752
|
+
ownerDaclError = ApplyOwnerAndDefaultDacl(restrictedToken, true, true);
|
|
317
753
|
return restrictedToken;
|
|
318
754
|
}
|
|
319
755
|
finally
|
|
@@ -354,7 +790,7 @@ namespace Xeno.Security
|
|
|
354
790
|
}
|
|
355
791
|
}
|
|
356
792
|
|
|
357
|
-
public static int Run(string command, string workingDirectory, bool lowIntegrity, bool restrictedToken)
|
|
793
|
+
public static int Run(string command, string workingDirectory, bool lowIntegrity, bool restrictedToken, string tokenProbe)
|
|
358
794
|
{
|
|
359
795
|
IntPtr token = IntPtr.Zero;
|
|
360
796
|
IntPtr job = IntPtr.Zero;
|
|
@@ -363,7 +799,23 @@ namespace Xeno.Security
|
|
|
363
799
|
var startup = new STARTUPINFOEX();
|
|
364
800
|
try
|
|
365
801
|
{
|
|
366
|
-
|
|
802
|
+
string desktopGrantError = null;
|
|
803
|
+
if (restrictedToken)
|
|
804
|
+
{
|
|
805
|
+
token = CreateHardenedToken(lowIntegrity, tokenProbe);
|
|
806
|
+
desktopGrantError = GrantDesktopAccessForToken(token, tokenProbe);
|
|
807
|
+
// 🔴 Report the grant when asked, because otherwise a FAILED grant is
|
|
808
|
+
// invisible on every machine where the launch succeeds without it — which is
|
|
809
|
+
// every unelevated parent. A sensor that only speaks when something else has
|
|
810
|
+
// already failed cannot tell you the sensor itself is broken.
|
|
811
|
+
if (Environment.GetEnvironmentVariable("XENO_CONTAINMENT_DIAGNOSTICS") == "1")
|
|
812
|
+
{
|
|
813
|
+
Console.Error.WriteLine("[xeno-containment] desktop-grant="
|
|
814
|
+
+ (desktopGrantError == null ? "ok" : "FAILED: " + desktopGrantError)
|
|
815
|
+
+ (ownerDaclError == "" ? "" : " owner-dacl=" + ownerDaclError)
|
|
816
|
+
+ " detail=" + lastGrantReport);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
367
819
|
job = CreateKillOnCloseJob();
|
|
368
820
|
string shell = Environment.GetEnvironmentVariable("COMSPEC");
|
|
369
821
|
if (String.IsNullOrWhiteSpace(shell))
|
|
@@ -402,7 +854,16 @@ namespace Xeno.Security
|
|
|
402
854
|
workingDirectory,
|
|
403
855
|
ref startup,
|
|
404
856
|
out processInfo);
|
|
405
|
-
if (!created)
|
|
857
|
+
if (!created)
|
|
858
|
+
{
|
|
859
|
+
// A failed launch names the desktop grant when that grant also failed. Without
|
|
860
|
+
// this the caller sees only a Win32 code and has no way to reach the cause.
|
|
861
|
+
ThrowLastError(restrictedToken
|
|
862
|
+
? (desktopGrantError == null
|
|
863
|
+
? "CreateProcessAsUser"
|
|
864
|
+
: "CreateProcessAsUser (window station/desktop grant failed first: " + desktopGrantError + ")")
|
|
865
|
+
: "CreateProcess");
|
|
866
|
+
}
|
|
406
867
|
if (!AssignProcessToJobObject(job, processInfo.hProcess))
|
|
407
868
|
{
|
|
408
869
|
TerminateProcess(processInfo.hProcess, 126);
|
|
@@ -435,4 +896,4 @@ namespace Xeno.Security
|
|
|
435
896
|
}
|
|
436
897
|
}
|
|
437
898
|
}
|
|
438
|
-
`;var Kt=512*1024,Yt=744*60*60*1e3,Zt=Object.freeze(["allowedFilesystemRead","allowedFilesystemWrite","outsideFilesystemReadDenied","outsideFilesystemWriteDenied","unlistedFilesystemReadDenied","unlistedFilesystemWriteDenied","symlinkEscapeDenied","networkDefaultDeny","environmentSecretsExcluded","childProcessPolicyInherited","standardCommandLaunch","processTreeCleanup","timeoutEnforced","hostPolicyRecoveryClean","uiRestrictionsEnforced"]);var or=10*6e4,ir=Object.freeze({"win32-x64-wxc-exec.exe":"db0a3422be9e1b396cc1b2547c70ff16b27412438a31c10a45abf370cac86ae2","win32-arm64-wxc-exec.exe":"e430d0e4f44f616e91db684f8d825a6dc93e06a1262b8d00bcaac7522a317aab","linux-x64-lxc-exec":"1efe3f817f5ae9ba9f9f2fd59142e79b2dde7a4dd024576b0ddb635fc87edeb5","linux-arm64-lxc-exec":"fdc00ccecf362ae3ddd8ccad93fd7c8b5b1f03c68897ab05ef1c9f1d327ea3fe","darwin-arm64-mxc-exec-mac":"77c07a86f483ed91dd5be290cd77878ac0c56fe113cdb8c764e7e5201c744ad2"}),ar=Object.freeze({"win32-x64-wxc-host-prep.exe":"531fb3cdb4b0c964908fd71b71d40961417afb399cbab72f92a25e95309a6416","win32-arm64-wxc-host-prep.exe":"3ef702332286a39153fc259310b5021e3de3c191751d7522684f6475f73af5ef"});var Er=Object.freeze({policyEnforcement:!0,restrictedIdentity:!1,lowIntegrity:!1,processTreeControl:!1,filesystemIsolation:!1,networkIsolation:!1,inheritedHandleAllowlist:!1,environmentSanitization:!0}),yr=Object.freeze({policyEnforcement:!0,restrictedIdentity:!0,lowIntegrity:!0,processTreeControl:!0,filesystemIsolation:!1,networkIsolation:!1,inheritedHandleAllowlist:!0,environmentSanitization:!0}),vr=Object.freeze({policyEnforcement:!0,restrictedIdentity:!0,lowIntegrity:!1,processTreeControl:!0,filesystemIsolation:!1,networkIsolation:!1,inheritedHandleAllowlist:!1,environmentSanitization:!0}),Tr=Object.freeze({policyEnforcement:!0,restrictedIdentity:!0,lowIntegrity:!1,processTreeControl:!0,filesystemIsolation:!0,networkIsolation:!0,inheritedHandleAllowlist:!0,environmentSanitization:!0}),Sr=Object.freeze({policyEnforcement:!0,restrictedIdentity:!1,lowIntegrity:!1,processTreeControl:!1,filesystemIsolation:!1,networkIsolation:!1,inheritedHandleAllowlist:!1,environmentSanitization:!0});var Ve=1,z=class extends Error{constructor(r,n){super(n);this.code=r;this.name="XenoSecureExecutionContractError"}code};function $e(e){let t=[];if(!e||typeof e!="object")return["Contract must be an object"];if(e.schemaVersion!==Ve&&t.push("Unsupported schemaVersion"),e.contractId?.trim()||t.push("contractId is required"),(!e.identity?.runId?.trim()||!e.identity?.agentId?.trim())&&t.push("runId and agentId are required"),ue(e.createdAt)||t.push("createdAt must be an ISO 8601 timestamp"),e.expiresAt&&(!ue(e.expiresAt)||Date.parse(e.expiresAt)<=Date.parse(e.createdAt))&&t.push("expiresAt must be later than createdAt"),e.requestedLevel!==e.effectiveLevel&&t.push("requestedLevel and effectiveLevel must match"),e.trustMode==="untrusted"&&e.effectiveLevel!=="contained"&&t.push("Untrusted execution requires contained mode"),e.effectiveLevel==="contained"&&(e.adapter.certified||t.push("Contained mode requires a certified adapter"),e.adapter.certificationArtifactId||t.push("Contained mode requires a certification artifact"),(!e.adapter.capabilities.filesystemIsolation||e.filesystem.enforcement!=="os")&&t.push("Contained mode requires OS filesystem isolation"),(!e.adapter.capabilities.networkIsolation||e.network.enforcement!=="os")&&t.push("Contained mode requires OS network isolation"),e.network.default!=="deny"&&t.push("Contained networking must default deny")),e.network.enforcement==="none"&&!e.network.acknowledgedUnrestricted&&t.push("Unrestricted networking must be explicitly acknowledged"),e.filesystem.writeRoots.length>0&&e.profile.permissionProfile==="read-only"&&t.push("Read-only profiles cannot receive write roots"),(!Array.isArray(e.browser.readAllowedDomains)||!Array.isArray(e.browser.actAllowedDomains))&&t.push("Browser read/action domain policies are required"),(!Array.isArray(e.browser.allowedSchemes)||e.browser.allowedSchemes.some(r=>r!=="http"&&r!=="https"))&&t.push("Browser allowed schemes are invalid"),(!Array.isArray(e.browser.allowedPorts)||e.browser.allowedPorts.some(r=>!Number.isInteger(r)||r<1||r>65535))&&t.push("Browser allowed ports are invalid"),e.secrets.some(r=>!r.secretId.trim())&&t.push("Secret projections require non-empty secret IDs"),new Set(e.secrets.map(r=>r.secretId)).size!==e.secrets.length&&t.push("Secret IDs must be unique"),new Set(e.capabilityLeaseIds).size!==e.capabilityLeaseIds.length&&t.push("Capability lease IDs must be unique"),/^[a-f0-9]{64}$/.test(e.profile.fingerprint)||t.push("Profile fingerprint is invalid"),!/^[a-f0-9]{64}$/.test(e.fingerprint))t.push("Contract fingerprint is invalid");else{let r={...e};delete r.fingerprint,Be(r)!==e.fingerprint&&t.push("Contract fingerprint does not match content")}return t}function pe(e){let t=$e(e);if(t.length>0){let r=t.includes("Contract fingerprint does not match content");throw new z(r?"SECURITY_CONTRACT_FINGERPRINT_MISMATCH":"SECURITY_CONTRACT_INVALID",t.join("; "))}}function Be(e){return(0,fe.createHash)("sha256").update(JSON.stringify(W(e))).digest("hex")}function W(e){return Array.isArray(e)?e.map(W):!e||typeof e!="object"?e:Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,W(r)]))}function ue(e){return typeof e=="string"&&e.includes("T")&&Number.isFinite(Date.parse(e))}var Fe=/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/,me=1048576;function G(e){(!e||typeof e!="object")&&I("Automation request must be an object"),e.protocolVersion!==1&&I("Unsupported automation protocol version"),E(e.operationId,"operationId"),E(e.idempotencyKey,"idempotencyKey"),E(e.capabilityLeaseId,"capabilityLeaseId"),E(e.governingToolName,"governingToolName"),(!Number.isSafeInteger(e.expectedLeaseVersion)||e.expectedLeaseVersion<1)&&I("expectedLeaseVersion must be a positive safe integer"),be(e.requestedAt)||I("requestedAt must be an ISO 8601 timestamp"),x(e.operation)||I(`Unsupported automation operation: ${e.operation}`),(!e.parameters||typeof e.parameters!="object"||Array.isArray(e.parameters))&&I("parameters must be a JSON object");let t;try{t=Buffer.byteLength(JSON.stringify(e.parameters),"utf8")}catch(r){throw new i("AUTOMATION_REQUEST_INVALID","parameters must be serializable JSON",void 0,{cause:r})}if(t>me&&I(`parameters exceed ${me} bytes`),E(e.identity?.runId,"identity.runId"),E(e.identity?.agentId,"identity.agentId"),pe(e.executionContract),He(e),!e.executionContract.capabilityLeaseIds.includes(e.capabilityLeaseId))throw new i("AUTOMATION_CONTRACT_INVALID",`Capability lease ${e.capabilityLeaseId} is not bound to the execution contract`);if(e.executionContract.expiresAt&&Date.parse(e.executionContract.expiresAt)<=Date.now())throw new i("AUTOMATION_CONTRACT_EXPIRED","Execution contract has expired")}function S(e){(!e||typeof e!="object")&&m("Adapter manifest must be an object"),e.protocolVersion!==1&&m("Adapter protocol version is unsupported"),O(e.adapterId,"adapterId"),O(e.adapterVersion,"adapterVersion"),O(e.platform,"platform"),O(e.architecture,"architecture"),e.targetBinding!=="atomic-preflight"&&m("Adapter must implement atomic preflight target binding"),(!Array.isArray(e.operations)||e.operations.length===0)&&m("Adapter must declare operations");let t=new Set;for(let r of e.operations)x(r)||m(`Adapter declares unknown operation ${r}`),t.has(r)&&m(`Adapter declares duplicate operation ${r}`),t.add(r);e.certified&&!e.certificationArtifactId&&m("A certified automation adapter requires a certification artifact"),Array.isArray(e.limitations)||m("Adapter limitations must be an array"),e.operations.some(r=>r.startsWith("browser."))&&(!e.policyEnforcement.domain||!e.policyEnforcement.scheme||!e.policyEnforcement.port)&&m("Browser adapters must enforce domain, scheme, and port policy"),e.operations.some(r=>r.startsWith("computer."))&&!e.supports.immediateStop&&m("Computer-use adapters must support immediate stop")}function N(e,t,r){let n=A(e.operation);if(!t.operations.includes(e.operation))throw new i("AUTOMATION_OPERATION_UNSUPPORTED",`Adapter ${t.adapterId} does not support ${e.operation}`);if(n.surface==="browser"){let o=e.executionContract.browser.mode;if(o==="none"||n.effect==="act"&&o!=="act")throw new i("AUTOMATION_POLICY_DENIED",`Execution contract denies ${e.operation}`);if(je(e,r,n.effect==="act"),e.operation==="browser.download"&&e.executionContract.browser.downloads==="deny")throw new i("AUTOMATION_POLICY_DENIED","Execution contract denies browser downloads");if(e.operation==="browser.upload"&&e.executionContract.browser.uploads==="deny")throw new i("AUTOMATION_POLICY_DENIED","Execution contract denies browser uploads");if(e.operation.startsWith("browser.record.")&&e.executionContract.browser.recording!=="bounded")throw new i("AUTOMATION_POLICY_DENIED","Execution contract denies browser recording")}else{let o=e.executionContract.computer.mode;if(o==="none"||n.effect==="act"&&o!=="act")throw new i("AUTOMATION_POLICY_DENIED",`Execution contract denies ${e.operation}`);let a=e.executionContract.computer.allowedApplications,d=e.operation==="computer.launch"?We(e):r.applicationId??e.declaredTarget?.applicationId;if((e.operation==="computer.launch"||a.length>0)&&(!d||!a.includes(d)))throw new i("AUTOMATION_POLICY_DENIED",`Application ${d??"<unknown>"} is not allowed`)}}function q(e,t){let r=A(e.operation),n=r.surface==="browser"?r.effect==="act"?"browser-act":"browser-read":r.effect==="act"?"computer-act":"computer-observe";return{subject:{runId:e.identity.runId,agentId:e.identity.agentId,...e.identity.turnId?{turnId:e.identity.turnId}:{},toolName:e.governingToolName,operationId:e.operationId},kind:n,operation:e.operation,...Ae(e,t)?{resource:Ae(e,t)}:{},...Ie(t)?{destination:Ie(t)}:{}}}function Ae(e,t){return e.declaredTarget?.resource??ze(t)}function K(e){let t=A(e.operation).evidence,r=e.evidencePolicy??{},n=J(t.before,r.before),o=J(t.after,r.after),a=J(t.recording,r.recording),d=r.retainUntil;return d&&!be(d)&&I("evidencePolicy.retainUntil must be an ISO 8601 timestamp"),{before:n,after:o,recording:a,sensitivity:r.sensitivity??"confidential",...d?{retainUntil:d}:{},...r.legalHold!==void 0?{legalHold:r.legalHold}:{}}}function He(e){let t=e.executionContract.identity,r=["runId","agentId","sessionId","turnId","taskId","workspaceId"];for(let n of r){let o=t[n],a=e.identity[n];if(o!==void 0&&o!==a)throw new i("AUTOMATION_IDENTITY_MISMATCH",`Automation identity ${n} does not match the execution contract`)}}function je(e,t,r){let n=t.url??t.origin??e.declaredTarget?.url??e.declaredTarget?.origin;if(!n){if(["browser.tabs.list","browser.wait"].includes(e.operation))return;throw new i("AUTOMATION_PREFLIGHT_INVALID","Browser preflight did not bind an effective URL")}let o;try{o=new URL(n)}catch{throw new i("AUTOMATION_POLICY_DENIED","Browser target URL is invalid")}if(o.username||o.password)throw new i("AUTOMATION_POLICY_DENIED","Browser target URLs cannot contain credentials");let a=o.protocol.replace(/:$/,"");a!=="http"&&a!=="https"&&w(`Browser scheme ${o.protocol} is not supported`);let d=a,c=o.hostname.toLowerCase().replace(/\.$/,""),l=o.port?Number(o.port):d==="https"?443:80,s=e.executionContract.browser,u=Je(c);if(ge(c,s.deniedDomains)&&w(`Browser domain ${c} is denied`),he(o,e.executionContract.network.denyDestinations)&&w(`Network policy explicitly denies ${d}://${c}:${l}`),u&&s.allowLoopbackDevelopment)return;s.allowedSchemes.includes(d)||w(`Browser scheme ${o.protocol} is not allowed`),s.allowedPorts.includes(l)||w(`Browser port ${l} is not allowed`);let p=r?s.actAllowedDomains:s.readAllowedDomains;ge(c,p)||w(`Browser domain ${c} is outside the ${r?"action":"read"} allowlist`),e.executionContract.network.default==="deny"&&(he(o,e.executionContract.network.allowDestinations)||w(`Network policy does not allow ${d}://${c}:${l}`))}function he(e,t){let r=e.protocol.replace(/:$/,""),n=e.hostname.toLowerCase().replace(/\.$/,""),o=e.port?Number(e.port):r==="https"?443:80;return t.some(a=>{let d=a.scheme?.replace(/^custom:/,"");return a.host===n&&(a.port===void 0||a.port===o)&&(d===void 0||d===r)})}function ze(e){return e.resource??e.tabId??e.pageId??e.deviceId??e.applicationId}function Ie(e){let t=e.origin??e.url;if(!t)return e.domain;try{return new URL(t).origin}catch{return t}}function We(e){let t=e.parameters.target,r=t&&typeof t=="object"&&!Array.isArray(t)?t.app_id:void 0,n=e.parameters.applicationId??r??e.declaredTarget?.applicationId;return typeof n=="string"&&n.length>0?n:void 0}function ge(e,t){return t.some(r=>{let n=r.toLowerCase();if(n.startsWith(".")){let o=n.slice(1);return e===o||e.endsWith(`.${o}`)}return e===n})}function Je(e){return e==="localhost"||e==="127.0.0.1"||e==="[::1]"||e==="::1"}function J(e,t){let r={disabled:0,optional:1,required:2};return!t||r[t]<=r[e]?e:t}function E(e,t){(typeof e!="string"||!Fe.test(e))&&I(`${t} is invalid`)}function O(e,t){(typeof e!="string"||e.trim().length===0||e.length>256)&&m(`${t} is invalid`)}function be(e){return typeof e=="string"&&e.includes("T")&&Number.isFinite(Date.parse(e))}function w(e){throw new i("AUTOMATION_POLICY_DENIED",e)}function I(e){throw new i("AUTOMATION_REQUEST_INVALID",e)}function m(e){throw new i("AUTOMATION_OPERATION_UNSUPPORTED",e)}var ye=require("node:crypto");var we=require("node:crypto");function P(e){return{algorithm:"sha256",value:(0,we.createHash)("sha256").update(e).digest("hex")}}var Ge="[\\u0000-\\u001f\\u007f]",Br=new RegExp(Ge);var X=class extends Error{constructor(r,n,o,a){super(n,a);this.code=r;this.detail=o;this.name="XenoArtifactRepositoryError"}code;detail};var Z=32*1024*1024,qe=1024*1024*1024,Ke=/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/;function L(e){let r=(e.now??(()=>new Date().toISOString()))(),n=new Set;return e.evidence.map((o,a)=>{if(Ze(o),n.has(o.evidenceId))throw new i("AUTOMATION_EVIDENCE_INVALID",`Duplicate evidence ID ${o.evidenceId}`);n.add(o.evidenceId);let d=Ye(o),c=(0,ye.createHash)("sha256").update(`${e.request.operationId}\0${o.evidenceId}\0${d.hash.value}`).digest("hex").slice(0,32);return{schemaVersion:1,artifactId:`automation_${c}`,revision:1,kind:o.kind,title:o.title,...o.description?{description:o.description}:{},state:"draft",createdAt:r,mediaType:o.mediaType,sensitivity:o.sensitivity??e.policy.sensitivity,identity:{...e.request.identity},content:d,provenance:{producer:{kind:"tool",id:e.request.governingToolName,displayName:e.manifest.adapterId,profileFingerprint:e.request.executionContract.profile.fingerprint},operationId:e.request.operationId,executionContractFingerprint:e.request.executionContract.fingerprint,capabilityLeaseIds:[e.request.capabilityLeaseId],attributes:{automationProtocolVersion:e.request.protocolVersion,automationOperation:e.request.operation,automationEvidenceId:o.evidenceId,automationEvidencePhase:o.phase,adapterId:e.manifest.adapterId,adapterVersion:e.manifest.adapterVersion,evidenceIndex:a}},...o.anchors?{anchors:structuredClone(o.anchors)}:{},...e.policy.retainUntil||e.policy.legalHold!==void 0?{retention:{...e.policy.retainUntil?{retainUntil:e.policy.retainUntil}:{},...e.policy.legalHold!==void 0?{legalHold:e.policy.legalHold}:{}}}:{},extensions:{...o.extensions??{},xenoAutomation:{phase:o.phase,adapter:e.manifest.adapterId,certifiedAdapter:e.manifest.certified}}}})}async function D(e,t){if(!e)return t.map(n=>structuredClone(n));let r=[];for(let n of t)try{let o=await e.create(n);r.push(o.artifact)}catch(o){if(o instanceof X&&o.code==="ARTIFACT_ALREADY_EXISTS"){let a=await e.require(n.artifactId,n.revision);if(a.artifact.content.hash.value===n.content.hash.value){r.push(a.artifact);continue}}throw new i("AUTOMATION_EVIDENCE_PERSISTENCE_FAILED",`Failed to persist automation evidence ${n.artifactId}`,{artifactId:n.artifactId},{cause:o})}return r}function C(e,t,r){if(t[r]==="required"&&!e.some(n=>n.phase===r))throw new i("AUTOMATION_EVIDENCE_REQUIRED",`Automation operation requires ${r} evidence before it can be trusted`)}function Ye(e){if(e.content.type==="text"){let t=Buffer.byteLength(e.content.text,"utf8");return Ee(t),{hash:P(e.content.text),sizeBytes:t,storage:{type:"inline",text:e.content.text},encoding:"utf8",...e.redactedPreview?{redactedPreview:e.redactedPreview}:{}}}if(e.content.type==="base64"){let t=Qe(e.content.base64);return Ee(t.byteLength),{hash:P(t),sizeBytes:t.byteLength,storage:{type:"inline",base64:e.content.base64},encoding:"base64",...e.redactedPreview?{redactedPreview:e.redactedPreview}:{}}}if(!Number.isSafeInteger(e.content.sizeBytes)||e.content.sizeBytes<0||e.content.sizeBytes>qe)throw new i("AUTOMATION_EVIDENCE_INVALID","Referenced evidence size is invalid");if(!e.content.hash?.algorithm||!e.content.hash.value)throw new i("AUTOMATION_EVIDENCE_INVALID","Referenced evidence requires a content hash");if(e.content.hash.algorithm==="sha256"&&!/^[a-f0-9]{64}$/.test(e.content.hash.value))throw new i("AUTOMATION_EVIDENCE_INVALID","Referenced evidence SHA-256 is invalid");return{hash:{...e.content.hash},sizeBytes:e.content.sizeBytes,storage:structuredClone(e.content.storage),...e.content.encoding?{encoding:e.content.encoding}:{},...e.redactedPreview?{redactedPreview:e.redactedPreview}:{}}}function Ze(e){(!e||typeof e!="object")&&g("Evidence must be an object"),Ke.test(e.evidenceId)||g("Evidence ID is invalid"),["before","action","after","recording","diagnostic"].includes(e.phase)||g("Evidence phase is invalid"),(!e.kind||!e.title?.trim()||e.title.length>512)&&g("Evidence kind and title are required"),/^[\w!#$&^_.+-]+\/[\w!#$&^_.+-]+(?:;.*)?$/.test(e.mediaType)||g("Evidence media type is invalid")}function Qe(e){(e.length===0||e.length>Math.ceil(Z/3)*4+4)&&g("Inline base64 evidence is empty or too large"),(!/^[A-Za-z0-9+/]*={0,2}$/.test(e)||e.length%4!==0)&&g("Inline evidence is not canonical base64");let t=Buffer.from(e,"base64");return t.toString("base64")!==e&&g("Inline evidence is not canonical base64"),t}function Ee(e){e>Z&&g(`Inline evidence exceeds ${Z} bytes`)}function g(e){throw new i("AUTOMATION_EVIDENCE_INVALID",e)}var Te=require("node:crypto");var ve=3e4,et=1e3,te=class{adapter;leaseAuthority;artifactRepository;now;executions=new Map;active=new Map;constructor(t){this.adapter=t.adapter,this.leaseAuthority=t.leaseAuthority,this.artifactRepository=t.artifactRepository,this.now=t.now??(()=>new Date().toISOString())}execute(t,r){let n=it(t),o=this.executions.get(t.operationId);if(o){if(o.fingerprint!==n)throw new i("AUTOMATION_OPERATION_CONFLICT",`Operation ${t.operationId} was already submitted with different authority or parameters`);return o.promise}let a=new AbortController,d=at(r,a);this.active.set(t.operationId,a);let c=this.executeFresh(t,a.signal).finally(()=>{d(),this.active.delete(t.operationId)});return this.executions.set(t.operationId,{fingerprint:n,promise:c}),this.trimIdempotencyRecords(),c}async stop(t,r="human-stop"){let n=this.active.get(t);return n?(n.abort(new Error(r)),await this.adapter.stop?.(t,r),!0):!1}async executeFresh(t,r){G(t),ee(r);let n=await this.adapter.manifest();S(n);let o=A(t.operation),a=K(t),d=this.now(),c=await this.adapter.preflight(t,r);tt(c,this.now()),N(t,n,c.observedTarget),ee(r);let l=c.evidence??[];C(l,a,"before");let s=await D(this.artifactRepository,L({request:t,manifest:n,policy:a,evidence:l,now:this.now}));if(ee(r),Date.parse(c.expiresAt)<=Date.parse(this.now()))throw new i("AUTOMATION_PREFLIGHT_INVALID","Automation preflight expired before authority consumption");let u=q(t,c.observedTarget),p=await this.leaseAuthority.consume(t.capabilityLeaseId,t.expectedLeaseVersion,u),v={contractFingerprint:t.executionContract.fingerprint,lease:p,capabilityUse:u},f;try{f=await this.adapter.execute(t,c,v,r)}catch(h){return Q({request:t,manifest:n,descriptorEffect:o.effect,target:c.observedTarget,lease:p,artifacts:s,startedAt:d,completedAt:this.now(),status:r.aborted?"cancelled":"error",code:r.aborted?"AUTOMATION_CANCELLED":"AUTOMATION_ADAPTER_ERROR",message:r.aborted?"Automation operation was stopped":st(h)})}rt(f);let se=nt(c.observedTarget,f.observedTarget),M;try{N(t,n,f.observedTarget)}catch(h){M=h instanceof Error?h:new Error(String(h))}let R=f.evidence??[];ot(l,R);let ce=[],U;try{C(R,a,"after"),C([...l,...R],a,"recording"),ce=await D(this.artifactRepository,L({request:t,manifest:n,policy:a,evidence:R,now:this.now}))}catch(h){U=h instanceof Error?h:new Error(String(h))}let V=[...s,...ce];return se||M?Q({request:t,manifest:n,descriptorEffect:o.effect,target:f.observedTarget,lease:p,artifacts:V,startedAt:f.startedAt,completedAt:f.completedAt,status:"error",code:"AUTOMATION_TARGET_CHANGED",message:se??M.message}):U?Q({request:t,manifest:n,descriptorEffect:o.effect,target:f.observedTarget,lease:p,artifacts:V,startedAt:f.startedAt,completedAt:f.completedAt,status:"evidence-incomplete",code:"AUTOMATION_EVIDENCE_INCOMPLETE",message:U.message}):{operationId:t.operationId,status:f.status,effect:o.effect,startedAt:f.startedAt,completedAt:f.completedAt,adapter:Se(n),target:structuredClone(f.observedTarget),...f.data!==void 0?{data:structuredClone(f.data)}:{},artifacts:V,lease:Ce(p),...f.error?{error:{...f.error}}:{}}}trimIdempotencyRecords(){for(;this.executions.size>et;){let t=this.executions.keys().next().value;if(!t||this.active.has(t))break;this.executions.delete(t)}}};function tt(e,t){if(!e||typeof e!="object"||!e.token?.trim())throw new i("AUTOMATION_PREFLIGHT_INVALID","Adapter returned an invalid preflight token");let r=Date.parse(e.preparedAt),n=Date.parse(e.expiresAt),o=Date.parse(t);if(!Number.isFinite(r)||!Number.isFinite(n)||n<=r||n<=o)throw new i("AUTOMATION_PREFLIGHT_INVALID","Adapter returned an expired or invalid preflight window");if(n-r>ve)throw new i("AUTOMATION_PREFLIGHT_INVALID",`Preflight TTL exceeds ${ve} ms`);if(!e.observedTarget||typeof e.observedTarget!="object")throw new i("AUTOMATION_PREFLIGHT_INVALID","Adapter did not bind an observed target")}function rt(e){if(!e||typeof e!="object")throw new i("AUTOMATION_REQUEST_INVALID","Adapter returned no result");if(!["ok","denied","cancelled","error"].includes(e.status))throw new i("AUTOMATION_REQUEST_INVALID","Adapter returned an invalid status");if(!Number.isFinite(Date.parse(e.startedAt))||!Number.isFinite(Date.parse(e.completedAt)))throw new i("AUTOMATION_REQUEST_INVALID","Adapter result timestamps are invalid");if(Date.parse(e.completedAt)<Date.parse(e.startedAt))throw new i("AUTOMATION_REQUEST_INVALID","Adapter completion precedes its start");if(!e.observedTarget||typeof e.observedTarget!="object")throw new i("AUTOMATION_REQUEST_INVALID","Adapter result has no observed target")}function nt(e,t){let r=["tabId","deviceId","displayId"];for(let n of r)if(e[n]!==void 0&&e[n]!==t[n])return`Adapter changed bound ${n} between preflight and execution`}function ot(e,t){let r=new Set(e.map(n=>n.evidenceId));for(let n of t){if(r.has(n.evidenceId))throw new i("AUTOMATION_EVIDENCE_INVALID",`Duplicate evidence ID ${n.evidenceId}`);r.add(n.evidenceId)}}function Q(e){return{operationId:e.request.operationId,status:e.status,effect:e.descriptorEffect,startedAt:e.startedAt,completedAt:e.completedAt,adapter:Se(e.manifest),target:structuredClone(e.target),artifacts:e.artifacts,lease:Ce(e.lease),error:{code:e.code,message:e.message}}}function Se(e){return{adapterId:e.adapterId,adapterVersion:e.adapterVersion,platform:e.platform,architecture:e.architecture}}function Ce(e){return{leaseId:e.leaseId,version:e.version,state:e.state,useCount:e.useCount}}function it(e){return(0,Te.createHash)("sha256").update(JSON.stringify(re(e))).digest("hex")}function re(e){return Array.isArray(e)?e.map(re):!e||typeof e!="object"?e:Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,re(r)]))}function at(e,t){if(!e)return()=>{};let r=()=>t.abort(e.reason);return e.aborted?r():e.addEventListener("abort",r,{once:!0}),()=>e.removeEventListener("abort",r)}function ee(e){if(e.aborted)throw new i("AUTOMATION_CANCELLED","Automation operation was stopped before dispatch")}function st(e){return e instanceof Error&&e.message.slice(0,1024)||"Automation adapter failed"}function Re(e,t=()=>new Date().toISOString()){let r=[];dt(r,"manifest.valid",()=>S(e));let n=new Set(e.operations);return y(r,n,e.supports.consoleInspection,"browser.console.read","console inspection"),y(r,n,e.supports.networkInspection,"browser.network.read","network inspection"),y(r,n,e.supports.storageInspection,"browser.storage.read","storage inspection"),y(r,n,e.supports.pageErrorInspection,"browser.page-errors.read","page-error inspection"),y(r,n,e.supports.recordings,"browser.record.start","recording start"),y(r,n,e.supports.recordings,"browser.record.stop","recording stop"),n.has("browser.upload")&&b(r,"policy.upload",e.policyEnforcement.upload,"Upload policy is enforced"),n.has("browser.download")&&b(r,"policy.download",e.policyEnforcement.download,"Download policy is enforced"),[...n].some(o=>o.startsWith("browser."))&&(b(r,"policy.redirect",e.policyEnforcement.redirect,"Redirect policy is enforced"),b(r,"control.handback",e.supports.deterministicHandback,"Deterministic hand-back is supported")),[...n].some(o=>o.startsWith("computer."))&&(b(r,"control.stop",e.supports.immediateStop,"Immediate human stop is supported"),b(r,"privacy.redaction",e.supports.sensitiveRegionRedaction,"Sensitive-region redaction is supported")),b(r,"certification.binding",!e.certified||!!e.certificationArtifactId,e.certified?"Certification artifact is bound":"Adapter does not claim certification"),{protocolVersion:1,adapter:{adapterId:e.adapterId,adapterVersion:e.adapterVersion,platform:e.platform,architecture:e.architecture},generatedAt:t(),passed:r.every(o=>o.status==="pass"),checks:r}}function ct(e){let r=Re(e).checks.filter(n=>n.status==="fail");if(r.length>0)throw new i("AUTOMATION_OPERATION_UNSUPPORTED",r.map(n=>`${n.id}: ${n.message}`).join("; "))}function y(e,t,r,n,o){let a=t.has(n);b(e,`capability.${n}`,r===a,r===a?`${o} declaration matches the operation catalog`:`${o} declaration does not match ${n}`)}function dt(e,t,r){try{r(),e.push({id:t,status:"pass",message:"Manifest satisfies the protocol contract"})}catch(n){e.push({id:t,status:"fail",message:n instanceof Error?n.message:String(n)})}}function b(e,t,r,n){e.push({id:t,status:r?"pass":"fail",message:n})}var lt=6e4,ut=48*1024*1024,ne=class{endpoint;token;timeoutMs;maxResponseBytes;fetchImpl;constructor(t){if(this.endpoint=ft(t.baseUrl),this.token=pt(t.token),this.timeoutMs=_e(t.timeoutMs??lt,1e3,3e5,"timeoutMs"),this.maxResponseBytes=_e(t.maxResponseBytes??ut,1024,128*1024*1024,"maxResponseBytes"),this.fetchImpl=t.fetch??globalThis.fetch,typeof this.fetchImpl!="function")throw new i("AUTOMATION_TRANSPORT_ERROR","A Fetch API implementation is required")}manifest(){return this.request("manifest","GET")}preflight(t,r){return this.request("preflight","POST",t,r)}execute(t,r,n,o){return this.request("execute","POST",{request:t,preflight:r,grant:n},o)}async stop(t,r){await this.request("stop","POST",{operationId:t,reason:r.slice(0,512)})}async request(t,r,n,o){let a=new AbortController,d=ht(o,a),c=setTimeout(()=>a.abort(new Error("automation transport timeout")),this.timeoutMs);c.unref?.();try{let l=new URL(t,this.endpoint),s=await this.fetchImpl(l,{method:r,redirect:"error",cache:"no-store",credentials:"omit",headers:{authorization:`Bearer ${this.token}`,accept:"application/json",...r==="POST"?{"content-type":"application/json"}:{}},...r==="POST"?{body:JSON.stringify(n??{})}:{},signal:a.signal}),u=await mt(s,this.maxResponseBytes);if(!s.ok){let p=At(u);throw new i("AUTOMATION_TRANSPORT_ERROR",`XENO Browser rejected ${t}: ${p.message}`,{status:s.status,route:t,adapterCode:p.code})}if(!oe(u)||u.ok!==!0||!("result"in u))throw new i("AUTOMATION_RESPONSE_INVALID",`XENO Browser returned an invalid ${t} envelope`);return u.result}catch(l){if(l instanceof i)throw l;let s=o?.aborted;throw new i(s?"AUTOMATION_CANCELLED":"AUTOMATION_TRANSPORT_ERROR",s?"Automation transport was stopped":`XENO Browser ${t} transport failed`,{route:t},{cause:l})}finally{clearTimeout(c),d()}}};function ft(e){let t;try{t=new URL(e)}catch(n){throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser base URL is invalid",void 0,{cause:n})}if(t.protocol!=="http:"&&t.protocol!=="https:")throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser transport requires HTTP(S)");if(t.username||t.password||t.search||t.hash)throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser base URL cannot contain credentials, query, or fragment");let r=t.hostname.toLowerCase().replace(/^\[|\]$/g,"").replace(/\.$/,"");if(r!=="localhost"&&r!=="127.0.0.1"&&r!=="::1")throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser transport must use a loopback host");if(!t.port)throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser transport requires an explicit port");return t.pathname="/v1/automation/",t}function pt(e){if(typeof e!="string"||e.length<32||e.length>512||/\s/.test(e))throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser session token is invalid");return e}async function mt(e,t){let r=Number(e.headers.get("content-length"));if(Number.isFinite(r)&&r>t)throw new i("AUTOMATION_RESPONSE_INVALID",`XENO Browser response exceeds ${t} bytes`);if(e.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase()!=="application/json")throw new i("AUTOMATION_RESPONSE_INVALID","XENO Browser response is not application/json");if(!e.body)throw new i("AUTOMATION_RESPONSE_INVALID","XENO Browser response body is missing");let o=e.body.getReader(),a=[],d=0;for(;;){let{done:s,value:u}=await o.read();if(s)break;if(d+=u.byteLength,d>t)throw await o.cancel("response too large"),new i("AUTOMATION_RESPONSE_INVALID",`XENO Browser response exceeds ${t} bytes`);a.push(u)}let c=new Uint8Array(d),l=0;for(let s of a)c.set(s,l),l+=s.byteLength;try{return JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(c))}catch(s){throw new i("AUTOMATION_RESPONSE_INVALID","XENO Browser response contains invalid JSON",void 0,{cause:s})}}function At(e){return oe(e)&&oe(e.error)?{code:typeof e.error.code=="string"?e.error.code.slice(0,128):"UNKNOWN",message:typeof e.error.message=="string"?e.error.message.slice(0,1024):"request failed"}:{code:"UNKNOWN",message:"request failed"}}function ht(e,t){if(!e)return()=>{};let r=()=>t.abort(e.reason);return e.aborted?r():e.addEventListener("abort",r,{once:!0}),()=>e.removeEventListener("abort",r)}function _e(e,t,r,n){if(!Number.isSafeInteger(e)||e<t||e>r)throw new i("AUTOMATION_TRANSPORT_ERROR",`${n} must be an integer from ${t} to ${r}`);return e}function oe(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}var k=require("node:crypto");var It=8*1024*1024,gt=4;function bt(e){return Pt(e.operations??_).map(r=>wt(r,e.runtime))}function wt(e,t){return{definition:{name:e,description:St(e),input_schema:Et(e)},projectPolicyInput(r){let n=_t(e,r),o=Rt(e,r);return{observabilityInput:n,permissionInput:o,permissionPreview:xt(e,r),approvalKey:`xeno-automation:${e}:${Xt(r)}`,riskLevel:Ct(e)}},async execute(r,n){let o=n?.authorization;if(!o||o.toolName!==e)return{success:!1,output:"",error:"Governed automation requires a host-issued authorization receipt",errorCode:"AUTOMATION_AUTHORIZATION_RECEIPT_REQUIRED",retryable:!1};let a=n.operationId??`automation_${(0,k.randomUUID)()}`,d=ae(e,r);n.reportProgress?.({activity:"state",message:`Authorizing ${e}`});try{let c=await t.execute({operation:e,governingToolName:e,operationId:a,idempotencyKey:a,parameters:structuredClone(r),...d?{declaredTarget:d}:{},authorization:o,...n.signal?{signal:n.signal}:{},...n.reportProgress?{reportProgress:n.reportProgress}:{}}),l=c.status==="ok";return{success:l,output:JSON.stringify(Ot(c)),...l?{}:{error:c.error?.message??`Governed automation ended with ${c.status}`,errorCode:c.error?.code??`AUTOMATION_${c.status.toUpperCase().replaceAll("-","_")}`},assistantContent:Nt(c),evidence:c.artifacts.map(s=>({id:s.artifactId,kind:"artifact",status:c.status==="evidence-incomplete"?"failed":"verified",observedAt:s.updatedAt??s.createdAt,operationId:a,detail:{artifact_kind:s.kind,revision:s.revision,sensitivity:s.sensitivity}})),retryable:c.error?.retryable??!1}}catch(c){let l=n.signal?.aborted===!0;return{success:!1,output:"",error:l?"Governed automation was stopped":Lt(c),errorCode:l?"AUTOMATION_CANCELLED":Dt(c),retryable:!l}}}}}function Et(e){let t=yt(e),r=vt(e);return{type:"object",properties:t,...r.length>0?{required:r}:{},...e==="browser.locate"?{oneOf:[{required:["selector"]},{required:["text"]}]}:{},additionalProperties:!1}}function yt(e){let t=(d,c=8192)=>({type:"string",minLength:1,maxLength:c,description:d}),r={type:"number"},n={type:"boolean"},o={type:"object",properties:{x:r,y:r},required:["x","y"],additionalProperties:!1},a={deviceId:t("Target xeno-use device ID.",256),applicationId:t("Expected or launch-target application ID.",512),displayId:t("Target display ID.",256)};switch(e){case"browser.navigate":return{url:t("Destination URL."),waitUntil:{enum:["load","none"]},tabId:t("Expected active tab ID. Hosts with multiple visible tabs should require this.",256)};case"browser.wait":return{until:{enum:["load","ms"]},value:{type:"integer",minimum:0,maximum:6e4}};case"browser.snapshot":return{mode:{enum:["text","html","markdown","a11y"]},tabId:t("Expected active tab ID.",256)};case"browser.screenshot":return{tabId:t("Expected active tab ID.",256)};case"browser.locate":return{selector:t("CSS selector."),text:t("Visible text to locate.")};case"browser.click":return{selector:t("CSS selector returned by browser.snapshot or browser.locate."),tabId:t("Expected active tab ID.",256)};case"browser.type":return{selector:t("CSS selector."),text:{type:"string",maxLength:65536},clear:n,submit:n,tabId:t("Expected active tab ID.",256)};case"browser.key":return{combo:t("Key combo such as Control+a or Enter.",256),tabId:t("Expected active tab ID.",256)};case"browser.select":return{selector:t("CSS selector."),value:t("Option value."),tabId:t("Expected active tab ID.",256)};case"browser.scroll":return{selector:t("Optional CSS selector."),dx:r,dy:r,tabId:t("Expected active tab ID.",256)};case"browser.tabs.open":return{url:t("URL to open in an isolated browser tab.")};case"browser.tabs.close":return{tabId:t("Tab ID to close.",256)};case"browser.console.read":case"browser.network.read":case"browser.page-errors.read":return{limit:{type:"integer",minimum:1,maximum:1e3},clear:n,tabId:t("Expected active tab ID.",256)};case"browser.storage.read":return{tabId:t("Expected active tab ID.",256)};case"browser.upload":return{selector:t("File input or drop-target selector."),backendNodeId:{type:"integer",minimum:1},hostPaths:{type:"array",items:t("Host file path.",32768),minItems:1,maxItems:64},drop:n,tabId:t("Expected active tab ID.",256)};case"browser.download":return{toPath:t("Approved destination path.",32768),trigger:{type:"object",properties:{url:t("Download URL."),selector:t("Download trigger selector.")},oneOf:[{required:["url"]},{required:["selector"]}],additionalProperties:!1},tabId:t("Expected active tab ID.",256)};case"browser.record.start":return{maxDurationMs:{type:"integer",minimum:1e3,maximum:6e4},intervalMs:{type:"integer",minimum:100,maximum:5e3},tabId:t("Expected active tab ID.",256)};case"browser.record.stop":return{tabId:t("Expected active tab ID.",256)};case"computer.observe":case"computer.screenshot":case"computer.window-state":return a;case"computer.point":return{...a,point:o,durationMs:{type:"integer",minimum:0,maximum:6e4}};case"computer.click":return{...a,point:o,button:{enum:["left","right","middle"]},modifiers:Tt()};case"computer.double-click":case"computer.right-click":return{...a,point:o};case"computer.drag":return{...a,from:o,to:o,durationMs:{type:"integer",minimum:0,maximum:6e4}};case"computer.type":return{...a,text:{type:"string",maxLength:65536}};case"computer.key":return{...a,combo:t("Key combo such as Control+a or Enter.",256)};case"computer.scroll":return{...a,at:o,delta:o};case"computer.wait":return{...a,milliseconds:{type:"integer",minimum:0,maximum:3e5}};case"computer.launch":return{...a,applicationId:t("Explicitly allowlisted application ID.",512)};case"browser.back":case"browser.forward":case"browser.reload":return{tabId:t("Expected active tab ID.",256)};case"browser.tabs.list":return{}}}function vt(e){switch(e){case"browser.navigate":case"browser.tabs.open":return["url"];case"browser.click":return["selector"];case"browser.type":return["selector","text"];case"browser.key":return["combo"];case"browser.select":return["selector","value"];case"browser.upload":return["hostPaths"];case"browser.download":return["toPath","trigger"];case"computer.point":case"computer.click":case"computer.double-click":case"computer.right-click":return["point"];case"computer.drag":return["from","to"];case"computer.type":return["text"];case"computer.key":return["combo"];case"computer.scroll":return["at","delta"];case"computer.launch":return["applicationId"];default:return[]}}function Tt(){return{type:"object",properties:{shift:{type:"boolean"},ctrl:{type:"boolean"},alt:{type:"boolean"},meta:{type:"boolean"}},additionalProperties:!1}}function St(e){let t=A(e),r=e.split(".").slice(1).join(" ");return`${t.effect==="act"?"Act on":"Inspect"} the ${t.surface} using governed ${r} automation. The host requires policy approval, an exact capability lease, target-bound preflight, and durable evidence.`}function Ct(e){return A(e).effect==="observe"?e==="browser.navigate"||e==="browser.tabs.open"?"medium":"low":["browser.type","browser.upload","browser.download","computer.type","computer.key","computer.launch"].includes(e)?"high":"medium"}function Rt(e,t){let r=ae(e,t),n={operation:e};r&&(n.target=r);for(let o of["selector","clear","submit","combo","value","dx","dy","point","from","to","at","delta","milliseconds"])t[o]!==void 0&&(n[o]=structuredClone(t[o]));return typeof t.text=="string"&&(n.text=`[redacted ${t.text.length} characters]`),Array.isArray(t.hostPaths)&&(n.hostPaths=t.hostPaths.map(()=>"[redacted host path]")),typeof t.toPath=="string"&&(n.toPath="[redacted destination path]"),n}function _t(e,t){let r=structuredClone(t);return typeof r.text=="string"&&(r.text=`[redacted ${r.text.length} characters]`),Array.isArray(r.hostPaths)&&(r.hostPaths=r.hostPaths.map(()=>"[redacted host path]")),typeof r.toPath=="string"&&(r.toPath="[redacted destination path]"),Oe(r.trigger)&&typeof r.trigger.url=="string"&&(r.trigger={...r.trigger,url:xe(r.trigger.url)}),typeof r.url=="string"&&(r.url=xe(r.url)),{operation:e,...r}}function xt(e,t){let r=ae(e,t),n=r?.url??r?.applicationId??r?.deviceId??r?.tabId??r?.resource??"current target";return`${e} on ${n}`}function ae(e,t){let r={};return typeof t.url=="string"&&(r.url=t.url),typeof t.tabId=="string"&&(r.tabId=t.tabId),typeof t.deviceId=="string"&&(r.deviceId=t.deviceId),typeof t.applicationId=="string"&&(r.applicationId=t.applicationId),typeof t.displayId=="string"&&(r.displayId=t.displayId),(e==="browser.navigate"||e==="browser.tabs.list"||e==="browser.tabs.open")&&(r.resource="browser-tabs"),Object.keys(r).length>0?r:void 0}function Ot(e){return{operationId:e.operationId,status:e.status,effect:e.effect,startedAt:e.startedAt,completedAt:e.completedAt,adapter:e.adapter,target:e.target,...e.data!==void 0?{data:e.data}:{},artifacts:e.artifacts.map(t=>({artifactId:t.artifactId,revision:t.revision,kind:t.kind,title:t.title,state:t.state,mediaType:t.mediaType,sensitivity:t.sensitivity,hash:t.content.hash,sizeBytes:t.content.sizeBytes})),lease:e.lease,...e.error?{error:e.error}:{}}}function Nt(e){let t=[];for(let r of e.artifacts){if(t.length>=gt||!r.mediaType.startsWith("image/"))continue;let n=r.content.storage;if(n.type!=="inline"||typeof n.base64!="string")continue;let o=Math.floor(n.base64.length*.75);o<=0||o>It||t.push({type:"image_url",image_url:{url:`data:${r.mediaType};base64,${n.base64}`,detail:"high"}})}return t}function Pt(e){let t=new Set(_),r=new Set;for(let n of e){if(!t.has(n))throw new TypeError(`Unsupported XENO automation operation: ${String(n)}`);r.add(n)}return Array.from(r)}function Xt(e){return(0,k.createHash)("sha256").update(ie(e)).digest("hex")}function ie(e){return Array.isArray(e)?`[${e.map(ie).join(",")}]`:Oe(e)?`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${ie(e[t])}`).join(",")}}`:JSON.stringify(e)??"null"}function xe(e){try{let t=new URL(e);return`${t.origin}${t.pathname}`}catch{return"[invalid URL]"}}function Lt(e){return e instanceof Error?e.message:String(e)}function Dt(e){return e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"?e.code:"AUTOMATION_EXECUTION_FAILED"}function Oe(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}0&&(module.exports={XENO_AUTOMATION_OPERATIONS,XENO_AUTOMATION_PROTOCOL_VERSION,XenoAutomationError,XenoGovernedAutomationExecutor,XenoLoopbackAutomationAdapter,assertRequiredXenoAutomationEvidence,assertValidXenoAutomationAdapterManifest,assertValidXenoAutomationRequest,assertXenoAutomationAdapterConformant,assertXenoAutomationAuthority,buildXenoAutomationCapabilityUse,createXenoAutomationConformanceReport,createXenoGovernedAutomationTools,describeXenoAutomationOperation,isXenoAutomationOperation,materializeXenoAutomationEvidence,persistXenoAutomationEvidence,resolveXenoAutomationEvidencePolicy});
|
|
899
|
+
`;var Kt=512*1024,Yt=744*60*60*1e3,Zt=Object.freeze(["allowedFilesystemRead","allowedFilesystemWrite","outsideFilesystemReadDenied","outsideFilesystemWriteDenied","unlistedFilesystemReadDenied","unlistedFilesystemWriteDenied","symlinkEscapeDenied","networkDefaultDeny","environmentSecretsExcluded","childProcessPolicyInherited","standardCommandLaunch","processTreeCleanup","timeoutEnforced","hostPolicyRecoveryClean","uiRestrictionsEnforced"]);var or=10*6e4,ir=Object.freeze({"win32-x64-wxc-exec.exe":"db0a3422be9e1b396cc1b2547c70ff16b27412438a31c10a45abf370cac86ae2","win32-arm64-wxc-exec.exe":"e430d0e4f44f616e91db684f8d825a6dc93e06a1262b8d00bcaac7522a317aab","linux-x64-lxc-exec":"1efe3f817f5ae9ba9f9f2fd59142e79b2dde7a4dd024576b0ddb635fc87edeb5","linux-arm64-lxc-exec":"fdc00ccecf362ae3ddd8ccad93fd7c8b5b1f03c68897ab05ef1c9f1d327ea3fe","darwin-arm64-mxc-exec-mac":"77c07a86f483ed91dd5be290cd77878ac0c56fe113cdb8c764e7e5201c744ad2"}),ar=Object.freeze({"win32-x64-wxc-host-prep.exe":"531fb3cdb4b0c964908fd71b71d40961417afb399cbab72f92a25e95309a6416","win32-arm64-wxc-host-prep.exe":"3ef702332286a39153fc259310b5021e3de3c191751d7522684f6475f73af5ef"});var Er=Object.freeze({policyEnforcement:!0,restrictedIdentity:!1,lowIntegrity:!1,processTreeControl:!1,filesystemIsolation:!1,networkIsolation:!1,inheritedHandleAllowlist:!1,environmentSanitization:!0}),yr=Object.freeze({policyEnforcement:!0,restrictedIdentity:!0,lowIntegrity:!0,processTreeControl:!0,filesystemIsolation:!1,networkIsolation:!1,inheritedHandleAllowlist:!0,environmentSanitization:!0}),vr=Object.freeze({policyEnforcement:!0,restrictedIdentity:!0,lowIntegrity:!1,processTreeControl:!0,filesystemIsolation:!1,networkIsolation:!1,inheritedHandleAllowlist:!1,environmentSanitization:!0}),Tr=Object.freeze({policyEnforcement:!0,restrictedIdentity:!0,lowIntegrity:!1,processTreeControl:!0,filesystemIsolation:!0,networkIsolation:!0,inheritedHandleAllowlist:!0,environmentSanitization:!0}),Sr=Object.freeze({policyEnforcement:!0,restrictedIdentity:!1,lowIntegrity:!1,processTreeControl:!1,filesystemIsolation:!1,networkIsolation:!1,inheritedHandleAllowlist:!1,environmentSanitization:!0});var Be=1,G=class extends Error{constructor(r,n){super(n);this.code=r;this.name="XenoSecureExecutionContractError"}code};function Ve(e){let t=[];if(!e||typeof e!="object")return["Contract must be an object"];if(e.schemaVersion!==Be&&t.push("Unsupported schemaVersion"),e.contractId?.trim()||t.push("contractId is required"),(!e.identity?.runId?.trim()||!e.identity?.agentId?.trim())&&t.push("runId and agentId are required"),ue(e.createdAt)||t.push("createdAt must be an ISO 8601 timestamp"),e.expiresAt&&(!ue(e.expiresAt)||Date.parse(e.expiresAt)<=Date.parse(e.createdAt))&&t.push("expiresAt must be later than createdAt"),e.requestedLevel!==e.effectiveLevel&&t.push("requestedLevel and effectiveLevel must match"),e.trustMode==="untrusted"&&e.effectiveLevel!=="contained"&&t.push("Untrusted execution requires contained mode"),e.effectiveLevel==="contained"&&(e.adapter.certified||t.push("Contained mode requires a certified adapter"),e.adapter.certificationArtifactId||t.push("Contained mode requires a certification artifact"),(!e.adapter.capabilities.filesystemIsolation||e.filesystem.enforcement!=="os")&&t.push("Contained mode requires OS filesystem isolation"),(!e.adapter.capabilities.networkIsolation||e.network.enforcement!=="os")&&t.push("Contained mode requires OS network isolation"),e.network.default!=="deny"&&t.push("Contained networking must default deny")),e.network.enforcement==="none"&&!e.network.acknowledgedUnrestricted&&t.push("Unrestricted networking must be explicitly acknowledged"),e.filesystem.writeRoots.length>0&&e.profile.permissionProfile==="read-only"&&t.push("Read-only profiles cannot receive write roots"),(!Array.isArray(e.browser.readAllowedDomains)||!Array.isArray(e.browser.actAllowedDomains))&&t.push("Browser read/action domain policies are required"),(!Array.isArray(e.browser.allowedSchemes)||e.browser.allowedSchemes.some(r=>r!=="http"&&r!=="https"))&&t.push("Browser allowed schemes are invalid"),(!Array.isArray(e.browser.allowedPorts)||e.browser.allowedPorts.some(r=>!Number.isInteger(r)||r<1||r>65535))&&t.push("Browser allowed ports are invalid"),e.secrets.some(r=>!r.secretId.trim())&&t.push("Secret projections require non-empty secret IDs"),new Set(e.secrets.map(r=>r.secretId)).size!==e.secrets.length&&t.push("Secret IDs must be unique"),new Set(e.capabilityLeaseIds).size!==e.capabilityLeaseIds.length&&t.push("Capability lease IDs must be unique"),/^[a-f0-9]{64}$/.test(e.profile.fingerprint)||t.push("Profile fingerprint is invalid"),!/^[a-f0-9]{64}$/.test(e.fingerprint))t.push("Contract fingerprint is invalid");else{let r={...e};delete r.fingerprint,$e(r)!==e.fingerprint&&t.push("Contract fingerprint does not match content")}return t}function pe(e){let t=Ve(e);if(t.length>0){let r=t.includes("Contract fingerprint does not match content");throw new G(r?"SECURITY_CONTRACT_FINGERPRINT_MISMATCH":"SECURITY_CONTRACT_INVALID",t.join("; "))}}function $e(e){return(0,fe.createHash)("sha256").update(JSON.stringify(W(e))).digest("hex")}function W(e){return Array.isArray(e)?e.map(W):!e||typeof e!="object"?e:Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,W(r)]))}function ue(e){return typeof e=="string"&&e.includes("T")&&Number.isFinite(Date.parse(e))}var Fe=/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/,me=1048576;function J(e){(!e||typeof e!="object")&&A("Automation request must be an object"),e.protocolVersion!==1&&A("Unsupported automation protocol version"),E(e.operationId,"operationId"),E(e.idempotencyKey,"idempotencyKey"),E(e.capabilityLeaseId,"capabilityLeaseId"),E(e.governingToolName,"governingToolName"),(!Number.isSafeInteger(e.expectedLeaseVersion)||e.expectedLeaseVersion<1)&&A("expectedLeaseVersion must be a positive safe integer"),be(e.requestedAt)||A("requestedAt must be an ISO 8601 timestamp"),O(e.operation)||A(`Unsupported automation operation: ${e.operation}`),(!e.parameters||typeof e.parameters!="object"||Array.isArray(e.parameters))&&A("parameters must be a JSON object");let t;try{t=Buffer.byteLength(JSON.stringify(e.parameters),"utf8")}catch(r){throw new i("AUTOMATION_REQUEST_INVALID","parameters must be serializable JSON",void 0,{cause:r})}if(t>me&&A(`parameters exceed ${me} bytes`),E(e.identity?.runId,"identity.runId"),E(e.identity?.agentId,"identity.agentId"),pe(e.executionContract),He(e),!e.executionContract.capabilityLeaseIds.includes(e.capabilityLeaseId))throw new i("AUTOMATION_CONTRACT_INVALID",`Capability lease ${e.capabilityLeaseId} is not bound to the execution contract`);if(e.executionContract.expiresAt&&Date.parse(e.executionContract.expiresAt)<=Date.now())throw new i("AUTOMATION_CONTRACT_EXPIRED","Execution contract has expired")}function S(e){(!e||typeof e!="object")&&m("Adapter manifest must be an object"),e.protocolVersion!==1&&m("Adapter protocol version is unsupported"),N(e.adapterId,"adapterId"),N(e.adapterVersion,"adapterVersion"),N(e.platform,"platform"),N(e.architecture,"architecture"),e.targetBinding!=="atomic-preflight"&&m("Adapter must implement atomic preflight target binding"),(!Array.isArray(e.operations)||e.operations.length===0)&&m("Adapter must declare operations");let t=new Set;for(let r of e.operations)O(r)||m(`Adapter declares unknown operation ${r}`),t.has(r)&&m(`Adapter declares duplicate operation ${r}`),t.add(r);e.certified&&!e.certificationArtifactId&&m("A certified automation adapter requires a certification artifact"),Array.isArray(e.limitations)||m("Adapter limitations must be an array"),e.operations.some(r=>r.startsWith("browser."))&&(!e.policyEnforcement.domain||!e.policyEnforcement.scheme||!e.policyEnforcement.port)&&m("Browser adapters must enforce domain, scheme, and port policy"),e.operations.some(r=>r.startsWith("computer."))&&!e.supports.immediateStop&&m("Computer-use adapters must support immediate stop")}function x(e,t,r){let n=h(e.operation);if(!t.operations.includes(e.operation))throw new i("AUTOMATION_OPERATION_UNSUPPORTED",`Adapter ${t.adapterId} does not support ${e.operation}`);if(n.surface==="browser"){let o=e.executionContract.browser.mode;if(o==="none"||n.effect==="act"&&o!=="act")throw new i("AUTOMATION_POLICY_DENIED",`Execution contract denies ${e.operation}`);if(je(e,r,n.effect==="act"),e.operation==="browser.download"&&e.executionContract.browser.downloads==="deny")throw new i("AUTOMATION_POLICY_DENIED","Execution contract denies browser downloads");if(e.operation==="browser.upload"&&e.executionContract.browser.uploads==="deny")throw new i("AUTOMATION_POLICY_DENIED","Execution contract denies browser uploads");if(e.operation.startsWith("browser.record.")&&e.executionContract.browser.recording!=="bounded")throw new i("AUTOMATION_POLICY_DENIED","Execution contract denies browser recording")}else{let o=e.executionContract.computer.mode;if(o==="none"||n.effect==="act"&&o!=="act")throw new i("AUTOMATION_POLICY_DENIED",`Execution contract denies ${e.operation}`);let a=e.executionContract.computer.allowedApplications,d=e.operation==="computer.launch"?We(e):r.applicationId??e.declaredTarget?.applicationId;if((e.operation==="computer.launch"||a.length>0)&&(!d||!a.includes(d)))throw new i("AUTOMATION_POLICY_DENIED",`Application ${d??"<unknown>"} is not allowed`)}}function q(e,t){let r=h(e.operation),n=r.surface==="browser"?r.effect==="act"?"browser-act":"browser-read":r.effect==="act"?"computer-act":"computer-observe";return{subject:{runId:e.identity.runId,agentId:e.identity.agentId,...e.identity.turnId?{turnId:e.identity.turnId}:{},toolName:e.governingToolName,operationId:e.operationId},kind:n,operation:e.operation,...he(e,t)?{resource:he(e,t)}:{},...Ae(t)?{destination:Ae(t)}:{}}}function he(e,t){return e.declaredTarget?.resource??Ge(t)}function K(e){let t=h(e.operation).evidence,r=e.evidencePolicy??{},n=z(t.before,r.before),o=z(t.after,r.after),a=z(t.recording,r.recording),d=r.retainUntil;return d&&!be(d)&&A("evidencePolicy.retainUntil must be an ISO 8601 timestamp"),{before:n,after:o,recording:a,sensitivity:r.sensitivity??"confidential",...d?{retainUntil:d}:{},...r.legalHold!==void 0?{legalHold:r.legalHold}:{}}}function He(e){let t=e.executionContract.identity,r=["runId","agentId","sessionId","turnId","taskId","workspaceId"];for(let n of r){let o=t[n],a=e.identity[n];if(o!==void 0&&o!==a)throw new i("AUTOMATION_IDENTITY_MISMATCH",`Automation identity ${n} does not match the execution contract`)}}function je(e,t,r){let n=t.url??t.origin??e.declaredTarget?.url??e.declaredTarget?.origin;if(!n){if(["browser.tabs.list","browser.wait"].includes(e.operation))return;throw new i("AUTOMATION_PREFLIGHT_INVALID","Browser preflight did not bind an effective URL")}let o;try{o=new URL(n)}catch{throw new i("AUTOMATION_POLICY_DENIED","Browser target URL is invalid")}if(o.username||o.password)throw new i("AUTOMATION_POLICY_DENIED","Browser target URLs cannot contain credentials");let a=o.protocol.replace(/:$/,"");a!=="http"&&a!=="https"&&w(`Browser scheme ${o.protocol} is not supported`);let d=a,c=o.hostname.toLowerCase().replace(/\.$/,""),l=o.port?Number(o.port):d==="https"?443:80,s=e.executionContract.browser,u=ze(c);if(ge(c,s.deniedDomains)&&w(`Browser domain ${c} is denied`),Ie(o,e.executionContract.network.denyDestinations)&&w(`Network policy explicitly denies ${d}://${c}:${l}`),u&&s.allowLoopbackDevelopment)return;s.allowedSchemes.includes(d)||w(`Browser scheme ${o.protocol} is not allowed`),s.allowedPorts.includes(l)||w(`Browser port ${l} is not allowed`);let p=r?s.actAllowedDomains:s.readAllowedDomains;ge(c,p)||w(`Browser domain ${c} is outside the ${r?"action":"read"} allowlist`),e.executionContract.network.default==="deny"&&(Ie(o,e.executionContract.network.allowDestinations)||w(`Network policy does not allow ${d}://${c}:${l}`))}function Ie(e,t){let r=e.protocol.replace(/:$/,""),n=e.hostname.toLowerCase().replace(/\.$/,""),o=e.port?Number(e.port):r==="https"?443:80;return t.some(a=>{let d=a.scheme?.replace(/^custom:/,"");return a.host===n&&(a.port===void 0||a.port===o)&&(d===void 0||d===r)})}function Ge(e){return e.resource??e.tabId??e.pageId??e.deviceId??e.applicationId}function Ae(e){let t=e.origin??e.url;if(!t)return e.domain;try{return new URL(t).origin}catch{return t}}function We(e){let t=e.parameters.target,r=t&&typeof t=="object"&&!Array.isArray(t)?t.app_id:void 0,n=e.parameters.applicationId??r??e.declaredTarget?.applicationId;return typeof n=="string"&&n.length>0?n:void 0}function ge(e,t){return t.some(r=>{let n=r.toLowerCase();if(n.startsWith(".")){let o=n.slice(1);return e===o||e.endsWith(`.${o}`)}return e===n})}function ze(e){return e==="localhost"||e==="127.0.0.1"||e==="[::1]"||e==="::1"}function z(e,t){let r={disabled:0,optional:1,required:2};return!t||r[t]<=r[e]?e:t}function E(e,t){(typeof e!="string"||!Fe.test(e))&&A(`${t} is invalid`)}function N(e,t){(typeof e!="string"||e.trim().length===0||e.length>256)&&m(`${t} is invalid`)}function be(e){return typeof e=="string"&&e.includes("T")&&Number.isFinite(Date.parse(e))}function w(e){throw new i("AUTOMATION_POLICY_DENIED",e)}function A(e){throw new i("AUTOMATION_REQUEST_INVALID",e)}function m(e){throw new i("AUTOMATION_OPERATION_UNSUPPORTED",e)}var ye=require("node:crypto");var we=require("node:crypto");function P(e){return{algorithm:"sha256",value:(0,we.createHash)("sha256").update(e).digest("hex")}}var Je="[\\u0000-\\u001f\\u007f]",$r=new RegExp(Je);var X=class extends Error{constructor(r,n,o,a){super(n,a);this.code=r;this.detail=o;this.name="XenoArtifactRepositoryError"}code;detail};var Z=32*1024*1024,qe=1024*1024*1024,Ke=/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/;function L(e){let r=(e.now??(()=>new Date().toISOString()))(),n=new Set;return e.evidence.map((o,a)=>{if(Ze(o),n.has(o.evidenceId))throw new i("AUTOMATION_EVIDENCE_INVALID",`Duplicate evidence ID ${o.evidenceId}`);n.add(o.evidenceId);let d=Ye(o),c=(0,ye.createHash)("sha256").update(`${e.request.operationId}\0${o.evidenceId}\0${d.hash.value}`).digest("hex").slice(0,32);return{schemaVersion:1,artifactId:`automation_${c}`,revision:1,kind:o.kind,title:o.title,...o.description?{description:o.description}:{},state:"draft",createdAt:r,mediaType:o.mediaType,sensitivity:o.sensitivity??e.policy.sensitivity,identity:{...e.request.identity},content:d,provenance:{producer:{kind:"tool",id:e.request.governingToolName,displayName:e.manifest.adapterId,profileFingerprint:e.request.executionContract.profile.fingerprint},operationId:e.request.operationId,executionContractFingerprint:e.request.executionContract.fingerprint,capabilityLeaseIds:[e.request.capabilityLeaseId],attributes:{automationProtocolVersion:e.request.protocolVersion,automationOperation:e.request.operation,automationEvidenceId:o.evidenceId,automationEvidencePhase:o.phase,adapterId:e.manifest.adapterId,adapterVersion:e.manifest.adapterVersion,evidenceIndex:a}},...o.anchors?{anchors:structuredClone(o.anchors)}:{},...e.policy.retainUntil||e.policy.legalHold!==void 0?{retention:{...e.policy.retainUntil?{retainUntil:e.policy.retainUntil}:{},...e.policy.legalHold!==void 0?{legalHold:e.policy.legalHold}:{}}}:{},extensions:{...o.extensions??{},xenoAutomation:{phase:o.phase,adapter:e.manifest.adapterId,certifiedAdapter:e.manifest.certified}}}})}async function D(e,t){if(!e)return t.map(n=>structuredClone(n));let r=[];for(let n of t)try{let o=await e.create(n);r.push(o.artifact)}catch(o){if(o instanceof X&&o.code==="ARTIFACT_ALREADY_EXISTS"){let a=await e.require(n.artifactId,n.revision);if(a.artifact.content.hash.value===n.content.hash.value){r.push(a.artifact);continue}}throw new i("AUTOMATION_EVIDENCE_PERSISTENCE_FAILED",`Failed to persist automation evidence ${n.artifactId}`,{artifactId:n.artifactId},{cause:o})}return r}function C(e,t,r){if(t[r]==="required"&&!e.some(n=>n.phase===r))throw new i("AUTOMATION_EVIDENCE_REQUIRED",`Automation operation requires ${r} evidence before it can be trusted`)}function Ye(e){if(e.content.type==="text"){let t=Buffer.byteLength(e.content.text,"utf8");return Ee(t),{hash:P(e.content.text),sizeBytes:t,storage:{type:"inline",text:e.content.text},encoding:"utf8",...e.redactedPreview?{redactedPreview:e.redactedPreview}:{}}}if(e.content.type==="base64"){let t=Qe(e.content.base64);return Ee(t.byteLength),{hash:P(t),sizeBytes:t.byteLength,storage:{type:"inline",base64:e.content.base64},encoding:"base64",...e.redactedPreview?{redactedPreview:e.redactedPreview}:{}}}if(!Number.isSafeInteger(e.content.sizeBytes)||e.content.sizeBytes<0||e.content.sizeBytes>qe)throw new i("AUTOMATION_EVIDENCE_INVALID","Referenced evidence size is invalid");if(!e.content.hash?.algorithm||!e.content.hash.value)throw new i("AUTOMATION_EVIDENCE_INVALID","Referenced evidence requires a content hash");if(e.content.hash.algorithm==="sha256"&&!/^[a-f0-9]{64}$/.test(e.content.hash.value))throw new i("AUTOMATION_EVIDENCE_INVALID","Referenced evidence SHA-256 is invalid");return{hash:{...e.content.hash},sizeBytes:e.content.sizeBytes,storage:structuredClone(e.content.storage),...e.content.encoding?{encoding:e.content.encoding}:{},...e.redactedPreview?{redactedPreview:e.redactedPreview}:{}}}function Ze(e){(!e||typeof e!="object")&&g("Evidence must be an object"),Ke.test(e.evidenceId)||g("Evidence ID is invalid"),["before","action","after","recording","diagnostic"].includes(e.phase)||g("Evidence phase is invalid"),(!e.kind||!e.title?.trim()||e.title.length>512)&&g("Evidence kind and title are required"),/^[\w!#$&^_.+-]+\/[\w!#$&^_.+-]+(?:;.*)?$/.test(e.mediaType)||g("Evidence media type is invalid")}function Qe(e){(e.length===0||e.length>Math.ceil(Z/3)*4+4)&&g("Inline base64 evidence is empty or too large"),(!/^[A-Za-z0-9+/]*={0,2}$/.test(e)||e.length%4!==0)&&g("Inline evidence is not canonical base64");let t=Buffer.from(e,"base64");return t.toString("base64")!==e&&g("Inline evidence is not canonical base64"),t}function Ee(e){e>Z&&g(`Inline evidence exceeds ${Z} bytes`)}function g(e){throw new i("AUTOMATION_EVIDENCE_INVALID",e)}var Te=require("node:crypto");var ve=3e4,et=1e3,te=class{adapter;leaseAuthority;artifactRepository;now;executions=new Map;active=new Map;constructor(t){this.adapter=t.adapter,this.leaseAuthority=t.leaseAuthority,this.artifactRepository=t.artifactRepository,this.now=t.now??(()=>new Date().toISOString())}execute(t,r){let n=it(t),o=this.executions.get(t.operationId);if(o){if(o.fingerprint!==n)throw new i("AUTOMATION_OPERATION_CONFLICT",`Operation ${t.operationId} was already submitted with different authority or parameters`);return o.promise}let a=new AbortController,d=at(r,a);this.active.set(t.operationId,a);let c=this.executeFresh(t,a.signal).finally(()=>{d(),this.active.delete(t.operationId)});return this.executions.set(t.operationId,{fingerprint:n,promise:c}),this.trimIdempotencyRecords(),c}async stop(t,r="human-stop"){let n=this.active.get(t);return n?(n.abort(new Error(r)),await this.adapter.stop?.(t,r),!0):!1}async executeFresh(t,r){J(t),ee(r);let n=await this.adapter.manifest();S(n);let o=h(t.operation),a=K(t),d=this.now(),c=await this.adapter.preflight(t,r);tt(c,this.now()),x(t,n,c.observedTarget),ee(r);let l=c.evidence??[];C(l,a,"before");let s=await D(this.artifactRepository,L({request:t,manifest:n,policy:a,evidence:l,now:this.now}));if(ee(r),Date.parse(c.expiresAt)<=Date.parse(this.now()))throw new i("AUTOMATION_PREFLIGHT_INVALID","Automation preflight expired before authority consumption");let u=q(t,c.observedTarget),p=await this.leaseAuthority.consume(t.capabilityLeaseId,t.expectedLeaseVersion,u),v={contractFingerprint:t.executionContract.fingerprint,lease:p,capabilityUse:u},f;try{f=await this.adapter.execute(t,c,v,r)}catch(I){return Q({request:t,manifest:n,descriptorEffect:o.effect,target:c.observedTarget,lease:p,artifacts:s,startedAt:d,completedAt:this.now(),status:r.aborted?"cancelled":"error",code:r.aborted?"AUTOMATION_CANCELLED":"AUTOMATION_ADAPTER_ERROR",message:r.aborted?"Automation operation was stopped":st(I)})}rt(f);let se=nt(c.observedTarget,f.observedTarget),U;try{x(t,n,f.observedTarget)}catch(I){U=I instanceof Error?I:new Error(String(I))}let _=f.evidence??[];ot(l,_);let ce=[],M;try{C(_,a,"after"),C([...l,..._],a,"recording"),ce=await D(this.artifactRepository,L({request:t,manifest:n,policy:a,evidence:_,now:this.now}))}catch(I){M=I instanceof Error?I:new Error(String(I))}let B=[...s,...ce];return se||U?Q({request:t,manifest:n,descriptorEffect:o.effect,target:f.observedTarget,lease:p,artifacts:B,startedAt:f.startedAt,completedAt:f.completedAt,status:"error",code:"AUTOMATION_TARGET_CHANGED",message:se??U.message}):M?Q({request:t,manifest:n,descriptorEffect:o.effect,target:f.observedTarget,lease:p,artifacts:B,startedAt:f.startedAt,completedAt:f.completedAt,status:"evidence-incomplete",code:"AUTOMATION_EVIDENCE_INCOMPLETE",message:M.message}):{operationId:t.operationId,status:f.status,effect:o.effect,startedAt:f.startedAt,completedAt:f.completedAt,adapter:Se(n),target:structuredClone(f.observedTarget),...f.data!==void 0?{data:structuredClone(f.data)}:{},artifacts:B,lease:Ce(p),...f.error?{error:{...f.error}}:{}}}trimIdempotencyRecords(){for(;this.executions.size>et;){let t=this.executions.keys().next().value;if(!t||this.active.has(t))break;this.executions.delete(t)}}};function tt(e,t){if(!e||typeof e!="object"||!e.token?.trim())throw new i("AUTOMATION_PREFLIGHT_INVALID","Adapter returned an invalid preflight token");let r=Date.parse(e.preparedAt),n=Date.parse(e.expiresAt),o=Date.parse(t);if(!Number.isFinite(r)||!Number.isFinite(n)||n<=r||n<=o)throw new i("AUTOMATION_PREFLIGHT_INVALID","Adapter returned an expired or invalid preflight window");if(n-r>ve)throw new i("AUTOMATION_PREFLIGHT_INVALID",`Preflight TTL exceeds ${ve} ms`);if(!e.observedTarget||typeof e.observedTarget!="object")throw new i("AUTOMATION_PREFLIGHT_INVALID","Adapter did not bind an observed target")}function rt(e){if(!e||typeof e!="object")throw new i("AUTOMATION_REQUEST_INVALID","Adapter returned no result");if(!["ok","denied","cancelled","error"].includes(e.status))throw new i("AUTOMATION_REQUEST_INVALID","Adapter returned an invalid status");if(!Number.isFinite(Date.parse(e.startedAt))||!Number.isFinite(Date.parse(e.completedAt)))throw new i("AUTOMATION_REQUEST_INVALID","Adapter result timestamps are invalid");if(Date.parse(e.completedAt)<Date.parse(e.startedAt))throw new i("AUTOMATION_REQUEST_INVALID","Adapter completion precedes its start");if(!e.observedTarget||typeof e.observedTarget!="object")throw new i("AUTOMATION_REQUEST_INVALID","Adapter result has no observed target")}function nt(e,t){let r=["tabId","deviceId","displayId"];for(let n of r)if(e[n]!==void 0&&e[n]!==t[n])return`Adapter changed bound ${n} between preflight and execution`}function ot(e,t){let r=new Set(e.map(n=>n.evidenceId));for(let n of t){if(r.has(n.evidenceId))throw new i("AUTOMATION_EVIDENCE_INVALID",`Duplicate evidence ID ${n.evidenceId}`);r.add(n.evidenceId)}}function Q(e){return{operationId:e.request.operationId,status:e.status,effect:e.descriptorEffect,startedAt:e.startedAt,completedAt:e.completedAt,adapter:Se(e.manifest),target:structuredClone(e.target),artifacts:e.artifacts,lease:Ce(e.lease),error:{code:e.code,message:e.message}}}function Se(e){return{adapterId:e.adapterId,adapterVersion:e.adapterVersion,platform:e.platform,architecture:e.architecture}}function Ce(e){return{leaseId:e.leaseId,version:e.version,state:e.state,useCount:e.useCount}}function it(e){return(0,Te.createHash)("sha256").update(JSON.stringify(re(e))).digest("hex")}function re(e){return Array.isArray(e)?e.map(re):!e||typeof e!="object"?e:Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>[t,re(r)]))}function at(e,t){if(!e)return()=>{};let r=()=>t.abort(e.reason);return e.aborted?r():e.addEventListener("abort",r,{once:!0}),()=>e.removeEventListener("abort",r)}function ee(e){if(e.aborted)throw new i("AUTOMATION_CANCELLED","Automation operation was stopped before dispatch")}function st(e){return e instanceof Error&&e.message.slice(0,1024)||"Automation adapter failed"}function _e(e,t=()=>new Date().toISOString()){let r=[];dt(r,"manifest.valid",()=>S(e));let n=new Set(e.operations);return y(r,n,e.supports.consoleInspection,"browser.console.read","console inspection"),y(r,n,e.supports.networkInspection,"browser.network.read","network inspection"),y(r,n,e.supports.storageInspection,"browser.storage.read","storage inspection"),y(r,n,e.supports.pageErrorInspection,"browser.page-errors.read","page-error inspection"),y(r,n,e.supports.recordings,"browser.record.start","recording start"),y(r,n,e.supports.recordings,"browser.record.stop","recording stop"),n.has("browser.upload")&&b(r,"policy.upload",e.policyEnforcement.upload,"Upload policy is enforced"),n.has("browser.download")&&b(r,"policy.download",e.policyEnforcement.download,"Download policy is enforced"),[...n].some(o=>o.startsWith("browser."))&&(b(r,"policy.redirect",e.policyEnforcement.redirect,"Redirect policy is enforced"),b(r,"control.handback",e.supports.deterministicHandback,"Deterministic hand-back is supported")),[...n].some(o=>o.startsWith("computer."))&&(b(r,"control.stop",e.supports.immediateStop,"Immediate human stop is supported"),b(r,"privacy.redaction",e.supports.sensitiveRegionRedaction,"Sensitive-region redaction is supported")),b(r,"certification.binding",!e.certified||!!e.certificationArtifactId,e.certified?"Certification artifact is bound":"Adapter does not claim certification"),{protocolVersion:1,adapter:{adapterId:e.adapterId,adapterVersion:e.adapterVersion,platform:e.platform,architecture:e.architecture},generatedAt:t(),passed:r.every(o=>o.status==="pass"),checks:r}}function ct(e){let r=_e(e).checks.filter(n=>n.status==="fail");if(r.length>0)throw new i("AUTOMATION_OPERATION_UNSUPPORTED",r.map(n=>`${n.id}: ${n.message}`).join("; "))}function y(e,t,r,n,o){let a=t.has(n);b(e,`capability.${n}`,r===a,r===a?`${o} declaration matches the operation catalog`:`${o} declaration does not match ${n}`)}function dt(e,t,r){try{r(),e.push({id:t,status:"pass",message:"Manifest satisfies the protocol contract"})}catch(n){e.push({id:t,status:"fail",message:n instanceof Error?n.message:String(n)})}}function b(e,t,r,n){e.push({id:t,status:r?"pass":"fail",message:n})}var lt=6e4,ut=48*1024*1024,ne=class{endpoint;token;timeoutMs;maxResponseBytes;fetchImpl;constructor(t){if(this.endpoint=ft(t.baseUrl),this.token=pt(t.token),this.timeoutMs=Re(t.timeoutMs??lt,1e3,3e5,"timeoutMs"),this.maxResponseBytes=Re(t.maxResponseBytes??ut,1024,128*1024*1024,"maxResponseBytes"),this.fetchImpl=t.fetch??globalThis.fetch,typeof this.fetchImpl!="function")throw new i("AUTOMATION_TRANSPORT_ERROR","A Fetch API implementation is required")}manifest(){return this.request("manifest","GET")}preflight(t,r){return this.request("preflight","POST",t,r)}execute(t,r,n,o){return this.request("execute","POST",{request:t,preflight:r,grant:n},o)}async stop(t,r){await this.request("stop","POST",{operationId:t,reason:r.slice(0,512)})}async request(t,r,n,o){let a=new AbortController,d=It(o,a),c=setTimeout(()=>a.abort(new Error("automation transport timeout")),this.timeoutMs);c.unref?.();try{let l=new URL(t,this.endpoint),s=await this.fetchImpl(l,{method:r,redirect:"error",cache:"no-store",credentials:"omit",headers:{authorization:`Bearer ${this.token}`,accept:"application/json",...r==="POST"?{"content-type":"application/json"}:{}},...r==="POST"?{body:JSON.stringify(n??{})}:{},signal:a.signal}),u=await mt(s,this.maxResponseBytes);if(!s.ok){let p=ht(u);throw new i("AUTOMATION_TRANSPORT_ERROR",`XENO Browser rejected ${t}: ${p.message}`,{status:s.status,route:t,adapterCode:p.code})}if(!oe(u)||u.ok!==!0||!("result"in u))throw new i("AUTOMATION_RESPONSE_INVALID",`XENO Browser returned an invalid ${t} envelope`);return u.result}catch(l){if(l instanceof i)throw l;let s=o?.aborted;throw new i(s?"AUTOMATION_CANCELLED":"AUTOMATION_TRANSPORT_ERROR",s?"Automation transport was stopped":`XENO Browser ${t} transport failed`,{route:t},{cause:l})}finally{clearTimeout(c),d()}}};function ft(e){let t;try{t=new URL(e)}catch(n){throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser base URL is invalid",void 0,{cause:n})}if(t.protocol!=="http:"&&t.protocol!=="https:")throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser transport requires HTTP(S)");if(t.username||t.password||t.search||t.hash)throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser base URL cannot contain credentials, query, or fragment");let r=t.hostname.toLowerCase().replace(/^\[|\]$/g,"").replace(/\.$/,"");if(r!=="localhost"&&r!=="127.0.0.1"&&r!=="::1")throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser transport must use a loopback host");if(!t.port)throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser transport requires an explicit port");return t.pathname="/v1/automation/",t}function pt(e){if(typeof e!="string"||e.length<32||e.length>512||/\s/.test(e))throw new i("AUTOMATION_TRANSPORT_ERROR","XENO Browser session token is invalid");return e}async function mt(e,t){let r=Number(e.headers.get("content-length"));if(Number.isFinite(r)&&r>t)throw new i("AUTOMATION_RESPONSE_INVALID",`XENO Browser response exceeds ${t} bytes`);if(e.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase()!=="application/json")throw new i("AUTOMATION_RESPONSE_INVALID","XENO Browser response is not application/json");if(!e.body)throw new i("AUTOMATION_RESPONSE_INVALID","XENO Browser response body is missing");let o=e.body.getReader(),a=[],d=0;for(;;){let{done:s,value:u}=await o.read();if(s)break;if(d+=u.byteLength,d>t)throw await o.cancel("response too large"),new i("AUTOMATION_RESPONSE_INVALID",`XENO Browser response exceeds ${t} bytes`);a.push(u)}let c=new Uint8Array(d),l=0;for(let s of a)c.set(s,l),l+=s.byteLength;try{return JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(c))}catch(s){throw new i("AUTOMATION_RESPONSE_INVALID","XENO Browser response contains invalid JSON",void 0,{cause:s})}}function ht(e){return oe(e)&&oe(e.error)?{code:typeof e.error.code=="string"?e.error.code.slice(0,128):"UNKNOWN",message:typeof e.error.message=="string"?e.error.message.slice(0,1024):"request failed"}:{code:"UNKNOWN",message:"request failed"}}function It(e,t){if(!e)return()=>{};let r=()=>t.abort(e.reason);return e.aborted?r():e.addEventListener("abort",r,{once:!0}),()=>e.removeEventListener("abort",r)}function Re(e,t,r,n){if(!Number.isSafeInteger(e)||e<t||e>r)throw new i("AUTOMATION_TRANSPORT_ERROR",`${n} must be an integer from ${t} to ${r}`);return e}function oe(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}var k=require("node:crypto");var At=8*1024*1024,gt=4;function bt(e){return Pt(e.operations??R).map(r=>wt(r,e.runtime))}function wt(e,t){return{definition:{name:e,description:St(e),input_schema:Et(e)},projectPolicyInput(r){let n=Rt(e,r),o=_t(e,r);return{observabilityInput:n,permissionInput:o,permissionPreview:Ot(e,r),approvalKey:`xeno-automation:${e}:${Xt(r)}`,riskLevel:Ct(e)}},async execute(r,n){let o=n?.authorization;if(!o||o.toolName!==e)return{success:!1,output:"",error:"Governed automation requires a host-issued authorization receipt",errorCode:"AUTOMATION_AUTHORIZATION_RECEIPT_REQUIRED",retryable:!1};let a=n.operationId??`automation_${(0,k.randomUUID)()}`,d=ae(e,r);n.reportProgress?.({activity:"state",message:`Authorizing ${e}`});try{let c=await t.execute({operation:e,governingToolName:e,operationId:a,idempotencyKey:a,parameters:structuredClone(r),...d?{declaredTarget:d}:{},authorization:o,...n.signal?{signal:n.signal}:{},...n.reportProgress?{reportProgress:n.reportProgress}:{}}),l=c.status==="ok";return{success:l,output:JSON.stringify(Nt(c)),...l?{}:{error:c.error?.message??`Governed automation ended with ${c.status}`,errorCode:c.error?.code??`AUTOMATION_${c.status.toUpperCase().replaceAll("-","_")}`},assistantContent:xt(c),evidence:c.artifacts.map(s=>({id:s.artifactId,kind:"artifact",status:c.status==="evidence-incomplete"?"failed":"verified",observedAt:s.updatedAt??s.createdAt,operationId:a,detail:{artifact_kind:s.kind,revision:s.revision,sensitivity:s.sensitivity}})),retryable:c.error?.retryable??!1}}catch(c){let l=n.signal?.aborted===!0;return{success:!1,output:"",error:l?"Governed automation was stopped":Lt(c),errorCode:l?"AUTOMATION_CANCELLED":Dt(c),retryable:!l}}}}}function Et(e){let t=yt(e),r=vt(e);return{type:"object",properties:t,...r.length>0?{required:r}:{},...e==="browser.locate"?{oneOf:[{required:["selector"]},{required:["text"]}]}:{},additionalProperties:!1}}function yt(e){let t=(d,c=8192)=>({type:"string",minLength:1,maxLength:c,description:d}),r={type:"number"},n={type:"boolean"},o={type:"object",properties:{x:r,y:r},required:["x","y"],additionalProperties:!1},a={deviceId:t("Target xeno-use device ID.",256),applicationId:t("Expected or launch-target application ID.",512),displayId:t("Target display ID.",256)};switch(e){case"browser.navigate":return{url:t("Destination URL."),waitUntil:{enum:["load","none"]},tabId:t("Expected active tab ID. Hosts with multiple visible tabs should require this.",256)};case"browser.wait":return{until:{enum:["load","ms"]},value:{type:"integer",minimum:0,maximum:6e4}};case"browser.snapshot":return{mode:{enum:["text","html","markdown","a11y"]},tabId:t("Expected active tab ID.",256)};case"browser.screenshot":return{tabId:t("Expected active tab ID.",256)};case"browser.locate":return{selector:t("CSS selector."),text:t("Visible text to locate.")};case"browser.click":return{selector:t("CSS selector returned by browser.snapshot or browser.locate."),tabId:t("Expected active tab ID.",256)};case"browser.type":return{selector:t("CSS selector."),text:{type:"string",maxLength:65536},clear:n,submit:n,tabId:t("Expected active tab ID.",256)};case"browser.key":return{combo:t("Key combo such as Control+a or Enter.",256),tabId:t("Expected active tab ID.",256)};case"browser.select":return{selector:t("CSS selector."),value:t("Option value."),tabId:t("Expected active tab ID.",256)};case"browser.scroll":return{selector:t("Optional CSS selector."),dx:r,dy:r,tabId:t("Expected active tab ID.",256)};case"browser.tabs.open":return{url:t("URL to open in an isolated browser tab.")};case"browser.tabs.close":return{tabId:t("Tab ID to close.",256)};case"browser.console.read":case"browser.network.read":case"browser.page-errors.read":return{limit:{type:"integer",minimum:1,maximum:1e3},clear:n,tabId:t("Expected active tab ID.",256)};case"browser.storage.read":return{tabId:t("Expected active tab ID.",256)};case"browser.upload":return{selector:t("File input or drop-target selector."),backendNodeId:{type:"integer",minimum:1},hostPaths:{type:"array",items:t("Host file path.",32768),minItems:1,maxItems:64},drop:n,tabId:t("Expected active tab ID.",256)};case"browser.download":return{toPath:t("Approved destination path.",32768),trigger:{type:"object",properties:{url:t("Download URL."),selector:t("Download trigger selector.")},oneOf:[{required:["url"]},{required:["selector"]}],additionalProperties:!1},tabId:t("Expected active tab ID.",256)};case"browser.record.start":return{maxDurationMs:{type:"integer",minimum:1e3,maximum:6e4},intervalMs:{type:"integer",minimum:100,maximum:5e3},tabId:t("Expected active tab ID.",256)};case"browser.record.stop":return{tabId:t("Expected active tab ID.",256)};case"computer.observe":case"computer.screenshot":case"computer.window-state":return a;case"computer.point":return{...a,point:o,durationMs:{type:"integer",minimum:0,maximum:6e4}};case"computer.click":return{...a,point:o,button:{enum:["left","right","middle"]},modifiers:Tt()};case"computer.double-click":case"computer.right-click":return{...a,point:o};case"computer.drag":return{...a,from:o,to:o,durationMs:{type:"integer",minimum:0,maximum:6e4}};case"computer.type":return{...a,text:{type:"string",maxLength:65536}};case"computer.key":return{...a,combo:t("Key combo such as Control+a or Enter.",256)};case"computer.scroll":return{...a,at:o,delta:o};case"computer.wait":return{...a,milliseconds:{type:"integer",minimum:0,maximum:3e5}};case"computer.launch":return{...a,applicationId:t("Explicitly allowlisted application ID.",512)};case"browser.back":case"browser.forward":case"browser.reload":return{tabId:t("Expected active tab ID.",256)};case"browser.tabs.list":return{}}}function vt(e){switch(e){case"browser.navigate":case"browser.tabs.open":return["url"];case"browser.click":return["selector"];case"browser.type":return["selector","text"];case"browser.key":return["combo"];case"browser.select":return["selector","value"];case"browser.upload":return["hostPaths"];case"browser.download":return["toPath","trigger"];case"computer.point":case"computer.click":case"computer.double-click":case"computer.right-click":return["point"];case"computer.drag":return["from","to"];case"computer.type":return["text"];case"computer.key":return["combo"];case"computer.scroll":return["at","delta"];case"computer.launch":return["applicationId"];default:return[]}}function Tt(){return{type:"object",properties:{shift:{type:"boolean"},ctrl:{type:"boolean"},alt:{type:"boolean"},meta:{type:"boolean"}},additionalProperties:!1}}function St(e){let t=h(e),r=e.split(".").slice(1).join(" ");return`${t.effect==="act"?"Act on":"Inspect"} the ${t.surface} using governed ${r} automation. The host requires policy approval, an exact capability lease, target-bound preflight, and durable evidence.`}function Ct(e){return h(e).effect==="observe"?e==="browser.navigate"||e==="browser.tabs.open"?"medium":"low":["browser.type","browser.upload","browser.download","computer.type","computer.key","computer.launch"].includes(e)?"high":"medium"}function _t(e,t){let r=ae(e,t),n={operation:e};r&&(n.target=r);for(let o of["selector","clear","submit","combo","value","dx","dy","point","from","to","at","delta","milliseconds"])t[o]!==void 0&&(n[o]=structuredClone(t[o]));return typeof t.text=="string"&&(n.text=`[redacted ${t.text.length} characters]`),Array.isArray(t.hostPaths)&&(n.hostPaths=t.hostPaths.map(()=>"[redacted host path]")),typeof t.toPath=="string"&&(n.toPath="[redacted destination path]"),n}function Rt(e,t){let r=structuredClone(t);return typeof r.text=="string"&&(r.text=`[redacted ${r.text.length} characters]`),Array.isArray(r.hostPaths)&&(r.hostPaths=r.hostPaths.map(()=>"[redacted host path]")),typeof r.toPath=="string"&&(r.toPath="[redacted destination path]"),Ne(r.trigger)&&typeof r.trigger.url=="string"&&(r.trigger={...r.trigger,url:Oe(r.trigger.url)}),typeof r.url=="string"&&(r.url=Oe(r.url)),{operation:e,...r}}function Ot(e,t){let r=ae(e,t),n=r?.url??r?.applicationId??r?.deviceId??r?.tabId??r?.resource??"current target";return`${e} on ${n}`}function ae(e,t){let r={};return typeof t.url=="string"&&(r.url=t.url),typeof t.tabId=="string"&&(r.tabId=t.tabId),typeof t.deviceId=="string"&&(r.deviceId=t.deviceId),typeof t.applicationId=="string"&&(r.applicationId=t.applicationId),typeof t.displayId=="string"&&(r.displayId=t.displayId),(e==="browser.navigate"||e==="browser.tabs.list"||e==="browser.tabs.open")&&(r.resource="browser-tabs"),Object.keys(r).length>0?r:void 0}function Nt(e){return{operationId:e.operationId,status:e.status,effect:e.effect,startedAt:e.startedAt,completedAt:e.completedAt,adapter:e.adapter,target:e.target,...e.data!==void 0?{data:e.data}:{},artifacts:e.artifacts.map(t=>({artifactId:t.artifactId,revision:t.revision,kind:t.kind,title:t.title,state:t.state,mediaType:t.mediaType,sensitivity:t.sensitivity,hash:t.content.hash,sizeBytes:t.content.sizeBytes})),lease:e.lease,...e.error?{error:e.error}:{}}}function xt(e){let t=[];for(let r of e.artifacts){if(t.length>=gt||!r.mediaType.startsWith("image/"))continue;let n=r.content.storage;if(n.type!=="inline"||typeof n.base64!="string")continue;let o=Math.floor(n.base64.length*.75);o<=0||o>At||t.push({type:"image_url",image_url:{url:`data:${r.mediaType};base64,${n.base64}`,detail:"high"}})}return t}function Pt(e){let t=new Set(R),r=new Set;for(let n of e){if(!t.has(n))throw new TypeError(`Unsupported XENO automation operation: ${String(n)}`);r.add(n)}return Array.from(r)}function Xt(e){return(0,k.createHash)("sha256").update(ie(e)).digest("hex")}function ie(e){return Array.isArray(e)?`[${e.map(ie).join(",")}]`:Ne(e)?`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${ie(e[t])}`).join(",")}}`:JSON.stringify(e)??"null"}function Oe(e){try{let t=new URL(e);return`${t.origin}${t.pathname}`}catch{return"[invalid URL]"}}function Lt(e){return e instanceof Error?e.message:String(e)}function Dt(e){return e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"?e.code:"AUTOMATION_EXECUTION_FAILED"}function Ne(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}0&&(module.exports={XENO_AUTOMATION_OPERATIONS,XENO_AUTOMATION_PROTOCOL_VERSION,XenoAutomationError,XenoGovernedAutomationExecutor,XenoLoopbackAutomationAdapter,assertRequiredXenoAutomationEvidence,assertValidXenoAutomationAdapterManifest,assertValidXenoAutomationRequest,assertXenoAutomationAdapterConformant,assertXenoAutomationAuthority,buildXenoAutomationCapabilityUse,createXenoAutomationConformanceReport,createXenoGovernedAutomationTools,describeXenoAutomationOperation,isXenoAutomationOperation,materializeXenoAutomationEvidence,persistXenoAutomationEvidence,resolveXenoAutomationEvidencePolicy});
|