@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/cache.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small TTL cache with a bounded size.
|
|
3
|
+
*
|
|
4
|
+
* Wallets call `screenAddress` on the same handful of addresses constantly —
|
|
5
|
+
* the token they always trade, the exchange they always withdraw to, the
|
|
6
|
+
* router every swap goes through. Serving those from memory is the difference
|
|
7
|
+
* between a signing screen that appears instantly and one that flashes a
|
|
8
|
+
* spinner every time.
|
|
9
|
+
*
|
|
10
|
+
* Two deliberate limits:
|
|
11
|
+
*
|
|
12
|
+
* - **Bounded.** A long-lived extension background page would otherwise grow
|
|
13
|
+
* without end. Eviction is oldest-first, which is right for this shape of
|
|
14
|
+
* traffic and costs nothing to maintain.
|
|
15
|
+
* - **Short.** Threat intelligence changes. A cached "no critical risk"
|
|
16
|
+
* entry that outlives the discovery of a drainer is worse than no cache,
|
|
17
|
+
* so the default TTL is measured in minutes, not hours.
|
|
18
|
+
*/
|
|
19
|
+
export interface CacheOptions {
|
|
20
|
+
ttlMs: number;
|
|
21
|
+
maxEntries: number;
|
|
22
|
+
now?: () => number;
|
|
23
|
+
}
|
|
24
|
+
export declare class TtlCache<T> {
|
|
25
|
+
private readonly options;
|
|
26
|
+
private readonly entries;
|
|
27
|
+
private readonly now;
|
|
28
|
+
constructor(options: CacheOptions);
|
|
29
|
+
get(key: string): T | undefined;
|
|
30
|
+
set(key: string, value: T, ttlMs?: number): void;
|
|
31
|
+
delete(key: string): void;
|
|
32
|
+
clear(): void;
|
|
33
|
+
get size(): number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Cache keys are built from the request, never from a hash of a stringified
|
|
37
|
+
* object: two subjects that differ only in a field the key forgot would
|
|
38
|
+
* otherwise share an assessment, and one of them would be wrong.
|
|
39
|
+
*/
|
|
40
|
+
export declare function cacheKey(kind: string, parts: Array<string | number | undefined | null>): string;
|
|
41
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,qBAAa,QAAQ,CAAC,CAAC;IAIT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEN,OAAO,EAAE,YAAY;IAIlD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAe/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,SAAqB,GAAG,IAAI;IAa5D,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,MAAM,CAE/F"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A small TTL cache with a bounded size.
|
|
4
|
+
*
|
|
5
|
+
* Wallets call `screenAddress` on the same handful of addresses constantly —
|
|
6
|
+
* the token they always trade, the exchange they always withdraw to, the
|
|
7
|
+
* router every swap goes through. Serving those from memory is the difference
|
|
8
|
+
* between a signing screen that appears instantly and one that flashes a
|
|
9
|
+
* spinner every time.
|
|
10
|
+
*
|
|
11
|
+
* Two deliberate limits:
|
|
12
|
+
*
|
|
13
|
+
* - **Bounded.** A long-lived extension background page would otherwise grow
|
|
14
|
+
* without end. Eviction is oldest-first, which is right for this shape of
|
|
15
|
+
* traffic and costs nothing to maintain.
|
|
16
|
+
* - **Short.** Threat intelligence changes. A cached "no critical risk"
|
|
17
|
+
* entry that outlives the discovery of a drainer is worse than no cache,
|
|
18
|
+
* so the default TTL is measured in minutes, not hours.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.TtlCache = void 0;
|
|
22
|
+
exports.cacheKey = cacheKey;
|
|
23
|
+
class TtlCache {
|
|
24
|
+
options;
|
|
25
|
+
entries = new Map();
|
|
26
|
+
now;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.options = options;
|
|
29
|
+
this.now = options.now ?? (() => Date.now());
|
|
30
|
+
}
|
|
31
|
+
get(key) {
|
|
32
|
+
const entry = this.entries.get(key);
|
|
33
|
+
if (!entry)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (entry.expiresAt <= this.now()) {
|
|
36
|
+
this.entries.delete(key);
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
// Re-insert so recency ordering reflects use, not just insertion.
|
|
40
|
+
this.entries.delete(key);
|
|
41
|
+
this.entries.set(key, entry);
|
|
42
|
+
return entry.value;
|
|
43
|
+
}
|
|
44
|
+
set(key, value, ttlMs = this.options.ttlMs) {
|
|
45
|
+
if (ttlMs <= 0 || this.options.maxEntries <= 0)
|
|
46
|
+
return;
|
|
47
|
+
this.entries.delete(key);
|
|
48
|
+
this.entries.set(key, { value, expiresAt: this.now() + ttlMs });
|
|
49
|
+
while (this.entries.size > this.options.maxEntries) {
|
|
50
|
+
const oldest = this.entries.keys().next();
|
|
51
|
+
if (oldest.done)
|
|
52
|
+
break;
|
|
53
|
+
this.entries.delete(oldest.value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
delete(key) {
|
|
57
|
+
this.entries.delete(key);
|
|
58
|
+
}
|
|
59
|
+
clear() {
|
|
60
|
+
this.entries.clear();
|
|
61
|
+
}
|
|
62
|
+
get size() {
|
|
63
|
+
return this.entries.size;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.TtlCache = TtlCache;
|
|
67
|
+
/**
|
|
68
|
+
* Cache keys are built from the request, never from a hash of a stringified
|
|
69
|
+
* object: two subjects that differ only in a field the key forgot would
|
|
70
|
+
* otherwise share an assessment, and one of them would be wrong.
|
|
71
|
+
*/
|
|
72
|
+
function cacheKey(kind, parts) {
|
|
73
|
+
return [kind, ...parts.map((p) => (p === undefined || p === null ? '' : String(p)))].join('|');
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAmEH,4BAEC;AAxDD,MAAa,QAAQ;IAIU;IAHZ,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IACtC,GAAG,CAAe;IAEnC,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;QAChD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAE7B,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,kEAAkE;QAClE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7B,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAQ,EAAE,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK;QACnD,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC;YAAE,OAAO;QAEvD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;QAEhE,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,IAAI;gBAAE,MAAM;YACvB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;CACF;AA/CD,4BA+CC;AAED;;;;GAIG;AACH,SAAgB,QAAQ,CAAC,IAAY,EAAE,KAAgD;IACrF,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjG,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Assessment, type HistoryStatus, type ObservedFact, type RiskLevel, type TechnicalCapability, type ThreatSignal, type UnknownField } from './model';
|
|
2
|
+
/**
|
|
3
|
+
* Below this, a signal is reported but cannot raise the level on its own.
|
|
4
|
+
*
|
|
5
|
+
* A 30%-confidence four-hop association is a real observation and belongs in
|
|
6
|
+
* the response. It is not grounds for telling a user their transaction is
|
|
7
|
+
* dangerous.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Exported so a renderer cannot drift from the classifier.
|
|
11
|
+
*
|
|
12
|
+
* `@decentrys/ui` decides whether to show a signal as one that counted, and
|
|
13
|
+
* it has to use the same floor the classifier used to decide whether it did.
|
|
14
|
+
* Two copies of this number that disagree would put a signal in the "did not
|
|
15
|
+
* raise the level" list while it was in fact raising it.
|
|
16
|
+
*/
|
|
17
|
+
export declare const MIN_RAISING_CONFIDENCE = 0.5;
|
|
18
|
+
export interface ClassifyInput {
|
|
19
|
+
facts?: ObservedFact[];
|
|
20
|
+
capabilities?: TechnicalCapability[];
|
|
21
|
+
threatSignals?: ThreatSignal[];
|
|
22
|
+
unknowns?: UnknownField[];
|
|
23
|
+
historyStatus?: HistoryStatus;
|
|
24
|
+
/** 0–100. Coverage, not danger. */
|
|
25
|
+
historyConfidence?: number;
|
|
26
|
+
now?: Date;
|
|
27
|
+
}
|
|
28
|
+
export declare function classify(input: ClassifyInput): Assessment;
|
|
29
|
+
/**
|
|
30
|
+
* How an integrator turns an assessment into behaviour.
|
|
31
|
+
*
|
|
32
|
+
* The SDK never blocks on its own. Decentrys returns intelligence; the wallet
|
|
33
|
+
* or exchange decides policy, and a consumer wallet and an institutional
|
|
34
|
+
* custodian will reasonably decide differently.
|
|
35
|
+
*/
|
|
36
|
+
export type PolicyAction = 'allow' | 'inform' | 'warn' | 'warn_strong' | 'require_confirmation' | 'block';
|
|
37
|
+
export type Policy = Partial<Record<RiskLevel, PolicyAction>>;
|
|
38
|
+
/**
|
|
39
|
+
* Transparency over blocking.
|
|
40
|
+
*
|
|
41
|
+
* The default blocks only what is confirmed malicious with analyst-verified
|
|
42
|
+
* evidence. Everything else informs or warns, leaving the decision with the
|
|
43
|
+
* person whose funds are at stake.
|
|
44
|
+
*/
|
|
45
|
+
export declare const DEFAULT_POLICY: Required<Policy>;
|
|
46
|
+
export declare function applyPolicy(assessment: Assessment, policy?: Policy): {
|
|
47
|
+
action: PolicyAction;
|
|
48
|
+
reason: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* What the SDK does when Decentrys is unreachable.
|
|
52
|
+
*
|
|
53
|
+
* A wallet must not become unusable because a security service is down.
|
|
54
|
+
* `warn` is the consumer default; an institution may choose `closed`.
|
|
55
|
+
*/
|
|
56
|
+
export type FailMode = 'open' | 'warn' | 'closed';
|
|
57
|
+
export declare function unavailableAssessment(failMode: FailMode, reason: string, now?: Date): Assessment;
|
|
58
|
+
//# sourceMappingURL=classify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../src/classify.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EACnD,KAAK,mBAAmB,EAAE,KAAK,YAAY,EAC9D,KAAK,YAAY,EAClB,MAAM,SAAS,CAAC;AAwBjB;;;;;;GAMG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,mCAAmC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CA2HzD;AAqED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa,GAAG,sBAAsB,GAAG,OAAO,CAAC;AAE1G,MAAM,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAQ3C,CAAC;AAEF,wBAAgB,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,GAAE,MAAW,GAAG;IACxE,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAMA;AAED;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAElD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAa,GAAG,UAAU,CA0BtG"}
|
package/dist/classify.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_POLICY = exports.MIN_RAISING_CONFIDENCE = void 0;
|
|
4
|
+
exports.classify = classify;
|
|
5
|
+
exports.applyPolicy = applyPolicy;
|
|
6
|
+
exports.unavailableAssessment = unavailableAssessment;
|
|
7
|
+
const model_1 = require("./model");
|
|
8
|
+
/**
|
|
9
|
+
* Classification.
|
|
10
|
+
*
|
|
11
|
+
* The whole point of this function is what it *refuses* to do. Age, audit
|
|
12
|
+
* status, liquidity, holder concentration, deployer history and anonymity are
|
|
13
|
+
* inputs to `facts` and to `historyConfidence` — and to nothing else. There is
|
|
14
|
+
* no path in this file by which any of them raises a risk level.
|
|
15
|
+
*
|
|
16
|
+
* Risk comes from two places only:
|
|
17
|
+
*
|
|
18
|
+
* - **Capabilities**, which can reach CAUTION. A contract that can mint
|
|
19
|
+
* unlimited supply deserves attention; it does not deserve an accusation.
|
|
20
|
+
* - **Threat signals**, which are the only way past CAUTION, and which
|
|
21
|
+
* require evidence.
|
|
22
|
+
*
|
|
23
|
+
* `KNOWN_MALICIOUS` additionally requires analyst-verified evidence, because
|
|
24
|
+
* it is the one output that is a public accusation about a third party.
|
|
25
|
+
*/
|
|
26
|
+
/** Signals that are no longer live cannot raise a level. */
|
|
27
|
+
const RAISING_STATUSES = ['ACTIVE'];
|
|
28
|
+
/**
|
|
29
|
+
* Below this, a signal is reported but cannot raise the level on its own.
|
|
30
|
+
*
|
|
31
|
+
* A 30%-confidence four-hop association is a real observation and belongs in
|
|
32
|
+
* the response. It is not grounds for telling a user their transaction is
|
|
33
|
+
* dangerous.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Exported so a renderer cannot drift from the classifier.
|
|
37
|
+
*
|
|
38
|
+
* `@decentrys/ui` decides whether to show a signal as one that counted, and
|
|
39
|
+
* it has to use the same floor the classifier used to decide whether it did.
|
|
40
|
+
* Two copies of this number that disagree would put a signal in the "did not
|
|
41
|
+
* raise the level" list while it was in fact raising it.
|
|
42
|
+
*/
|
|
43
|
+
exports.MIN_RAISING_CONFIDENCE = 0.5;
|
|
44
|
+
function classify(input) {
|
|
45
|
+
const now = input.now ?? new Date();
|
|
46
|
+
const facts = input.facts ?? [];
|
|
47
|
+
const capabilities = input.capabilities ?? [];
|
|
48
|
+
const unknowns = input.unknowns ?? [];
|
|
49
|
+
const historyStatus = input.historyStatus ?? 'LIMITED';
|
|
50
|
+
// Expiry is applied here rather than trusted from the caller, so a stale
|
|
51
|
+
// signal cannot keep raising a level because nobody re-ran a job.
|
|
52
|
+
const threatSignals = (input.threatSignals ?? []).map((signal) => applyDecay(signal, now));
|
|
53
|
+
const raising = threatSignals.filter((s) => RAISING_STATUSES.includes(s.status) && s.confidence >= exports.MIN_RAISING_CONFIDENCE);
|
|
54
|
+
const explanation = [];
|
|
55
|
+
let level = 'NO_CRITICAL_RISK_DETECTED';
|
|
56
|
+
// --- 1. Confirmed malicious -----------------------------------------------
|
|
57
|
+
// The only claim that accuses. It needs a human behind it.
|
|
58
|
+
const confirmed = raising.filter((s) => s.severity === 'CRITICAL' && s.evidence.some((e) => e.analystVerified));
|
|
59
|
+
const confirmedMalicious = confirmed.length > 0;
|
|
60
|
+
if (confirmedMalicious) {
|
|
61
|
+
level = 'KNOWN_MALICIOUS';
|
|
62
|
+
for (const signal of confirmed) {
|
|
63
|
+
explanation.push(`${signal.explanation} (verified by an analyst)`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// --- 2. Threat signals --------------------------------------------------
|
|
68
|
+
const critical = raising.filter((s) => s.severity === 'CRITICAL');
|
|
69
|
+
const high = raising.filter((s) => s.severity === 'HIGH');
|
|
70
|
+
const medium = raising.filter((s) => s.severity === 'MEDIUM');
|
|
71
|
+
if (critical.length > 0) {
|
|
72
|
+
level = 'CRITICAL_THREAT';
|
|
73
|
+
for (const s of critical)
|
|
74
|
+
explanation.push(s.explanation);
|
|
75
|
+
}
|
|
76
|
+
else if (high.length > 0) {
|
|
77
|
+
level = high.length > 1 ? 'HIGH_RISK' : 'ELEVATED_RISK';
|
|
78
|
+
for (const s of high)
|
|
79
|
+
explanation.push(s.explanation);
|
|
80
|
+
}
|
|
81
|
+
else if (medium.length > 1) {
|
|
82
|
+
level = 'ELEVATED_RISK';
|
|
83
|
+
for (const s of medium)
|
|
84
|
+
explanation.push(s.explanation);
|
|
85
|
+
}
|
|
86
|
+
else if (medium.length === 1) {
|
|
87
|
+
level = 'CAUTION';
|
|
88
|
+
explanation.push(medium[0].explanation);
|
|
89
|
+
}
|
|
90
|
+
// --- 3. Capabilities ----------------------------------------------------
|
|
91
|
+
// Can reach CAUTION and no further. A capability is what the code can do,
|
|
92
|
+
// not proof that it will.
|
|
93
|
+
const significant = capabilities.filter((c) => c.severity === 'SIGNIFICANT');
|
|
94
|
+
if (significant.length > 0 && rank(level) < rank('CAUTION')) {
|
|
95
|
+
level = 'CAUTION';
|
|
96
|
+
for (const c of significant)
|
|
97
|
+
explanation.push(c.statement);
|
|
98
|
+
}
|
|
99
|
+
else if (rank(level) < rank('INFORMATIONAL') && capabilities.length > 0) {
|
|
100
|
+
level = 'INFORMATIONAL';
|
|
101
|
+
for (const c of capabilities.slice(0, 3))
|
|
102
|
+
explanation.push(c.statement);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Signals that exist but did not raise the level are still reported, and the
|
|
106
|
+
// explanation says why they did not — silence would look like we missed them.
|
|
107
|
+
for (const signal of threatSignals) {
|
|
108
|
+
if (raising.includes(signal))
|
|
109
|
+
continue;
|
|
110
|
+
if (signal.status !== 'ACTIVE') {
|
|
111
|
+
explanation.push(`${signal.explanation} — this signal is ${signal.status.toLowerCase()} and did not affect the assessment.`);
|
|
112
|
+
}
|
|
113
|
+
else if (signal.confidence < exports.MIN_RAISING_CONFIDENCE) {
|
|
114
|
+
explanation.push(`${signal.explanation} — reported at ${Math.round(signal.confidence * 100)}% confidence, which is too low `
|
|
115
|
+
+ 'to raise the risk level on its own.');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// --- 4. Facts worth showing make it informational -----------------------
|
|
119
|
+
// Independent of history. Promoting only for LIMITED history would have made
|
|
120
|
+
// the level depend on how new the subject is, which is precisely the
|
|
121
|
+
// behaviour this model exists to refuse — and a test caught it.
|
|
122
|
+
if (level === 'NO_CRITICAL_RISK_DETECTED' && facts.length > 0) {
|
|
123
|
+
level = 'INFORMATIONAL';
|
|
124
|
+
}
|
|
125
|
+
// Limited history is stated, never scored.
|
|
126
|
+
if (historyStatus === 'LIMITED' || historyStatus === 'NONE') {
|
|
127
|
+
explanation.push(model_1.HISTORY_STATUS_MEANING[historyStatus]);
|
|
128
|
+
}
|
|
129
|
+
if (unknowns.length > 0) {
|
|
130
|
+
explanation.push(`${unknowns.length} ${unknowns.length === 1 ? 'attribute is' : 'attributes are'} unknown. `
|
|
131
|
+
+ 'Unknown is reported as unknown; it does not contribute to risk.');
|
|
132
|
+
}
|
|
133
|
+
if (explanation.length === 0) {
|
|
134
|
+
explanation.push(model_1.RISK_LEVEL_MEANING[level]);
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
riskLevel: level,
|
|
138
|
+
confirmedMalicious,
|
|
139
|
+
confidence: confidenceFor(level, raising, historyStatus),
|
|
140
|
+
historyStatus,
|
|
141
|
+
facts,
|
|
142
|
+
capabilities,
|
|
143
|
+
threatSignals,
|
|
144
|
+
unknowns,
|
|
145
|
+
components: {
|
|
146
|
+
technicalRisk: technicalRisk(capabilities),
|
|
147
|
+
behavioralRisk: behavioralRisk(raising),
|
|
148
|
+
threatIntelligenceRisk: threatIntelligenceRisk(raising),
|
|
149
|
+
// Coverage, reported separately so it cannot be summed into a risk total.
|
|
150
|
+
historyConfidence: input.historyConfidence ?? historyConfidenceFor(historyStatus),
|
|
151
|
+
},
|
|
152
|
+
explanation,
|
|
153
|
+
modelVersion: model_1.PROTECT_MODEL_VERSION,
|
|
154
|
+
assessedAt: now.toISOString(),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function rank(level) {
|
|
158
|
+
return [
|
|
159
|
+
'NO_CRITICAL_RISK_DETECTED', 'INFORMATIONAL', 'CAUTION',
|
|
160
|
+
'ELEVATED_RISK', 'HIGH_RISK', 'CRITICAL_THREAT', 'KNOWN_MALICIOUS',
|
|
161
|
+
].indexOf(level);
|
|
162
|
+
}
|
|
163
|
+
/** Expire a signal whose window has passed, rather than trusting its status. */
|
|
164
|
+
function applyDecay(signal, now) {
|
|
165
|
+
if (signal.status !== 'ACTIVE')
|
|
166
|
+
return signal;
|
|
167
|
+
if (!signal.expiresAt)
|
|
168
|
+
return signal;
|
|
169
|
+
return Date.parse(signal.expiresAt) <= now.getTime()
|
|
170
|
+
? { ...signal, status: 'STALE' }
|
|
171
|
+
: signal;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* How sure we are of the *classification*.
|
|
175
|
+
*
|
|
176
|
+
* Bounded by the evidence behind it. A clean result on thin history is a
|
|
177
|
+
* low-confidence clean result, and saying so is the honest thing — but note it
|
|
178
|
+
* lowers confidence, not raises risk.
|
|
179
|
+
*/
|
|
180
|
+
function confidenceFor(level, raising, history) {
|
|
181
|
+
if (raising.length > 0) {
|
|
182
|
+
const best = Math.max(...raising.map((s) => s.confidence));
|
|
183
|
+
return Number(best.toFixed(2));
|
|
184
|
+
}
|
|
185
|
+
switch (history) {
|
|
186
|
+
case 'ESTABLISHED': return 0.85;
|
|
187
|
+
case 'MODERATE': return 0.7;
|
|
188
|
+
case 'LIMITED': return 0.5;
|
|
189
|
+
default: return 0.35;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/** Capabilities only. Contract age contributes nothing, by construction. */
|
|
193
|
+
function technicalRisk(capabilities) {
|
|
194
|
+
const weight = { INFO: 4, NOTABLE: 12, SIGNIFICANT: 25 };
|
|
195
|
+
return Math.min(100, capabilities.reduce((sum, c) => sum + weight[c.severity], 0));
|
|
196
|
+
}
|
|
197
|
+
function behavioralRisk(raising) {
|
|
198
|
+
const weight = { LOW: 5, MEDIUM: 20, HIGH: 40, CRITICAL: 70 };
|
|
199
|
+
return Math.min(100, raising
|
|
200
|
+
.filter((s) => s.hops === 0)
|
|
201
|
+
.reduce((sum, s) => sum + weight[s.severity] * s.confidence, 0));
|
|
202
|
+
}
|
|
203
|
+
function threatIntelligenceRisk(raising) {
|
|
204
|
+
const weight = { LOW: 5, MEDIUM: 15, HIGH: 35, CRITICAL: 60 };
|
|
205
|
+
return Math.min(100, raising.reduce((sum, s) => {
|
|
206
|
+
// Each hop is another inference; weight decays accordingly.
|
|
207
|
+
const decay = Math.pow(0.6, Math.max(0, s.hops));
|
|
208
|
+
return sum + weight[s.severity] * s.confidence * decay;
|
|
209
|
+
}, 0));
|
|
210
|
+
}
|
|
211
|
+
function historyConfidenceFor(status) {
|
|
212
|
+
switch (status) {
|
|
213
|
+
case 'ESTABLISHED': return 90;
|
|
214
|
+
case 'MODERATE': return 60;
|
|
215
|
+
case 'LIMITED': return 25;
|
|
216
|
+
default: return 5;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Transparency over blocking.
|
|
221
|
+
*
|
|
222
|
+
* The default blocks only what is confirmed malicious with analyst-verified
|
|
223
|
+
* evidence. Everything else informs or warns, leaving the decision with the
|
|
224
|
+
* person whose funds are at stake.
|
|
225
|
+
*/
|
|
226
|
+
exports.DEFAULT_POLICY = {
|
|
227
|
+
NO_CRITICAL_RISK_DETECTED: 'allow',
|
|
228
|
+
INFORMATIONAL: 'inform',
|
|
229
|
+
CAUTION: 'warn',
|
|
230
|
+
ELEVATED_RISK: 'warn_strong',
|
|
231
|
+
HIGH_RISK: 'require_confirmation',
|
|
232
|
+
CRITICAL_THREAT: 'require_confirmation',
|
|
233
|
+
KNOWN_MALICIOUS: 'block',
|
|
234
|
+
};
|
|
235
|
+
function applyPolicy(assessment, policy = {}) {
|
|
236
|
+
const action = policy[assessment.riskLevel] ?? exports.DEFAULT_POLICY[assessment.riskLevel];
|
|
237
|
+
return {
|
|
238
|
+
action,
|
|
239
|
+
reason: assessment.explanation[0] ?? model_1.RISK_LEVEL_MEANING[assessment.riskLevel],
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function unavailableAssessment(failMode, reason, now = new Date()) {
|
|
243
|
+
return {
|
|
244
|
+
riskLevel: 'NO_CRITICAL_RISK_DETECTED',
|
|
245
|
+
confirmedMalicious: false,
|
|
246
|
+
confidence: 0,
|
|
247
|
+
historyStatus: 'NONE',
|
|
248
|
+
facts: [],
|
|
249
|
+
capabilities: [],
|
|
250
|
+
threatSignals: [],
|
|
251
|
+
unknowns: [{
|
|
252
|
+
field: 'assessment',
|
|
253
|
+
reason: 'PROVIDER_UNAVAILABLE',
|
|
254
|
+
statement: `Decentrys could not be reached: ${reason}. Nothing was checked.`,
|
|
255
|
+
}],
|
|
256
|
+
components: { technicalRisk: 0, behavioralRisk: 0, threatIntelligenceRisk: 0, historyConfidence: 0 },
|
|
257
|
+
explanation: [
|
|
258
|
+
`Decentrys could not be reached: ${reason}.`,
|
|
259
|
+
failMode === 'closed'
|
|
260
|
+
? 'This deployment is configured to refuse unverified transactions.'
|
|
261
|
+
: failMode === 'warn'
|
|
262
|
+
? 'No security check was performed. Proceed with the care you would use without any tool.'
|
|
263
|
+
: 'No security check was performed.',
|
|
264
|
+
],
|
|
265
|
+
modelVersion: model_1.PROTECT_MODEL_VERSION,
|
|
266
|
+
assessedAt: now.toISOString(),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../src/classify.ts"],"names":[],"mappings":";;;AAyDA,4BA2HC;AAiGD,kCASC;AAUD,sDA0BC;AAlUD,mCAKiB;AAEjB;;;;;;;;;;;;;;;;;GAiBG;AAEH,4DAA4D;AAC5D,MAAM,gBAAgB,GAAmB,CAAC,QAAQ,CAAC,CAAC;AAEpD;;;;;;GAMG;AACH;;;;;;;GAOG;AACU,QAAA,sBAAsB,GAAG,GAAG,CAAC;AAa1C,SAAgB,QAAQ,CAAC,KAAoB;IAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;IAChC,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;IAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC;IACtC,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,SAAS,CAAC;IAEvD,yEAAyE;IACzE,kEAAkE;IAClE,MAAM,aAAa,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAE3F,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAClC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,IAAI,8BAAsB,CACrF,CAAC;IAEF,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,KAAK,GAAc,2BAA2B,CAAC;IAEnD,6EAA6E;IAC7E,2DAA2D;IAC3D,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAC9E,CAAC;IACF,MAAM,kBAAkB,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IAEhD,IAAI,kBAAkB,EAAE,CAAC;QACvB,KAAK,GAAG,iBAAiB,CAAC;QAC1B,KAAK,MAAM,MAAM,IAAI,SAAS,EAAE,CAAC;YAC/B,WAAW,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,WAAW,2BAA2B,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,2EAA2E;QAC3E,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC;QAClE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QAE9D,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,KAAK,GAAG,iBAAiB,CAAC;YAC1B,KAAK,MAAM,CAAC,IAAI,QAAQ;gBAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAC5D,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC;YACxD,KAAK,MAAM,CAAC,IAAI,IAAI;gBAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACxD,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,KAAK,GAAG,eAAe,CAAC;YACxB,KAAK,MAAM,CAAC,IAAI,MAAM;gBAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAC1D,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,KAAK,GAAG,SAAS,CAAC;YAClB,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAC1C,CAAC;QAED,2EAA2E;QAC3E,0EAA0E;QAC1E,0BAA0B;QAC1B,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,aAAa,CAAC,CAAC;QAC7E,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5D,KAAK,GAAG,SAAS,CAAC;YAClB,KAAK,MAAM,CAAC,IAAI,WAAW;gBAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC7D,CAAC;aAAM,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1E,KAAK,GAAG,eAAe,CAAC;YACxB,KAAK,MAAM,CAAC,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;gBAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,8EAA8E;IAC9E,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;QACnC,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,SAAS;QACvC,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,WAAW,CAAC,IAAI,CACd,GAAG,MAAM,CAAC,WAAW,qBAAqB,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,qCAAqC,CAC3G,CAAC;QACJ,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,GAAG,8BAAsB,EAAE,CAAC;YACtD,WAAW,CAAC,IAAI,CACd,GAAG,MAAM,CAAC,WAAW,kBAAkB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC,iCAAiC;kBACzG,qCAAqC,CACxC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,6EAA6E;IAC7E,qEAAqE;IACrE,gEAAgE;IAChE,IAAI,KAAK,KAAK,2BAA2B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9D,KAAK,GAAG,eAAe,CAAC;IAC1B,CAAC;IAED,2CAA2C;IAC3C,IAAI,aAAa,KAAK,SAAS,IAAI,aAAa,KAAK,MAAM,EAAE,CAAC;QAC5D,WAAW,CAAC,IAAI,CAAC,8BAAsB,CAAC,aAAa,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,WAAW,CAAC,IAAI,CACd,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,gBAAgB,YAAY;cACzF,iEAAiE,CACpE,CAAC;IACJ,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,WAAW,CAAC,IAAI,CAAC,0BAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO;QACL,SAAS,EAAE,KAAK;QAChB,kBAAkB;QAClB,UAAU,EAAE,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,aAAa,CAAC;QACxD,aAAa;QACb,KAAK;QACL,YAAY;QACZ,aAAa;QACb,QAAQ;QACR,UAAU,EAAE;YACV,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC;YAC1C,cAAc,EAAE,cAAc,CAAC,OAAO,CAAC;YACvC,sBAAsB,EAAE,sBAAsB,CAAC,OAAO,CAAC;YACvD,0EAA0E;YAC1E,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,IAAI,oBAAoB,CAAC,aAAa,CAAC;SAClF;QACD,WAAW;QACX,YAAY,EAAE,6BAAqB;QACnC,UAAU,EAAE,GAAG,CAAC,WAAW,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED,SAAS,IAAI,CAAC,KAAgB;IAC5B,OAAO;QACL,2BAA2B,EAAE,eAAe,EAAE,SAAS;QACvD,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB;KACnE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,gFAAgF;AAChF,SAAS,UAAU,CAAC,MAAoB,EAAE,GAAS;IACjD,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,CAAC,MAAM,CAAC,SAAS;QAAE,OAAO,MAAM,CAAC;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE;QAClD,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;QAChC,CAAC,CAAC,MAAM,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,KAAgB,EAAE,OAAuB,EAAE,OAAsB;IACtF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;QAC3D,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,aAAa,CAAC,CAAC,OAAO,IAAI,CAAC;QAChC,KAAK,UAAU,CAAC,CAAC,OAAO,GAAG,CAAC;QAC5B,KAAK,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC;QAC3B,OAAO,CAAC,CAAC,OAAO,IAAI,CAAC;IACvB,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,SAAS,aAAa,CAAC,YAAmC;IACxD,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAW,CAAC;IAClE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,cAAc,CAAC,OAAuB;IAC7C,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAW,CAAC;IACvE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO;SACzB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;SAC3B,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,sBAAsB,CAAC,OAAuB;IACrD,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAW,CAAC;IACvE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QAC7C,4DAA4D;QAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACjD,OAAO,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,UAAU,GAAG,KAAK,CAAC;IACzD,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACT,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAqB;IACjD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,aAAa,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9B,KAAK,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;QAC3B,KAAK,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;QAC1B,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAaD;;;;;;GAMG;AACU,QAAA,cAAc,GAAqB;IAC9C,yBAAyB,EAAE,OAAO;IAClC,aAAa,EAAE,QAAQ;IACvB,OAAO,EAAE,MAAM;IACf,aAAa,EAAE,aAAa;IAC5B,SAAS,EAAE,sBAAsB;IACjC,eAAe,EAAE,sBAAsB;IACvC,eAAe,EAAE,OAAO;CACzB,CAAC;AAEF,SAAgB,WAAW,CAAC,UAAsB,EAAE,SAAiB,EAAE;IAIrE,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,sBAAc,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACpF,OAAO;QACL,MAAM;QACN,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,0BAAkB,CAAC,UAAU,CAAC,SAAS,CAAC;KAC9E,CAAC;AACJ,CAAC;AAUD,SAAgB,qBAAqB,CAAC,QAAkB,EAAE,MAAc,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE;IACxF,OAAO;QACL,SAAS,EAAE,2BAA2B;QACtC,kBAAkB,EAAE,KAAK;QACzB,UAAU,EAAE,CAAC;QACb,aAAa,EAAE,MAAM;QACrB,KAAK,EAAE,EAAE;QACT,YAAY,EAAE,EAAE;QAChB,aAAa,EAAE,EAAE;QACjB,QAAQ,EAAE,CAAC;gBACT,KAAK,EAAE,YAAY;gBACnB,MAAM,EAAE,sBAAsB;gBAC9B,SAAS,EAAE,mCAAmC,MAAM,wBAAwB;aAC7E,CAAC;QACF,UAAU,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,sBAAsB,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE;QACpG,WAAW,EAAE;YACX,mCAAmC,MAAM,GAAG;YAC5C,QAAQ,KAAK,QAAQ;gBACnB,CAAC,CAAC,kEAAkE;gBACpE,CAAC,CAAC,QAAQ,KAAK,MAAM;oBACnB,CAAC,CAAC,wFAAwF;oBAC1F,CAAC,CAAC,kCAAkC;SACzC;QACD,YAAY,EAAE,6BAAqB;QACnC,UAAU,EAAE,GAAG,CAAC,WAAW,EAAE;KAC9B,CAAC;AACJ,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client an integrator installs.
|
|
3
|
+
*
|
|
4
|
+
* Its contract is narrow and absolute: **every method returns, and none of
|
|
5
|
+
* them throws.** This code sits between a user and a signing screen. A wallet
|
|
6
|
+
* that shows an error dialog because a security service had a bad minute has
|
|
7
|
+
* made the user's day worse for no security benefit, and the user learns to
|
|
8
|
+
* dismiss the dialog — which is the outcome the whole product exists to avoid.
|
|
9
|
+
*
|
|
10
|
+
* So an unreachable Decentrys produces an assessment that says exactly that,
|
|
11
|
+
* in `unknowns` and in `explanation`, and the configured `failMode` decides
|
|
12
|
+
* what the integrator does about it. Nothing is invented, and nothing is
|
|
13
|
+
* silently reported as clean.
|
|
14
|
+
*/
|
|
15
|
+
import { type Assessment, type ThreatSignal } from './model';
|
|
16
|
+
import { type FailMode, type Policy, type PolicyAction } from './classify';
|
|
17
|
+
import { type FetchLike, type Transport } from './transport';
|
|
18
|
+
import { type SubjectRef } from './wire';
|
|
19
|
+
import { type SimulationResult, type TransactionExplanation } from './simulation';
|
|
20
|
+
export declare const SDK_VERSION = "0.1.0";
|
|
21
|
+
export interface DecentrysConfig {
|
|
22
|
+
apiKey: string;
|
|
23
|
+
baseUrl?: string;
|
|
24
|
+
/**
|
|
25
|
+
* What the integrator does when Decentrys is unreachable. `warn` is the
|
|
26
|
+
* consumer default; `closed` is for deployments that would rather stop.
|
|
27
|
+
*/
|
|
28
|
+
failMode?: FailMode;
|
|
29
|
+
/**
|
|
30
|
+
* A deadline, not a target. Exceeding it returns an unavailable assessment
|
|
31
|
+
* rather than leaving the user on a spinner holding a signature.
|
|
32
|
+
*/
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
/** Retries apply to idempotent lookups only. */
|
|
35
|
+
retries?: number;
|
|
36
|
+
policy?: Policy;
|
|
37
|
+
cacheTtlMs?: number;
|
|
38
|
+
cacheMaxEntries?: number;
|
|
39
|
+
/** Injected so the SDK works in a browser, an extension, RN and Node alike. */
|
|
40
|
+
fetch?: FetchLike;
|
|
41
|
+
/** For tests and for integrators who route through their own gateway. */
|
|
42
|
+
transport?: Transport;
|
|
43
|
+
}
|
|
44
|
+
export interface TransactionRequest {
|
|
45
|
+
chain: string;
|
|
46
|
+
from: string;
|
|
47
|
+
to?: string;
|
|
48
|
+
/** Base units, as a string. Numbers lose precision at these magnitudes. */
|
|
49
|
+
value?: string;
|
|
50
|
+
/** EVM calldata. */
|
|
51
|
+
data?: string;
|
|
52
|
+
/** Non-EVM families carry their serialized payload here instead. */
|
|
53
|
+
raw?: string;
|
|
54
|
+
/** The site requesting the signature, when there is one. */
|
|
55
|
+
origin?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface AddressRequest {
|
|
58
|
+
chain: string;
|
|
59
|
+
address: string;
|
|
60
|
+
}
|
|
61
|
+
export interface ApprovalRequest {
|
|
62
|
+
chain: string;
|
|
63
|
+
owner: string;
|
|
64
|
+
spender: string;
|
|
65
|
+
token: string;
|
|
66
|
+
/** Base units, or `unlimited`. */
|
|
67
|
+
amount?: string;
|
|
68
|
+
}
|
|
69
|
+
export interface DappRequest {
|
|
70
|
+
origin: string;
|
|
71
|
+
chain?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface ProtectResult {
|
|
74
|
+
subject: SubjectRef;
|
|
75
|
+
assessment: Assessment;
|
|
76
|
+
/** What the integrator's policy says to do. Advice — never enforced here. */
|
|
77
|
+
decision: {
|
|
78
|
+
action: PolicyAction;
|
|
79
|
+
reason: string;
|
|
80
|
+
};
|
|
81
|
+
/** True when served from the local cache rather than the network. */
|
|
82
|
+
cached: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Coverage signals the SDK refused to treat as risk. Surfaced rather than
|
|
85
|
+
* hidden: an integrator should be able to see the rule being applied.
|
|
86
|
+
*/
|
|
87
|
+
demotedSignals: string[];
|
|
88
|
+
}
|
|
89
|
+
export declare class Decentrys {
|
|
90
|
+
private readonly transport;
|
|
91
|
+
private readonly failMode;
|
|
92
|
+
private readonly policy;
|
|
93
|
+
private readonly cache;
|
|
94
|
+
constructor(config: DecentrysConfig);
|
|
95
|
+
/** Pre-sign analysis of a transaction the user is about to approve. */
|
|
96
|
+
assessTransaction(tx: TransactionRequest, options?: CallOptions): Promise<ProtectResult>;
|
|
97
|
+
/** What a contract *can* do — capabilities, proxy status, admin controls. */
|
|
98
|
+
scanContract(request: AddressRequest, options?: CallOptions): Promise<ProtectResult>;
|
|
99
|
+
screenAddress(request: AddressRequest, options?: CallOptions): Promise<ProtectResult>;
|
|
100
|
+
screenToken(request: AddressRequest, options?: CallOptions): Promise<ProtectResult>;
|
|
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
|
+
screenApproval(request: ApprovalRequest, options?: CallOptions): Promise<ProtectResult>;
|
|
109
|
+
assessDapp(request: DappRequest, options?: CallOptions): Promise<ProtectResult>;
|
|
110
|
+
/**
|
|
111
|
+
* The threat signals on a subject, without a classification.
|
|
112
|
+
*
|
|
113
|
+
* For integrators building their own presentation. An empty array means no
|
|
114
|
+
* signals were found — which is not the same as safe, and the SDK will not
|
|
115
|
+
* pretend otherwise on their behalf.
|
|
116
|
+
*/
|
|
117
|
+
getThreatSignals(request: AddressRequest, options?: CallOptions): Promise<ThreatSignal[]>;
|
|
118
|
+
/** What this transaction does, in the words a user would use. */
|
|
119
|
+
explainTransaction(tx: TransactionRequest, options?: CallOptions): Promise<TransactionExplanation>;
|
|
120
|
+
/** Execute the transaction against a fork and report what would change. */
|
|
121
|
+
simulateTransaction(tx: TransactionRequest, options?: CallOptions): Promise<SimulationResult>;
|
|
122
|
+
/** Drop cached evidence. Call after a user reports a stale result. */
|
|
123
|
+
clearCache(): void;
|
|
124
|
+
private assess;
|
|
125
|
+
private finish;
|
|
126
|
+
}
|
|
127
|
+
export interface CallOptions {
|
|
128
|
+
signal?: AbortSignal;
|
|
129
|
+
/** Bypass the cache for this call. The result still populates it. */
|
|
130
|
+
skipCache?: boolean;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|