Skip to content

Commit 6e49261

Browse files
committedJan 27, 2025
Add security audits to wallet data.
This also involved sprucing up the way entities are handled so that security auditors could be represented as well. So revamped the Entity data structure to make it usable by things other than the data-collection-related features, and added icons.
1 parent 36e4ad7 commit 6e49261

34 files changed

+799
-57
lines changed
 

‎.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"autoswitch",
77
"brandable",
88
"Buterin",
9+
"bypassable",
910
"Chainalysis",
1011
"checkmark",
1112
"codesandbox",
@@ -41,11 +42,13 @@
4142
"Protip",
4243
"rabby",
4344
"Renderable",
45+
"slowmist",
4446
"subfeature",
4547
"Timelocks",
4648
"Uncorrelatable",
4749
"UNPROXIED",
4850
"Venmo",
51+
"Veridise",
4952
"Viem",
5053
"Vitalik",
5154
"walletbeat",

‎public/images/entities/binance.svg

+57
Loading

‎public/images/entities/cure53.png

5.5 KB
Loading

‎public/images/entities/daimo.svg

+1
Loading

‎public/images/entities/debank.svg

+14
Loading

‎public/images/entities/honeycomb.svg

+29
Loading
34.6 KB
Loading

‎public/images/entities/pimlico.png

723 Bytes
Loading

‎public/images/entities/slowmist.png

4.08 KB
Loading

‎public/images/entities/veridise.svg

+16
Loading

‎src/beta/components/ui/molecules/attributes/privacy/AddressCorrelationDetails.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ExternalLink } from '../../../atoms/ExternalLink';
1313
import { ReferenceLinks } from '../../../atoms/ReferenceLinks';
1414
import { subsectionWeight } from '@/beta/components/constants';
1515
import { WrapRatingIcon } from '../../../atoms/WrapRatingIcon';
16+
import { isUrl } from '@/beta/schema/url';
1617

