Skip to content

fix(ci): require write+ for Mantis label-triggered proof runs#4

Merged
shrad3r merged 734 commits into
mainfrom
fix/mantis-label-auth-bypass
Jul 1, 2026
Merged

fix(ci): require write+ for Mantis label-triggered proof runs#4
shrad3r merged 734 commits into
mainfrom
fix/mantis-label-auth-bypass

Conversation

@shrad3r

@shrad3r shrad3r commented Jul 1, 2026

Copy link
Copy Markdown
Owner

What Problem This Solves

Security fix: remove pull_request_target labeled-path authorization bypass in mantis-telegram-desktop-proof.yml.

Summary

  • Require write/maintain/admin for all Mantis proof trigger paths.
  • Update workflow test expectations.

Evidence

  • Cursor Security Agents high-severity finding (2026-07-01).
  • 6-line deletion + test alignment.

Made with Cursor

joelnishanth and others added 30 commits June 30, 2026 19:38
handleScannedLink does not set self.step = .connect after scanning a QR
code from the welcome step. The scanner sheet dismisses and the UI
returns to the welcome screen instead of showing connection progress.

The sibling handleScannedSetupCode path correctly advances the step;
this aligns the QR scan path.

Fixes openclaw#98297

Co-authored-by: Cursor <cursoragent@cursor.com>
…claw#96644)

postJson reads the Anthropic OAuth token endpoint response body with
an unbounded await response.text(). A compromised or hijacked OAuth
endpoint can stream an arbitrarily large body and force the runtime
to buffer the entire payload before parsing — an OOM/DoS vector.

Replace with readResponseWithLimit at 16 MiB cap + TextDecoder decode
to match the sibling bounded-read pattern (provider-http-errors.ts:308).

Co-authored-by: Claude <noreply@anthropic.com>
* fix(gateway): warn for blocked configured channel plugins

* fix(gateway): preserve configured channel warning source
…#96359)

* test: migrate src/commands tests to shared temp dir helpers

* fix(test): remove unused path import in migrate apply test
…penclaw#96293)

* fix(cron): clear agentTurn thinking override when patched with null

Cron agentTurn patches could clear model/fallbacks/toolsAllow overrides by
sending an explicit null, but thinking had no clear path: the patch schema and
normalizer dropped thinking:null before it reached the merge logic, and the
payload merge only handled string values. Blanking the Thinking/Effort field in
the Cron Control UI therefore silently preserved the old value.

Add thinking:null support across the patch schema, exported type, normalizer,
and payload merge (mirroring model). The Control UI now sends an explicit clear
for model/thinking when an edited job blanks a previously stored override, and
the CLI gains --clear-thinking for parity with --clear-model.

* docs(cron): document --clear-thinking beside sibling clear flags
…openclaw#96058)

Gap finding: tempdir-8h9i0j1k, tempdir-9i0j1k2l

Replace bare fs.mkdtemp/mkdtempSync calls in 4 test files with the
shared createSuiteTempRootTracker() helper, adding proper cleanup
where none existed.

Background: the project has standardized on shared temp dir helpers
since 88b87d8 (withTempDir) -> 13df67e (createSuiteTempRootTracker)
-> 06431fd / openclaw#87298 (CI guard + docs guidance). This PR continues
that migration for the highest-risk files.

