Commit Graph

19110 Commits

Author SHA1 Message Date
subrih 3c941eae23 fix: deep-copy nested scripts policy objects in mergeAccessPolicy, use SEATBELT_WRITE_OPS
- mergeAccessPolicy !base fast-path: scripts["policy"] and per-script entry.policy were
  shallow-copied, leaving them as references into the cached _fileCache object.
  autoExpandBareDir mutations would propagate back into the cache, violating the invariant
  established by the policy-copy fix. Now deep-copied via Object.fromEntries map.
- exec-sandbox-seatbelt: replace hardcoded "file-write*" with SEATBELT_WRITE_OPS constant
  in the /tmp write allowance branch, consistent with all other allowance lines in the file.
- Tests added for nested scripts deep-copy invariant.
2026-03-14 16:20:02 -07:00
subrih 8848bb82e9 fix: cross-layer enforcement gaps and cache mutation (vettri review)
- bwrap: '---' rules on SYSTEM_RO_BIND_PATHS (/etc /usr /bin /lib /sbin /opt) now emit
  --tmpfs in restrictive mode — previously the deny branch was gated to permissive mode
  only, leaving syscalls inside the sandbox able to read /etc/passwd etc. despite policy
- seatbelt: bracket globs [abc] now detected as wildcards (/[*?[]/ and strip regex updated);
  previously emitted as SBPL literals matching only a file literally named '[abc]'
- access-policy-file: mergeAccessPolicy fast-path (!base) returns shallow copy instead of
  reference — autoExpandBareDir was mutating the cached agents['*'].policy in-place,
  corrupting all subsequent resolveAccessPolicyForAgent calls in the same process
- access-policy: sha256 comparison normalizes to lowercase (.toLowerCase()) — validation
  regex accepts uppercase (/i) but crypto.digest always returns lowercase, causing uppercase
  sha256 in config to silently deny exec at runtime with no useful error
- Tests added for all four findings
2026-03-14 16:19:51 -07:00
subrih 65946937a0 fix: validation completeness and consumer invariant consistency across all enforcement layers
- permAllowsWrite (bwrap), permToOps/deniedOps (seatbelt): guard all positional perm accesses with VALID_PERM_RE
- catchAllPerm/tmpPerm (seatbelt): validate rawPerm before positional access; fail closed to '---'
- hasScriptOverride (exec-runtime): check entry shape (non-null object, not array) before setting bypass flag
- scripts["policy"] merged into overrideRules in applyScriptPolicyOverride (was silently dropped)
- mergeAccessPolicy: reject non-object script entries before propagating
- validateAccessPolicyFileStructure: recurse into per-script entries to catch removed deny/default fields
- validateAccessPolicyConfig: reject non-object entries, validate sha256 format, emit mid-path wildcard
  diagnostics for scripts["policy"] AND per-script policy blocks (previously only config.policy)
- env-prefix regex: handle escaped quotes in double-quoted values ((?:[^"\\]|\\.)*)
- _resetBwrapAvailableCacheForTest: export added for test isolation
- Tests added for all of the above
2026-03-14 16:19:51 -07:00
subrih eb40d49d44 fix(access-policy): merge scripts[policy], isDir guard in bwrap overrides, permAllows format check, script entry shape check 2026-03-14 16:19:51 -07:00
subrih c92c9c2181 refactor(access-policy): rename rules→policy, agents['*'] as universal base, docs rewrite 2026-03-14 16:19:51 -07:00
subrih 77beb444bc fix(access-policy): mid-path wildcard OS enforcement, structural validation, doc cleanup 2026-03-14 16:19:51 -07:00
subrih cd2d9c3b8d refactor(access-policy): drop deny[] and default — rules only, --- implies deny 2026-03-14 16:19:51 -07:00
subrih 4f4d1af887 fix(access-policy): resolve symlink keys in scripts lookup, document /bin/sh and network* intent 2026-03-14 16:19:51 -07:00
subrih 849077ecb5 fix(access-policy): resolveArgv0 -S= and -SVAL forms, depth cap, bwrap reset export, npmrc comment 2026-03-14 16:19:51 -07:00
subrih 4c0ffe0884 fix(access-policy): bwrap file-path tmpfs guard, seatbelt /tmp exec bit, mtime cache 2026-03-14 16:19:51 -07:00
subrih 6b9828182c fix(access-policy): remove dotdir looksLikeFile gap, drop dead bwrap tmpfs branch 2026-03-14 16:19:51 -07:00
subrih 8e0e02d7ac fix(access-policy): scripts deep-merge, mid-path wildcard diagnostic, test reset exports 2026-03-14 16:19:50 -07:00
subrih d1a3605177 fix(access-policy): env basename extname on Windows, normalize ~ separators, skip Unix-only resolveArgv0 tests on Windows 2026-03-14 16:19:50 -07:00
subrih 651599da52 fix(access-policy): dotfile expand gap, env -S recursion, quoted env arg, PATHEXT on win32, bwrap test skip on non-Linux 2026-03-14 16:19:50 -07:00
subrih ee289fb2d6 fix(access-policy): validate scripts rules/deny, tighten looksLikeFile, explicit bwrap /tmp guard 2026-03-14 16:19:50 -07:00
subrih 643a9fa9c8 fix(access-policy): denyVerdict default rwx, bwrap /tmp policy-gated, file-like path auto-expand 2026-03-14 16:19:50 -07:00
subrih a7fb92a606 fix(access-policy): expand ~ in hasScriptOverride to match tilde-keyed scripts entries 2026-03-14 16:19:50 -07:00
subrih 1c03f79f1f fix(access-policy): warn on file-specific bwrap deny entries, expand ~ in scripts keys 2026-03-14 16:19:50 -07:00
subrih a9a1e7d912 style: oxfmt reflow argv0 assignment 2026-03-14 16:19:50 -07:00
subrih 59abb2c577 fix(access-policy): deny[] beats scripts{} unconditionally, argv0 first-token fallback 2026-03-14 16:19:50 -07:00
subrih 15b1e75357 fix(access-policy): deny[] file guard, findBestRule dir-match, bwrap --proc in permissive mode 2026-03-14 16:19:50 -07:00
subrih db6ddaf0b3 fix(access-policy): bwrap tmpfs file guard, DENY_ALL freeze, access read-only check, seatbelt ? wildcard strip 2026-03-14 16:19:50 -07:00
subrih a5e8054a01 fix(access-policy): read TOCTOU, sandbox skips hash check, seatbelt profile file cleanup 2026-03-14 16:19:50 -07:00
subrih ab872e41d7 fix(access-policy): script-override exec auth, deny[] unconditional expand, fail-closed on broken file 2026-03-14 16:19:50 -07:00
subrih 2b5524b77d fix(access-policy): bwrap script-override -w- condition, misleading footer after auto-expand 2026-03-14 16:19:50 -07:00
subrih e63aa29e1c fix(access-policy): env look-through, PATH= partial-match, bwrap -w-, seatbelt mid-path wildcards, O_EXCL profile files 2026-03-14 16:19:50 -07:00
subrih 18a8707a2f fix: env option stripping, PATH resolution for bare names, exec tool-layer policy check 2026-03-14 16:19:50 -07:00
subrih d4d15435f8 fix: seatbelt race, env quoted argv0 look-through, TSDoc default perm 2026-03-14 16:19:50 -07:00
subrih 003cf88d71 feat(access-policy): filesystem RWX enforcement via access-policy.json 2026-03-14 16:19:50 -07:00
Brian Qu 8a607d7553
fix(feishu): fetch thread context so AI can see bot replies in topic threads (#45254)
* fix(feishu): fetch thread context so AI can see bot replies in topic threads

When a user replies in a Feishu topic thread, the AI previously could only
see the quoted parent message but not the bot's own prior replies in the
thread. This made multi-turn conversations in threads feel broken.

- Add `threadId` (omt_xxx) to `FeishuMessageInfo` and `getMessageFeishu`
- Add `listFeishuThreadMessages()` using `container_id_type=thread` API
  to fetch all messages in a thread including bot replies
- In `handleFeishuMessage`, fetch ThreadStarterBody and ThreadHistoryBody
  for topic session modes and pass them to the AI context
- Reuse quoted message result when rootId === parentId to avoid redundant
  API calls; exclude root message from thread history to prevent duplication
- Fall back to inbound ctx.threadId when rootId is absent or API fails
- Fetch newest messages first (ByCreateTimeDesc + reverse) so long threads
  keep the most recent turns instead of the oldest

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(feishu): skip redundant thread context injection on subsequent turns

Only inject ThreadHistoryBody on the first turn of a thread session.
On subsequent turns the session already contains prior context, so
re-injecting thread history (and starter) would waste tokens.

The heuristic checks whether the current user has already sent a
non-root message in the thread — if so, the session has prior turns
and thread context injection is skipped entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(feishu): handle thread_id-only events in prior-turn detection

When ctx.rootId is undefined (thread_id-only events), the starter
message exclusion check `msg.messageId !== ctx.rootId` was always
true, causing the first follow-up to be misclassified as a prior
turn. Fall back to the first message in the chronologically-sorted
thread history as the starter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(feishu): bootstrap topic thread context via session state

* test(memory): pin remote embedding hostnames in offline suites

* fix(feishu): use plugin-safe session runtime for thread bootstrap

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-14 18:01:59 -05:00
George Zhang 3704293e6f
browser: drop headless/remote MCP attach modes, simplify existing-session to autoConnect-only (#46628) 2026-03-14 15:54:22 -07:00
Josh Lehman 2f7e548a57
chore: regenerate config baseline (#46598) 2026-03-14 15:44:13 -07:00
George Zhang b1d8737017
browser: drop chrome-relay auto-creation, simplify to user profile only (#46596)
Merged via squash.

Prepared head SHA: 74becc8f7d
Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com>
Co-authored-by: odysseus0 <8635094+odysseus0@users.noreply.github.com>
Reviewed-by: @odysseus0
2026-03-14 15:40:02 -07:00
Vincent Koc 39b4185d0b revert: 9bffa3422c 2026-03-14 15:09:22 -07:00
Vincent Koc 173fe3cb54
feat(browser): add headless existing-session MCP support esp for Linux/Docker/VPS (#45769)
* fix(browser): prefer managed default profile in headless mode

* test(browser): cover headless default profile fallback

* feat(browser): support headless MCP profile resolution

* feat(browser): add headless and target-url Chrome MCP modes

* feat(browser): allow MCP target URLs in profile creation

* docs(browser): document headless MCP existing-session flows

* fix(browser): restore playwright browser act helpers

* fix(browser): preserve strict selector actions

* docs(changelog): add existing-session MCP note
2026-03-14 14:59:30 -07:00
Vincent Koc 92834c8440 fix(deps): update package yauzl 2026-03-14 14:35:17 -07:00
Vincent Koc 39377b7a20
UI: surface gateway restart reasons in dashboard disconnect state (#46580)
* UI: surface gateway shutdown reason

* UI: add gateway restart disconnect tests

* Changelog: add dashboard restart reason fix

* UI: cover reconnect shutdown state
2026-03-14 14:31:26 -07:00
Vincent Koc cbec476b6b
Docs: add config drift baseline statefile (#45891)
* Docs: add config drift statefile generator

* Docs: generate config drift baseline

* CI: move config docs drift runner into workflow sanity

* Docs: emit config drift baseline json

* Docs: commit config drift baseline json

* Docs: wire config baseline into release checks

* Config: fix baseline drift walker coverage

* Docs: regenerate config drift baselines
2026-03-14 14:23:30 -07:00
Vincent Koc 432ea11248
Security: add secops ownership for sensitive paths (#46440)
* Meta: add secops ownership for sensitive paths

* Docs: restrict Codeowners-managed security edits

* Meta: guide agents away from secops-owned paths

* Meta: broaden secops CODEOWNERS coverage

* Meta: narrow secops workflow ownership
2026-03-14 14:16:14 -07:00
Tak Hoffman e81442ac80 Fix full local gate on main 2026-03-14 15:52:11 -05:00
Andrew Demczuk 678ea77dcf
style(gateway): fix oxfmt formatting and remove unused test helper 2026-03-14 21:46:53 +01:00
Andrew Demczuk 747609d7d5
fix(node): remove debug console.log on node host startup
Fixes #46411

Fixes #46411
2026-03-14 21:17:48 +01:00
Tak Hoffman b49e1386d0 Fix test environment regressions on main 2026-03-14 14:26:22 -05:00
Andrew Demczuk bb06dc7cc9
fix(agents): restore usage tracking for non-native openai-completions providers
Fixes #46142

Stop forcing supportsUsageInStreaming=false on non-native openai-completions
endpoints. Most OpenAI-compatible APIs (DashScope, DeepSeek, Groq, Together,
etc.) handle stream_options: { include_usage: true } correctly. The blanket
disable broke usage/cost tracking for all non-OpenAI providers.

supportsDeveloperRole is still forced off for non-native endpoints since
the developer message role is genuinely OpenAI-specific.

Users on backends that reject stream_options can opt out with
compat.supportsUsageInStreaming: false in their model config.

Fixes #46142
2026-03-14 19:41:21 +01:00
Onur d33f3f843a
ci: allow fallback npm correction tags (#46486) 2026-03-14 19:38:14 +01:00
Sally O'Malley 8db6fcca77
fix(gateway/cli): relax local backend self-pairing and harden launchd restarts (#46290)
Signed-off-by: sallyom <somalley@redhat.com>
2026-03-14 14:27:52 -04:00
scoootscooob ac29edf6c3
fix(ci): update vitest configs after channel move to extensions/ (openclaw#46066)
Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: scoootscooob <167050519+scoootscooob@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-14 13:23:25 -05:00
Andrew Demczuk e490f450f3
fix(auth): clear stale lockout state when user re-authenticates
Fixes #43057

* fix(auth): clear stale lockout on re-login

Clear stale `auth_permanent` and `billing` disabled state for all
profiles matching the target provider when `openclaw models auth login`
is invoked, so users locked out by expired or revoked OAuth tokens can
recover by re-authenticating instead of waiting for the cooldown timer.

Uses the agent-scoped store (`loadAuthProfileStoreForRuntime`) for
correct multi-agent profile resolution and wraps the housekeeping in
try/catch so corrupt store files never block re-authentication.

Fixes #43057

* test(auth): remove unnecessary non-null assertions

oxlint no-unnecessary-type-assertion: invocationCallOrder[0]
already returns number, not number | undefined.
2026-03-14 19:20:12 +01:00
Andrew Demczuk 9bffa3422c
fix(gateway): skip device pairing when auth.mode=none
Fixes #42931

When gateway.auth.mode is set to "none", authentication succeeds with
method "none" but sharedAuthOk remains false because the auth-context
only recognises token/password/trusted-proxy methods. This causes all
pairing-skip conditions to fail, so Control UI browser connections get
closed with code 1008 "pairing required" despite auth being disabled.

Short-circuit the skipPairing check: if the operator explicitly
disabled authentication, device pairing (which is itself an auth
mechanism) must also be bypassed.

Fixes #42931
2026-03-14 19:17:39 +01:00
Andrew Demczuk c6e32835d4
fix(feishu): clear stale streamingStartPromise on card creation failure
Fixes #43322

* fix(feishu): clear stale streamingStartPromise on card creation failure

When FeishuStreamingSession.start() throws (HTTP 400), the catch block
sets streaming = null but leaves streamingStartPromise dangling. The
guard in startStreaming() checks streamingStartPromise first, so all
future deliver() calls silently skip streaming - the session locks
permanently.

Clear streamingStartPromise in the catch block so subsequent messages
can retry streaming instead of dropping all future replies.

Fixes #43322

* test(feishu): wrap push override in try/finally for cleanup safety
2026-03-14 19:15:49 +01:00