* [AI] fix(security): detect child_process calls through aliases and computed members
The advisory source scanner's dangerous-exec rule missed three
child_process invocation forms that each should produce a critical
finding, plus a fourth namespace-alias computed-exec form:
- ESM import alias: import { spawn as launch } from "node:child_process"; launch(...)
- CJS destructure alias: const { exec: run } = require("child_process"); run(...)
- computed member: import cp from "node:child_process"; cp["spawn"](...)
- namespace alias computed exec: const proc = require("child_process"); proc["exec"](...)
The rule pattern only matched bare command names and used a source-wide
child_process context gate with a literal receiver-name allowlist in
isBenignMemberExecMatch, so aliased bindings and computed calls on proven
namespace aliases escaped detection.
Add a provenance-aware collectChildProcessBindings pass that derives
renamed method bindings and whole-namespace bindings ONLY from actual
child_process imports/requires (so unrelated aliases such as a locally
imported launch() do not false-positive). Extend the dangerous-exec
pattern with a computed-member branch and make isBenignMemberExecMatch
consult namespace-alias provenance instead of a hard-coded receiver
allowlist, so computed exec on a proven child_process namespace alias
fires while the RegExp.exec exclusion is preserved.
Fixes#116255
Co-Authored-By: Maas <noreply@anthropic.com>
* style(security): apply oxfmt to scanner.ts
Fixes CI oxfmt --check failure on PR #116304 (oxfmt wrapped a long if
condition across multiple lines). No logic changes.
* fix(security): scope computed exec methods to proven child_process receiver
The dangerous-exec scanner's computed-member matcher accepted any
obj["spawn"]()/obj["execSync"]() call whenever child_process appeared
anywhere in the file, while receiver provenance was checked only for
exec. That turned unrelated APIs into critical findings, which could
incorrectly fail Skill Workshop proposals and distort deep-audit output.
Require a proven child_process namespace receiver for EVERY watched
computed execution method (exec/execSync/spawn/spawnSync/execFile/
execFileSync), preserving the long-standing RegExp.exec exclusion only
for the direct dot-member exec case. Add regression coverage for
unrelated computed spawn/execSync calls and a proven computed execSync
positive.
Addresses ClawSweeper P1 on PR #116304.
* [AI] fix(security): recognize direct and computed calls through collected namespace aliases
Address ClawSweeper P1/P2: consult provenance namespaceAliases for direct
dot-member exec calls (e.g. proc.exec for const proc = require(...)) and
collect ESM namespace imports (import * as proc from "node:child_process").
Co-Authored-By: Codex <noreply@anthropic.com>
* [AI] fix(security): report every proven alias execution call on a line
ClawSweeper P1: the alias attribution path emitted only the first proven
child_process alias call per source line, violating the scanner's
per-occurrence reporting. Match all alias call positions and emit a finding
for each (skipping positions the literal pattern already reported), with a
regression test for two calls on one line.
Co-Authored-By: Codex <noreply@anthropic.com>
---------
Co-authored-by: Maas <noreply@anthropic.com>
* fix(media): fall back when providers return empty output
* test(media): type the synthetic attachment cache fixture
* test(media): avoid object spread in fallback test cases
* fix(config): stop plugin schemas rejecting the channel key core writes
A published channel plugin declares a closed config schema listing only the keys
it knew about when that version shipped. Core's doctor migration writes
heartbeatVisibility into channels.<id> and its accounts for every channel, so a
plugin predating that key turns config core itself produced into a gateway that
refuses to start with 78/CONFIG — and doctor --fix reports it without repairing
it, leaving no in-product recovery. This took the live gateway down after the
last deploy: @openclaw/signal 2026.7.2-beta.3 ships a 44-property closed schema
with no heartbeatVisibility, while core reads all three precedence layers
(src/infra/heartbeat-visibility.ts) and documents them
(docs/gateway/heartbeat.md).
Channel schemas built from core's shape may omit common fields on purpose —
buildCommonChannelAccountShape takes an `omit` list — so widening every common
key would silently accept config that can never take effect. The fix stays on
the keys core writes: core wrote it, so core accepts it. Everything else is
untouched, including unknown-key rejection, which is covered by a test and
verified end to end.
Applied once when the channel schema map is built, so the AJV cache key still
maps to exactly one schema per channel, and unchanged schemas are returned by
identity.
Verified against the real failure: with the live plugin set loaded, the config
that crashed the gateway now validates at both channel and per-account level,
while an unknown key on the same channel is still rejected.
* fix(config): normalize external channel schemas at metadata owner
* fix(config): normalize core-owned channel visibility schemas
* style(config): format channel metadata regression coverage
* refactor(config): simplify channel schema normalization coverage
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>