fix(security): classify broad Windows SIDs as world principals · openclaw/openclaw@cffae53
dwc1997
·
2026-05-14
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
|
10 | 10 | |
11 | 11 | ### Fixes |
12 | 12 | |
| 13 | +- Security/Windows ACL audit: classify Anonymous Logon, Guests, Interactive, Local, and Network SIDs as world-equivalent principals so broadly writable paths stay critical instead of being downgraded to group-writable. Fixes #74350. (#74383) Thanks @dwc1997. |
13 | 14 | - Media-understanding: retry transient remote attachment fetch failures before audio or vision processing, so Discord voice notes are not lost after one network/CDN blip. Fixes #74316. Thanks @vyctorbrzezowski and @gabrielexito-stack. |
14 | 15 | - Control UI: order timestamped live stream and tool items before untimestamped history fallbacks, keeping chat history in visible time order. Fixes #80759. (#81016) Thanks @akrimm702. |
15 | 16 | - iMessage: stop sending visible `<media:image>` placeholder text for media-only native image sends while preserving the internal echo key that prevents self-echo duplicate replies. (#81209) Thanks @homer-byte. |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +diff --git a/dist/permissions.js b/dist/permissions.js |
| 2 | +index 67df110..445b04d 100644 |
| 3 | +--- a/dist/permissions.js |
| 4 | ++++ b/dist/permissions.js |
| 5 | +@@ -12,6 +12,15 @@ const WORLD_PRINCIPALS = new Set([ |
| 6 | +"builtin\\users", |
| 7 | +"authenticated users", |
| 8 | +"nt authority\\authenticated users", |
| 9 | ++ "anonymous logon", |
| 10 | ++ "nt authority\\anonymous logon", |
| 11 | ++ "guests", |
| 12 | ++ "builtin\\guests", |
| 13 | ++ "interactive", |
| 14 | ++ "nt authority\\interactive", |
| 15 | ++ "network", |
| 16 | ++ "nt authority\\network", |
| 17 | ++ "local", |
| 18 | + ]); |
| 19 | + const TRUSTED_BASE = new Set([ |
| 20 | +"nt authority\\system", |
| 21 | +@@ -31,7 +40,16 @@ const TRUSTED_SIDS = new Set([ |
| 22 | +"s-1-5-32-544", |
| 23 | +"s-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464", |
| 24 | + ]); |
| 25 | +-const WORLD_SIDS = new Set(["s-1-1-0", "s-1-5-11", "s-1-5-32-545"]); |
| 26 | ++const WORLD_SIDS = new Set([ |
| 27 | ++ "s-1-1-0", |
| 28 | ++ "s-1-5-11", |
| 29 | ++ "s-1-5-32-545", |
| 30 | ++ "s-1-5-7", |
| 31 | ++ "s-1-5-32-546", |
| 32 | ++ "s-1-5-4", |
| 33 | ++ "s-1-2-0", |
| 34 | ++ "s-1-5-2", |
| 35 | ++]); |
| 36 | + const STATUS_PREFIXES = [ |
| 37 | +"successfully processed", |
| 38 | +"processed", |
| 39 | +@@ -202,6 +220,8 @@ function buildTrustedPrincipals(env) { |
| 40 | + } |
| 41 | + } |
| 42 | + const userSid = normalizeSid(env?.USERSID ?? ""); |
| 43 | ++ // Guard: never add any world-equivalent SID to the trusted set, even if |
| 44 | ++ // USERSID is set to one by a malicious process. |
| 45 | + if (userSid && SID_RE.test(userSid) && !WORLD_SIDS.has(userSid)) { |
| 46 | + trusted.add(userSid); |
| 47 | + } |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -95,5 +95,6 @@ peerDependencyRules:
|
95 | 95 | "prism-media>opusscript": "^0.0.8 || ^0.1.1" |
96 | 96 | |
97 | 97 | patchedDependencies: |
| 98 | +"@openclaw/fs-safe@0.2.2": "patches/@openclaw__fs-safe@0.2.2.patch" |
98 | 99 | "baileys@7.0.0-rc11": "patches/baileys@7.0.0-rc11.patch" |
99 | 100 | "@agentclientprotocol/claude-agent-acp@0.33.1": "patches/@agentclientprotocol__claude-agent-acp@0.33.1.patch" |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -85,6 +85,42 @@ describe("security audit filesystem Windows findings", () => {
|
85 | 85 | ), |
86 | 86 | ).toBe(true); |
87 | 87 | })(), |
| 88 | +(async () => { |
| 89 | +const tmp = await tempCases.makeTmpDir("win-anon-world"); |
| 90 | +const stateDir = path.join(tmp, "state"); |
| 91 | +await fs.mkdir(stateDir, { recursive: true }); |
| 92 | +const configPath = path.join(stateDir, "openclaw.json"); |
| 93 | +await fs.writeFile(configPath, "{}\n", "utf-8"); |
| 94 | +const findings = await collectFilesystemFindings({ |
| 95 | + stateDir, |
| 96 | + configPath, |
| 97 | +platform: "win32", |
| 98 | +env: windowsAuditEnv, |
| 99 | +execIcacls: async (_cmd: string, args: string[]) => { |
| 100 | +const target = args[0]; |
| 101 | +if (target.endsWith(`${path.sep}state`)) { |
| 102 | +return { |
| 103 | +stdout: `${target} *S-1-5-18:(F)\n *S-1-5-7:(F)\n`, |
| 104 | +stderr: "", |
| 105 | +}; |
| 106 | +} |
| 107 | +return { |
| 108 | +stdout: `${target} *S-1-5-18:(F)\n DESKTOP-TEST\\Tester:(F)\n`, |
| 109 | +stderr: "", |
| 110 | +}; |
| 111 | +}, |
| 112 | +}); |
| 113 | +expect( |
| 114 | +findings.some( |
| 115 | +(finding) => |
| 116 | +finding.checkId === "fs.state_dir.perms_world_writable" && |
| 117 | +finding.severity === "critical", |
| 118 | +), |
| 119 | +).toBe(true); |
| 120 | +expect( |
| 121 | +findings.some((finding) => finding.checkId === "fs.state_dir.perms_group_writable"), |
| 122 | +).toBe(false); |
| 123 | +})(), |
88 | 124 | ]); |
89 | 125 | }); |
90 | 126 | }); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -396,6 +396,71 @@ Successfully processed 1 files`;
|
396 | 396 | ], |
397 | 397 | expected: { untrustedWorld: 1 }, |
398 | 398 | }, |
| 399 | +{ |
| 400 | +name: "Anonymous Logon SID (S-1-5-7) is world, not group", |
| 401 | +entries: [ |
| 402 | +aclEntry({ |
| 403 | +principal: "*S-1-5-7", |
| 404 | +rights: ["R"], |
| 405 | +rawRights: "(R)", |
| 406 | +canRead: true, |
| 407 | +canWrite: false, |
| 408 | +}), |
| 409 | +], |
| 410 | +expected: { untrustedWorld: 1 }, |
| 411 | +}, |
| 412 | +{ |
| 413 | +name: "BUILTIN\\\\Guests SID (S-1-5-32-546) is world, not group", |
| 414 | +entries: [ |
| 415 | +aclEntry({ |
| 416 | +principal: "*S-1-5-32-546", |
| 417 | +rights: ["R"], |
| 418 | +rawRights: "(R)", |
| 419 | +canRead: true, |
| 420 | +canWrite: false, |
| 421 | +}), |
| 422 | +], |
| 423 | +expected: { untrustedWorld: 1 }, |
| 424 | +}, |
| 425 | +{ |
| 426 | +name: "Interactive SID (S-1-5-4) is world, not group", |
| 427 | +entries: [ |
| 428 | +aclEntry({ |
| 429 | +principal: "*S-1-5-4", |
| 430 | +rights: ["R"], |
| 431 | +rawRights: "(R)", |
| 432 | +canRead: true, |
| 433 | +canWrite: false, |
| 434 | +}), |
| 435 | +], |
| 436 | +expected: { untrustedWorld: 1 }, |
| 437 | +}, |
| 438 | +{ |
| 439 | +name: "Local SID (S-1-2-0) is world, not group", |
| 440 | +entries: [ |
| 441 | +aclEntry({ |
| 442 | +principal: "*S-1-2-0", |
| 443 | +rights: ["R"], |
| 444 | +rawRights: "(R)", |
| 445 | +canRead: true, |
| 446 | +canWrite: false, |
| 447 | +}), |
| 448 | +], |
| 449 | +expected: { untrustedWorld: 1 }, |
| 450 | +}, |
| 451 | +{ |
| 452 | +name: "Network SID (S-1-5-2) is world, not group", |
| 453 | +entries: [ |
| 454 | +aclEntry({ |
| 455 | +principal: "*S-1-5-2", |
| 456 | +rights: ["R"], |
| 457 | +rawRights: "(R)", |
| 458 | +canRead: true, |
| 459 | +canWrite: false, |
| 460 | +}), |
| 461 | +], |
| 462 | +expected: { untrustedWorld: 1 }, |
| 463 | +}, |
399 | 464 | ] as const)("$name", ({ entries, env, expected }) => { |
400 | 465 | expectSummaryCounts(entries, expected, env); |
401 | 466 | }); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -1107,10 +1107,10 @@ describe("grouped chat rendering", () => {
|
1107 | 1107 | const objectUrl = "blob:managed-image"; |
1108 | 1108 | vi.stubGlobal( |
1109 | 1109 | "URL", |
1110 | | -Object.assign(URL, { |
1111 | | -createObjectURL: vi.fn(() => objectUrl), |
1112 | | -revokeObjectURL: vi.fn(), |
1113 | | -}), |
| 1110 | +class extends URL { |
| 1111 | +static override createObjectURL = vi.fn(() => objectUrl); |
| 1112 | +static override revokeObjectURL = vi.fn(); |
| 1113 | +}, |
1114 | 1114 | ); |
1115 | 1115 | const fetchMock = vi.fn(async (_url: string, init?: RequestInit) => { |
1116 | 1116 | const headers = init?.headers as Headers; |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。