1718
export function AddressCorrelationDetails({
1819
wallet,
@@ -69,7 +70,7 @@ export function AddressCorrelationDetails({
6970
return;
7071
}
7172
let entityName: React.ReactNode | null = null;
72-
if (entity.legalName === null) {
73+
if (entity.legalName === 'NOT_A_LEGAL_ENTITY') {
7374
entityName = <strong>{entity.name}</strong>;
7475
} else if (entity.legalName.soundsDifferent) {
7576
entityName = (
@@ -80,14 +81,14 @@ export function AddressCorrelationDetails({
8081
} else {
8182
entityName = <strong>{entity.legalName.name}</strong>;
8283
}
83-
if (entity.url !== null) {
84+
if (isUrl(entity.url)) {
8485
entityName = <ExternalLink url={entity.url}>{entityName}</ExternalLink>;
8586
}
8687
leaksList.push(
8788
<li key={sourceName}>
8889
<Typography>
8990
{entityName}{' '}
90-
{entity.privacyPolicy !== null ? (
91+
{isUrl(entity.privacyPolicy) ? (
9192
<>
9293
{' ('}
9394
<ExternalLink url={entity.privacyPolicy} defaultLabel="Privacy policy" />

‎src/beta/data/entities/binance.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1-
import type { Entity } from '@/beta/schema/features/privacy/data-collection';
1+
import type { CorporateEntity, Exchange } from '@/beta/schema/entity';
22

3-
export const binance: Entity = {
3+
export const binance: CorporateEntity & Exchange = {
4+
id: 'binance',
45
name: 'Binance',
56
legalName: { name: 'Binance Holdings Ltd', soundsDifferent: false },
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: false,
11+
exchange: true,
12+
offchainDataProvider: false,
13+
securityAuditor: false,
14+
transactionBroadcastProvider: false,
15+
walletDeveloper: false,
16+
},
17+
icon: {
18+
extension: 'svg',
19+
},
620
jurisdiction: 'Malta',
721
url: 'https://binance.com/',
822
privacyPolicy: 'https://www.binance.com/en/about-legal/privacy-portal',

‎src/beta/data/entities/cure53.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { CorporateEntity, SecurityAuditor } from '@/beta/schema/entity';
2+
3+
export const cure53: CorporateEntity & SecurityAuditor = {
4+
id: 'cure53',
5+
name: 'Cure53',
6+
legalName: { name: 'Cure53', soundsDifferent: false },
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: false,
11+
exchange: false,
12+
offchainDataProvider: false,
13+
securityAuditor: true,
14+
transactionBroadcastProvider: false,
15+
walletDeveloper: false,
16+
},
17+
icon: {
18+
extension: 'png',
19+
width: 137,
20+
height: 136,
21+
},
22+
jurisdiction: 'Berlin, Germany',
23+
url: 'https://cure53.de/',
24+
privacyPolicy: 'https://cure53.de/datenschutz.php',
25+
crunchbase: 'https://www.crunchbase.com/organization/cure53',
26+
};

‎src/beta/data/entities/daimo.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1-
import type { Entity } from '@/beta/schema/features/privacy/data-collection';
1+
import type { CorporateEntity, WalletDeveloper } from '@/beta/schema/entity';
22

3-
export const daimoInc: Entity = {
3+
export const daimoInc: CorporateEntity & WalletDeveloper = {
4+
id: 'daimo',
45
name: 'Daimo',
56
legalName: { name: 'Daimo, Inc', soundsDifferent: false },
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: false,
11+
exchange: false,
12+
offchainDataProvider: false,
13+
securityAuditor: false,
14+
transactionBroadcastProvider: false,
15+
walletDeveloper: true,
16+
},
17+
icon: {
18+
extension: 'svg',
19+
},
620
jurisdiction: 'United States',
721
url: 'https://daimo.com/',
822
privacyPolicy: 'https://daimo.com/privacy',

‎src/beta/data/entities/debank.ts

+24-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
1-
import type { Entity } from '@/beta/schema/features/privacy/data-collection';
1+
import type {
2+
ChainDataProvider,
3+
CorporateEntity,
4+
TransactionBroadcastProvider,
5+
WalletDeveloper,
6+
} from '@/beta/schema/entity';
27

3-
export const deBank: Entity = {
8+
export const deBank: ChainDataProvider &
9+
CorporateEntity &
10+
TransactionBroadcastProvider &
11+
WalletDeveloper = {
12+
id: 'debank',
413
name: 'DeBank',
514
legalName: { name: 'DeBank Global PTE Ltd', soundsDifferent: false },
15+
type: {
16+
chainDataProvider: true,
17+
corporate: true,
18+
dataBroker: false,
19+
exchange: false,
20+
offchainDataProvider: false,
21+
securityAuditor: false,
22+
transactionBroadcastProvider: true,
23+
walletDeveloper: true,
24+
},
25+
icon: {
26+
extension: 'svg',
27+
},
628
jurisdiction: 'Singapore',
729
url: 'https://debank.com/',
830
privacyPolicy: 'https://rabby.io/docs/privacy/',

‎src/beta/data/entities/example.ts

+36-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,50 @@
1-
import type { Entity } from '@/beta/schema/features/privacy/data-collection';
1+
import type {
2+
ChainDataProvider,
3+
CorporateEntity,
4+
Exchange,
5+
TransactionBroadcastProvider,
6+
} from '@/beta/schema/entity';
27

3-
export const exampleNodeCompany: Entity = {
8+
export const exampleNodeCompany: CorporateEntity &
9+
ChainDataProvider &
10+
TransactionBroadcastProvider = {
11+
id: 'exampleNodeCompany',
412
name: 'Example RPC Company',
513
legalName: { name: 'Example RPC Corp', soundsDifferent: false },
14+
type: {
15+
chainDataProvider: true,
16+
corporate: true,
17+
dataBroker: false,
18+
exchange: false,
19+
offchainDataProvider: false,
20+
securityAuditor: false,
21+
transactionBroadcastProvider: true,
22+
walletDeveloper: false,
23+
},
24+
icon: 'NO_ICON',
625
jurisdiction: 'Atlantis',
726
url: 'https://example.com/',
827
privacyPolicy: 'https://example.com/privacy',
9-
crunchbase: null,
28+
crunchbase: { type: 'NO_CRUNCHBASE_URL' },
1029
};
1130

12-
export const exampleCex: Entity = {
31+
export const exampleCex: CorporateEntity & Exchange = {
32+
id: 'exampleCex',
1333
name: 'Example Centralized Exchange',
1434
legalName: { name: 'Example Centralized Exchange Corp', soundsDifferent: false },
35+
type: {
36+
chainDataProvider: false,
37+
corporate: true,
38+
dataBroker: false,
39+
exchange: true,
40+
offchainDataProvider: false,
41+
securityAuditor: false,
42+
transactionBroadcastProvider: false,
43+
walletDeveloper: false,
44+
},
45+
icon: 'NO_ICON',
1546
jurisdiction: 'Atlantis',
1647
url: 'https://example.com/',
1748
privacyPolicy: 'https://example.com/privacy',
18-
crunchbase: null,
49+
crunchbase: { type: 'NO_CRUNCHBASE_URL' },
1950
};

‎src/beta/data/entities/honeycomb.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1-
import type { Entity } from '@/beta/schema/features/privacy/data-collection';
1+
import type { CorporateEntity, DataBroker } from '@/beta/schema/entity';
22

3-
export const honeycomb: Entity = {
3+
export const honeycomb: CorporateEntity & DataBroker = {
4+
id: 'honeycomb',
45
name: 'Honeycomb',
56
legalName: { name: 'Hound Technology, Inc', soundsDifferent: true },
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: true,
11+
exchange: false,
12+
offchainDataProvider: false,
13+
securityAuditor: false,
14+
transactionBroadcastProvider: false,
15+
walletDeveloper: false,
16+
},
17+
icon: {
18+
extension: 'svg',
19+
},
620
jurisdiction: 'San Francisco, California, United States',
721
url: 'https://www.honeycomb.io/',
822
privacyPolicy: 'https://www.honeycomb.io/privacy',
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { CorporateEntity, SecurityAuditor } from '@/beta/schema/entity';
2+
3+
export const leastAuthority: CorporateEntity & SecurityAuditor = {
4+
id: 'least-authority',
5+
name: 'Least Authority',
6+
legalName: { name: 'Least Authority', soundsDifferent: false },
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: false,
11+
exchange: false,
12+
offchainDataProvider: false,
13+
securityAuditor: true,
14+
transactionBroadcastProvider: false,
15+
walletDeveloper: false,
16+
},
17+
icon: {
18+
extension: 'png',
19+
width: 1246,
20+
height: 1043,
21+
},
22+
jurisdiction: 'Berlin, Germany',
23+
url: 'https://leastauthority.com/',
24+
privacyPolicy: 'https://leastauthority.com/privacy-policy/',
25+
crunchbase: 'https://www.crunchbase.com/organization/least-authority-enterprises',
26+
};
+15-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1-
import type { Entity } from '@/beta/schema/features/privacy/data-collection';
1+
import type { CorporateEntity } from '@/beta/schema/entity';
22

3-
export const merkleManufactory: Entity = {
3+
export const merkleManufactory: CorporateEntity = {
4+
id: 'merkle-manufactory',
45
name: 'Merkle Manufactory',
56
legalName: { name: 'Merkle Manufactory Inc', soundsDifferent: false },
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: false,
11+
exchange: false,
12+
offchainDataProvider: false,
13+
securityAuditor: false,
14+
transactionBroadcastProvider: false,
15+
walletDeveloper: false,
16+
},
17+
icon: 'NO_ICON',
618
jurisdiction: 'Los Angeles, California, United States',
719
url: 'https://merklemanufactory.com/',
8-
privacyPolicy: null,
20+
privacyPolicy: { type: 'NO_PRIVACY_POLICY' },
921
crunchbase: 'https://www.crunchbase.com/organization/merkle-manufactory',
1022
};

‎src/beta/data/entities/open-exchange-rates.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1-
import type { Entity } from '@/beta/schema/features/privacy/data-collection';
1+
import type { CorporateEntity, OffchainDataProvider } from '@/beta/schema/entity';
22

3-
export const openExchangeRates: Entity = {
3+
export const openExchangeRates: CorporateEntity & OffchainDataProvider = {
4+
id: 'open-exchange-rates',
45
name: 'Open Exchange Rates',
56
legalName: { name: 'Open Exchange Rates Ltd', soundsDifferent: false },
6-
jurisdiction: null, // Unclear
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: false,
11+
exchange: false,
12+
offchainDataProvider: true,
13+
securityAuditor: false,
14+
transactionBroadcastProvider: false,
15+
walletDeveloper: false,
16+
},
17+
icon: 'NO_ICON',
18+
jurisdiction: { type: 'UNKNOWN' }, // Unclear
719
url: 'https://openexchangerates.org/',
820
privacyPolicy: 'https://openexchangerates.org/privacy',
921
crunchbase: 'https://www.crunchbase.com/organization/open-exchange-rates',

‎src/beta/data/entities/pimlico.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
1-
import type { Entity } from '@/beta/schema/features/privacy/data-collection';
1+
import type { CorporateEntity, TransactionBroadcastProvider } from '@/beta/schema/entity';
22

3-
export const pimlico: Entity = {
3+
export const pimlico: CorporateEntity & TransactionBroadcastProvider = {
4+
id: 'pimlico',
45
name: 'Pimlico',
56
legalName: { name: 'Austerlitz Labs Ltd', soundsDifferent: true },
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: false,
11+
exchange: false,
12+
offchainDataProvider: false,
13+
securityAuditor: false,
14+
transactionBroadcastProvider: true,
15+
walletDeveloper: false,
16+
},
17+
icon: {
18+
extension: 'png',
19+
width: 200,
20+
height: 200,
21+
},
622
jurisdiction: 'London, England, United Kingdom',
723
url: 'https://www.pimlico.io/',
824
privacyPolicy: 'https://www.pimlico.io/privacy',

‎src/beta/data/entities/slowmist.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { CorporateEntity, SecurityAuditor } from '@/beta/schema/entity';
2+
3+
export const slowMist: CorporateEntity & SecurityAuditor = {
4+
id: 'slowmist',
5+
name: 'SlowMist',
6+
legalName: { name: 'SlowMist Ltd', soundsDifferent: false },
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: false,
11+
exchange: false,
12+
offchainDataProvider: false,
13+
securityAuditor: true,
14+
transactionBroadcastProvider: false,
15+
walletDeveloper: false,
16+
},
17+
icon: {
18+
extension: 'png',
19+
width: 375,
20+
height: 375,
21+
},
22+
jurisdiction: 'China',
23+
url: 'https://www.slowmist.com/',
24+
privacyPolicy: { type: 'NO_PRIVACY_POLICY' },
25+
crunchbase: 'https://www.crunchbase.com/organization/slowmist',
26+
};

‎src/beta/data/entities/veridise.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { CorporateEntity, SecurityAuditor } from '@/beta/schema/entity';
2+
3+
export const veridise: CorporateEntity & SecurityAuditor = {
4+
id: 'veridise',
5+
name: 'Veridise',
6+
legalName: { name: 'Veridise Inc', soundsDifferent: false },
7+
type: {
8+
chainDataProvider: false,
9+
corporate: true,
10+
dataBroker: false,
11+
exchange: false,
12+
offchainDataProvider: false,
13+
securityAuditor: true,
14+
transactionBroadcastProvider: false,
15+
walletDeveloper: false,
16+
},
17+
icon: {
18+
extension: 'svg',
19+
},
20+
jurisdiction: 'Austin, Texas, United States',
21+
url: 'https://veridise.com/',
22+
privacyPolicy: 'https://veridise.com/privacy-policy/',
23+
crunchbase: 'https://www.crunchbase.com/organization/veridise',
24+
};

‎src/beta/data/wallets/daimo.ts

+18
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { pimlico } from '../entities/pimlico';
1111
import { honeycomb } from '../entities/honeycomb';
1212
import { WalletProfile } from '@/beta/schema/features/profile';
1313
import { RpcEndpointConfiguration } from '@/beta/schema/features/chain-configurability';
14+
import { veridise } from '../entities/veridise';
1415

1516
export const daimo: Wallet = {
1617
metadata: {
@@ -60,6 +61,23 @@ export const daimo: Wallet = {
6061
browser: 'NOT_A_BROWSER_WALLET',
6162
},
6263
security: {
64+
// Note: Daimo has had their p256 verifier contract audited:
65+
// https://github.com/daimo-eth/p256-verifier/blob/master/audits/2023-10-veridise.pdf
66+
// However while the wallet relies on it, it is not the wallet software itself,
67+
// so it is not a full-stack audit of the wallet software.
68+
publicSecurityAudits: [
69+
{
70+
auditor: veridise,
71+
auditDate: '2023-10-06',
72+
ref: 'https://github.com/daimo-eth/daimo/blob/master/audits/2023-10-veridise-daimo.pdf',
73+
variantsScope: { mobile: true },
74+
codeSnapshot: {
75+
date: '2023-09-12',
76+
commit: 'f0dc56d68852c1488461e88a506ff7b0f027f245',
77+
},
78+
unpatchedFlaws: 'ALL_FIXED',
79+
},
80+
],
6381
lightClient: {
6482
ethereumL1: false,
6583
},

‎src/beta/data/wallets/rabby.ts

+160
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import type { Wallet } from '@/beta/schema/wallet';
66
import { License } from '@/beta/schema/features/license';
77
import { WalletProfile } from '@/beta/schema/features/profile';
88
import { RpcEndpointConfiguration } from '@/beta/schema/features/chain-configurability';
9+
import { leastAuthority } from '../entities/least-authority';
10+
import { slowMist } from '../entities/slowmist';
11+
import { SecurityFlawSeverity } from '@/beta/schema/features/security/security-audits';
12+
import { cure53 } from '../entities/cure53';
913

1014
export const rabby: Wallet = {
1115
metadata: {
@@ -60,6 +64,162 @@ export const rabby: Wallet = {
6064
},
6165
},
6266
security: {
67+
publicSecurityAudits: [
68+
{
69+
auditor: slowMist,
70+
auditDate: '2021-06-18',
71+
ref: 'https://github.com/RabbyHub/Rabby/blob/master/docs/Rabby%20chrome%20extension%20Penetration%20Testing%20Report.pdf',
72+
variantsScope: { browser: true },
73+
codeSnapshot: {
74+
date: '2021-06-23',
75+
},
76+
unpatchedFlaws: 'ALL_FIXED',
77+
},
78+
{
79+
auditor: slowMist,
80+
auditDate: '2022-03-18',
81+
ref: 'https://github.com/RabbyHub/Rabby/blob/master/docs/SlowMist%20Audit%20Report%20-%20Rabby%20browser%20extension%20wallet-2022.03.18.pdf',
82+
variantsScope: { browser: true },
83+
codeSnapshot: {
84+
date: '2022-01-26',
85+
commit: 'f6d19bd70664a7214677918e298619d583f9c3f1',
86+
tag: 'v0.21.1',
87+
},
88+
unpatchedFlaws: 'ALL_FIXED',
89+
},
90+
{
91+
auditor: slowMist,
92+
auditDate: '2023-07-20',
93+
ref: 'https://github.com/RabbyHub/Rabby/blob/master/docs/SlowMist%20Audit%20Report%20-%20Rabby%20Wallet-2023.07.20.pdf',
94+
variantsScope: { browser: true },
95+
codeSnapshot: {
96+
date: '2023-06-19',
97+
commit: 'f6221693b877b3c4eb1c7ac61146137eb1908997',
98+
tag: 'v0.91.0',
99+
},
100+
unpatchedFlaws: 'ALL_FIXED',
101+
},
102+
{
103+
auditor: slowMist,
104+
auditDate: '2023-09-26',
105+
ref: 'https://github.com/RabbyHub/RabbyDesktop/blob/publish/prod/docs/SlowMist%20Audit%20Report%20-%20Rabby%20Wallet%20Desktop.pdf',
106+
variantsScope: { desktop: true },
107+
codeSnapshot: {
108+
date: '2023-09-01',
109+
commit: '586447a46bcd0abab6356076e369357050c97796',
110+
tag: 'v0.33.0-prod',
111+
},
112+
unpatchedFlaws: 'ALL_FIXED',
113+
},
114+
{
115+
auditor: leastAuthority,
116+
auditDate: '2024-10-18',
117+
ref: 'https://github.com/RabbyHub/rabby-mobile/blob/develop/docs/Least%20Authority%20-%20Debank%20Rabby%20Walle%20Audit%20Report.pdf',
118+
variantsScope: { mobile: true },
119+
codeSnapshot: {
120+
date: '2024-09-08',
121+
commit: 'a8dea5d8c530cb1acf9104a7854089256c36d85a',
122+
},
123+
unpatchedFlaws: [
124+
{
125+
name: 'Issue B: Insecure Key Derivation Function',
126+
severityAtAuditPublication: SecurityFlawSeverity.NOT_CATEGORIZED,
127+
presentStatus: 'NOT_FIXED',
128+
},
129+
{
130+
name: 'Issue C: Weak Encryption Method Used',
131+
severityAtAuditPublication: SecurityFlawSeverity.NOT_CATEGORIZED,
132+
presentStatus: 'NOT_FIXED',
133+
},
134+
{
135+
name: 'Issue D: Weak PBKDF2 Parameters Used',
136+
severityAtAuditPublication: SecurityFlawSeverity.NOT_CATEGORIZED,
137+
presentStatus: 'NOT_FIXED',
138+
},
139+
],
140+
},
141+
{
142+
auditor: cure53,
143+
auditDate: '2024-10-22',
144+
ref: 'https://github.com/RabbyHub/rabby-mobile/blob/develop/docs/Cure53%20-%20Debank%20Rabby%20Wallet%20Audit%20Report.pdf',
145+
variantsScope: { mobile: true },
146+
codeSnapshot: {
147+
date: '2024-09-08',
148+
commit: 'a8dea5d8c530cb1acf9104a7854089256c36d85a',
149+
},
150+
unpatchedFlaws: [
151+
{
152+
name: 'RBY-01-001 WP1-WP2: Mnemonic recoverable via process dump',
153+
severityAtAuditPublication: SecurityFlawSeverity.HIGH,
154+
presentStatus: 'NOT_FIXED',
155+
},
156+
{
157+
name: 'RBY-01-002 WP1-WP2: Password recoverable via process dump',
158+
severityAtAuditPublication: SecurityFlawSeverity.HIGH,
159+
presentStatus: 'NOT_FIXED',
160+
},
161+
{
162+
name: 'RBY-01-012 WP1-WP2: RabbitCode secret recoverable from installer files',
163+
severityAtAuditPublication: SecurityFlawSeverity.HIGH,
164+
presentStatus: 'NOT_FIXED',
165+
},
166+
{
167+
name: 'RBY-01-014 WP1-WP2: Backup password prompt bypassable',
168+
severityAtAuditPublication: SecurityFlawSeverity.MEDIUM,
169+
presentStatus: 'NOT_FIXED',
170+
},
171+
{
172+
name: 'RBY-01-003 WP1-WP2: Lack of rate limiting for password unlock',
173+
severityAtAuditPublication: SecurityFlawSeverity.MEDIUM,
174+
presentStatus: 'NOT_FIXED',
175+
},
176+
],
177+
},
178+
{
179+
auditor: slowMist,
180+
auditDate: '2024-10-23',
181+
variantsScope: { mobile: true },
182+
ref: 'https://github.com/RabbyHub/rabby-mobile/blob/develop/docs/SlowMist%20Audit%20Report%20-%20Rabby%20mobile%20wallet%20iOS.pdf',
183+
codeSnapshot: {
184+
date: '2024-06-17',
185+
commit: 'a424dbe54bba464da7585769140f6b7136c9108b',
186+
},
187+
unpatchedFlaws: 'ALL_FIXED',
188+
},
189+
{
190+
auditor: leastAuthority,
191+
auditDate: '2024-12-12',
192+
ref: 'https://github.com/RabbyHub/Rabby/blob/develop/docs/Least%20Authority%20-%20DeBank%20Rabby%20Wallet%20Extension%20Final%20Audit%20Report-20241212.pdf',
193+
variantsScope: { browser: true },
194+
codeSnapshot: {
195+
date: '2024-10-14',
196+
commit: 'eb5da18727b38a3fd693af8b74f6f151f2fd361c',
197+
},
198+
unpatchedFlaws: [
199+
{
200+
name: 'Issue B: Setting the Cache Before It Has Been Initialized Will Cause an Exception',
201+
severityAtAuditPublication: SecurityFlawSeverity.NOT_CATEGORIZED,
202+
presentStatus: 'NOT_FIXED',
203+
},
204+
{
205+
name: 'Issue C: persistStore Module Can Become Out of Sync With Browser Local Storage',
206+
severityAtAuditPublication: SecurityFlawSeverity.NOT_CATEGORIZED,
207+
presentStatus: 'NOT_FIXED',
208+
},
209+
],
210+
},
211+
{
212+
auditor: slowMist,
213+
auditDate: '2024-12-17',
214+
variantsScope: { browser: true },
215+
ref: 'https://github.com/RabbyHub/Rabby/blob/develop/docs/Rabby%20Browser%20Extension%20Wallet%20-%20SlowMist%20Audit%20Report-20241217.pdf',
216+
codeSnapshot: {
217+
date: '2024-11-28',
218+
commit: '4e900e5944a671e99a135eea417bdfdb93072d99',
219+
},
220+
unpatchedFlaws: 'ALL_FIXED',
221+
},
222+
],
63223
lightClient: {
64224
ethereumL1: {
65225
helios: false,

‎src/beta/schema/attributes/privacy/address-correlation.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
import { pickWorstRating, unrated } from '../common';
1212
import {
1313
compareLeakedInfo,
14-
type Entity,
1514
inferLeaks,
1615
Leak,
1716
type LeakedInfo,
@@ -35,6 +34,7 @@ import type { WalletMetadata } from '@/beta/schema/wallet';
3534
import { AddressCorrelationDetails } from '@/beta/components/ui/molecules/attributes/privacy/AddressCorrelationDetails';
3635
import { isNonEmptyArray, type NonEmptyArray, nonEmptyFirst } from '@/beta/types/utils/non-empty';
3736
import { type FullyQualifiedReference, refs } from '../../reference';
37+
import type { Entity } from '../../entity';
3838

3939
const brand = 'attributes.privacy.address_correlation';
4040
export type AddressCorrelationValue = Value & {

‎src/beta/schema/entity.ts

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import type { Url } from './url';
2+
3+
export enum EntityType {
4+
chainDataProvider = 'chainDataProvider',
5+
corporate = 'corporate',
6+
dataBroker = 'dataBroker',
7+
exchange = 'exchange',
8+
offchainDataProvider = 'offchainDataProvider',
9+
transactionBroadcastProvider = 'transactionBroadcastProvider',
10+
walletDeveloper = 'walletDeveloper',
11+
securityAuditor = 'securityAuditor',
12+
}
13+
14+
/**
15+
* An entity, typically corporate.
16+
*
17+
* `Ts` is a set of entity types that the entity must have.
18+
*/
19+
export interface Entity<Ts extends EntityType[] = []> {
20+
/** The ID of the entity. */
21+
id: string;
22+
23+
/** The name of the entity. */
24+
name: string;
25+
26+
/**
27+
* Legal name of the entity, if any.
28+
*
29+
* `soundsDifferent` indicates whether the legal name sounds significantly
30+
* different from `name`, such that most people may not be able to tell that
31+
* these names refer to the same entity.
32+
*/
33+
legalName: { name: string; soundsDifferent: boolean } | 'NOT_A_LEGAL_ENTITY';
34+
35+
/* Type(s) of the entity. */
36+
type: {
37+
[K in EntityType]: K extends Ts[number] ? true : boolean;
38+
};
39+
40+
/**
41+
* The entity's icon, if any.
42+
* If specified, there must exist an icon file at
43+
* `/public/images/entities/${id}.${icon.extension}`.
44+
*/
45+
icon:
46+
| 'NO_ICON'
47+
| {
48+
extension: 'png';
49+
width: number;
50+
height: number;
51+
}
52+
| { extension: 'svg' };
53+
54+
/**
55+
* Website of the entity.
56+
*/
57+
url: Url | { type: 'NO_WEBSITE' };
58+
59+
/**
60+
* The jurisdiction in which the entity is located.
61+
*
62+
* 'GLOBAL' should be used when an entity exists in such a manner that
63+
* no single jurisdiction has singular control over it.
64+
*/
65+
jurisdiction: string | { type: 'GLOBAL' | 'UNKNOWN' };
66+
67+
/**
68+
* The privacy policy URL of the entity.
69+
*/
70+
privacyPolicy: Url | { type: 'NO_PRIVACY_POLICY' };
71+
72+
/** The Crunchbase URL of the entity, if any. */
73+
crunchbase: Url | { type: 'NO_CRUNCHBASE_URL' };
74+
}
75+
76+
type EntityWithType<T extends EntityType> = Entity & Entity<[T]>;
77+
78+
export type ChainDataProvider = EntityWithType<EntityType.chainDataProvider>;
79+
export type CorporateEntity = EntityWithType<EntityType.corporate>;
80+
export type DataBroker = EntityWithType<EntityType.dataBroker>;
81+
export type Exchange = EntityWithType<EntityType.exchange>;
82+
export type OffchainDataProvider = EntityWithType<EntityType.offchainDataProvider>;
83+
export type TransactionBroadcastProvider = EntityWithType<EntityType.transactionBroadcastProvider>;
84+
export type SecurityAuditor = EntityWithType<EntityType.securityAuditor>;
85+
export type WalletDeveloper = EntityWithType<EntityType.walletDeveloper>;

‎src/beta/schema/features.ts

+16
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { ChainConfigurability } from './features/chain-configurability';
1313
import type { WalletProfile } from './features/profile';
1414
import type { WalletIntegration } from './features/integration';
1515
import type { AddressResolution } from './features/address-resolution';
16+
import type { SecurityAudit } from './features/security/security-audits';
1617

1718
/**
1819
* A set of features about a wallet, each of which may or may not depend on
@@ -31,6 +32,13 @@ export interface WalletFeatures {
3132

3233
/** Security features. */
3334
security: {
35+
/**
36+
* Public security audits the wallet has gone through.
37+
* If never audited, this should be an empty array, as 'null' represents
38+
* the fact that we haven't checked whether there have been any audit.
39+
*/
40+
publicSecurityAudits: null | SecurityAudit[];
41+
3442
/** Light clients. */
3543
lightClient: {
3644
/** Light client used for Ethereum L1. False means no light client support. */
@@ -81,6 +89,7 @@ export interface ResolvedFeatures {
8189
profile: WalletProfile;
8290

8391
security: {
92+
publicSecurityAudits: null | SecurityAudit[];
8493
lightClient: {
8594
ethereumL1: ResolvedFeature<WithRef<EthereumL1LightClientSupport> | false>;
8695
};
@@ -105,6 +114,13 @@ export function resolveFeatures(features: WalletFeatures, variant: Variant): Res
105114
variant,
106115
profile: features.profile,
107116
security: {
117+
publicSecurityAudits:
118+
features.security.publicSecurityAudits === null
119+
? null
120+
: features.security.publicSecurityAudits.filter(
121+
audit =>
122+
audit.variantsScope === 'ALL_VARIANTS' || audit.variantsScope[variant] === true
123+
),
108124
lightClient: {
109125
ethereumL1: feat(features.security.lightClient.ethereumL1),
110126
},

‎src/beta/schema/features/privacy/data-collection.ts

+1-24
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { WithRef } from '@/beta/schema/reference';
2-
import type { Url } from '@/beta/schema/url';
32
import type { Dict } from '@/beta/types/utils/dict';
43
import type { WalletMetadata } from '../../wallet';
4+
import type { Entity } from '../../entity';
55

66
/**
77
* An enum representing when data collection or leak occurs.
@@ -152,29 +152,6 @@ export function leaksByDefault(leak: Leak): boolean {
152152
return leak >= Leak.BY_DEFAULT;
153153
}
154154

155-
/**
156-
* An entity to which some data may be sent.
157-
*/
158-
export interface Entity {
159-
/** The name of the entity to which data may be sent. */
160-
name: string;
161-
/**
162-
* Legal name of the entity, if any.
163-
* `soundsDifferent` indicates whether the legal name sounds significantly
164-
* different from `name`, such that most people may not be able to tell that
165-
* these names refer to the same entity.
166-
*/
167-
legalName: { name: string; soundsDifferent: boolean } | null;
168-
/** Website of the entity to which data may be sent. */
169-
url: Url | null;
170-
/** The jurisdiction in which the entity is located. */
171-
jurisdiction: string | null;
172-
/** The privacy policy URL of the entity. */
173-
privacyPolicy: Url | null;
174-
/** The Crunchbase URL of the entity, if any. */
175-
crunchbase: Url | null;
176-
}
177-
178155
/** Personal information types. */
179156
export enum LeakedPersonalInfo {
180157
/** The user's IP address. */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import type { NonEmptyArray } from '@/beta/types/utils/non-empty';
2+
import type { AtLeastOneTrueVariant } from '../../variants';
3+
import type { MustRef } from '../../reference';
4+
import type { CalendarDate } from '@/beta/types/date';
5+
import type { SecurityAuditor } from '../../entity';
6+
7+
/** The severity of a security flaw, as assigned by the security auditor. */
8+
export enum SecurityFlawSeverity {
9+
CRITICAL = 'CRITICAL',
10+
HIGH = 'HIGH',
11+
MEDIUM = 'MEDIUM',
12+
13+
// Lower-than-medium security flaws are not tracked.
14+
15+
/** The security auditing entity did not assign a severity rating. */
16+
NOT_CATEGORIZED = 'NOT_CATEGORIZED',
17+
}
18+
19+
/** A security flaw which was not addressed at audit publication time. */
20+
export type UnpatchedSecurityFlaw = {
21+
/** The name/description of the security flaw. */
22+
name: string;
23+
24+
/** The severity level of the security flaw. */
25+
severityAtAuditPublication: SecurityFlawSeverity;
26+
} & (
27+
| {
28+
/** The status of this flaw in the present day. */
29+
presentStatus: 'NOT_FIXED';
30+
}
31+
| MustRef<{
32+
/**
33+
* The status of this flaw in the present day.
34+
* If a flaw was fixed after audit publication, it must come with a
35+
* reference, such as a link to a code commit or to a statement or
36+
* newer audit from the same auditor.
37+
*/
38+
presentStatus: 'FIXED';
39+
40+
/** The date at which the flaw was fixed. */
41+
fixedDate: CalendarDate;
42+
}>
43+
);
44+
45+
/**
46+
* A single security audit.
47+
* A URL to the audit report must be added as `ref`.
48+
*/
49+
export type SecurityAudit = MustRef<{
50+
/** The entity that performed the security audit. */
51+
auditor: SecurityAuditor;
52+
53+
/** The date the audit report was done or published. */
54+
auditDate: CalendarDate;
55+
56+
/**
57+
* The snapshot of the code being audited, if provided in the report.
58+
*/
59+
codeSnapshot?: {
60+
/** When the snapshot of code was taken. */
61+
date: CalendarDate;
62+
63+
/** The commit hash of the code snapshot, if known. */
64+
commit?: string;
65+
66+
/** The release tag of the code snapshot, if known. */
67+
tag?: string;
68+
};
69+
70+
/**
71+
* Which variants of the wallet were audited.
72+
*/
73+
variantsScope: AtLeastOneTrueVariant | 'ALL_VARIANTS';
74+
75+
/**
76+
* The set of security flaws that were found but not addressed by the time
77+
* the audit was published.
78+
*
79+
* Only medium-severity or higher flaws are tracked.
80+
*
81+
* 'NONE_FOUND' means there were either no flaws found at all.
82+
* 'ALL_FIXED' means that all flaws that were found by the auditor were
83+
* either fixed by time the audit was published, or were of a lower
84+
* severity than MEDIUM.
85+
*/
86+
unpatchedFlaws: 'NONE_FOUND' | 'ALL_FIXED' | NonEmptyArray<UnpatchedSecurityFlaw>;
87+
}>;

‎src/beta/schema/reference.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
type LabeledUrl,
2727
type Url,
2828
} from './url';
29+
import type { CalendarDate } from '../types/date';
2930

3031
/**
3132
* A loose reference which can be converted to a FullyQualifiedReference.
@@ -41,7 +42,7 @@ export interface LooseReference {
4142
explanation?: string;
4243

4344
/** The date the reference was last retrieved. */
44-
lastRetrieved?: string;
45+
lastRetrieved?: CalendarDate;
4546
}
4647

4748
/**
@@ -55,7 +56,7 @@ export interface FullyQualifiedReference {
5556
explanation?: string;
5657

5758
/** The date the reference was last retrieved. */
58-
lastRetrieved?: string;
59+
lastRetrieved?: CalendarDate;
5960
}
6061

6162
type Reference = Url | LooseReference | FullyQualifiedReference;
@@ -69,8 +70,11 @@ export function isFullyQualifiedReference(
6970

7071
type References = Reference | NonEmptyArray<Reference>;
7172

72-
/** An object that can be annotated with References. */
73-
export type WithRef<T> = T & { ref?: References };
73+
/** An object that *must* be annotated with References. */
74+
export type MustRef<T> = T & { ref: References };
75+
76+
/** An object that *may or may not* be annotated with References. */
77+
export type WithRef<T> = MustRef<T> | (T & { ref?: undefined });
7478

7579
/** Fully qualify a `Reference`. */
7680
function toFullyQualified(reference: Reference): FullyQualifiedReference[] {

‎src/beta/schema/variants.ts

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ export type ComprehensiveVariants<T> = Record<Variant, T>;
1717
/** Maps at least one variant to a T. */
1818
export type AtLeastOneVariant<T> = NonEmptyRecord<Variant, T>;
1919

20+
/** Maps at least one variant to a T. */
21+
export type AtLeastOneTrueVariant = {
22+
[V in Variant]: Record<V, true> & Partial<Record<Exclude<Variant, V>, boolean>>;
23+
}[Variant];
24+
2025
/**
2126
* A feature that may or may not depend on the wallet variant.
2227
* 'null' represents the fact that the feature was not evaluated on a wallet.

‎src/beta/schema/wallet.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { Paragraph, Renderable, WithTypography } from '@/beta/types/text';
1111
import type { Url } from './url';
1212
import { Rating, type Attribute, type EvaluatedAttribute, type Value } from './attributes';
1313
import type { Dict } from '../types/utils/dict';
14+
import type { CalendarDate } from '../types/date';
1415

1516
/** A contributor to walletbeat. */
1617
export interface Contributor {
@@ -23,15 +24,15 @@ export interface WalletMetadata {
2324
/**
2425
* ID of the wallet.
2526
* It is expected that a wallet image exists at
26-
* `/public/images/wallet/${id}.${iconExtension}`.
27+
* `/public/images/wallets/${id}.${iconExtension}`.
2728
*/
2829
id: string;
2930

3031
/** Human-readable name of the wallet. */
3132
displayName: string;
3233

3334
/** Extension of the wallet icon image at
34-
* `/public/images/wallet/${id}.${iconExtension}`.
35+
* `/public/images/wallets/${id}.${iconExtension}`.
3536
* Wallet icons should be cropped to touch all edges, then minimal margins
3637
* added to make the image aspect ratio be 1:1 (square).
3738
*/
@@ -60,8 +61,8 @@ export interface WalletMetadata {
6061
/** Link to the wallet's source code repository, if public. */
6162
repoUrl: Url | null;
6263

63-
/** The last time the wallet information was updated. */
64-
lastUpdated: string;
64+
/** The last date the wallet information was updated. */
65+
lastUpdated: CalendarDate;
6566

6667
/** List of people who contributed to the information for this wallet. */
6768
contributors: NonEmptyArray<Contributor>;

‎src/beta/types/date.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
2+
type ThirtyDays = `0${Exclude<Digit, '0'>}` | `1${Digit}` | `2${Digit}` | '30';
3+
type ThirtyOneDays = ThirtyDays | '31';
4+
type FebruaryDays = Exclude<ThirtyDays, '30'>;
5+
type ThirtyDayMonths = '04' | '06' | '08' | '10' | '12';
6+
type ThirtyOneDayMonths = '01' | '03' | '05' | '07' | '09' | '11';
7+
type MonthAndDay =
8+
| `${ThirtyDayMonths}-${ThirtyDays}`
9+
| `${ThirtyOneDayMonths}-${ThirtyOneDays}`
10+
| `02-${FebruaryDays}`;
11+
type Century = '20' | '21'; // We're good from 2000 to 2199.
12+
13+
/** A valid date in YYYY-MM-DD format. */
14+
export type CalendarDate = `${Century}${Digit}${Digit}-${MonthAndDay}`;
15+
16+
/** The components of a calendar date in YYYY-MM-DD format. */
17+
export interface CalendarDateParts {
18+
year: number;
19+
month: number;
20+
day: number;
21+
}
22+
23+
/** Break a CalendarDate into its components. */
24+
export function calendarParts(calendarDate: CalendarDate): CalendarDateParts {
25+
const [year, month, day] = calendarDate.split('-');
26+
return {
27+
year: +year,
28+
month: +month,
29+
day: +day,
30+
};
31+
}

0 commit comments

Comments
 (0)
Please sign in to comment.