@decentrys/sentinel-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/browser/decentrys-sentinel.js +377 -0
- package/dist/browser/decentrys-sentinel.mjs +352 -0
- package/dist/client.d.ts +118 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +218 -0
- package/dist/client.js.map +1 -0
- package/dist/from-audit.d.ts +70 -0
- package/dist/from-audit.d.ts.map +1 -0
- package/dist/from-audit.js +207 -0
- package/dist/from-audit.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/model.d.ts +77 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +27 -0
- package/dist/model.js.map +1 -0
- package/package.json +62 -0
- package/src/client.ts +289 -0
- package/src/from-audit.test.ts +131 -0
- package/src/from-audit.ts +253 -0
- package/src/index.ts +3 -0
- package/src/model.ts +85 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The Sentinel client.
|
|
4
|
+
*
|
|
5
|
+
* The contract is the opposite of Protect's, and deliberately.
|
|
6
|
+
*
|
|
7
|
+
* Protect sits between a user and a signing screen, so it never throws: a
|
|
8
|
+
* security service having a bad minute must not cost someone their
|
|
9
|
+
* transaction. Sentinel is management and reporting — you are registering a
|
|
10
|
+
* contract, writing a rule, acknowledging an alert. Swallowing a failure
|
|
11
|
+
* there would leave an operator believing they are monitored when they are
|
|
12
|
+
* not, which is the more dangerous silence of the two.
|
|
13
|
+
*
|
|
14
|
+
* So Sentinel throws. Loudly, with the API's own message.
|
|
15
|
+
*
|
|
16
|
+
* The one exception is `reportEvent`, which runs on a hot path in a
|
|
17
|
+
* customer's own system and must never be able to break it.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.Sentinel = exports.SentinelError = exports.SDK_VERSION = void 0;
|
|
21
|
+
exports.SDK_VERSION = '0.1.0';
|
|
22
|
+
const DEFAULT_BASE_URL = 'https://api.decentrys.com';
|
|
23
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
24
|
+
class SentinelError extends Error {
|
|
25
|
+
status;
|
|
26
|
+
code;
|
|
27
|
+
constructor(status, message, code) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.status = status;
|
|
30
|
+
this.code = code;
|
|
31
|
+
this.name = 'SentinelError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.SentinelError = SentinelError;
|
|
35
|
+
class Sentinel {
|
|
36
|
+
baseUrl;
|
|
37
|
+
apiKey;
|
|
38
|
+
timeoutMs;
|
|
39
|
+
fetchImpl;
|
|
40
|
+
constructor(config) {
|
|
41
|
+
if (!config.apiKey?.trim()) {
|
|
42
|
+
throw new Error('Sentinel: an apiKey is required. Create one at https://decentrys.com/developers.');
|
|
43
|
+
}
|
|
44
|
+
// A publishable key is readable by anyone who downloads the app carrying
|
|
45
|
+
// it. Registering monitoring targets and writing detection rules is not
|
|
46
|
+
// something that credential may do, and saying so here is clearer than a
|
|
47
|
+
// 403 from the server later.
|
|
48
|
+
if (config.apiKey.startsWith('dk_pub_')) {
|
|
49
|
+
throw new Error('Sentinel: that is a publishable key. Monitoring is managed server-side with a secret key — a '
|
|
50
|
+
+ 'publishable key ships inside clients and cannot be trusted to configure detection.');
|
|
51
|
+
}
|
|
52
|
+
this.apiKey = config.apiKey;
|
|
53
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
54
|
+
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
55
|
+
this.fetchImpl = config.fetch ?? resolveFetch();
|
|
56
|
+
}
|
|
57
|
+
// --- Targets ------------------------------------------------------------
|
|
58
|
+
/** Watch a deployed contract. */
|
|
59
|
+
registerContract(input) {
|
|
60
|
+
return this.addTarget(input, 'CONTRACT');
|
|
61
|
+
}
|
|
62
|
+
registerWallet(input) {
|
|
63
|
+
return this.addTarget(input, 'WALLET');
|
|
64
|
+
}
|
|
65
|
+
registerTreasury(input) {
|
|
66
|
+
return this.addTarget(input, 'TREASURY');
|
|
67
|
+
}
|
|
68
|
+
registerTarget(input, targetType) {
|
|
69
|
+
return this.addTarget(input, targetType);
|
|
70
|
+
}
|
|
71
|
+
listTargets(projectId) {
|
|
72
|
+
const query = projectId ? `?projectId=${encodeURIComponent(projectId)}` : '';
|
|
73
|
+
return this.request('GET', `/v1/monitoring/targets${query}`);
|
|
74
|
+
}
|
|
75
|
+
async setTargetEnabled(targetId, enabled) {
|
|
76
|
+
return this.request('PATCH', `/v1/monitoring/targets/${targetId}`, { enabled });
|
|
77
|
+
}
|
|
78
|
+
async removeTarget(targetId) {
|
|
79
|
+
await this.request('DELETE', `/v1/monitoring/targets/${targetId}`);
|
|
80
|
+
}
|
|
81
|
+
// --- Rules --------------------------------------------------------------
|
|
82
|
+
createRule(input) {
|
|
83
|
+
return this.request('POST', '/v1/monitoring/rules', input);
|
|
84
|
+
}
|
|
85
|
+
updateRule(ruleId, changes) {
|
|
86
|
+
return this.request('PATCH', `/v1/monitoring/rules/${ruleId}`, changes);
|
|
87
|
+
}
|
|
88
|
+
async deleteRule(ruleId) {
|
|
89
|
+
await this.request('DELETE', `/v1/monitoring/rules/${ruleId}`);
|
|
90
|
+
}
|
|
91
|
+
listRules(projectId) {
|
|
92
|
+
const query = projectId ? `?projectId=${encodeURIComponent(projectId)}` : '';
|
|
93
|
+
return this.request('GET', `/v1/monitoring/rules${query}`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Evaluate a rule against facts without arming it.
|
|
97
|
+
*
|
|
98
|
+
* A rule that has never been tested against a fact set is a rule nobody
|
|
99
|
+
* knows the behaviour of, and finding out during an incident is the worst
|
|
100
|
+
* possible time.
|
|
101
|
+
*/
|
|
102
|
+
testRule(ruleId, facts) {
|
|
103
|
+
return this.request('POST', `/v1/monitoring/rules/${ruleId}/test`, { facts });
|
|
104
|
+
}
|
|
105
|
+
/** The vocabulary rules are built from: fields, operators, actions, templates. */
|
|
106
|
+
catalog() {
|
|
107
|
+
return this.request('GET', '/v1/monitoring/catalog');
|
|
108
|
+
}
|
|
109
|
+
// --- Alerts -------------------------------------------------------------
|
|
110
|
+
listAlerts(input = {}) {
|
|
111
|
+
const params = new URLSearchParams();
|
|
112
|
+
if (input.status)
|
|
113
|
+
params.set('status', input.status);
|
|
114
|
+
if (input.severity)
|
|
115
|
+
params.set('severity', input.severity);
|
|
116
|
+
if (input.limit)
|
|
117
|
+
params.set('limit', String(input.limit));
|
|
118
|
+
const query = params.toString();
|
|
119
|
+
return this.request('GET', `/v1/alerts${query ? `?${query}` : ''}`);
|
|
120
|
+
}
|
|
121
|
+
getAlert(alertId) {
|
|
122
|
+
return this.request('GET', `/v1/alerts/${alertId}`);
|
|
123
|
+
}
|
|
124
|
+
acknowledgeAlert(alertId, note) {
|
|
125
|
+
return this.request('PATCH', `/v1/alerts/${alertId}`, {
|
|
126
|
+
status: 'ACKNOWLEDGED',
|
|
127
|
+
...(note ? { note } : {}),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
// --- Reporting ----------------------------------------------------------
|
|
131
|
+
/**
|
|
132
|
+
* Report an event from your own system.
|
|
133
|
+
*
|
|
134
|
+
* The one method here that never throws. It is called from a customer's hot
|
|
135
|
+
* path — a deploy script, a treasury movement, a governance execution — and
|
|
136
|
+
* a monitoring call must not be able to fail the thing it is monitoring.
|
|
137
|
+
* The boolean says whether it was recorded, so a caller who cares can check.
|
|
138
|
+
*/
|
|
139
|
+
async reportEvent(event) {
|
|
140
|
+
try {
|
|
141
|
+
await this.request('POST', '/v1/monitoring/events', event);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// --- Internals ----------------------------------------------------------
|
|
149
|
+
addTarget(input, targetType) {
|
|
150
|
+
return this.request('POST', '/v1/monitoring/targets', {
|
|
151
|
+
projectId: input.projectId,
|
|
152
|
+
chainKey: input.chain,
|
|
153
|
+
address: input.address,
|
|
154
|
+
targetType,
|
|
155
|
+
...(input.label ? { label: input.label } : {}),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async request(method, path, body) {
|
|
159
|
+
const controller = new AbortController();
|
|
160
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
161
|
+
try {
|
|
162
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
163
|
+
method,
|
|
164
|
+
headers: {
|
|
165
|
+
'content-type': 'application/json',
|
|
166
|
+
'x-api-key': this.apiKey,
|
|
167
|
+
'user-agent': `decentrys-sentinel/${exports.SDK_VERSION}`,
|
|
168
|
+
},
|
|
169
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
170
|
+
signal: controller.signal,
|
|
171
|
+
});
|
|
172
|
+
const text = await response.text();
|
|
173
|
+
if (!response.ok) {
|
|
174
|
+
const parsed = safeParse(text);
|
|
175
|
+
throw new SentinelError(response.status, typeof parsed?.message === 'string' ? parsed.message : `Decentrys returned HTTP ${response.status}.`, typeof parsed?.code === 'string' ? parsed.code : undefined);
|
|
176
|
+
}
|
|
177
|
+
if (!text)
|
|
178
|
+
return undefined;
|
|
179
|
+
const parsed = safeParse(text);
|
|
180
|
+
if (parsed === null)
|
|
181
|
+
throw new SentinelError(response.status, 'The response was not valid JSON.');
|
|
182
|
+
// The platform wraps successful bodies as `{ data: ... }`, except for
|
|
183
|
+
// paginated responses which carry `data` alongside pagination fields.
|
|
184
|
+
return (('data' in parsed && !('total' in parsed) && !('page' in parsed))
|
|
185
|
+
? parsed.data
|
|
186
|
+
: parsed);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
if (error instanceof SentinelError)
|
|
190
|
+
throw error;
|
|
191
|
+
if (controller.signal.aborted) {
|
|
192
|
+
throw new SentinelError(0, `No response within ${this.timeoutMs}ms.`);
|
|
193
|
+
}
|
|
194
|
+
throw new SentinelError(0, error instanceof Error ? error.message : 'Request failed.');
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
clearTimeout(timer);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
exports.Sentinel = Sentinel;
|
|
202
|
+
function safeParse(text) {
|
|
203
|
+
try {
|
|
204
|
+
const parsed = JSON.parse(text);
|
|
205
|
+
return typeof parsed === 'object' && parsed !== null ? parsed : null;
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function resolveFetch() {
|
|
212
|
+
const candidate = globalThis.fetch;
|
|
213
|
+
if (typeof candidate !== 'function') {
|
|
214
|
+
throw new Error('Sentinel: no global fetch was found. Pass one via `new Sentinel({ fetch })`.');
|
|
215
|
+
}
|
|
216
|
+
return candidate.bind(globalThis);
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit findings → monitoring rules.
|
|
3
|
+
*
|
|
4
|
+
* The single most valuable thing the platform does, and the reason Audit and
|
|
5
|
+
* Sentinel belong in one product.
|
|
6
|
+
*
|
|
7
|
+
* An audit ends with a report, and a report is a document. Two months later
|
|
8
|
+
* the upgrade path a reviewer spent a day understanding is a paragraph nobody
|
|
9
|
+
* has reopened, and the person who reads it next is reading it during an
|
|
10
|
+
* incident. This turns what the audit *learned* into what the system
|
|
11
|
+
* *watches*: the privileged functions a reviewer identified become the
|
|
12
|
+
* functions that page someone when they are called.
|
|
13
|
+
*
|
|
14
|
+
* Two constraints shape the output:
|
|
15
|
+
*
|
|
16
|
+
* - **A capability is not a finding.** A contract being upgradeable is not a
|
|
17
|
+
* vulnerability, and the generated rule does not say it is. It watches for
|
|
18
|
+
* the upgrade *happening*, which is an event worth knowing about however
|
|
19
|
+
* legitimate the protocol.
|
|
20
|
+
* - **Every rule must be actionable.** A rule that fires on something nobody
|
|
21
|
+
* can respond to trains a team to close alerts unread, and then the real
|
|
22
|
+
* one is closed unread too. Nothing here generates a rule for an event
|
|
23
|
+
* with no plausible response.
|
|
24
|
+
*/
|
|
25
|
+
import type { RuleAction } from './model';
|
|
26
|
+
import type { CreateRuleInput } from './client';
|
|
27
|
+
/** What an audit observed about a contract, in the shape Protect reports it. */
|
|
28
|
+
export interface AuditCapability {
|
|
29
|
+
/** e.g. UPGRADEABLE, MINT_AUTHORITY, PAUSABLE, BLACKLIST, FEE_CONTROL. */
|
|
30
|
+
type: string;
|
|
31
|
+
statement?: string;
|
|
32
|
+
/** The function, role or slot that grants it, when known. */
|
|
33
|
+
grantedBy?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface AuditHandoverInput {
|
|
36
|
+
projectId: string;
|
|
37
|
+
/** Capabilities the audit confirmed the deployed code holds. */
|
|
38
|
+
capabilities: AuditCapability[];
|
|
39
|
+
/** Where alerts go. At minimum an in-platform alert. */
|
|
40
|
+
actions?: RuleAction[];
|
|
41
|
+
/**
|
|
42
|
+
* Treasury alert threshold in USD. Omitted means no treasury rule is
|
|
43
|
+
* generated: a threshold guessed on a customer's behalf is either so low it
|
|
44
|
+
* pages constantly or so high it never fires, and both teach people to
|
|
45
|
+
* ignore it.
|
|
46
|
+
*/
|
|
47
|
+
treasuryThresholdUsd?: number;
|
|
48
|
+
}
|
|
49
|
+
interface Generated {
|
|
50
|
+
capability: string;
|
|
51
|
+
rule: CreateRuleInput;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Turn confirmed capabilities into rules.
|
|
55
|
+
*
|
|
56
|
+
* Deduplicated: a contract with both a proxy and an upgrade authority holds
|
|
57
|
+
* one capability described twice, and generating two identical rules would
|
|
58
|
+
* page someone twice for one event.
|
|
59
|
+
*/
|
|
60
|
+
export declare function rulesFromAudit(input: AuditHandoverInput): Generated[];
|
|
61
|
+
/**
|
|
62
|
+
* Which capabilities produced no rule.
|
|
63
|
+
*
|
|
64
|
+
* Returned rather than silently dropped: a customer handed six rules for nine
|
|
65
|
+
* capabilities should be told which three are unwatched, so the gap is a
|
|
66
|
+
* decision rather than an assumption.
|
|
67
|
+
*/
|
|
68
|
+
export declare function unmappedCapabilities(capabilities: AuditCapability[]): string[];
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=from-audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"from-audit.d.ts","sourceRoot":"","sources":["../src/from-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,wDAAwD;IACxD,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,UAAU,SAAS;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,eAAe,CAAC;CACvB;AAuHD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,kBAAkB,GAAG,SAAS,EAAE,CA0DrE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,CAI9E"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Audit findings → monitoring rules.
|
|
4
|
+
*
|
|
5
|
+
* The single most valuable thing the platform does, and the reason Audit and
|
|
6
|
+
* Sentinel belong in one product.
|
|
7
|
+
*
|
|
8
|
+
* An audit ends with a report, and a report is a document. Two months later
|
|
9
|
+
* the upgrade path a reviewer spent a day understanding is a paragraph nobody
|
|
10
|
+
* has reopened, and the person who reads it next is reading it during an
|
|
11
|
+
* incident. This turns what the audit *learned* into what the system
|
|
12
|
+
* *watches*: the privileged functions a reviewer identified become the
|
|
13
|
+
* functions that page someone when they are called.
|
|
14
|
+
*
|
|
15
|
+
* Two constraints shape the output:
|
|
16
|
+
*
|
|
17
|
+
* - **A capability is not a finding.** A contract being upgradeable is not a
|
|
18
|
+
* vulnerability, and the generated rule does not say it is. It watches for
|
|
19
|
+
* the upgrade *happening*, which is an event worth knowing about however
|
|
20
|
+
* legitimate the protocol.
|
|
21
|
+
* - **Every rule must be actionable.** A rule that fires on something nobody
|
|
22
|
+
* can respond to trains a team to close alerts unread, and then the real
|
|
23
|
+
* one is closed unread too. Nothing here generates a rule for an event
|
|
24
|
+
* with no plausible response.
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.rulesFromAudit = rulesFromAudit;
|
|
28
|
+
exports.unmappedCapabilities = unmappedCapabilities;
|
|
29
|
+
const DEFAULT_ACTIONS = [{ type: 'ALERT', config: {} }];
|
|
30
|
+
/**
|
|
31
|
+
* Capability → the event worth watching, and why.
|
|
32
|
+
*
|
|
33
|
+
* Severity reflects how hard the change is to reverse, not how suspicious it
|
|
34
|
+
* is. A logic replacement is CRITICAL because every assumption in the audit
|
|
35
|
+
* stops holding the moment it lands — not because upgrading is wrong.
|
|
36
|
+
*/
|
|
37
|
+
const RULES = {
|
|
38
|
+
UPGRADEABLE: {
|
|
39
|
+
name: 'Contract logic replaced',
|
|
40
|
+
description: 'The proxy now points at different code. Every finding in the audit describes the previous '
|
|
41
|
+
+ 'implementation and stops applying the moment this fires.',
|
|
42
|
+
severity: 'CRITICAL',
|
|
43
|
+
triggerType: 'contract.upgraded',
|
|
44
|
+
conditions: { op: 'eq', field: 'upgrade.changed', value: true },
|
|
45
|
+
},
|
|
46
|
+
ADMIN_CONTROL: {
|
|
47
|
+
name: 'Admin address changed',
|
|
48
|
+
description: 'Whoever can upgrade or configure this contract is now a different account.',
|
|
49
|
+
severity: 'CRITICAL',
|
|
50
|
+
triggerType: 'contract.admin_changed',
|
|
51
|
+
conditions: { op: 'eq', field: 'admin.changed', value: true },
|
|
52
|
+
},
|
|
53
|
+
OWNERSHIP: {
|
|
54
|
+
name: 'Ownership transferred',
|
|
55
|
+
description: 'Owner-only functions identified during the audit are now controlled by someone else.',
|
|
56
|
+
severity: 'HIGH',
|
|
57
|
+
triggerType: 'contract.ownership_transferred',
|
|
58
|
+
conditions: { op: 'eq', field: 'ownership.changed', value: true },
|
|
59
|
+
},
|
|
60
|
+
MINT_AUTHORITY: {
|
|
61
|
+
name: 'Supply increased',
|
|
62
|
+
description: 'The mint authority the audit identified was used. Legitimate for many designs — this reports it '
|
|
63
|
+
+ 'so holders are not the last to know.',
|
|
64
|
+
severity: 'HIGH',
|
|
65
|
+
triggerType: 'token.supply_changed',
|
|
66
|
+
conditions: {
|
|
67
|
+
op: 'AND',
|
|
68
|
+
conditions: [
|
|
69
|
+
{ op: 'eq', field: 'token.supplyIncreased', value: true },
|
|
70
|
+
// A percentage rather than an absolute: a fixed figure is meaningless
|
|
71
|
+
// across tokens with different supplies and decimals.
|
|
72
|
+
{ op: 'gt', field: 'token.supplyChangePercent', value: 1 },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
PAUSABLE: {
|
|
77
|
+
name: 'Pause state changed',
|
|
78
|
+
description: 'The contract was paused or unpaused. Pausing is usually a defence, and knowing it happened is '
|
|
79
|
+
+ 'how you find out an incident started without you.',
|
|
80
|
+
severity: 'HIGH',
|
|
81
|
+
triggerType: 'contract.paused',
|
|
82
|
+
conditions: { op: 'eq', field: 'contract.pauseChanged', value: true },
|
|
83
|
+
},
|
|
84
|
+
BLACKLIST: {
|
|
85
|
+
name: 'Transfer restriction applied',
|
|
86
|
+
description: 'An address was restricted from transferring. Reported because it is invisible on-chain otherwise.',
|
|
87
|
+
severity: 'MEDIUM',
|
|
88
|
+
triggerType: 'contract.function_called',
|
|
89
|
+
conditions: { op: 'contains', field: 'event.types', value: 'BLACKLIST' },
|
|
90
|
+
},
|
|
91
|
+
FORCED_BALANCE_CHANGE: {
|
|
92
|
+
name: 'Balance moved without the holder acting',
|
|
93
|
+
description: 'A permanent delegate or equivalent authority moved tokens from an account that did not sign for it. '
|
|
94
|
+
+ 'Legitimate for some regulated designs, and always worth knowing about.',
|
|
95
|
+
severity: 'CRITICAL',
|
|
96
|
+
triggerType: 'contract.function_called',
|
|
97
|
+
conditions: { op: 'contains', field: 'event.types', value: 'FORCED_TRANSFER' },
|
|
98
|
+
},
|
|
99
|
+
GOVERNANCE: {
|
|
100
|
+
name: 'Governance proposal executed',
|
|
101
|
+
description: 'An executed proposal can change anything the audit assumed was fixed.',
|
|
102
|
+
severity: 'HIGH',
|
|
103
|
+
triggerType: 'governance.executed',
|
|
104
|
+
conditions: { op: 'eq', field: 'governance.executed', value: true },
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
/** Capability names normalised, so an audit may say MINT or MINT_AUTHORITY. */
|
|
108
|
+
const ALIASES = {
|
|
109
|
+
// The canonical names the Protect layer emits. Chain-specific spellings —
|
|
110
|
+
// Sui's TreasuryCap, Solana's mintAuthority, an EIP-1967 slot — are already
|
|
111
|
+
// normalised to these before an audit hands anything over, so mapping raw
|
|
112
|
+
// chain vocabulary here would be mapping names that never arrive.
|
|
113
|
+
UPGRADEABLE: 'UPGRADEABLE',
|
|
114
|
+
DELEGATED_EXECUTION: 'UPGRADEABLE',
|
|
115
|
+
MINT_AUTHORITY: 'MINT_AUTHORITY',
|
|
116
|
+
PAUSABLE: 'PAUSABLE',
|
|
117
|
+
ACCOUNT_FREEZE: 'BLACKLIST',
|
|
118
|
+
FORCED_BALANCE_CHANGE: 'FORCED_BALANCE_CHANGE',
|
|
119
|
+
GOVERNANCE: 'GOVERNANCE',
|
|
120
|
+
// Accepted because an audit report may use them in prose, and rejecting a
|
|
121
|
+
// reviewer's own wording would silently drop a rule they expected.
|
|
122
|
+
PROXY: 'UPGRADEABLE',
|
|
123
|
+
UPGRADE_AUTHORITY: 'UPGRADEABLE',
|
|
124
|
+
ADMIN: 'ADMIN_CONTROL',
|
|
125
|
+
ADMIN_CONTROL: 'ADMIN_CONTROL',
|
|
126
|
+
OWNER: 'OWNERSHIP',
|
|
127
|
+
OWNERSHIP: 'OWNERSHIP',
|
|
128
|
+
MINT: 'MINT_AUTHORITY',
|
|
129
|
+
PAUSE: 'PAUSABLE',
|
|
130
|
+
FREEZE_AUTHORITY: 'BLACKLIST',
|
|
131
|
+
BLACKLIST: 'BLACKLIST',
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Turn confirmed capabilities into rules.
|
|
135
|
+
*
|
|
136
|
+
* Deduplicated: a contract with both a proxy and an upgrade authority holds
|
|
137
|
+
* one capability described twice, and generating two identical rules would
|
|
138
|
+
* page someone twice for one event.
|
|
139
|
+
*/
|
|
140
|
+
function rulesFromAudit(input) {
|
|
141
|
+
const actions = input.actions?.length ? input.actions : DEFAULT_ACTIONS;
|
|
142
|
+
const seen = new Set();
|
|
143
|
+
const generated = [];
|
|
144
|
+
for (const capability of input.capabilities) {
|
|
145
|
+
const key = ALIASES[capability.type.toUpperCase()];
|
|
146
|
+
if (!key || seen.has(key))
|
|
147
|
+
continue;
|
|
148
|
+
seen.add(key);
|
|
149
|
+
const template = RULES[key];
|
|
150
|
+
if (!template)
|
|
151
|
+
continue;
|
|
152
|
+
generated.push({
|
|
153
|
+
capability: capability.type,
|
|
154
|
+
rule: {
|
|
155
|
+
projectId: input.projectId,
|
|
156
|
+
name: template.name,
|
|
157
|
+
description: capability.grantedBy
|
|
158
|
+
? `${template.description} Granted by ${capability.grantedBy}, identified during the audit.`
|
|
159
|
+
: `${template.description} Identified during the audit.`,
|
|
160
|
+
severity: template.severity,
|
|
161
|
+
triggerType: template.triggerType,
|
|
162
|
+
conditions: template.conditions,
|
|
163
|
+
actions,
|
|
164
|
+
// Five minutes. Long enough that one event in a loop does not page a
|
|
165
|
+
// team repeatedly, short enough that a second, genuinely separate
|
|
166
|
+
// occurrence still gets through.
|
|
167
|
+
cooldownSeconds: 300,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (typeof input.treasuryThresholdUsd === 'number' && input.treasuryThresholdUsd > 0) {
|
|
172
|
+
generated.push({
|
|
173
|
+
capability: 'TREASURY',
|
|
174
|
+
rule: {
|
|
175
|
+
projectId: input.projectId,
|
|
176
|
+
name: 'Large treasury outflow',
|
|
177
|
+
description: `An outbound movement above $${input.treasuryThresholdUsd.toLocaleString()}, the threshold agreed `
|
|
178
|
+
+ 'during the audit.',
|
|
179
|
+
severity: 'HIGH',
|
|
180
|
+
triggerType: 'treasury.transfer',
|
|
181
|
+
conditions: {
|
|
182
|
+
op: 'AND',
|
|
183
|
+
conditions: [
|
|
184
|
+
{ op: 'eq', field: 'transfer.direction', value: 'OUT' },
|
|
185
|
+
{ op: 'gt', field: 'transfer.valueUsd', value: input.treasuryThresholdUsd },
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
actions,
|
|
189
|
+
cooldownSeconds: 300,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return generated;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Which capabilities produced no rule.
|
|
197
|
+
*
|
|
198
|
+
* Returned rather than silently dropped: a customer handed six rules for nine
|
|
199
|
+
* capabilities should be told which three are unwatched, so the gap is a
|
|
200
|
+
* decision rather than an assumption.
|
|
201
|
+
*/
|
|
202
|
+
function unmappedCapabilities(capabilities) {
|
|
203
|
+
return capabilities
|
|
204
|
+
.filter((capability) => !ALIASES[capability.type.toUpperCase()])
|
|
205
|
+
.map((capability) => capability.type);
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=from-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"from-audit.js","sourceRoot":"","sources":["../src/from-audit.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./model"), exports);
|
|
18
|
+
__exportStar(require("./client"), exports);
|
|
19
|
+
__exportStar(require("./from-audit"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,2CAAyB;AACzB,+CAA6B"}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Sentinel model.
|
|
3
|
+
*
|
|
4
|
+
* Sentinel answers a different question from Protect. Protect asks "should I
|
|
5
|
+
* sign this?" about something a user is about to do; Sentinel asks "did
|
|
6
|
+
* something happen to what I own?" about systems already deployed. That
|
|
7
|
+
* changes what matters:
|
|
8
|
+
*
|
|
9
|
+
* - **The subject is yours.** You register your own contracts, wallets and
|
|
10
|
+
* treasuries. Nothing here classifies a third party, so none of Protect's
|
|
11
|
+
* machinery for avoiding accusations applies — and neither does its
|
|
12
|
+
* restraint about severity.
|
|
13
|
+
* - **A rule is a statement about facts, not a score.** A rule fires or it
|
|
14
|
+
* does not, and the alert says which fact made it fire. There is no
|
|
15
|
+
* threshold model to argue with.
|
|
16
|
+
* - **An alert must be actionable.** An alert nobody can act on trains
|
|
17
|
+
* people to close alerts, which is worse than having none.
|
|
18
|
+
*/
|
|
19
|
+
export declare const ALERT_SEVERITIES: readonly ["INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"];
|
|
20
|
+
export type AlertSeverity = (typeof ALERT_SEVERITIES)[number];
|
|
21
|
+
/** What a monitored thing is. Determines which rules can apply to it. */
|
|
22
|
+
export declare const TARGET_TYPES: readonly ["CONTRACT", "WALLET", "TREASURY", "MULTISIG", "LP_POOL", "ORACLE", "BRIDGE", "GOVERNANCE"];
|
|
23
|
+
export type TargetType = (typeof TARGET_TYPES)[number];
|
|
24
|
+
export interface MonitoringTarget {
|
|
25
|
+
id: string;
|
|
26
|
+
projectId: string;
|
|
27
|
+
chainKey: string;
|
|
28
|
+
address: string;
|
|
29
|
+
targetType: TargetType;
|
|
30
|
+
label: string | null;
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
createdAt: string;
|
|
33
|
+
}
|
|
34
|
+
export interface RuleAction {
|
|
35
|
+
type: string;
|
|
36
|
+
config: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
export interface MonitoringRule {
|
|
39
|
+
id: string;
|
|
40
|
+
projectId: string;
|
|
41
|
+
name: string;
|
|
42
|
+
description: string | null;
|
|
43
|
+
severity: AlertSeverity;
|
|
44
|
+
triggerType: string;
|
|
45
|
+
conditions: Record<string, unknown>;
|
|
46
|
+
actions: RuleAction[];
|
|
47
|
+
/**
|
|
48
|
+
* Suppression window. Without one, a contract emitting the same event in a
|
|
49
|
+
* loop pages someone a thousand times for one incident, and the thousandth
|
|
50
|
+
* page is less useful than the first.
|
|
51
|
+
*/
|
|
52
|
+
cooldownSeconds: number;
|
|
53
|
+
enabled: boolean;
|
|
54
|
+
createdAt: string;
|
|
55
|
+
}
|
|
56
|
+
export interface Alert {
|
|
57
|
+
id: string;
|
|
58
|
+
ruleId: string | null;
|
|
59
|
+
ruleName: string | null;
|
|
60
|
+
severity: AlertSeverity;
|
|
61
|
+
title: string;
|
|
62
|
+
description: string | null;
|
|
63
|
+
chainKey: string | null;
|
|
64
|
+
address: string | null;
|
|
65
|
+
txHash: string | null;
|
|
66
|
+
/** The facts that made the rule fire. An alert without these is unactionable. */
|
|
67
|
+
facts: Record<string, unknown> | null;
|
|
68
|
+
status: string;
|
|
69
|
+
acknowledgedAt: string | null;
|
|
70
|
+
createdAt: string;
|
|
71
|
+
}
|
|
72
|
+
export interface RuleTestResult {
|
|
73
|
+
matched: boolean;
|
|
74
|
+
/** Which conditions passed and which did not, so a rule can be debugged. */
|
|
75
|
+
explanation: string[];
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,gBAAgB,wDAAyD,CAAC;AACvF,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D,yEAAyE;AACzE,eAAO,MAAM,YAAY,sGAEf,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,aAAa,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB;;;;OAIG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB"}
|