@decentrys/protect 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 +82 -0
- package/dist/browser/decentrys-protect.js +901 -0
- package/dist/browser/decentrys-protect.mjs +876 -0
- package/dist/cache.d.ts +41 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +75 -0
- package/dist/cache.js.map +1 -0
- package/dist/classify.d.ts +58 -0
- package/dist/classify.d.ts.map +1 -0
- package/dist/classify.js +269 -0
- package/dist/classify.js.map +1 -0
- package/dist/client.d.ts +132 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +307 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/model.d.ts +156 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +80 -0
- package/dist/model.js.map +1 -0
- package/dist/simulation.d.ts +57 -0
- package/dist/simulation.d.ts.map +1 -0
- package/dist/simulation.js +23 -0
- package/dist/simulation.js.map +1 -0
- package/dist/transport.d.ts +61 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +151 -0
- package/dist/transport.js.map +1 -0
- package/dist/wire.d.ts +63 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +274 -0
- package/dist/wire.js.map +1 -0
- package/package.json +64 -0
- package/src/cache.test.ts +67 -0
- package/src/cache.ts +87 -0
- package/src/classify.test.ts +294 -0
- package/src/classify.ts +323 -0
- package/src/client.test.ts +224 -0
- package/src/client.ts +420 -0
- package/src/index.ts +7 -0
- package/src/model.ts +237 -0
- package/src/simulation.ts +71 -0
- package/src/transport.test.ts +129 -0
- package/src/transport.ts +203 -0
- package/src/wire.test.ts +172 -0
- package/src/wire.ts +321 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The client an integrator installs.
|
|
4
|
+
*
|
|
5
|
+
* Its contract is narrow and absolute: **every method returns, and none of
|
|
6
|
+
* them throws.** This code sits between a user and a signing screen. A wallet
|
|
7
|
+
* that shows an error dialog because a security service had a bad minute has
|
|
8
|
+
* made the user's day worse for no security benefit, and the user learns to
|
|
9
|
+
* dismiss the dialog — which is the outcome the whole product exists to avoid.
|
|
10
|
+
*
|
|
11
|
+
* So an unreachable Decentrys produces an assessment that says exactly that,
|
|
12
|
+
* in `unknowns` and in `explanation`, and the configured `failMode` decides
|
|
13
|
+
* what the integrator does about it. Nothing is invented, and nothing is
|
|
14
|
+
* silently reported as clean.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.Decentrys = exports.SDK_VERSION = void 0;
|
|
18
|
+
const classify_1 = require("./classify");
|
|
19
|
+
const transport_1 = require("./transport");
|
|
20
|
+
const cache_1 = require("./cache");
|
|
21
|
+
const wire_1 = require("./wire");
|
|
22
|
+
const simulation_1 = require("./simulation");
|
|
23
|
+
exports.SDK_VERSION = '0.1.0';
|
|
24
|
+
const DEFAULT_BASE_URL = 'https://api.decentrys.com';
|
|
25
|
+
const DEFAULT_TIMEOUT_MS = 4_000;
|
|
26
|
+
const DEFAULT_CACHE_TTL_MS = 120_000;
|
|
27
|
+
const DEFAULT_CACHE_ENTRIES = 500;
|
|
28
|
+
class Decentrys {
|
|
29
|
+
transport;
|
|
30
|
+
failMode;
|
|
31
|
+
policy;
|
|
32
|
+
cache;
|
|
33
|
+
constructor(config) {
|
|
34
|
+
if (!config.apiKey || !config.apiKey.trim()) {
|
|
35
|
+
// Thrown at construction, which is a developer error at wiring time —
|
|
36
|
+
// as opposed to anything at call time, which is a user's transaction.
|
|
37
|
+
throw new Error('Decentrys: an apiKey is required. Create one at https://decentrys.com/developers.');
|
|
38
|
+
}
|
|
39
|
+
this.failMode = config.failMode ?? 'warn';
|
|
40
|
+
this.policy = config.policy ?? {};
|
|
41
|
+
this.cache = new cache_1.TtlCache({
|
|
42
|
+
ttlMs: config.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS,
|
|
43
|
+
maxEntries: config.cacheMaxEntries ?? DEFAULT_CACHE_ENTRIES,
|
|
44
|
+
});
|
|
45
|
+
this.transport = config.transport ?? new transport_1.HttpTransport({
|
|
46
|
+
baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
|
|
47
|
+
apiKey: config.apiKey,
|
|
48
|
+
timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
49
|
+
retries: config.retries ?? 1,
|
|
50
|
+
fetch: config.fetch ?? resolveFetch(),
|
|
51
|
+
userAgent: `decentrys-protect/${exports.SDK_VERSION}`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// -------------------------------------------------------------------------
|
|
55
|
+
// Assessments
|
|
56
|
+
// -------------------------------------------------------------------------
|
|
57
|
+
/** Pre-sign analysis of a transaction the user is about to approve. */
|
|
58
|
+
async assessTransaction(tx, options = {}) {
|
|
59
|
+
return this.assess({
|
|
60
|
+
path: '/v1/protect/transaction',
|
|
61
|
+
body: tx,
|
|
62
|
+
subject: { kind: 'transaction', chain: tx.chain, identifier: tx.to ?? tx.from },
|
|
63
|
+
// A transaction's assessment depends on its calldata and its moment.
|
|
64
|
+
// Caching one would serve a stale answer for a different transaction.
|
|
65
|
+
cacheable: false,
|
|
66
|
+
idempotent: true,
|
|
67
|
+
options,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** What a contract *can* do — capabilities, proxy status, admin controls. */
|
|
71
|
+
async scanContract(request, options = {}) {
|
|
72
|
+
return this.assess({
|
|
73
|
+
path: '/v1/protect/contract',
|
|
74
|
+
body: request,
|
|
75
|
+
subject: { kind: 'contract', chain: request.chain, identifier: request.address },
|
|
76
|
+
cacheable: true,
|
|
77
|
+
idempotent: true,
|
|
78
|
+
options,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async screenAddress(request, options = {}) {
|
|
82
|
+
return this.assess({
|
|
83
|
+
path: '/v1/protect/address',
|
|
84
|
+
body: request,
|
|
85
|
+
subject: { kind: 'address', chain: request.chain, identifier: request.address },
|
|
86
|
+
cacheable: true,
|
|
87
|
+
idempotent: true,
|
|
88
|
+
options,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async screenToken(request, options = {}) {
|
|
92
|
+
return this.assess({
|
|
93
|
+
path: '/v1/protect/token',
|
|
94
|
+
body: request,
|
|
95
|
+
subject: { kind: 'token', chain: request.chain, identifier: request.address },
|
|
96
|
+
cacheable: true,
|
|
97
|
+
idempotent: true,
|
|
98
|
+
options,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* An approval is assessed on the spender and the allowance together.
|
|
103
|
+
*
|
|
104
|
+
* Not cached: the same spender with an unlimited allowance and with a
|
|
105
|
+
* one-off allowance are different decisions, and the amount is the part a
|
|
106
|
+
* user most needs told.
|
|
107
|
+
*/
|
|
108
|
+
async screenApproval(request, options = {}) {
|
|
109
|
+
return this.assess({
|
|
110
|
+
path: '/v1/protect/approval',
|
|
111
|
+
body: request,
|
|
112
|
+
subject: { kind: 'approval', chain: request.chain, identifier: request.spender },
|
|
113
|
+
cacheable: false,
|
|
114
|
+
idempotent: true,
|
|
115
|
+
options,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async assessDapp(request, options = {}) {
|
|
119
|
+
return this.assess({
|
|
120
|
+
path: '/v1/protect/dapp',
|
|
121
|
+
body: request,
|
|
122
|
+
subject: { kind: 'dapp', chain: request.chain ?? 'multi', identifier: request.origin },
|
|
123
|
+
cacheable: true,
|
|
124
|
+
idempotent: true,
|
|
125
|
+
options,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The threat signals on a subject, without a classification.
|
|
130
|
+
*
|
|
131
|
+
* For integrators building their own presentation. An empty array means no
|
|
132
|
+
* signals were found — which is not the same as safe, and the SDK will not
|
|
133
|
+
* pretend otherwise on their behalf.
|
|
134
|
+
*/
|
|
135
|
+
async getThreatSignals(request, options = {}) {
|
|
136
|
+
try {
|
|
137
|
+
const raw = await this.transport.request({
|
|
138
|
+
path: '/v1/protect/signals',
|
|
139
|
+
body: request,
|
|
140
|
+
idempotent: true,
|
|
141
|
+
signal: options.signal,
|
|
142
|
+
});
|
|
143
|
+
return (0, wire_1.normalizeEvidence)(raw, {
|
|
144
|
+
kind: 'address', chain: request.chain, identifier: request.address,
|
|
145
|
+
}).threatSignals;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// No signals is the only honest answer when we could not look. The
|
|
149
|
+
// caller distinguishes it from a real empty result by asking for a full
|
|
150
|
+
// assessment, whose `unknowns` say so explicitly.
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// -------------------------------------------------------------------------
|
|
155
|
+
// Decoding and simulation
|
|
156
|
+
// -------------------------------------------------------------------------
|
|
157
|
+
/** What this transaction does, in the words a user would use. */
|
|
158
|
+
async explainTransaction(tx, options = {}) {
|
|
159
|
+
try {
|
|
160
|
+
const raw = await this.transport.request({
|
|
161
|
+
path: '/v1/protect/explain',
|
|
162
|
+
body: tx,
|
|
163
|
+
idempotent: true,
|
|
164
|
+
signal: options.signal,
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
summary: typeof raw?.summary === 'string' && raw.summary
|
|
168
|
+
? raw.summary
|
|
169
|
+
: 'This transaction could not be decoded.',
|
|
170
|
+
actions: stringList(raw?.actions),
|
|
171
|
+
exposure: stringList(raw?.exposure),
|
|
172
|
+
undecoded: stringList(raw?.undecoded),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
return {
|
|
177
|
+
summary: 'This transaction could not be decoded.',
|
|
178
|
+
actions: [],
|
|
179
|
+
exposure: [],
|
|
180
|
+
undecoded: [`Decentrys could not be reached: ${describe(error)}.`],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Execute the transaction against a fork and report what would change. */
|
|
185
|
+
async simulateTransaction(tx, options = {}) {
|
|
186
|
+
try {
|
|
187
|
+
const raw = await this.transport.request({
|
|
188
|
+
path: '/v1/protect/simulate',
|
|
189
|
+
body: tx,
|
|
190
|
+
idempotent: true,
|
|
191
|
+
signal: options.signal,
|
|
192
|
+
});
|
|
193
|
+
return normalizeSimulation(raw);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
return (0, simulation_1.unavailableSimulation)(describe(error));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// -------------------------------------------------------------------------
|
|
200
|
+
// Cache control
|
|
201
|
+
// -------------------------------------------------------------------------
|
|
202
|
+
/** Drop cached evidence. Call after a user reports a stale result. */
|
|
203
|
+
clearCache() {
|
|
204
|
+
this.cache.clear();
|
|
205
|
+
}
|
|
206
|
+
// -------------------------------------------------------------------------
|
|
207
|
+
// Internals
|
|
208
|
+
// -------------------------------------------------------------------------
|
|
209
|
+
async assess(params) {
|
|
210
|
+
const key = params.cacheable
|
|
211
|
+
? (0, cache_1.cacheKey)(params.subject.kind, [params.subject.chain, params.subject.identifier])
|
|
212
|
+
: null;
|
|
213
|
+
if (key && !params.options.skipCache) {
|
|
214
|
+
const hit = this.cache.get(key);
|
|
215
|
+
if (hit)
|
|
216
|
+
return this.finish(hit, true);
|
|
217
|
+
}
|
|
218
|
+
let evidence;
|
|
219
|
+
try {
|
|
220
|
+
const raw = await this.transport.request({
|
|
221
|
+
path: params.path,
|
|
222
|
+
body: params.body,
|
|
223
|
+
idempotent: params.idempotent,
|
|
224
|
+
signal: params.options.signal,
|
|
225
|
+
});
|
|
226
|
+
evidence = (0, wire_1.normalizeEvidence)(raw, params.subject);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
return {
|
|
230
|
+
subject: params.subject,
|
|
231
|
+
assessment: (0, classify_1.unavailableAssessment)(this.failMode, describe(error)),
|
|
232
|
+
decision: {
|
|
233
|
+
// `closed` is the only mode where unavailability is itself a stop.
|
|
234
|
+
// The others must not fabricate a risk level to justify blocking.
|
|
235
|
+
action: this.failMode === 'closed' ? 'block' : 'warn',
|
|
236
|
+
reason: `Decentrys could not be reached: ${describe(error)}.`,
|
|
237
|
+
},
|
|
238
|
+
cached: false,
|
|
239
|
+
demotedSignals: [],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
if (key)
|
|
243
|
+
this.cache.set(key, evidence);
|
|
244
|
+
return this.finish(evidence, false);
|
|
245
|
+
}
|
|
246
|
+
finish(evidence, cached) {
|
|
247
|
+
// Classification happens here, on the client, from evidence — not read
|
|
248
|
+
// off a server response. That is what makes the rule auditable by the
|
|
249
|
+
// integrator rather than a promise made in a marketing page.
|
|
250
|
+
const assessment = (0, classify_1.classify)({
|
|
251
|
+
facts: evidence.facts,
|
|
252
|
+
capabilities: evidence.capabilities,
|
|
253
|
+
threatSignals: evidence.threatSignals,
|
|
254
|
+
unknowns: evidence.unknowns,
|
|
255
|
+
historyStatus: evidence.historyStatus,
|
|
256
|
+
historyConfidence: evidence.historyConfidence,
|
|
257
|
+
});
|
|
258
|
+
return {
|
|
259
|
+
subject: evidence.subject,
|
|
260
|
+
assessment,
|
|
261
|
+
decision: (0, classify_1.applyPolicy)(assessment, this.policy),
|
|
262
|
+
cached,
|
|
263
|
+
demotedSignals: evidence.demotedSignals,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.Decentrys = Decentrys;
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// Helpers
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
function resolveFetch() {
|
|
272
|
+
const candidate = globalThis.fetch;
|
|
273
|
+
if (typeof candidate !== 'function') {
|
|
274
|
+
throw new Error('Decentrys: no global fetch was found. Pass one via `new Decentrys({ fetch })` '
|
|
275
|
+
+ '(Node 18+, modern browsers and React Native provide one).');
|
|
276
|
+
}
|
|
277
|
+
return candidate.bind(globalThis);
|
|
278
|
+
}
|
|
279
|
+
function describe(error) {
|
|
280
|
+
if (error instanceof transport_1.TransportError)
|
|
281
|
+
return error.message;
|
|
282
|
+
if (error instanceof Error)
|
|
283
|
+
return error.message;
|
|
284
|
+
return 'unknown error';
|
|
285
|
+
}
|
|
286
|
+
function stringList(value) {
|
|
287
|
+
return Array.isArray(value) ? value.filter((v) => typeof v === 'string' && v.length > 0) : [];
|
|
288
|
+
}
|
|
289
|
+
function normalizeSimulation(raw) {
|
|
290
|
+
const body = (typeof raw === 'object' && raw !== null ? raw : {});
|
|
291
|
+
const outcomes = ['SUCCESS', 'REVERT', 'NOT_SUPPORTED', 'UNAVAILABLE'];
|
|
292
|
+
return {
|
|
293
|
+
// An unrecognised outcome is not a success. Defaulting the other way would
|
|
294
|
+
// let a malformed response read as "this transaction is fine".
|
|
295
|
+
outcome: outcomes.includes(body.outcome)
|
|
296
|
+
? body.outcome
|
|
297
|
+
: 'UNAVAILABLE',
|
|
298
|
+
revertReason: typeof body.revertReason === 'string' ? body.revertReason : undefined,
|
|
299
|
+
balanceChanges: Array.isArray(body.balanceChanges) ? body.balanceChanges : [],
|
|
300
|
+
approvalChanges: Array.isArray(body.approvalChanges) ? body.approvalChanges : [],
|
|
301
|
+
contractsCalled: stringList(body.contractsCalled),
|
|
302
|
+
gasUsed: typeof body.gasUsed === 'string' ? body.gasUsed : undefined,
|
|
303
|
+
unavailableReason: typeof body.unavailableReason === 'string' ? body.unavailableReason : undefined,
|
|
304
|
+
simulatedAt: typeof body.simulatedAt === 'string' ? body.simulatedAt : new Date().toISOString(),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.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,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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("./classify"), exports);
|
|
19
|
+
__exportStar(require("./wire"), exports);
|
|
20
|
+
__exportStar(require("./simulation"), exports);
|
|
21
|
+
__exportStar(require("./transport"), exports);
|
|
22
|
+
__exportStar(require("./cache"), exports);
|
|
23
|
+
__exportStar(require("./client"), exports);
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,6CAA2B;AAC3B,yCAAuB;AACvB,+CAA6B;AAC7B,8CAA4B;AAC5B,0CAAwB;AACxB,2CAAyB"}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Decentrys risk model.
|
|
3
|
+
*
|
|
4
|
+
* One rule governs this file, and every other decision follows from it:
|
|
5
|
+
*
|
|
6
|
+
* **LACK OF EVIDENCE IS NOT EVIDENCE OF MALICE.**
|
|
7
|
+
*
|
|
8
|
+
* A contract deployed two hours ago by an anonymous wallet with no audit and
|
|
9
|
+
* thin liquidity is *unknown*, not dangerous. The industry habit of scoring
|
|
10
|
+
* those facts as risk produces a system that protects incumbents and taxes
|
|
11
|
+
* every new project — which is a gatekeeping product, not a security one.
|
|
12
|
+
*
|
|
13
|
+
* The model therefore separates four things that are usually collapsed:
|
|
14
|
+
*
|
|
15
|
+
* - **Facts** — directly verifiable, carrying no accusation.
|
|
16
|
+
* - **Capabilities** — what the code *can* do. A mint authority is a
|
|
17
|
+
* capability, not a vulnerability; it becomes one when behaviour or context
|
|
18
|
+
* shows it being abused.
|
|
19
|
+
* - **Threat signals** — require actual technical or behavioural evidence.
|
|
20
|
+
* These, and only these, can raise a risk level.
|
|
21
|
+
* - **Unknowns** — stated as unknown. Never silently converted to risk.
|
|
22
|
+
*
|
|
23
|
+
* `historyConfidence` exists to express how much Decentrys knows. It is
|
|
24
|
+
* deliberately not part of the risk computation: it is a measure of our
|
|
25
|
+
* coverage, not of the subject's danger.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Deliberately not SAFE/SCAM.
|
|
29
|
+
*
|
|
30
|
+
* "Safe" is a claim no analysis can support, and "scam" is an accusation that
|
|
31
|
+
* needs evidence. The scale runs from "we found no critical threat evidence"
|
|
32
|
+
* to "confirmed malicious infrastructure", and every step above CAUTION
|
|
33
|
+
* requires a threat signal to reach it.
|
|
34
|
+
*/
|
|
35
|
+
export declare const RISK_LEVELS: readonly ["NO_CRITICAL_RISK_DETECTED", "INFORMATIONAL", "CAUTION", "ELEVATED_RISK", "HIGH_RISK", "CRITICAL_THREAT", "KNOWN_MALICIOUS"];
|
|
36
|
+
export type RiskLevel = (typeof RISK_LEVELS)[number];
|
|
37
|
+
export declare function isAtLeast(level: RiskLevel, floor: RiskLevel): boolean;
|
|
38
|
+
/** What each level is allowed to mean, in the words an integrator may show. */
|
|
39
|
+
export declare const RISK_LEVEL_MEANING: Record<RiskLevel, string>;
|
|
40
|
+
export type FactValue = string | number | boolean | null;
|
|
41
|
+
/**
|
|
42
|
+
* Something directly verifiable.
|
|
43
|
+
*
|
|
44
|
+
* Facts carry no severity. "Deployed two hours ago" and "mint authority
|
|
45
|
+
* present" are both simply true; what they mean is the integrator's call and,
|
|
46
|
+
* where it matters, the threat signals' job to establish.
|
|
47
|
+
*/
|
|
48
|
+
export interface ObservedFact {
|
|
49
|
+
type: string;
|
|
50
|
+
value: FactValue;
|
|
51
|
+
/** Plain language, shown to a user as-is. Never accusatory. */
|
|
52
|
+
statement: string;
|
|
53
|
+
/** Where the fact came from, so it can be checked. */
|
|
54
|
+
source: string;
|
|
55
|
+
observedAt: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* `INFO` is the default and the common case.
|
|
59
|
+
*
|
|
60
|
+
* A capability's severity describes how much power it confers, not how likely
|
|
61
|
+
* it is to be abused. Nothing here alone can push a subject past CAUTION.
|
|
62
|
+
*/
|
|
63
|
+
export type CapabilitySeverity = 'INFO' | 'NOTABLE' | 'SIGNIFICANT';
|
|
64
|
+
export interface TechnicalCapability {
|
|
65
|
+
type: string;
|
|
66
|
+
severity: CapabilitySeverity;
|
|
67
|
+
/** What the contract *can* do, phrased as capability, never as accusation. */
|
|
68
|
+
statement: string;
|
|
69
|
+
/** The function, role or storage slot that grants it. */
|
|
70
|
+
grantedBy?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface Evidence {
|
|
73
|
+
id: string;
|
|
74
|
+
type: string;
|
|
75
|
+
source: string;
|
|
76
|
+
chain?: string;
|
|
77
|
+
txHash?: string;
|
|
78
|
+
contract?: string;
|
|
79
|
+
address?: string;
|
|
80
|
+
observedAt: string;
|
|
81
|
+
confidence: number;
|
|
82
|
+
/** True when a human analyst confirmed it. Required for KNOWN_MALICIOUS. */
|
|
83
|
+
analystVerified: boolean;
|
|
84
|
+
metadata?: Record<string, unknown>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Signals decay.
|
|
88
|
+
*
|
|
89
|
+
* A low-confidence association from three years ago must not poison an address
|
|
90
|
+
* permanently. A signal that is STALE still appears — suppressing it would
|
|
91
|
+
* hide a real observation — but it cannot raise the risk level.
|
|
92
|
+
*/
|
|
93
|
+
export type SignalStatus = 'ACTIVE' | 'STALE' | 'RESOLVED' | 'DISPUTED_FACT' | 'REMOVED';
|
|
94
|
+
export type ThreatSeverity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
|
95
|
+
export interface ThreatSignal {
|
|
96
|
+
type: string;
|
|
97
|
+
severity: ThreatSeverity;
|
|
98
|
+
/** 0–1. Presented alongside the signal, never rounded away. */
|
|
99
|
+
confidence: number;
|
|
100
|
+
/** Why this signal exists, in language a user can act on. */
|
|
101
|
+
explanation: string;
|
|
102
|
+
/** Hops from the subject. 0 is direct; anything above is inference. */
|
|
103
|
+
hops: number;
|
|
104
|
+
evidence: Evidence[];
|
|
105
|
+
status: SignalStatus;
|
|
106
|
+
createdAt: string;
|
|
107
|
+
lastSeen: string;
|
|
108
|
+
expiresAt?: string;
|
|
109
|
+
}
|
|
110
|
+
export type UnknownReason = 'UNKNOWN' | 'INSUFFICIENT_DATA' | 'PROVIDER_UNAVAILABLE' | 'NOT_APPLICABLE';
|
|
111
|
+
export interface UnknownField {
|
|
112
|
+
field: string;
|
|
113
|
+
reason: UnknownReason;
|
|
114
|
+
/** Says plainly that we do not know, rather than implying anything. */
|
|
115
|
+
statement: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* How much Decentrys knows about a subject — never how dangerous it is.
|
|
119
|
+
*
|
|
120
|
+
* `LIMITED` is the correct and expected state for anything new, and it must
|
|
121
|
+
* never be rendered as a warning.
|
|
122
|
+
*/
|
|
123
|
+
export type HistoryStatus = 'ESTABLISHED' | 'MODERATE' | 'LIMITED' | 'NONE';
|
|
124
|
+
export declare const HISTORY_STATUS_MEANING: Record<HistoryStatus, string>;
|
|
125
|
+
export interface RiskComponents {
|
|
126
|
+
/** From capabilities the code genuinely holds. Never inflated by age. */
|
|
127
|
+
technicalRisk: number;
|
|
128
|
+
/** From observed behaviour. */
|
|
129
|
+
behavioralRisk: number;
|
|
130
|
+
/** From confirmed threat intelligence. */
|
|
131
|
+
threatIntelligenceRisk: number;
|
|
132
|
+
/**
|
|
133
|
+
* How much we know, 0–100. **Not a danger score.** A low value means thin
|
|
134
|
+
* coverage on our side, and it is reported separately so no integrator can
|
|
135
|
+
* accidentally add it to a risk total.
|
|
136
|
+
*/
|
|
137
|
+
historyConfidence: number;
|
|
138
|
+
}
|
|
139
|
+
export interface Assessment {
|
|
140
|
+
riskLevel: RiskLevel;
|
|
141
|
+
/** True only with analyst-verified evidence. Never inferred. */
|
|
142
|
+
confirmedMalicious: boolean;
|
|
143
|
+
confidence: number;
|
|
144
|
+
historyStatus: HistoryStatus;
|
|
145
|
+
facts: ObservedFact[];
|
|
146
|
+
capabilities: TechnicalCapability[];
|
|
147
|
+
threatSignals: ThreatSignal[];
|
|
148
|
+
unknowns: UnknownField[];
|
|
149
|
+
components: RiskComponents;
|
|
150
|
+
/** Why this level was reached. Never empty — no black-box classifications. */
|
|
151
|
+
explanation: string[];
|
|
152
|
+
modelVersion: string;
|
|
153
|
+
assessedAt: string;
|
|
154
|
+
}
|
|
155
|
+
export declare const PROTECT_MODEL_VERSION = "protect-1.0.0";
|
|
156
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The Decentrys risk model.
|
|
4
|
+
*
|
|
5
|
+
* One rule governs this file, and every other decision follows from it:
|
|
6
|
+
*
|
|
7
|
+
* **LACK OF EVIDENCE IS NOT EVIDENCE OF MALICE.**
|
|
8
|
+
*
|
|
9
|
+
* A contract deployed two hours ago by an anonymous wallet with no audit and
|
|
10
|
+
* thin liquidity is *unknown*, not dangerous. The industry habit of scoring
|
|
11
|
+
* those facts as risk produces a system that protects incumbents and taxes
|
|
12
|
+
* every new project — which is a gatekeeping product, not a security one.
|
|
13
|
+
*
|
|
14
|
+
* The model therefore separates four things that are usually collapsed:
|
|
15
|
+
*
|
|
16
|
+
* - **Facts** — directly verifiable, carrying no accusation.
|
|
17
|
+
* - **Capabilities** — what the code *can* do. A mint authority is a
|
|
18
|
+
* capability, not a vulnerability; it becomes one when behaviour or context
|
|
19
|
+
* shows it being abused.
|
|
20
|
+
* - **Threat signals** — require actual technical or behavioural evidence.
|
|
21
|
+
* These, and only these, can raise a risk level.
|
|
22
|
+
* - **Unknowns** — stated as unknown. Never silently converted to risk.
|
|
23
|
+
*
|
|
24
|
+
* `historyConfidence` exists to express how much Decentrys knows. It is
|
|
25
|
+
* deliberately not part of the risk computation: it is a measure of our
|
|
26
|
+
* coverage, not of the subject's danger.
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.PROTECT_MODEL_VERSION = exports.HISTORY_STATUS_MEANING = exports.RISK_LEVEL_MEANING = exports.RISK_LEVELS = void 0;
|
|
30
|
+
exports.isAtLeast = isAtLeast;
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Risk levels
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
/**
|
|
35
|
+
* Deliberately not SAFE/SCAM.
|
|
36
|
+
*
|
|
37
|
+
* "Safe" is a claim no analysis can support, and "scam" is an accusation that
|
|
38
|
+
* needs evidence. The scale runs from "we found no critical threat evidence"
|
|
39
|
+
* to "confirmed malicious infrastructure", and every step above CAUTION
|
|
40
|
+
* requires a threat signal to reach it.
|
|
41
|
+
*/
|
|
42
|
+
exports.RISK_LEVELS = [
|
|
43
|
+
'NO_CRITICAL_RISK_DETECTED',
|
|
44
|
+
'INFORMATIONAL',
|
|
45
|
+
'CAUTION',
|
|
46
|
+
'ELEVATED_RISK',
|
|
47
|
+
'HIGH_RISK',
|
|
48
|
+
'CRITICAL_THREAT',
|
|
49
|
+
'KNOWN_MALICIOUS',
|
|
50
|
+
];
|
|
51
|
+
const RISK_ORDER = {
|
|
52
|
+
NO_CRITICAL_RISK_DETECTED: 0,
|
|
53
|
+
INFORMATIONAL: 1,
|
|
54
|
+
CAUTION: 2,
|
|
55
|
+
ELEVATED_RISK: 3,
|
|
56
|
+
HIGH_RISK: 4,
|
|
57
|
+
CRITICAL_THREAT: 5,
|
|
58
|
+
KNOWN_MALICIOUS: 6,
|
|
59
|
+
};
|
|
60
|
+
function isAtLeast(level, floor) {
|
|
61
|
+
return RISK_ORDER[level] >= RISK_ORDER[floor];
|
|
62
|
+
}
|
|
63
|
+
/** What each level is allowed to mean, in the words an integrator may show. */
|
|
64
|
+
exports.RISK_LEVEL_MEANING = {
|
|
65
|
+
NO_CRITICAL_RISK_DETECTED: 'No known critical threat evidence was identified. This is not an assurance of safety.',
|
|
66
|
+
INFORMATIONAL: 'Facts worth knowing before proceeding. Nothing here indicates danger.',
|
|
67
|
+
CAUTION: 'A security-sensitive capability or behaviour exists that deserves attention.',
|
|
68
|
+
ELEVATED_RISK: 'Several meaningful risk signals are present together.',
|
|
69
|
+
HIGH_RISK: 'Strong technical or behavioural evidence of significant danger.',
|
|
70
|
+
CRITICAL_THREAT: 'Severe threat supported by concrete evidence.',
|
|
71
|
+
KNOWN_MALICIOUS: 'Confirmed malicious infrastructure or behaviour, verified against evidence.',
|
|
72
|
+
};
|
|
73
|
+
exports.HISTORY_STATUS_MEANING = {
|
|
74
|
+
ESTABLISHED: 'Substantial on-chain history is available.',
|
|
75
|
+
MODERATE: 'Some history is available.',
|
|
76
|
+
LIMITED: 'Little history is available yet. This is normal for anything recently deployed and is not a risk finding.',
|
|
77
|
+
NONE: 'No history is available. This is not a risk finding.',
|
|
78
|
+
};
|
|
79
|
+
exports.PROTECT_MODEL_VERSION = 'protect-1.0.0';
|
|
80
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;;AAmCH,8BAEC;AAnCD,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E;;;;;;;GAOG;AACU,QAAA,WAAW,GAAG;IACzB,2BAA2B;IAC3B,eAAe;IACf,SAAS;IACT,eAAe;IACf,WAAW;IACX,iBAAiB;IACjB,iBAAiB;CACT,CAAC;AAGX,MAAM,UAAU,GAA8B;IAC5C,yBAAyB,EAAE,CAAC;IAC5B,aAAa,EAAE,CAAC;IAChB,OAAO,EAAE,CAAC;IACV,aAAa,EAAE,CAAC;IAChB,SAAS,EAAE,CAAC;IACZ,eAAe,EAAE,CAAC;IAClB,eAAe,EAAE,CAAC;CACnB,CAAC;AAEF,SAAgB,SAAS,CAAC,KAAgB,EAAE,KAAgB;IAC1D,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,+EAA+E;AAClE,QAAA,kBAAkB,GAA8B;IAC3D,yBAAyB,EACvB,uFAAuF;IACzF,aAAa,EACX,uEAAuE;IACzE,OAAO,EACL,8EAA8E;IAChF,aAAa,EACX,uDAAuD;IACzD,SAAS,EACP,iEAAiE;IACnE,eAAe,EACb,+CAA+C;IACjD,eAAe,EACb,6EAA6E;CAChF,CAAC;AAiHW,QAAA,sBAAsB,GAAkC;IACnE,WAAW,EAAE,4CAA4C;IACzD,QAAQ,EAAE,4BAA4B;IACtC,OAAO,EAAE,2GAA2G;IACpH,IAAI,EAAE,sDAAsD;CAC7D,CAAC;AAsCW,QAAA,qBAAqB,GAAG,eAAe,CAAC"}
|