Skip to content

Commit a512cd1

Browse files
ScreamingHawkclaude
andcommitted
fix: reject gate signer at any depth in gated subtree
Gated leaves may be nested trees, so check the gate identity against the full recursive signer set (WalletConfig.Signers) instead of only top-level terminal leaves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 18cf2c0 commit a512cd1

2 files changed

Lines changed: 42 additions & 5 deletions

File tree

‎intent_config.go‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,11 @@ func wrapGate(gateLeaf v3.WalletConfigTree, gateableLeaves ...v3.WalletConfigTre
204204
if gateWeight.Cmp(maxUint64) > 0 {
205205
return nil, fmt.Errorf("invalid gateLeafNode: weight is too large")
206206
}
207-
// A gated signer leaf sharing the gate's identity satisfies both sides of the outer
207+
// The gate's identity anywhere in the gated subtree satisfies both sides of the outer
208208
// threshold with one signature, letting the gate authorize alone
209-
for _, leaf := range gateableLeaves {
210-
if signer, _, err := signerLeaf(leaf); err == nil && signer == gateSigner {
211-
return nil, fmt.Errorf("invalid gateLeafNode: gate signer must not appear among gated leaves")
212-
}
209+
gatedSigners := (&v3.WalletConfig{Tree: v3.WalletConfigTreeNodes(gateableLeaves...)}).Signers()
210+
if _, ok := gatedSigners[gateSigner]; ok {
211+
return nil, fmt.Errorf("invalid gateLeafNode: gate signer must not appear among gated leaves")
213212
}
214213
gateableTree := &v3.WalletConfigTreeNestedLeaf{
215214
Weight: 1,

‎intent_config_test.go‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,44 @@ func TestCreateIntentConfigurationGateDuplicateSapientRejected(t *testing.T) {
573573
require.ErrorContains(t, err, "gate signer must not appear among gated leaves")
574574
})
575575

576+
t.Run("gate signer inside a nested leaf is rejected", func(t *testing.T) {
577+
nested := &v3.WalletConfigTreeNestedLeaf{
578+
Weight: 1,
579+
Threshold: 1,
580+
Tree: gateLeaf,
581+
}
582+
_, err := sequence.CreateIntentConfiguration(mainSigner, []*v3.CallsPayload{&payload}, 0,
583+
sequence.WithGate(gateLeaf), sequence.WithSapientSigner(nested))
584+
require.ErrorContains(t, err, "gate signer must not appear among gated leaves")
585+
})
586+
587+
t.Run("gate signer inside a node branch is rejected", func(t *testing.T) {
588+
otherLeaf := &v3.WalletConfigTreeAddressLeaf{
589+
Weight: 1,
590+
Address: common.HexToAddress("0x3333333333333333333333333333333333333333"),
591+
}
592+
branch := v3.WalletConfigTreeNodes(otherLeaf, gateLeaf)
593+
_, err := sequence.CreateIntentConfiguration(mainSigner, []*v3.CallsPayload{&payload}, 0,
594+
sequence.WithGate(gateLeaf), sequence.WithSapientSigner(branch))
595+
require.ErrorContains(t, err, "gate signer must not appear among gated leaves")
596+
})
597+
598+
t.Run("nested tree without the gate signer is allowed", func(t *testing.T) {
599+
nested := &v3.WalletConfigTreeNestedLeaf{
600+
Weight: 1,
601+
Threshold: 1,
602+
Tree: &v3.WalletConfigTreeSapientSignerLeaf{
603+
Weight: 1,
604+
Address: common.HexToAddress("0x4444444444444444444444444444444444444444"),
605+
ImageHash_: core.ImageHash{Hash: common.BigToHash(big.NewInt(4))},
606+
},
607+
}
608+
config, err := sequence.CreateIntentConfiguration(mainSigner, []*v3.CallsPayload{&payload}, 0,
609+
sequence.WithGate(gateLeaf), sequence.WithSapientSigner(nested))
610+
require.NoError(t, err)
611+
require.NotNil(t, config)
612+
})
613+
576614
t.Run("payload-matching leaf as gate is rejected", func(t *testing.T) {
577615
subdigestGate := &v3.WalletConfigTreeAnyAddressSubdigestLeaf{
578616
Digest: common.BigToHash(big.NewInt(3)),

0 commit comments

Comments
 (0)