fix(agents): reject bind specs with extra colon segments#95572
fix(agents): reject bind specs with extra colon segments#95572clawsweeper[bot] merged 2 commits into
Conversation
|
Codex review: passed. Reviewed June 21, 2026, 11:06 PM ET / 03:06 UTC. Summary PR surface: Source +8, Tests +26. Total +34 across 2 files. Reproducibility: yes. Source inspection of current main shows Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Merge the shared parser rejection and regression coverage once maintainers accept the malformed-spec fail-closed behavior and exact-head gates pass. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection of current main shows Is this the best way to solve the issue? Yes. The shared AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against a1828110704f. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +8, Tests +26. Total +34 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
NianJiuZst
left a comment
There was a problem hiding this comment.
Detailed review on top of ClawSweeper's diamond-lobster verdict (which is right — this is a narrow, well-tested fix).
The split(":", 2) → split(":") change is correct. Old code silently dropped extra segments ("matrix:work:extra" → ("matrix", "work")), which is exactly the kind of "looks like it worked, silently lost data" bug that bites users months later. New code rejects explicitly with a clear error message.
Error message consistency check. The error string starts with 'Invalid binding "..."'. Worth a quick grep across src/commands/agents.bindings.ts to confirm this matches the convention for other rejection paths. If other rejections use a different prefix ('Error:', 'Cannot parse:', etc.), this one will feel inconsistent in CLI output.
Telemetry/log gap. A rejected bind spec is currently a no-op except for printing the error. If users hit this in the wild (legitimate typo, or a workaround they were relying on), there's no signal that why their bind is failing beyond the one error line. Consider whether the parser should also log the count of rejected specs at the end of parseBindingSpecs so users see "5 valid, 2 invalid, run openclaw agents list-bindings to inspect" rather than just an error stream.
Migration risk. The old behavior silently accepted extra colons. If any user has a working bind spec with extra colons (maybe in a config file they paste from a doc or template), they'll suddenly start getting this error after upgrading. Worth either:
- Documenting the breaking change in CHANGELOG.md / release notes.
- Adding a one-time migration helper that detects "specs with extra colons that would now fail" and warns users to fix them.
Option 2 is overkill for a parse fix, but option 1 (a single release note line) is cheap insurance.
Test coverage. The new tests cover the positive and negative cases. One edge case worth a test: "matrix:work:" (trailing colon). The current split(":") returns ("matrix", "work", "") — extraSegments would be [""] (length 1), so it would be rejected. Worth pinning the behavior with a test that asserts the trailing-colon case is rejected, not silently accepted.
Diamond lobster maintsream note. With CS + my review both diamond-lobster-clean, this is ready for maintainer review as soon as required CI checks finish. The "Risk before merge: required CI queued" line in CS's review is just a status note, not a blocker.
504a980 to
7faf2cd
Compare
|
@clawsweeper automerge |
|
🦞✅ Source: What merged:
Automerge notes:
The automerge loop is complete. Automerge progress:
|
`parseBindingSpecs` split each `--bind <spec>` with `split(":", 2)`, which
truncates: `matrix:work:extra` parsed as `["matrix","work"]`, silently
dropping `:extra` and binding to account `work` with no error. Account ids
never contain `:` (VALID_ID_RE in routing/account-id), so a third segment is
always a malformed spec, not an account id to truncate to.
Split fully and reject a spec that carries extra segments, matching the
existing empty-account error shape. `matrix:work` and `matrix` are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7faf2cd to
90061fb
Compare
|
Clownfish 🐠 reef update Thanks for the contribution here. Clownfish was able to repair this branch in place, which keeps the original PR as the clean canonical path. Source PR: #95572 fish notes: model gpt-5.5, reasoning medium; reviewed against 3e1af31. |
) Summary: - The PR changes `parseBindingSpecs` to reject `<channel>:<account>:...` bind specs and adds Matrix parser regression coverage for the malformed and valid forms. - PR surface: Source +8, Tests +26. Total +34 across 2 files. - Reproducibility: yes. Source inspection of current main shows `parseBindingSpecs` truncating with `split(":", 2)`, and the PR body provides before/after terminal output from a harness importing the real parser. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(agents): reject bind specs with extra colon segments Validation: - ClawSweeper review passed for head 3e1af31. - Required merge gates passed before the squash merge. Prepared head SHA: 3e1af31 Review: openclaw#95572 (comment) Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
Summary
openclaw agents bind --bind <spec>silently mis-parsed any spec carrying an extra colon segment.parseBindingSpecs(src/commands/agents.bindings.ts) split each spec withsplit(":", 2), which truncates:matrix:work:extraparsed as["matrix", "work"], the:extrawas dropped, and a binding was created for accountworkwith no error reported — the user asked to bind one thing and silently got another.Account ids can never contain
:— they are constrained byVALID_ID_RE(/^[a-z0-9][a-z0-9_-]{0,63}$/i,src/routing/account-id.ts:9) — so a third colon segment is always a malformed spec, not an account id to truncate to.Changes
src/commands/agents.bindings.ts— split the spec fully (split(":")capturing...extraSegments); if any extra segment is present, push anInvalid binding ... Account id cannot contain ":"error and skip, matching the existing empty-account guard's shape. Check ordering is unknown-channel → extra-segments → empty-account → resolve.src/commands/agents.bind.matrix.integration.test.ts— regression test thatmatrix:work:extrayields the extra-segments error with no binding, plus a control thatmatrix:workstill parses.This is the correct owner boundary:
parseBindingSpecsis the only place that owns splitting<channel>:<account>;account-id.tsis a coercing normalizer (never rejects); and there is noVALID_ID_REvalidation later on the bind write path, so a malformed id would otherwise persist toopenclaw.jsonand later canonicalize to a different account (work:extra→work-extra). Rejecting at parse time is strictly safer than truncating or rejoining.Real behavior proof
Behavior addressed:
agents bind --bind matrix:work:extrasilently dropped:extraand bound accountworkwith no error (split(":", 2)truncation). It now reportsInvalid binding "matrix:work:extra". Account id cannot contain ":". Use <channel>:<account>, for example telegram:default.and creates no binding. Valid specs are unchanged.Real environment tested: Node 22.22.1, macOS, no model / network / device. A
./node_modules/.bin/tsxharness imports the realparseBindingSpecs, registers amatrixchannel in the plugin registry, and calls it withmatrix:work:extra,matrix:work, andmatrix. BEFORE isorigin/main'sparseBindingSpecs(swapped in viagit show, no other change).Exact steps or command run after this patch:
# harness imports the real parseBindingSpecs + sets up the channel registry ./node_modules/.bin/tsx _bind-proof.mtsEvidence after fix:
Observed result after fix: the malformed 3-segment spec is rejected with an explicit error and produces no binding;
matrix:workandmatrixare byte-identical before and after. Single trailing colon (telegram:) still hits the pre-existing empty-account error (unchanged).What was not tested: a full
openclaw agents bindrun against a built CLI (the harness drives the realparseBindingSpecs, which is exactly what the command calls to turn--bindargs into route bindings). The new regression test inagents.bind.matrix.integration.test.tsfails onorigin/mainand passes after this change.Additional checks
node scripts/run-vitest.mjs src/commands/agents.bind.matrix.integration.test.ts— 3 pass with the fix; verified fail-before by swapping inorigin/main'sagents.bindings.ts→ the new test fails.oxfmt), static analysis (oxlint --tsconfig), andtsgo:corepass for the changed files. Diff: +9/−1 prod, +26 test.