Files changed:
- src/skills/lifecycle/install-fallback.test.ts: variable overwrite risk
- src/auto-reply/reply/session-hooks-context.test.ts: helper leaks temp dirs
- src/auto-reply/reply/abort.test.ts: createAbortConfig leaks root dir
- src/auto-reply/inbound.test.ts: test cases without cleanup

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…nt OOM (openclaw#97693)

* fix(discord): bound happy-path API response reads to prevent OOM

Replace the unbounded res.text() call in requestDiscord's success path with
readResponseTextLimited capped at 4 MiB. Discord channel message lists and
attachment payloads can accumulate to large sizes; without a cap the process
can exhaust available memory. The error path already used readResponseTextLimited
with DISCORD_API_ERROR_BODY_LIMIT_BYTES — this applies the same guard to the
happy path using a separate DISCORD_API_RESPONSE_BODY_LIMIT_BYTES constant
sized appropriately for valid API payloads.

* test(discord): upgrade to real HTTP server proof for bound requestDiscord

* fix(discord): remove unnecessary type assertion in bound test
…er (openclaw#97683)

Two-step decode in decodeLiteralEscapes:
- Step 1: regex anchored to high-surrogate range (U+D800–U+DBFF) so a
  preceding BMP escape (e.g. \u0041) cannot consume the high-surrogate
  half of a valid pair like \uD83D\uDE00 (😀), leaving \uDE00 lone.
- Step 2: decode remaining BMP codepoints; preserve lone surrogates as
  six-character literals instead of corrupting them to U+FFFD in the
  outbound IRC UTF-8 stream.
* fix(utils): keep reply directive ids unicode-safe

* test(utils): catch trailing lone surrogate in hasUnpairedSurrogate helper

Per PR openclaw#96938 review: the test helper missed a high surrogate at end of
string because charCodeAt(out-of-bounds) returns NaN, and NaN comparisons
are always false. Guard bounds explicitly so the truncation test actually
proves what it claims, plus two cases pinning helper behavior.

* chore(utils): rerun QA smoke to confirm memory-index timeout flake on openclaw#96938

---------

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
openclaw#97814) (openclaw#97857)

* fix(memory): detect unindexed session transcripts in status mode (fixes openclaw#97814)

The status-purpose MemoryIndexManager init skips ensureSessionStartupCatchup(),
so openclaw memory status reports dirty=false while unindexed session files
exist on disk. This is a false-clean state: the operator sees no backlog,
but memory search silently misses unindexed transcripts.

Fix: run markSessionStartupCatchupDirtyFiles() in status mode. It checks
for on-disk session files without corresponding memory_index_sources rows
and sets sessionsDirty=true if found, without triggering a full sync.

The full catchup+sync runs on the next non-transient manager cycle (CLI
sync or normal runtime init).

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

* fix(memory): await status dirty detection before status() reads the flag (fixes openclaw#97814)

The status-purpose MemoryIndexManager init skips ensureSessionStartupCatchup(),
so openclaw memory status reports dirty=false while unindexed session files
exist on disk. This is a false-clean state: the operator sees no backlog,
but memory search silently misses unindexed transcripts.

Fix: move status-mode markSessionStartupCatchupDirtyFiles() from the
constructor (fire-and-forget via void) into the create() factory method
where it is awaited. This guarantees sessionsDirty is set before any
caller reads manager.status(). The detection does NOT trigger a sync,
so status remains lightweight.

Review: verified manually — unit tests 21/21 pass, compilation 0 errors.

* test(memory): add regression test for status-purpose dirty detection (fixes openclaw#97814)

Adds a regression test that exercises the actual purpose:'status' manager
flow: create a session file without index row, create status manager, verify
status().dirty is true. This addresses the ClawSweeper P1 finding requesting
a test that exercises the real status path, not only the protected helper.

Also fixes the timing issue: move dirty detection from constructor (void)
to create() factory (await), guaranteeing sessionsDirty is set before any
caller reads manager.status().

---------

Co-authored-by: Claude <noreply@anthropic.com>
* fix(android): clarify gateway auth recovery states

* fix(android): preserve retryable pairing recovery copy

* fix(android): prefer auth recovery detail before stale address

* fix(android): show auth recovery while approval loads
…g seam (openclaw#98205)

Add 17 unit tests for nodes-wake-state.ts covering:
- Exported wait/poll constants
- nodeWakeById Map operations (insert, overwrite, multi-entry, inFlight)
- nodeWakeNudgeById Map operations (independent tracking)
- clearNodeWakeState function (removal, no-op, scoped deletion)
- testing seam (getNodeWakeByIdSize, hasNodeWakeEntry, resetWakeState)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix: surface node approval guidance from devices CLI

* fix: preserve node approval connection guidance

* fix: handle admin retry unknown device approvals

* fix: require stable node approval matches

---------

Co-authored-by: welfo-beo <187608477+welfo-beo@users.noreply.github.com>
Co-authored-by: welfo-beo <welfo-beo@users.noreply.github.com>
* docs: clarify source checkout Node floor

* chore: refresh CI for PR openclaw#97898

---------

Co-authored-by: lin-hongkuan <lin-hongkuan@users.noreply.github.com>
… with streamed reasoning (openclaw#94526)

* test(telegram): add regression test for forum topic message_thread_id with streamed reasoning

After thorough code tracing of all delivery paths (draft stream, durable,
non-durable, preview hooks), the current main branch already correctly
passes message_thread_id through all paths for the streaming reasoning +
final answer scenario in forum topics. The bug reported in openclaw#89352 may have
been resolved by earlier merged changes (media-path preservation,
draft/progress streaming, etc.).

This commit adds a focused regression test covering the combined
scenario to prevent future regressions.

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

* fix(telegram): assert forum thread at draft-stream boundary for both lanes

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…nclaw#98145)

* fix(device-pairing): don't churn requestId on subset re-requests

A reconnect that re-requested a subset of an already-pending device
pairing request still superseded it with a fresh requestId. This is the
root cause of the "unknown requestId" failures during device approval:

1. A TUI connect files a broad scope-upgrade request; the owner copies its
   id from `openclaw devices list`.
2. `openclaw devices approve <id>` reconnects as a CLI probe that only needs
   `operator.pairing` — a subset of the pending scopes.
3. That subset re-request superseded the pending request with a new id, so
   the originally-listed id no longer existed and approve failed.

Refresh the existing request in place when the incoming request only asks
for roles/scopes a single pending request (same device key + role) already
covers. Escalations that request *more* than the pending request still
supersede with a fresh id, so the requestId stays bound to at least the
scope snapshot the owner saw (the existing security-motivated behavior and
its test are preserved).

AI-assisted (Claude Code).

* fix(device-pairing): align subset pairing with scope coverage
…he boundary (openclaw#98267)

* fix(system-prompt): move exec-approval + Authorized Senders below cache boundary

buildExecApprovalPromptGuidance (channel-varying: CLI /approve vs native
approval UI) and buildUserIdentitySection / "## Authorized Senders"
(owner/identity-varying, dropped when minimal) were emitted into the static,
cacheable prefix *above* SYSTEM_PROMPT_CACHE_BOUNDARY. They fork the cacheable
prefix at ~token 1,460, invalidating client-side prefix caching for the rest of
the ~17.8K-token system prompt — causing minutes-long cold prefills on local
models (llama.cpp / MLX / Ollama) after any channel-varying cron/heartbeat turn.

Follow-on to openclaw#40256, which moved Messaging/Voice/Reactions below the boundary
but missed these two. Relocates both into the existing below-boundary
channel-guidance block. Pure cache-stability change with no behavioural change
(the guidance is position-independent for correctness). Extends the boundary
test to assert both sections sit below SYSTEM_PROMPT_CACHE_BOUNDARY.

Measured on a local deployment: shared cross-channel prefix grew ~1,460 ->
~15,486 tokens; post-cron interactive turns went from minutes to ~10s.

Fixes openclaw#98261

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6Hz9UEpxQ4W3d8XecvupH

* fix(system-prompt): suppress relocated exec-approval line under tool_call_style override

Addresses review (clawsweeper): the exec-approval guidance lived inside the
`tool_call_style` fallback, so a provider override of that section previously
replaced it. Relocating it below the boundary emitted it unconditionally, which
changed behaviour for providers/plugins that override `tool_call_style`. Gate the
relocated line on the absence of a `tool_call_style` override, restoring the
original "override replaces the whole section" contract. Extends the
provider-override test to assert the default approval line is suppressed when
the section is overridden.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6Hz9UEpxQ4W3d8XecvupH

* fix(system-prompt): tighten cache boundary proof

* fix(system-prompt): tighten cache boundary proof

* fix(system-prompt): tighten cache boundary proof

---------

Co-authored-by: headbouyJB <23249268+headbouyJB@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(openrouter): send explicit auth headers

* test(openrouter): type stream mock calls
Render Control UI item and preamble progressText events as chat stream segments and preserve pre-final stream commentary before appending final assistant messages. Keyed preamble segments stay independent from accumulated stream snapshots, so distinct same-text commentary items render intact.

Co-authored-by: Chisel <chisel@psiclawops.dev>
Co-authored-by: Chisel <chisel@psiclawops.dev>
Add a per-viewer 'Keep commentary' toggle (UiSettings.chatPersistCommentary,
default true) that controls whether keyed Codex preamble/commentary blocks
stay after the final answer or clear with it.

- Persist (default): keyed commentary materializes as durable blocks, current
  behavior, existing proof unchanged.
- Transient (toggle off): commentary stays live during streaming but is never
  materialized, so it disappears as the final message arrives. This is the
  transient-only behavior from openclaw#92236, now user-selectable instead of a
  maintainer-level either/or policy choice.

Single gating point in materializeVisibleStreamState (skip itemId-keyed parts
when persistCommentary is false); threaded from settings through the chat
event handler. Adds desktop + mobile header toggles and an en.ts label
(locale bundles regenerated via ui:i18n:sync, English fallback).

Tests: reconciliation persist/transient coverage, final-event handler honors
the setting, settings round-trip + header button assertions updated.
Keyed preamble commentary was appended after the whole tool loop, so it relied solely on the final visible-time sort for placement and lost the insertion-order tiebreaker against tool cards. Splice each keyed commentary segment into the items list before the first item with a strictly-later timestamp, so a preamble that arrived before a later tool renders above that tool while the run is live (not only after final materialization). Tools sharing the commentary timestamp that are already visible stay above it. Adds a buildChatItems regression covering a keyed preamble between two tools.
steipete and others added 24 commits July 1, 2026 14:04
* test: update transcript helper routing expectation

* test: update transcript helper routing expectation

---------

Co-authored-by: Peter Steinberger <58493+steipete@users.noreply.github.com>
Remove the pull_request_target labeled-path bypass that unconditionally
authorized triage-level actors. All trigger paths now enforce admin,
maintain, or write collaborator permission before secret-bearing jobs run.

Co-authored-by: Cursor <cursoragent@cursor.com>
Update workflow expectations after removing the pull_request_target
labeled-path authorization bypass.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shrad3r shrad3r merged commit 9c7a784 into main Jul 1, 2026
1 check passed
@shrad3r shrad3r deleted the fix/mantis-label-auth-bypass branch July 1, 2026 13:45
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Dependency Guard

This PR changes dependency-related files. Maintainers should confirm these changes are intentional.

Changed files:

  • extensions/acpx/npm-shrinkwrap.json
  • extensions/acpx/package.json
  • extensions/admin-http-rpc/package.json
  • extensions/alibaba/package.json
  • extensions/amazon-bedrock-mantle/npm-shrinkwrap.json
  • extensions/amazon-bedrock-mantle/package.json
  • extensions/amazon-bedrock/npm-shrinkwrap.json
  • extensions/amazon-bedrock/package.json
  • extensions/anthropic-vertex/npm-shrinkwrap.json
  • extensions/anthropic-vertex/package.json
  • extensions/anthropic/package.json
  • extensions/arcee/npm-shrinkwrap.json
  • extensions/arcee/package.json
  • extensions/azure-speech/package.json
  • extensions/bonjour/package.json
  • extensions/brave/npm-shrinkwrap.json
  • extensions/brave/package.json
  • extensions/browser/package.json
  • extensions/byteplus/package.json
  • extensions/canvas/package.json
  • extensions/cerebras/npm-shrinkwrap.json
  • extensions/cerebras/package.json
  • extensions/chutes/npm-shrinkwrap.json
  • extensions/chutes/package.json
  • extensions/clickclack/npm-shrinkwrap.json
  • 182 additional dependency-related files not shown

Maintainer follow-up:

  • Review whether the dependency changes are intentional.
  • Inspect resolved package deltas when lockfile, shrinkwrap, or workspace dependency policy changes are present.
  • Treat package-lock.json and npm-shrinkwrap.json diffs as security-review surfaces.
  • Run pnpm deps:changes:report -- --base-ref origin/main --markdown /tmp/dependency-changes.md --json /tmp/dependency-changes.json locally for detailed release-style evidence.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Dependency graph changes noted

This PR includes dependency graph changes. The dependency guard is informational because the PR author is a repository admin or a member of @openclaw/openclaw-secops.

  • Current SHA: e9d41c277fc034d20837f65bb74e5306f6f6a9fc
  • Trusted actor: @shrad3r
  • Trusted role: pull request author; repository admin

Security review is still recommended before merge when the dependency graph change is intentional.

shrad3r added a commit that referenced this pull request Jul 1, 2026
Fork-only security fix: remove pull_request_target labeled-path auth bypass.
Reverts accidental upstream bulk import from PR #4 squash merge.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.