Commit Graph

9965 Commits

Author SHA1 Message Date
Peter Steinberger abf6b4997e test(archive): accept drive-path absolute tar errors 2026-02-14 15:52:38 +01:00
Peter Steinberger b87b16e2b6 docs(changelog): note browser CSRF hardening 2026-02-14 15:51:46 +01:00
Peter Steinberger b566b09f81 fix(security): block cross-origin mutations on loopback browser routes 2026-02-14 15:51:09 +01:00
Peter Steinberger 1f1fc095a0
refactor(sandbox): auto-recreate browser container on config changes (#16254) 2026-02-14 15:47:59 +01:00
Peter Steinberger 31791233d6 fix(security): reject oversized base64 before decode 2026-02-14 15:45:41 +01:00
Peter Steinberger 4f043991e0
fix: suppress false duplicate plugin warnings (#16222) (thanks @shadril238) (#16245) 2026-02-14 15:45:21 +01:00
Peter Steinberger 4c7838e3cf refactor(archive): centralize limits and budgets 2026-02-14 15:43:44 +01:00
Peter Steinberger 5f4b29145c test(archive): cover archive size and absolute tar paths 2026-02-14 15:36:41 +01:00
Peter Steinberger d3ee5deb87 fix(archive): enforce extraction resource limits 2026-02-14 15:36:41 +01:00
Peter Steinberger c8424bf29a
fix(googlechat): deprecate users/<email> allowlists (#16243) 2026-02-14 15:31:26 +01:00
Aether AI 3967ece625
fix(security): OC-25 — Validate OAuth state parameter to prevent CSRF attacks (#16058)
* fix(security): validate OAuth state parameter to prevent CSRF attacks (OC-25)

The parseOAuthCallbackInput() function in the Chutes OAuth flow had two
critical bugs that completely defeated CSRF state validation:

1. State extracted from callback URL was never compared against the
   expected cryptographic nonce, allowing attacker-controlled state values
2. When URL parsing failed (bare authorization code input), the catch block
   fabricated a matching state using expectedState, making the caller's
   CSRF check always pass

## Attack Flow

1. Victim runs `openclaw login chutes --manual`
2. System generates cryptographic state: randomBytes(16).toString("hex")
3. Browser opens: https://api.chutes.ai/idp/authorize?state=abc123...
4. Attacker obtains their OWN OAuth authorization code (out of band)
5. Attacker tricks victim into pasting just "EVIL_CODE" (not full URL)
6. parseOAuthCallbackInput("EVIL_CODE", "abc123...") is called
7. new URL("EVIL_CODE") throws → catch block executes
8. catch returns { code: "EVIL_CODE", state: "abc123..." } ← FABRICATED
9. Caller checks: parsed.state !== state → "abc123..." !== "abc123..." → FALSE
10. CSRF check passes! System calls exchangeChutesCodeForTokens()
11. Attacker's code exchanged for access + refresh tokens
12. Victim's account linked to attacker's OAuth session

Fix:
- Add explicit state validation against expectedState before returning
- Remove state fabrication from catch block; always return error for
  non-URL input
- Add comprehensive unit tests for state validation

Remediated by Aether AI Agent security analysis.

* fix(security): harden chutes manual oauth state check (#16058) (thanks @aether-ai-agent)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 15:28:52 +01:00
seheepeak cb9a5e1cb9
feat(sandbox): separate bind mounts for browser containers (#16230)
* feat(sandbox): add separate browser.binds config for browser containers

Allow configuring bind mounts independently for browser containers via
sandbox.browser.binds. When set, browser containers use browser-specific
binds instead of inheriting docker.binds. Falls back to docker.binds
when browser.binds is not configured for backwards compatibility.

Closes #14614

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

* fix(sandbox): honor empty browser binds override (#16230) (thanks @seheepeak)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 15:27:41 +01:00
Tak Hoffman 302dafbe1a
Docs: move submission guidance to GitHub templates (#16232)
* Docs: move submission guidance to GitHub templates

* Docs: make PR risk template entries flexible

* Docs: remove PR reviewer checklist section
2026-02-14 08:27:01 -06:00
Peter Steinberger 493f6f458b perf(test): speed up browser test suites 2026-02-14 14:25:54 +00:00
Peter Steinberger 57f40a5da6 perf(test): speed up config tests 2026-02-14 14:25:54 +00:00
shadril238 788ea6e9d1 fix: suppress false duplicate plugin id warning for symlinked extensions
When the same plugin directory is discovered through different path
representations (e.g. symlinks), the manifest registry incorrectly
warns about a duplicate plugin id. This is a false positive that
appears for bundled extensions like feishu (#16208).

Compare fs.realpathSync() of both candidates' rootDir before emitting
the duplicate warning. If they resolve to the same physical directory,
silently skip the duplicate instead of warning.

Also change seenIds from Set<string> to Map<string, PluginCandidate>
to track the first-seen candidate for comparison.

Closes #16208
2026-02-14 15:25:51 +01:00
Peter Steinberger 1a7e180e68
refactor(media): normalize inbound MediaType/MediaTypes defaults (#16233)
* refactor(media): normalize inbound media type defaults

* test(browser): fix Windows path expectation in file chooser hook
2026-02-14 15:18:19 +01:00
Peter Steinberger 00a0890889 fix(media): bound input media payload sizes 2026-02-14 15:16:06 +01:00
Peter Steinberger 4b1cadaecb
refactor(media): normalize inbound media type defaults (#16228) 2026-02-14 15:06:13 +01:00
Peter Steinberger e53a221e5c chore: format changelog 2026-02-14 15:03:27 +01:00
Peter Steinberger 28d9dd7a77 fix(macos): harden openclaw deep links 2026-02-14 15:03:27 +01:00
Peter Steinberger 644bef157a docs: clarify hook transform module path constraints 2026-02-14 15:03:27 +01:00
Peter Steinberger 35c0e66ed0 fix(security): harden hooks module loading 2026-02-14 15:03:27 +01:00
Peter Steinberger 3d0a41b584 test(gateway): isolate device identity in auth e2e 2026-02-14 14:57:19 +01:00
Peter Steinberger 3a67721dae docs(security): fix canvas host docs formatting 2026-02-14 14:57:19 +01:00
Peter Steinberger 6a386a7886 docs(security): clarify canvas host exposure and auth 2026-02-14 14:57:19 +01:00
jasonftl 8025e7c6c2
fix(discord): respect gateway TLS config in exec approvals handler (#16216) (thanks @jasonftl) 2026-02-14 14:53:38 +01:00
Peter Steinberger 842499d6c5
test(security): reject hook archives with traversal entries (#16224) 2026-02-14 14:53:33 +01:00
Peter Steinberger 3aa94afcfd
fix(security): harden archive extraction (#16203)
* fix(browser): confine upload paths for file chooser

* fix(browser): sanitize suggested download filenames

* chore(lint): avoid control regex in download sanitizer

* test(browser): cover absolute escape paths

* docs(browser): update upload example path

* refactor(browser): centralize upload path confinement

* fix(infra): harden tmp dir selection

* fix(security): harden archive extraction

* fix(infra): harden tar extraction filter
2026-02-14 14:42:08 +01:00
Peter Steinberger 9a134c8a10 perf(test): tune parallel vitest worker split 2026-02-14 13:27:18 +00:00
Peter Steinberger ce0eddd384 test: isolate test home before runtime imports 2026-02-14 13:27:18 +00:00
Peter Steinberger 7d3e5788e8 fix: stop enforcing <final> for ollama (#16191) (thanks @Glucksberg) 2026-02-14 14:21:34 +01:00
Glucksberg 74193ff754 fix(ollama): remove Ollama from isReasoningTagProvider (#2279)
Ollama's OpenAI-compatible endpoint handles reasoning natively via the
`reasoning` field in streaming chunks. Treating Ollama as a
reasoning-tag provider incorrectly forces <think>/<final> tag
enforcement, which causes stripBlockTags() to discard all output
(since Ollama models don't emit <final> tags), resulting in
'(no output)' for every Ollama model.

This fix removes 'ollama' from the isReasoningTagProvider() check,
allowing Ollama models to work correctly through the standard
content/reasoning field separation.
2026-02-14 14:21:34 +01:00
Tanwa Arpornthip c76288bdf1
fix(slack): download all files in multi-image messages (#15447)
* fix(slack): download all files in multi-image messages

resolveSlackMedia() previously returned after downloading the first
file, causing multi-image Slack messages to lose all but the first
attachment. This changes the function to collect all successfully
downloaded files into an array, matching the pattern already used by
Telegram, Line, Discord, and iMessage adapters.

The prepare handler now populates MediaPaths, MediaUrls, and
MediaTypes arrays so downstream media processing (vision, sandbox
staging, media notes) works correctly with multiple attachments.

Fixes #11892, #7536

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

* fix(slack): preserve MediaTypes index alignment with MediaPaths/MediaUrls

The filter(Boolean) on MediaTypes removed entries with undefined contentType,
shrinking the array and breaking index correlation with MediaPaths and MediaUrls.
Downstream code (media-note.ts, attachments.ts) requires these arrays to have
equal lengths for correct per-attachment MIME type lookup. Replace filter(Boolean)
with a nullish coalescing fallback to "application/octet-stream".

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

* fix(slack): align MediaType fallback and tests (#15447) (thanks @CommanderCrowCode)

* fix: unblock plugin-sdk account-id typing (#15447)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 14:16:02 +01:00
Peter Steinberger ef70a55b7a
refactor(reply): clarify explicit reply tags in off mode (#16189)
* refactor(reply): clarify explicit reply tags in off mode

* fix(plugin-sdk): alias account-id subpath for extensions
2026-02-14 14:15:37 +01:00
Peter Steinberger 6f7d31c426 fix(security): harden plugin/hook npm installs 2026-02-14 14:07:14 +01:00
Peter Steinberger d69b32a073 docs(changelog): clarify hooks transform dir restriction 2026-02-14 14:02:16 +01:00
Peter Steinberger d73b48b32c fix(ts): map plugin-sdk subpaths 2026-02-14 13:01:02 +00:00
Peter Steinberger ec399aaddf perf(test): parallelize unit-isolated 2026-02-14 13:01:02 +00:00
Peter Steinberger 18e8bd68c5 fix(security): block hook manifest path escapes 2026-02-14 14:00:37 +01:00
Peter Steinberger 3bbd29bef9 perf(gateway): cache session list transcript fields 2026-02-14 12:52:51 +00:00
Peter Steinberger a0361b8ba9 fix(security): restrict hook transform module loading 2026-02-14 13:46:09 +01:00
Peter Steinberger 6543ce717c perf(test): avoid plugin-sdk barrel imports 2026-02-14 12:42:19 +00:00
Peter Steinberger 1ba266a8e8 refactor: split minimax-cn provider 2026-02-14 13:37:47 +01:00
Peter Steinberger bf080c2338 Merge remote-tracking branch 'origin/main' 2026-02-14 13:36:18 +01:00
Tak Hoffman 274da72c38
Revert "fix: don't auto-create HEARTBEAT.md on workspace init (openclaw#12027) thanks @shadril238" (#16183)
This reverts commit 386bb0c618.
2026-02-14 06:33:14 -06:00
Peter Steinberger 83248f7603 Merge remote-tracking branch 'origin/main' 2026-02-14 13:30:22 +01:00
Peter Steinberger af50b914a4 refactor(browser): centralize http auth 2026-02-14 13:30:11 +01:00
Peter Steinberger a2b45e1c13 fix(gateway): relax http tool deny typing 2026-02-14 13:30:05 +01:00
Aldo 7b39543e8d
fix(reply): honour explicit [[reply_to_*]] tags when replyToMode is off (#16174)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 778fc2559a
Co-authored-by: aldoeliacim <17973757+aldoeliacim@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-02-14 13:29:42 +01:00