Skip to content

Commit e30f06e

Browse files
authored
Merge pull request #221 from identity-com/bugfix/FYEO-CRY-01-extension
Security Audit Feedback - Bugfix
2 parents 5b6a063 + 7896119 commit e30f06e

17 files changed

Lines changed: 169 additions & 81 deletions

packages/client/core/src/api/unauthorizedCryptidClient.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CryptidClient, CryptidOptions } from "./cryptidClient";
22
import { Wallet } from "../types/crypto";
33
import { CryptidAccountDetails } from "../lib/CryptidAccountDetails";
4-
import { PublicKey, Transaction } from "@solana/web3.js";
4+
import { Transaction } from "@solana/web3.js";
55
import { ProposalResult, TransactionState } from "../types/cryptid";
66
import { AbstractCryptidClient } from "./abstractCryptidClient";
77
import { ControlledCryptidClient } from "./controlledCryptidClient";
@@ -42,22 +42,6 @@ export class UnauthorizedCryptidClient extends AbstractCryptidClient {
4242
);
4343
}
4444

45-
async extend(
46-
transactionAccountAddress: PublicKey,
47-
transaction: Transaction,
48-
state?: TransactionState
49-
): Promise<Transaction> {
50-
return this.service().then((service) =>
51-
service.extend(
52-
this.details,
53-
transactionAccountAddress,
54-
transaction,
55-
state,
56-
true
57-
)
58-
);
59-
}
60-
6145
unauthorized(): CryptidClient {
6246
return this;
6347
}

