Skip to content

Commit 65b91d4

Browse files
CoveMBericglau
andauthored
[Stellar] security contact as contract metadata (#679)
Co-authored-by: Eric Lau <[email protected]>
1 parent f1c7c6d commit 65b91d4

File tree

8 files changed

+117
-31
lines changed

8 files changed

+117
-31
lines changed

.changeset/silent-bags-repair.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@openzeppelin/wizard-stellar': patch
3+
---
4+
5+
Set security contact as contract metadata

packages/core/stellar/src/contract.test.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import test from 'ava';
33
import type { BaseFunction, BaseTraitImplBlock } from './contract';
44
import { ContractBuilder } from './contract';
55
import { printContract } from './print';
6+
import { setInfo } from './set-info';
67

78
test('contract basics', t => {
89
const Foo = new ContractBuilder('Foo');
@@ -89,15 +90,41 @@ test('contract with documentation', t => {
8990
t.snapshot(printContract(Foo));
9091
});
9192

92-
test('contract with security info', t => {
93+
test('contract with security contact metadata', t => {
9394
const Foo = new ContractBuilder('Foo');
94-
Foo.addSecurityTag('[email protected]');
95+
Foo.addContractMetadata({ key: 'contact', value: '[email protected]' });
9596
t.snapshot(printContract(Foo));
9697
});
9798

98-
test('contract with security info and documentation', t => {
99+
test('setting metadata with same key throws', t => {
99100
const Foo = new ContractBuilder('Foo');
100-
Foo.addSecurityTag('[email protected]');
101+
Foo.addContractMetadata({ key: 'contact', value: '[email protected]' });
102+
103+
t.throws(() => Foo.addContractMetadata({ key: 'contact', value: '[email protected]' }));
104+
});
105+
106+
test('contract with multiple metadata', t => {
107+
const Foo = new ContractBuilder('Foo');
108+
Foo.addContractMetadata([
109+
{ key: 'contact', value: '[email protected]' },
110+
{ key: 'meta', value: 'data' },
111+
]);
112+
t.snapshot(printContract(Foo));
113+
});
114+
115+
test('contract with multiple metadata and documentation', t => {
116+
const Foo = new ContractBuilder('Foo');
117+
Foo.addContractMetadata([
118+
{ key: 'contact', value: '[email protected]' },
119+
{ key: 'meta', value: 'data' },
120+
]);
101121
Foo.addDocumentation('Some documentation');
102122
t.snapshot(printContract(Foo));
103123
});
124+
125+
test('contract with setInfo', t => {
126+
const Foo = new ContractBuilder('Foo');
127+
setInfo(Foo, { securityContact: '[email protected]', license: 'MIT' });
128+
129+
t.snapshot(printContract(Foo));
130+
});

packages/core/stellar/src/contract.test.ts.md

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,36 +155,70 @@ Generated by [AVA](https://avajs.dev).
155155
pub struct Foo;␊
156156
`
157157

158-
## contract with security info
158+
## contract with security contact metadata
159159

160160
> Snapshot 1
161161
162162
`// SPDX-License-Identifier: MIT␊
163163
// Compatible with OpenZeppelin Stellar Soroban Contracts ^0.4.1␊
164+
#![no_std]␊
165+
166+
use soroban_sdk::contractmeta;␊
164167
165-
//! # Security␊
166-
//!␊
167-
//! For security issues, please contact: [email protected]
168+
contractmeta!(key="contact", val="[email protected]");␊
169+
170+
#[contract]␊
171+
pub struct Foo;␊
172+
`
173+
174+
## contract with multiple metadata
175+
176+
> Snapshot 1
177+
178+
`// SPDX-License-Identifier: MIT␊
179+
// Compatible with OpenZeppelin Stellar Soroban Contracts ^0.4.1␊
168180
#![no_std]␊
169181
182+
use soroban_sdk::contractmeta;␊
183+
184+
contractmeta!(key="contact", val="[email protected]");␊
185+
contractmeta!(key="meta", val="data");␊
186+
170187
#[contract]␊
171188
pub struct Foo;␊
172189
`
173190

174-
## contract with security info and documentation
191+
## contract with multiple metadata and documentation
175192

176193
> Snapshot 1
177194
178195
`// SPDX-License-Identifier: MIT␊
179196
// Compatible with OpenZeppelin Stellar Soroban Contracts ^0.4.1␊
180197
181198
//! Some documentation␊
199+
#![no_std]␊
200+
201+
use soroban_sdk::contractmeta;␊
182202
183-
//! # Security␊
184-
//!␊
185-
//! For security issues, please contact: [email protected]
203+
contractmeta!(key="contact", val="[email protected]");␊
204+
contractmeta!(key="meta", val="data");␊
205+
206+
#[contract]␊
207+
pub struct Foo;␊
208+
`
209+
210+
## contract with setInfo
211+
212+
> Snapshot 1
213+
214+
`// SPDX-License-Identifier: MIT␊
215+
// Compatible with OpenZeppelin Stellar Soroban Contracts ^0.4.1␊
186216
#![no_std]␊
187217
218+
use soroban_sdk::contractmeta;␊
219+
220+
contractmeta!(key="security_contact", val="[email protected]");␊
221+
188222
#[contract]␊
189223
pub struct Foo;␊
190224
`
74 Bytes
Binary file not shown.

packages/core/stellar/src/contract.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { toIdentifier } from './utils/convert-strings';
1+
import { toByteArray, toIdentifier } from './utils/convert-strings';
22

33
export interface Contract {
4+
metadata: Map<string, string>;
45
license: string;
5-
securityContact: string;
66
documentations: string[];
77
name: string;
88
useClauses: UseClause[];
@@ -74,7 +74,7 @@ export interface Argument {
7474
export class ContractBuilder implements Contract {
7575
readonly name: string;
7676
license = 'MIT';
77-
securityContact = '';
77+
metadata = new Map<string, string>();
7878
ownable = false;
7979

8080
readonly documentations: string[] = [];
@@ -267,7 +267,13 @@ export class ContractBuilder implements Contract {
267267
this.documentations.push(description);
268268
}
269269

270-
addSecurityTag(securityContact: string) {
271-
this.securityContact = securityContact;
270+
addContractMetadata(metadata: { key: string; value: string }[] | { key: string; value: string }) {
271+
(Array.isArray(metadata) ? metadata : [metadata]).forEach(({ key, value }) => {
272+
if (this.metadata.has(key)) throw new Error(`Setting metadata failed: ${key} is already set`);
273+
274+
this.metadata.set(key, toByteArray(value));
275+
});
276+
277+
if (this.metadata.size > 0) this.addUseClause('soroban_sdk', 'contractmeta');
272278
}
273279
}

packages/core/stellar/src/fungible.compile.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,24 @@ test.serial(
290290
{ snapshotResult: false },
291291
),
292292
);
293+
294+
test.serial(
295+
'compilation fungible upgradable mintable pausable with security contact metadata',
296+
runRustCompilationTest(
297+
buildFungible,
298+
{
299+
kind: 'Fungible',
300+
name: 'MyToken',
301+
symbol: 'MTK',
302+
premint: '2000',
303+
burnable: false,
304+
mintable: true,
305+
pausable: true,
306+
upgradeable: true,
307+
info: {
308+
securityContact: '[email protected]',
309+
},
310+
},
311+
{ snapshotResult: false },
312+
),
313+
);

packages/core/stellar/src/print.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Lines } from './utils/format-lines';
44
import { formatLines, spaceBetween } from './utils/format-lines';
55
import { getSelfArg } from './common-options';
66
import { compatibleContractsSemver } from './utils/version';
7+
import { toByteArray } from './utils/convert-strings';
78

89
const DEFAULT_SECTION = '1. with no section';
910
const STANDALONE_IMPORTS_GROUP = 'Standalone Imports';
@@ -24,11 +25,11 @@ export function printContract(contract: Contract): string {
2425
`// SPDX-License-Identifier: ${contract.license}`,
2526
`// Compatible with OpenZeppelin Stellar Soroban Contracts ${compatibleContractsSemver}`,
2627
...(contract.documentations.length ? ['', ...printDocumentations(contract.documentations)] : []),
27-
...(contract.securityContact ? ['', ...printSecurityTag(contract.securityContact)] : []),
2828
...createLevelAttributes,
2929
],
3030
spaceBetween(
3131
printUseClauses(contract),
32+
printMetadata(contract),
3233
printVariables(contract),
3334
printContractStruct(contract),
3435
printContractErrors(contract),
@@ -367,6 +368,6 @@ function printDocumentations(documentations: string[]): string[] {
367368
return documentations.map(documentation => `//! ${documentation}`);
368369
}
369370

370-
function printSecurityTag(securityContact: string) {
371-
return ['//! # Security', '//!', `//! For security issues, please contact: ${securityContact}`];
371+
function printMetadata(contract: Contract) {
372+
return Array.from(contract.metadata.entries()).map(([key, value]) => `contractmeta!(key="${key}", val="${value}");`);
372373
}
Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import type { ContractBuilder } from './contract';
22

3-
export const TAG_SECURITY_CONTACT = `@custom:security-contact`;
4-
53
export const infoOptions = [{}, { license: 'WTFPL' }] as const;
64

75
export const defaults: Info = { license: 'MIT' };
@@ -11,14 +9,8 @@ export type Info = {
119
securityContact?: string;
1210
};
1311

14-
export function setInfo(c: ContractBuilder, info: Info): void {
15-
const { securityContact, license } = info;
16-
17-
if (securityContact) {
18-
c.addSecurityTag(securityContact);
19-
}
12+
export function setInfo(c: ContractBuilder, { securityContact, license }: Info): void {
13+
if (securityContact) c.addContractMetadata({ key: 'security_contact', value: securityContact });
2014

21-
if (license) {
22-
c.license = license;
23-
}
15+
if (license) c.license = license;
2416
}

0 commit comments

Comments
 (0)