Commit 69ea799
* fix(cli): #15 part 3 (fix-15b) — agentkeys scope command
Closes part 3 of #15.
Adds `agentkeys scope <AGENT>` subcommand with `--add`/`--remove`/`--set`/`--list`
for editing a child agent's scope. Under the hood: new trait methods
`CredentialBackend::get_scope` + `update_scope` backed by a new mock-server
`PUT /session/scope` endpoint (owner-checked, updates all active sessions
for the target wallet).
Tests: 5 new CLI integration tests (list/add/remove/set/conflict). 62 total
tests across cli/core/mock-server, all green under --test-threads=1.
Minor: CommandContext::load_session made pub so integration tests can
access the current session token.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli/mock-server): #15b v2 — address codex P1+P2
Codex P1: update_scope handler allowed a master session to target its
own wallet, silently writing a restrictive scope_json onto the master's
session row. Subsequent credential/read calls would start failing for
services outside the accidental scope. Now rejects self-targeting with
a clear bad_request.
Codex P2 (URL encoding): cmd_scope's identity resolution built the
/identity/resolve query string via raw interpolation, breaking
aliases/emails with reserved characters ('+', '&', '=', '%', spaces).
Switched to reqwest's .query() builder which percent-encodes per RFC.
Codex P3 (--list exclusivity): --list now rejects combination with
--add/--remove/--set up front instead of silently dropping the mutation.
Deferred (tracked as follow-up): CBOR vs JSON parsing of ScopeChange
request_details in mint_scope_change_session — the scope CLI uses the
direct /session/scope endpoint, not the auth-request flow, so this
finding doesn't affect the CLI path. Will be addressed when
AuthRequest::ScopeChange approval is wired end-to-end.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli/mock-server): #29 v3 — claude P1+P2 review fixes after rebase
Post-rebase review (claude code-reviewer) flagged 3 P1 issues that map
directly to patterns in `.github/REVIEW_GUIDELINES.md`. All fixed in
this commit plus one P2 nit that was cheap.
**P1 — URL encoding via reqwest `.query()` on scope + list_credentials**
`get_scope` and `list_credentials` in `mock_client.rs` built queries with
raw `format!()` interpolation. Wallet strings with reserved characters
(`&`, `#`, `%`, `+`, spaces) would break the request or smuggle extra
params server-side. Switched both to `.get(self.url("/path")).query(&[...])`
so reqwest percent-encodes per RFC 3986. Same pattern as the fix on PR
#20 `aa0cc95` / PR #22 `ffdc908` / PR #29 `7aa9e70`.
**P1 — Case-insensitive self-target check in `update_scope`**
The self-target reject `session.wallet_address == target_wallet` was
string-equal, but the backend stores wallets in lowercase while EIP-55
checksummed input preserves case. A master passing its own wallet in
mixed-case form would bypass the self-target guard and silently
restrict its own scope (the exact failure the guard was designed to
prevent). Switched to `eq_ignore_ascii_case` matching the pattern
established in PR #18's self-revoke detection.
**P1 — Missing DENIED audit rows on scope endpoints**
`update_scope` and `get_session_scope` returned 403 on ownership
failure without inserting an audit row, breaking the
cross-agent-probing-visibility invariant established for
`list_credentials` / `read_credential` in PR #19. Added `'scope_update'
'DENIED'` and `'scope_read' 'DENIED'` inserts before the 403 return
in both handlers.
**P2 — `--add foo --remove foo` would no-op silently**
`cmd_scope` accepted combinations like `--add X --remove X`. The add
loop would push, then `retain(!remove.contains)` would cancel, and the
backend PUT would still fire with the original scope + misleading
"Scope updated" output. Added an up-front `add ∩ remove` check that
lists overlapping services and rejects.
**P2 — Narrow UPDATE to most-recent active session**
Write-side `update_scope` UPDATEd ALL active sessions for the target
wallet; read-side `get_session_scope` always returned the scope from
the most-recent session via `ORDER BY created_at DESC LIMIT 1`.
Read/write contract mismatched on wallets with multiple active
sessions (paired + recovered). Narrowed the UPDATE to use the same
ORDER/LIMIT subquery.
Test: cargo test -p agentkeys-core -p agentkeys-cli -p agentkeys-mock-server -- --test-threads=1
core: 22 passed; cli: 35 passed; mock-server: 56 passed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli/mock-server): #29 v4 — claude re-review nits (dead sort, pct_encode consistency, test coverage)
Follow-up on the claude-code-action re-review posted against commit
57dc9fb. All 3 substantive findings addressed; the 4th (malformed `/`
doc-comments) was a false positive from pre-rebase state — grep for
`^\s*/ ` returns nothing in the current tree.
**Low — Redundant `sort_by` after `update_scope`**
`lib.rs:769` was re-sorting `new_scope.services` after the backend
call returned, but both the `--set` branch (line 749) and the
`--add`/`--remove` branch (line 760) sort before the `update_scope`
call. Dead op; removed. Added a comment so a future reader doesn't
re-add it.
**Low — `InProcessBackend::get_scope` raw URL concat**
`test_client.rs:725` built the query string via `format!()` while the
`resolve_identity` method right above uses `pct_encode`. Wallet
strings are hex so this was safe in practice, but the inconsistency
violates the URL-encoding invariant documented in
`.github/REVIEW_GUIDELINES.md` pattern #3. Swapped to `pct_encode`.
**Low — Missing test for `--list + --add` conflict**
Added `cmd_scope_list_and_add_conflict_errors`. Also added
`cmd_scope_add_remove_overlap_errors` (covering the P2 overlap guard
from v3) that wasn't yet under test.
Test: cargo test -p agentkeys-core -p agentkeys-cli -p agentkeys-mock-server -- --test-threads=1
core: 22 passed; cli: 37 passed (+2 new); mock-server: 56 passed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0b68f94 commit 69ea799
11 files changed
Lines changed: 888 additions & 13 deletions
File tree
- crates
- agentkeys-cli
- src
- tests
- agentkeys-core/src
- agentkeys-mock-server
- src
- handlers
- tests
- docs
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
7 | 9 | | |
8 | 10 | | |
9 | 11 | | |
| |||
68 | 70 | | |
69 | 71 | | |
70 | 72 | | |
71 | | - | |
| 73 | + | |
72 | 74 | | |
73 | 75 | | |
74 | 76 | | |
| |||
629 | 631 | | |
630 | 632 | | |
631 | 633 | | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
632 | 780 | | |
633 | 781 | | |
634 | 782 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
139 | 139 | | |
140 | 140 | | |
141 | 141 | | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
142 | 159 | | |
143 | 160 | | |
144 | 161 | | |
| |||
168 | 185 | | |
169 | 186 | | |
170 | 187 | | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
171 | 191 | | |
172 | 192 | | |
173 | 193 | | |
| |||
0 commit comments