packages/client/core/src/lib/CryptidTransaction.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,15 +298,13 @@ export class CryptidTransaction {
298298
* @param program
299299
* @param transactionAccountAddress
300300
* @param state
301-
* @param allowUnauthorized
302301
*/
303302
// TODO move transactionAccountAddress into constructor?
304303
extend(
305304
program: Program<Cryptid>,
306305
transactionAccountAddress: PublicKey,
307306
// by default, extend the transaction and seal it at the same time
308-
state = TransactionState.Ready,
309-
allowUnauthorized = false
307+
state = TransactionState.Ready
310308
) {
311309
return (
312310
program.methods
@@ -316,7 +314,6 @@ export class CryptidTransaction {
316314
this.cryptidAccount.index,
317315
this.cryptidAccount.didAccountBump,
318316
TransactionState.toBorsh(state),
319-
allowUnauthorized,
320317
this.instructions,
321318
this.accountMetas.length
322319
)

packages/client/core/src/service/cryptid.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,7 @@ export class CryptidService {
314314
account: CryptidAccountDetails,
315315
transactionAccountAddress: PublicKey,
316316
transaction: Transaction,
317-
state?: TransactionState,
318-
allowUnauthorized = false
317+
state?: TransactionState
319318
): Promise<Transaction> {
320319
const cryptidTransaction = CryptidTransaction.fromSolanaInstructions(
321320
account,
@@ -327,8 +326,7 @@ export class CryptidService {
327326
let builder = cryptidTransaction.extend(
328327
this.program,
329328
transactionAccountAddress,
330-
state,
331-
allowUnauthorized
329+
state
332330
);
333331

334332
if (state === TransactionState.Ready) {

packages/client/idl/src/cryptid.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -298,10 +298,6 @@ export type Cryptid = {
298298
"defined": "TransactionState"
299299
}
300300
},
301-
{
302-
"name": "allowUnauthorized",
303-
"type": "bool"
304-
},
305301
{
306302
"name": "instructions",
307303
"type": {
@@ -853,6 +849,11 @@ export type Cryptid = {
853849
"code": 6016,
854850
"name": "InvalidMiddlewareAccount",
855851
"msg": "Approve Execution needs to be called with a valid Middleware Account."
852+
},
853+
{
854+
"code": 6017,
855+
"name": "AlreadyAuthorizedTransactionAccount",
856+
"msg": "Already authorized Transaction Account."
856857
}
857858
]
858859
};
@@ -1157,10 +1158,6 @@ export const IDL: Cryptid = {
11571158
"defined": "TransactionState"
11581159
}
11591160
},
1160-
{
1161-
"name": "allowUnauthorized",
1162-
"type": "bool"
1163-
},
11641161
{
11651162
"name": "instructions",
11661163
"type": {
@@ -1712,6 +1709,11 @@ export const IDL: Cryptid = {
17121709
"code": 6016,
17131710
"name": "InvalidMiddlewareAccount",
17141711
"msg": "Approve Execution needs to be called with a valid Middleware Account."
1712+
},
1713+
{
1714+
"code": 6017,
1715+
"name": "AlreadyAuthorizedTransactionAccount",
1716+
"msg": "Already authorized Transaction Account."
17151717
}
17161718
]
17171719
};

packages/tests/src/middleware/superuserCheckSigner.ts

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
import { Keypair, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
1+
import {
2+
Keypair,
3+
LAMPORTS_PER_SOL,
4+
PublicKey,
5+
SystemProgram,
6+
} from "@solana/web3.js";
27
import chai from "chai";
38
import chaiAsPromised from "chai-as-promised";
4-
import { makeTransfer } from "../util/cryptid";
9+
import { makeTransfer, toAccountMeta } from "../util/cryptid";
510
import { initializeDIDAccount } from "../util/did";
611
import { balanceOf, createTestContext, fund } from "../util/anchorUtils";
7-
import { DID_SOL_PREFIX } from "@identity.com/sol-did-client";
8-
import { Cryptid } from "@identity.com/cryptid";
12+
import { DID_SOL_PREFIX, DID_SOL_PROGRAM } from "@identity.com/sol-did-client";
13+
import { Cryptid, TransactionState } from "@identity.com/cryptid";
914
import {
1015
SuperuserCheckSignerMiddleware,
1116
deriveMiddlewareAccountAddress,
@@ -17,13 +22,16 @@ const { expect } = chai;
1722

1823
describe("Middleware: superuserCheckSigner", () => {
1924
const {
25+
program,
2026
keypair,
2127
provider,
2228
authority,
2329
middleware: { superuserCheckSigner: superuserCheckSignerMiddlewareProgram },
2430
} = createTestContext();
2531

2632
let cryptid: CryptidClient;
33+
let authorizedCryptid: CryptidClient;
34+
2735
const cryptidIndex = 1;
2836

2937
let middlewareAccount: PublicKey;
@@ -33,6 +41,31 @@ describe("Middleware: superuserCheckSigner", () => {
3341
const makeTransaction = (to = signer.publicKey) =>
3442
makeTransfer(cryptid.address(), to);
3543

44+
const execute = (transactionAccount: PublicKey) =>
45+
// execute the Cryptid transaction
46+
program.methods
47+
.executeTransaction(
48+
[], // no controller chain
49+
cryptid.details.bump,
50+
cryptid.details.index,
51+
cryptid.details.didAccountBump,
52+
0
53+
)
54+
.accounts({
55+
cryptidAccount: cryptid.address(),
56+
didProgram: DID_SOL_PROGRAM,
57+
did: cryptid.details.didAccount,
58+
authority: signer.publicKey,
59+
destination: signer.publicKey,
60+
transactionAccount,
61+
})
62+
.remainingAccounts([
63+
toAccountMeta(signer.publicKey, true, false),
64+
toAccountMeta(SystemProgram.programId),
65+
])
66+
.signers([signer])
67+
.rpc();
68+
3669
before("Fund the authority and signer", async () => {
3770
await Promise.all([
3871
fund(authority.publicKey, LAMPORTS_PER_SOL),
@@ -99,6 +132,22 @@ describe("Middleware: superuserCheckSigner", () => {
99132
}
100133
)
101134
).unauthorized();
135+
136+
authorizedCryptid = await Cryptid.buildFromDID(
137+
DID_SOL_PREFIX + ":" + authority.publicKey,
138+
authority,
139+
{
140+
connection: provider.connection,
141+
accountIndex: cryptidIndex,
142+
middlewares: [
143+
{
144+
programId: superuserCheckSignerMiddlewareProgram.programId,
145+
address: middlewareAccount,
146+
isSuperuser: true,
147+
},
148+
],
149+
}
150+
);
102151
};
103152

104153
before(
@@ -159,6 +208,37 @@ describe("Middleware: superuserCheckSigner", () => {
159208
);
160209
});
161210

211+
it("cannot execute with an unauthorized signer if the transaction was proposed by none (middleware error)", async () => {
212+
// propose the Cryptid transaction
213+
const { proposeTransaction, transactionAccount, proposeSigners } =
214+
await authorizedCryptid.propose(makeTransaction());
215+
// this will be signed by a DID authority
216+
await authorizedCryptid.send(proposeTransaction, proposeSigners);
217+
218+
// This should fail!
219+
const { transactions, signers } = await cryptid.execute(transactionAccount);
220+
221+
const shouldFail = cryptid.send(transactions[0], signers);
222+
223+
return expect(shouldFail).to.be.rejectedWith(
224+
"Error Code: AlreadyAuthorizedTransactionAccount"
225+
);
226+
});
227+
228+
it("cannot execute with an unauthorized signer if the transaction was proposed by none (execute error)", async () => {
229+
// propose the Cryptid transaction
230+
const { proposeTransaction, transactionAccount, proposeSigners } =
231+
await authorizedCryptid.propose(makeTransaction());
232+
// this will be signed by a DID authority
233+
await authorizedCryptid.send(proposeTransaction, proposeSigners);
234+
235+
// This should fail!
236+
// Manual execute skips the middleware execution. (which would fail)
237+
const shouldFail = execute(transactionAccount);
238+
239+
return expect(shouldFail).to.be.rejectedWith("Error Code: KeyMustBeSigner");
240+
});
241+
162242
it("blocks a transfer not signed by the signer", async () => {
163243
// change the signer
164244
signer = Keypair.generate();
@@ -177,4 +257,26 @@ describe("Middleware: superuserCheckSigner", () => {
177257

178258
return expect(shouldFail).to.be.rejectedWith("Error Code: InvalidSigner.");
179259
});
260+
261+
it("cannot extend with an unauthorized signer of the transaction was proposed by none", async () => {
262+
// propose the Cryptid transaction
263+
const { proposeTransaction, transactionAccount, proposeSigners } =
264+
await authorizedCryptid.propose(
265+
makeTransaction(),
266+
TransactionState.NotReady
267+
);
268+
// this will be signed by the non-did signer
269+
await authorizedCryptid.send(proposeTransaction, proposeSigners);
270+
271+
// This should fail!
272+
const extendTx = await cryptid.extend(
273+
transactionAccount,
274+
makeTransaction()
275+
);
276+
const shouldFail = cryptid.send(extendTx, []);
277+
278+
return expect(shouldFail).to.be.rejectedWith(
279+
"Error Code: KeyMustBeSigner."
280+
);
281+
});
180282
});

programs/cryptid/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,7 @@ pub enum CryptidError {
5858
/// An invalid Middleware Account was passed.
5959
#[msg("Approve Execution needs to be called with a valid Middleware Account.")]
6060
InvalidMiddlewareAccount,
61+
/// Already authorized Transaction Account.
62+
#[msg("Transaction Account is already authorized and cannot be authorized again.")]
63+
AlreadyAuthorizedTransactionAccount,
6164
}

programs/cryptid/src/instructions/approve_execution.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ pub struct ApproveExecution<'info> {
1515
}
1616

1717
/// Executes a transaction directly if all required keys sign
18-
pub fn approve_execution<'a, 'b, 'c, 'info>(
19-
ctx: Context<'a, 'b, 'c, 'info, ApproveExecution<'info>>,
18+
pub fn approve_execution<'info>(
19+
ctx: Context<'_, '_, '_, 'info, ApproveExecution<'info>>,
2020
) -> Result<()> {
2121
// Make sure owner is NOT the System Program
2222
// TODO(ticket): Consider implementing an approval registry on middleware

programs/cryptid/src/instructions/close_transaction.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ impl<'a, 'b, 'c, 'info> AllAccounts<'a, 'b, 'c, 'info>
7777
}
7878

7979
/// Close an existing transaction account
80-
pub fn close_transaction<'a, 'b, 'c, 'info>(
81-
ctx: Context<'a, 'b, 'c, 'info, CloseTransaction<'info>>,
80+
pub fn close_transaction<'info>(
81+
ctx: Context<'_, '_, '_, 'info, CloseTransaction<'info>>,
8282
controller_chain: Vec<DIDReference>,
8383
cryptid_account_bump: u8,
8484
cryptid_account_index: u32,

programs/cryptid/src/instructions/direct_execute.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ impl<'a, 'b, 'c, 'info> AllAccounts<'a, 'b, 'c, 'info>
6161
}
6262

6363
/// Executes a transaction directly if all required keys sign
64-
pub fn direct_execute<'a, 'b, 'c, 'info>(
65-
ctx: Context<'a, 'b, 'c, 'info, DirectExecute<'info>>,
64+
pub fn direct_execute<'info>(
65+
ctx: Context<'_, '_, '_, 'info, DirectExecute<'info>>,
6666
controller_chain: Vec<DIDReference>,
6767
instructions: Vec<AbbreviatedInstructionData>,
6868
cryptid_account_bump: u8,

programs/cryptid/src/instructions/execute_transaction.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ impl<'a, 'b, 'c, 'info> AllAccounts<'a, 'b, 'c, 'info>
8686
}
8787

8888
/// Executes a transaction directly if all required keys sign
89-
pub fn execute_transaction<'a, 'b, 'c, 'info>(
90-
ctx: Context<'a, 'b, 'c, 'info, ExecuteTransaction<'info>>,
89+
pub fn execute_transaction<'info>(
90+
ctx: Context<'_, '_, '_, 'info, ExecuteTransaction<'info>>,
9191
controller_chain: Vec<DIDReference>,
9292
cryptid_account_bump: u8,
9393
cryptid_account_index: u32,

0 commit comments

Comments
 (0)