feat: update to new acpx version, reduce noise in acp messages#30036
feat: update to new acpx version, reduce noise in acp messages#30036osolmaz merged 33 commits intoopenclaw:mainfrom
Conversation
nikolasdehor
left a comment
There was a problem hiding this comment.
The configurable command/version feature is well-implemented with good validation and test coverage. Two concerns: (1) PR scope: This bundles a significant refactor of the event parsing system (new PromptStreamProjector class, control-errors module, JSON-RPC handling) with the config flexibility feature. These are logically separate changes and would benefit from being split. (2) PR description: The title 'update to new acpx version' doesn't reflect the actual scope — a more descriptive title like 'feat(acpx): add configurable command path and version policy' would help reviewers.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b3d250548
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| export function parseControlJsonError(value: unknown): AcpxErrorEvent | null { | ||
| if (!isRecord(value)) { | ||
| return null; | ||
| } | ||
| const error = isRecord(value.error) ? value.error : null; |
There was a problem hiding this comment.
Wire structured control error parser into runtime commands
The new parseControlJsonError helper handles { error: { ... } } control responses, but AcpxRuntime.runControlCommand still only checks toAcpxErrorEvent (type: "error") and can therefore treat structured failures as success when acpx exits 0. In practice this can make control calls like setMode/setConfigOption silently no-op and also break the intended ignoreNoSession handling for cancel/close; the structured parser needs to be applied in the control-command error path before returning events.
Useful? React with 👍 / 👎.
Greptile SummaryUpdated to acpx 0.1.14 with enhanced configuration flexibility and noise reduction features for ACP agent messages. Key improvements:
Implementation quality:
Confidence Score: 5/5
Last reviewed commit: 9b3d250 |
🔒 Aisle Security AnalysisWe found 3 potential security issue(s) in this PR:
1. 🟠 Unvalidated acpx version spec enables untrusted package source during auto-install
Description
Vulnerable code: const expectedVersion = params.expectedVersion?.trim() || undefined;
const installVersion = expectedVersion ?? ACPX_PINNED_VERSION;
...
await spawnAndCollect({
command: "npm",
args: ["install", "--omit=dev", "--no-save", `acpx@${installVersion}`],
cwd: pluginRoot,
});This is especially risky if plugin configuration can be influenced by untrusted workspaces/repositories. RecommendationTreat the install target as data requiring strict validation. Recommended defenses (choose based on intended product behavior):
import semver from "semver";
function assertSafeVersion(v: string): string {
const cleaned = semver.valid(v);
if (!cleaned) throw new Error("expectedVersion must be a strict semver (e.g. 0.1.15)");
return cleaned;
}
const installVersion = expectedVersion ? assertSafeVersion(expectedVersion) : ACPX_PINNED_VERSION;
Also consider defaulting 2. 🟡 Arbitrary command execution via configurable
|
| Property | Value |
|---|---|
| Severity | Medium |
| CWE | CWE-78 |
| Location | extensions/acpx/src/config.ts:162-172 |
Description
The ACPX plugin now allows the runtime executable to be overridden via plugin configuration (command) and allows disabling version verification (expectedVersion: "any").
This is dangerous because:
commandis accepted as any non-empty string (including bare command names resolved fromPATH, or relative paths resolved against the workspace directory /process.cwd()).- The configured value is later executed via
child_process.spawn()for every ACP operation (turn execution and control commands). - Setting
expectedVersion: "any"disables strict version matching, so a non-acpxbinary that simply exits 0 on--helpcan pass health checks and then be used as the backend.
If an attacker can influence OpenClaw config (e.g., via the gateway control-plane config endpoints/UI, or by modifying the config file on disk), they can cause the gateway to execute an arbitrary local binary/script with the gateway's privileges.
Vulnerable code (configurable command resolution):
function resolveConfiguredCommand(params: { configured?: string; workspaceDir?: string }): string {
const configured = params.configured?.trim();
if (!configured) {
return ACPX_BUNDLED_BIN;
}
if (path.isAbsolute(configured) || configured.includes(path.sep) || configured.includes("/")) {
const baseDir = params.workspaceDir?.trim() || process.cwd();
return path.resolve(baseDir, configured);
}
return configured;
}Execution sink (example):
const child = spawnWithResolvedCommand({
command: this.config.command,
args,
cwd: state.cwd,
});Recommendation
Treat the command override as a high-risk escape hatch and add hard guardrails.
Suggested mitigations (choose one or combine):
- Disallow bare commands and relative paths (require absolute path):
if (configured && !path.isAbsolute(configured)) {
throw new Error("acpx.command must be an absolute path");
}- Restrict to a safe allowlist (e.g., bundled binary only, or absolute paths under an allowlisted directory):
const resolved = path.resolve(configured);
const allowedRoots = [ACPX_PLUGIN_ROOT /*, maybe workspaceDir */];
if (!allowedRoots.some((root) => resolved.startsWith(root + path.sep))) {
throw new Error("acpx.command must be inside an allowed directory");
}- Keep version enforcement on by default and limit override values:
- Only allow
expectedVersionto be a strict semver string (no npm specifiers/tags/URLs). - Consider removing/guarding
expectedVersion: "any"behind an explicitunsafeflag.
- Operational controls:
- Mark these fields as advanced/unsafe in UI hints.
- If the product has multiple privilege tiers, ensure only the highest-privilege operators can set
plugins.entries.acpx.config.command/expectedVersion.
3. 🔵 Remote GitHub tarball dependency without integrity pinning in pnpm lockfile
| Property | Value |
|---|---|
| Severity | Low |
| CWE | CWE-494 |
| Location | pnpm-lock.yaml:3134-3136 |
Description
The lockfile resolves @whiskeysockets/libsignal-node via a remote GitHub codeload tarball URL without an integrity (SRI) hash.
- The dependency is fetched from
https://codeload.github.com/.../tar.gz/<commit>. - The commit SHA in the URL provides some immutability, but there is no cryptographic content hash recorded in the lockfile to verify the downloaded bytes.
- This increases exposure to download-of-code-without-integrity-check scenarios (e.g., network/TLS interception in hostile environments, compromised endpoint/CDN returning unexpected bytes), and reduces reproducibility guarantees compared to registry packages which include
resolution.integrity. - This dependency is pulled into production via
@whiskeysockets/baileys(a top-leveldependenciesentry inpackage.json).
Vulnerable/at-risk lock entry:
'@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67':
resolution: {tarball: https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67}Recommendation
Prefer dependencies that ship via the npm registry (or another registry) with an immutable resolution.integrity recorded in pnpm-lock.yaml.
Options:
- Switch to a registry release of
@whiskeysockets/libsignal-node(if available) rather than a GitHub tarball. - If a registry release is not available:
- Consider vendoring the dependency (e.g., internal mirror/artifact repository) and pin it with a checksum.
- Enforce install-time integrity controls in CI (e.g.,
pnpm install --frozen-lockfile) and consider restricting/allowlisting external fetch hosts.
If you can move to a registry-published version, the lock entry should look like other packages with an integrity field, e.g.:
'@scope/pkg@1.2.3':
resolution:
integrity: sha512-... Analyzed PR: #30036 at commit 87ead23
Last updated on: 2026-03-01T20:01:33Z
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6c742a1f0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
b6c742a to
87ead23
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 87ead23d75
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
live | final_only)