agents-bridge 0.3.9 → 0.3.10
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/agents/model-control-files.d.ts +10 -4
- package/dist/agents/model-control-files.d.ts.map +1 -1
- package/dist/agents/model-control-files.js +127 -79
- package/dist/agents/model-control-files.js.map +1 -1
- package/dist/agents/windows-model-control-security.d.ts +4 -0
- package/dist/agents/windows-model-control-security.d.ts.map +1 -0
- package/dist/agents/windows-model-control-security.js +86 -0
- package/dist/agents/windows-model-control-security.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
export declare function
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export declare function createModelControlFileAccess({ platform, windowsSecurity, }?: {
|
|
2
|
+
platform?: NodeJS.Platform;
|
|
3
|
+
windowsSecurity?: (path: string) => Promise<void>;
|
|
4
|
+
}): {
|
|
5
|
+
ensureSecureModelControlDirectory: (path: string) => Promise<void>;
|
|
6
|
+
readSecureModelControlFile: (path: string) => Promise<string>;
|
|
7
|
+
createSecureModelControlFile: (path: string, contents: string) => Promise<void>;
|
|
8
|
+
writeSecureModelControlFile: (path: string, contents: string) => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
export declare const ensureSecureModelControlDirectory: (path: string) => Promise<void>, readSecureModelControlFile: (path: string) => Promise<string>, createSecureModelControlFile: (path: string, contents: string) => Promise<void>, writeSecureModelControlFile: (path: string, contents: string) => Promise<void>;
|
|
5
11
|
//# sourceMappingURL=model-control-files.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-control-files.d.ts","sourceRoot":"","sources":["../../src/agents/model-control-files.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model-control-files.d.ts","sourceRoot":"","sources":["../../src/agents/model-control-files.ts"],"names":[],"mappings":"AAcA,wBAAgB,4BAA4B,CAAC,EAC3C,QAA2B,EAC3B,eAAmD,GACpD,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAAC,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAO;8CAIhC,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;uCAuC7B,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;yCAmBtB,MAAM,YAAY,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;wCAkBzC,MAAM,YAAY,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;EA6B1F;AAED,eAAO,MACL,iCAAiC,SA5GsB,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,EA4G1C,0BAA0B,SArEb,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC,EAsExE,4BAA4B,SAnDsB,MAAM,YAAY,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,EAmD5D,2BAA2B,SAjCR,MAAM,YAAY,MAAM,KAAG,OAAO,CAAC,IAAI,CAkCxD,CAAA"}
|
|
@@ -2,100 +2,148 @@ import { constants } from 'node:fs';
|
|
|
2
2
|
import { lstat, mkdir, open, realpath, rename, unlink } from 'node:fs/promises';
|
|
3
3
|
import { dirname, resolve } from 'node:path';
|
|
4
4
|
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { ensureWindowsModelControlSecurity, sameWindowsModelControlPath } from './windows-model-control-security.js';
|
|
5
6
|
const MAX_MODEL_CONTROL_FILE_BYTES = 64 * 1024;
|
|
6
7
|
function expectedUid() {
|
|
7
8
|
return typeof process.geteuid === 'function' ? process.geteuid() : typeof process.getuid === 'function' ? process.getuid() : undefined;
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if ((info.mode & 0o777) !== 0o600)
|
|
31
|
-
throw new Error('INSECURE_MODEL_CONTROL_FILE_MODE');
|
|
32
|
-
if (info.size > MAX_MODEL_CONTROL_FILE_BYTES)
|
|
33
|
-
throw new Error('MODEL_CONTROL_FILE_TOO_LARGE');
|
|
34
|
-
}
|
|
35
|
-
export async function readSecureModelControlFile(path) {
|
|
36
|
-
await ensureSecureModelControlDirectory(dirname(path));
|
|
37
|
-
const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
38
|
-
try {
|
|
39
|
-
await validateOpenFile(file);
|
|
40
|
-
return await file.readFile('utf8');
|
|
41
|
-
}
|
|
42
|
-
finally {
|
|
43
|
-
await file.close();
|
|
10
|
+
// The explicit platform/security dependencies let non-Windows CI exercise the
|
|
11
|
+
// complete Windows IO lifecycle without claiming native ACL/OS acceptance.
|
|
12
|
+
export function createModelControlFileAccess({ platform = process.platform, windowsSecurity = ensureWindowsModelControlSecurity, } = {}) {
|
|
13
|
+
const windows = platform === 'win32';
|
|
14
|
+
const noFollow = windows ? 0 : constants.O_NOFOLLOW;
|
|
15
|
+
async function ensureSecureModelControlDirectory(path) {
|
|
16
|
+
const directory = resolve(path);
|
|
17
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
18
|
+
const info = await lstat(directory);
|
|
19
|
+
if (!info.isDirectory() || info.isSymbolicLink())
|
|
20
|
+
throw new Error('INSECURE_MODEL_CONTROL_DIRECTORY');
|
|
21
|
+
const uid = expectedUid();
|
|
22
|
+
if (!windows && uid !== undefined && info.uid !== uid)
|
|
23
|
+
throw new Error('INSECURE_MODEL_CONTROL_DIRECTORY_OWNER');
|
|
24
|
+
if (!windows && (info.mode & 0o022) !== 0)
|
|
25
|
+
throw new Error('INSECURE_MODEL_CONTROL_DIRECTORY_MODE');
|
|
26
|
+
const canonical = await realpath(directory);
|
|
27
|
+
if (windows ? !sameWindowsModelControlPath(canonical, directory) : canonical !== directory)
|
|
28
|
+
throw new Error('INSECURE_MODEL_CONTROL_DIRECTORY_PATH');
|
|
29
|
+
if (windows)
|
|
30
|
+
await windowsSecurity(directory);
|
|
44
31
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
32
|
+
async function validateOpenFile(file, path) {
|
|
33
|
+
const info = await file.stat();
|
|
34
|
+
if (!info.isFile() || info.isSymbolicLink())
|
|
35
|
+
throw new Error('INSECURE_MODEL_CONTROL_FILE_TYPE');
|
|
36
|
+
const uid = expectedUid();
|
|
37
|
+
if (!windows && uid !== undefined && info.uid !== uid)
|
|
38
|
+
throw new Error('INSECURE_MODEL_CONTROL_FILE_OWNER');
|
|
39
|
+
if (!windows && (info.mode & 0o777) !== 0o600)
|
|
40
|
+
throw new Error('INSECURE_MODEL_CONTROL_FILE_MODE');
|
|
41
|
+
if (info.size > MAX_MODEL_CONTROL_FILE_BYTES)
|
|
42
|
+
throw new Error('MODEL_CONTROL_FILE_TOO_LARGE');
|
|
43
|
+
if (windows) {
|
|
44
|
+
const entry = await lstat(path);
|
|
45
|
+
if (!entry.isFile() || entry.isSymbolicLink() || entry.dev !== info.dev || entry.ino !== info.ino || entry.nlink !== 1)
|
|
46
|
+
throw new Error('INSECURE_MODEL_CONTROL_FILE_TYPE');
|
|
47
|
+
const canonical = await realpath(path);
|
|
48
|
+
if (!sameWindowsModelControlPath(canonical, resolve(path)))
|
|
49
|
+
throw new Error('INSECURE_MODEL_CONTROL_FILE_PATH');
|
|
50
|
+
await windowsSecurity(path);
|
|
51
|
+
}
|
|
53
52
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
async function openExisting(path) {
|
|
54
|
+
// O_NOFOLLOW is unavailable on Windows. Check before opening, then compare
|
|
55
|
+
// the opened file's identity to lstat before reading any secret bytes.
|
|
56
|
+
if (windows) {
|
|
57
|
+
const entry = await lstat(path);
|
|
58
|
+
if (!entry.isFile() || entry.isSymbolicLink())
|
|
59
|
+
throw new Error('INSECURE_MODEL_CONTROL_FILE_TYPE');
|
|
60
|
+
}
|
|
61
|
+
return open(path, constants.O_RDONLY | noFollow);
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
await
|
|
63
|
+
async function readSecureModelControlFile(path) {
|
|
64
|
+
await ensureSecureModelControlDirectory(dirname(path));
|
|
65
|
+
const file = await openExisting(path);
|
|
66
|
+
try {
|
|
67
|
+
await validateOpenFile(file, path);
|
|
68
|
+
return await file.readFile('utf8');
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
await file.close();
|
|
72
|
+
}
|
|
66
73
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
74
|
+
async function syncDirectory(path) {
|
|
75
|
+
// Node cannot open/fsync a Windows directory with these POSIX flags.
|
|
76
|
+
// File contents are still fsynced before close/atomic rename below.
|
|
77
|
+
if (windows)
|
|
78
|
+
return;
|
|
79
|
+
const directory = await open(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
73
80
|
try {
|
|
74
|
-
await
|
|
81
|
+
await directory.sync();
|
|
75
82
|
}
|
|
76
83
|
finally {
|
|
77
|
-
await
|
|
84
|
+
await directory.close();
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
|
-
|
|
81
|
-
if (
|
|
87
|
+
async function createSecureModelControlFile(path, contents) {
|
|
88
|
+
if (Buffer.byteLength(contents, 'utf8') > MAX_MODEL_CONTROL_FILE_BYTES)
|
|
89
|
+
throw new Error('MODEL_CONTROL_FILE_TOO_LARGE');
|
|
90
|
+
await ensureSecureModelControlDirectory(dirname(path));
|
|
91
|
+
const file = await open(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollow, 0o600);
|
|
92
|
+
try {
|
|
93
|
+
await validateOpenFile(file, path);
|
|
94
|
+
await file.writeFile(contents);
|
|
95
|
+
await validateOpenFile(file, path);
|
|
96
|
+
await file.sync();
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
await file.close().catch(() => undefined);
|
|
100
|
+
await unlink(path).catch(() => undefined);
|
|
82
101
|
throw error;
|
|
102
|
+
}
|
|
103
|
+
await file.close();
|
|
104
|
+
await syncDirectory(dirname(path));
|
|
83
105
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
await
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
async function writeSecureModelControlFile(path, contents) {
|
|
107
|
+
if (Buffer.byteLength(contents, 'utf8') > MAX_MODEL_CONTROL_FILE_BYTES)
|
|
108
|
+
throw new Error('MODEL_CONTROL_FILE_TOO_LARGE');
|
|
109
|
+
await ensureSecureModelControlDirectory(dirname(path));
|
|
110
|
+
try {
|
|
111
|
+
const existing = await openExisting(path);
|
|
112
|
+
try {
|
|
113
|
+
await validateOpenFile(existing, path);
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
await existing.close();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error?.code !== 'ENOENT')
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
const temporaryPath = `${path}.${randomUUID()}.tmp`;
|
|
124
|
+
const file = await open(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollow, 0o600);
|
|
125
|
+
try {
|
|
126
|
+
await validateOpenFile(file, temporaryPath);
|
|
127
|
+
await file.writeFile(contents);
|
|
128
|
+
await validateOpenFile(file, temporaryPath);
|
|
129
|
+
await file.sync();
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
await file.close().catch(() => undefined);
|
|
133
|
+
await unlink(temporaryPath).catch(() => undefined);
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
await file.close();
|
|
137
|
+
try {
|
|
138
|
+
await rename(temporaryPath, path);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
await unlink(temporaryPath).catch(() => undefined);
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
await syncDirectory(dirname(path));
|
|
96
145
|
}
|
|
97
|
-
|
|
98
|
-
await rename(temporaryPath, path);
|
|
99
|
-
await syncDirectory(dirname(path));
|
|
146
|
+
return { ensureSecureModelControlDirectory, readSecureModelControlFile, createSecureModelControlFile, writeSecureModelControlFile };
|
|
100
147
|
}
|
|
148
|
+
export const { ensureSecureModelControlDirectory, readSecureModelControlFile, createSecureModelControlFile, writeSecureModelControlFile, } = createModelControlFileAccess();
|
|
101
149
|
//# sourceMappingURL=model-control-files.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-control-files.js","sourceRoot":"","sources":["../../src/agents/model-control-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC/E,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"model-control-files.js","sourceRoot":"","sources":["../../src/agents/model-control-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function sameWindowsModelControlPath(left: string, right: string): boolean;
|
|
2
|
+
export declare function validateWindowsModelControlAcl(value: unknown): void;
|
|
3
|
+
export declare function ensureWindowsModelControlSecurity(path: string): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=windows-model-control-security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows-model-control-security.d.ts","sourceRoot":"","sources":["../../src/agents/windows-model-control-security.ts"],"names":[],"mappings":"AAiCA,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEhF;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAuBnE;AAED,wBAAsB,iCAAiC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBnF"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { win32 } from 'node:path';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
const execute = promisify(execFile);
|
|
5
|
+
const TRUSTED_SYSTEM_SIDS = new Set(['S-1-5-18', 'S-1-5-32-544']); // SYSTEM, Administrators
|
|
6
|
+
// Use SIDs rather than localized account names. Inspect inherited ACEs too; a
|
|
7
|
+
// Unix mode returned by stat on Windows says nothing about the actual DACL.
|
|
8
|
+
const INSPECT_ACL = `
|
|
9
|
+
$ErrorActionPreference = 'Stop'
|
|
10
|
+
$item = Get-Item -LiteralPath $env:AGENTS_BRIDGE_ACL_TARGET -Force
|
|
11
|
+
$cursor = $item
|
|
12
|
+
while ($null -ne $cursor) {
|
|
13
|
+
if (($cursor.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { throw 'MODEL_CONTROL_REPARSE_POINT' }
|
|
14
|
+
if ($cursor -is [IO.DirectoryInfo]) { $cursor = $cursor.Parent } else { $cursor = $cursor.Directory }
|
|
15
|
+
}
|
|
16
|
+
$acl = Get-Acl -LiteralPath $env:AGENTS_BRIDGE_ACL_TARGET
|
|
17
|
+
$rules = @($acl.GetAccessRules($true, $true, [Security.Principal.SecurityIdentifier]) | ForEach-Object {
|
|
18
|
+
@{
|
|
19
|
+
sid = $_.IdentityReference.Value
|
|
20
|
+
allow = ($_.AccessControlType -eq [Security.AccessControl.AccessControlType]::Allow)
|
|
21
|
+
rights = [long]$_.FileSystemRights
|
|
22
|
+
inheritOnly = (($_.PropagationFlags -band [Security.AccessControl.PropagationFlags]::InheritOnly) -ne 0)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
@{
|
|
26
|
+
currentSid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
|
27
|
+
ownerSid = $acl.GetOwner([Security.Principal.SecurityIdentifier]).Value
|
|
28
|
+
rules = $rules
|
|
29
|
+
} | ConvertTo-Json -Depth 4 -Compress
|
|
30
|
+
`;
|
|
31
|
+
export function sameWindowsModelControlPath(left, right) {
|
|
32
|
+
return win32.normalize(left).toLowerCase() === win32.normalize(right).toLowerCase();
|
|
33
|
+
}
|
|
34
|
+
export function validateWindowsModelControlAcl(value) {
|
|
35
|
+
const invalid = () => new Error('INSECURE_MODEL_CONTROL_WINDOWS_ACL');
|
|
36
|
+
if (!value || typeof value !== 'object')
|
|
37
|
+
throw invalid();
|
|
38
|
+
const acl = value;
|
|
39
|
+
if (typeof acl.currentSid !== 'string' || !/^S-1-\d+(?:-\d+)+$/.test(acl.currentSid)
|
|
40
|
+
|| typeof acl.ownerSid !== 'string' || !Array.isArray(acl.rules))
|
|
41
|
+
throw invalid();
|
|
42
|
+
const trusted = new Set([acl.currentSid, ...TRUSTED_SYSTEM_SIDS]);
|
|
43
|
+
if (!trusted.has(acl.ownerSid))
|
|
44
|
+
throw invalid();
|
|
45
|
+
let effectiveAllow = false;
|
|
46
|
+
for (const entry of acl.rules) {
|
|
47
|
+
if (!entry || typeof entry !== 'object')
|
|
48
|
+
throw invalid();
|
|
49
|
+
const rule = entry;
|
|
50
|
+
if (typeof rule.sid !== 'string' || typeof rule.allow !== 'boolean'
|
|
51
|
+
|| typeof rule.inheritOnly !== 'boolean' || typeof rule.rights !== 'number'
|
|
52
|
+
|| !Number.isSafeInteger(rule.rights) || rule.rights < 0)
|
|
53
|
+
throw invalid();
|
|
54
|
+
if (!rule.allow || rule.rights === 0)
|
|
55
|
+
continue;
|
|
56
|
+
// Inherit-only grants still expose newly created state/temporary files.
|
|
57
|
+
if (!trusted.has(rule.sid) && !(rule.sid === 'S-1-3-0' && rule.inheritOnly))
|
|
58
|
+
throw invalid();
|
|
59
|
+
if (!rule.inheritOnly)
|
|
60
|
+
effectiveAllow = true;
|
|
61
|
+
}
|
|
62
|
+
// Also rejects empty/missing and null DACL representations rather than
|
|
63
|
+
// treating "no rules returned" as proof of privacy.
|
|
64
|
+
if (!effectiveAllow)
|
|
65
|
+
throw invalid();
|
|
66
|
+
}
|
|
67
|
+
export async function ensureWindowsModelControlSecurity(path) {
|
|
68
|
+
const executable = win32.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
69
|
+
let snapshot;
|
|
70
|
+
try {
|
|
71
|
+
const { stdout } = await execute(executable, [
|
|
72
|
+
'-NoLogo', '-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(INSPECT_ACL, 'utf16le').toString('base64'),
|
|
73
|
+
], {
|
|
74
|
+
env: { ...process.env, AGENTS_BRIDGE_ACL_TARGET: path },
|
|
75
|
+
windowsHide: true, timeout: 10_000, maxBuffer: 128 * 1024,
|
|
76
|
+
});
|
|
77
|
+
snapshot = JSON.parse(stdout.replace(/^\uFEFF/, '').trim());
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Do not include shell output, environment, or a control-file path in logs.
|
|
81
|
+
// Fail closed if PowerShell/ACL inspection is unavailable.
|
|
82
|
+
throw new Error('MODEL_CONTROL_WINDOWS_ACL_UNAVAILABLE: Windows PowerShell ACL inspection failed');
|
|
83
|
+
}
|
|
84
|
+
validateWindowsModelControlAcl(snapshot);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=windows-model-control-security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows-model-control-security.js","sourceRoot":"","sources":["../../src/agents/windows-model-control-security.ts"],"names":[],"mappings":"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"}
|