@enbox/dids 0.0.6 → 0.0.8
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 +3 -2
- package/dist/browser.mjs +1 -1
- package/dist/browser.mjs.map +3 -3
- package/dist/esm/did.js +4 -0
- package/dist/esm/did.js.map +1 -1
- package/dist/esm/methods/did-dht-pkarr.js +3 -2
- package/dist/esm/methods/did-dht-pkarr.js.map +1 -1
- package/dist/esm/methods/did-ion.js +3 -2
- package/dist/esm/methods/did-ion.js.map +1 -1
- package/dist/esm/methods/did-web.js +64 -1
- package/dist/esm/methods/did-web.js.map +1 -1
- package/dist/esm/resolver/resolver-cache-level.js +9 -0
- package/dist/esm/resolver/resolver-cache-level.js.map +1 -1
- package/dist/esm/resolver/resolver-cache-memory.js +8 -0
- package/dist/esm/resolver/resolver-cache-memory.js.map +1 -1
- package/dist/esm/resolver/resolver-cache-noop.js +3 -0
- package/dist/esm/resolver/resolver-cache-noop.js.map +1 -1
- package/dist/esm/resolver/universal-resolver.js +18 -0
- package/dist/esm/resolver/universal-resolver.js.map +1 -1
- package/dist/types/did.d.ts.map +1 -1
- package/dist/types/methods/did-dht-pkarr.d.ts.map +1 -1
- package/dist/types/methods/did-ion.d.ts.map +1 -1
- package/dist/types/methods/did-web.d.ts.map +1 -1
- package/dist/types/resolver/resolver-cache-level.d.ts +7 -0
- package/dist/types/resolver/resolver-cache-level.d.ts.map +1 -1
- package/dist/types/resolver/resolver-cache-memory.d.ts +4 -0
- package/dist/types/resolver/resolver-cache-memory.d.ts.map +1 -1
- package/dist/types/resolver/resolver-cache-noop.d.ts.map +1 -1
- package/dist/types/resolver/universal-resolver.d.ts +10 -0
- package/dist/types/resolver/universal-resolver.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/did.ts +3 -0
- package/src/methods/did-dht-pkarr.ts +3 -2
- package/src/methods/did-ion.ts +3 -2
- package/src/methods/did-web.ts +52 -1
- package/src/resolver/resolver-cache-level.ts +10 -0
- package/src/resolver/resolver-cache-memory.ts +7 -0
- package/src/resolver/resolver-cache-noop.ts +3 -0
- package/src/resolver/universal-resolver.ts +16 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enbox/dids",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "TBD DIDs library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/esm/index.js",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"@decentralized-identity/ion-sdk": "1.0.4",
|
|
82
82
|
"@dnsquery/dns-packet": "6.1.1",
|
|
83
|
-
"@enbox/common": "0.0.
|
|
84
|
-
"@enbox/crypto": "0.0.
|
|
83
|
+
"@enbox/common": "0.0.6",
|
|
84
|
+
"@enbox/crypto": "0.0.7",
|
|
85
85
|
"abstract-level": "1.0.4",
|
|
86
86
|
"bencode": "4.0.0",
|
|
87
87
|
"level": "8.0.1",
|
package/src/did.ts
CHANGED
|
@@ -145,6 +145,9 @@ export class Did {
|
|
|
145
145
|
// Return null if the input string is empty or not provided.
|
|
146
146
|
if (!didUri) {return null;}
|
|
147
147
|
|
|
148
|
+
// Guard against ReDoS: reject unreasonably long URIs before executing the regex.
|
|
149
|
+
if (didUri.length > 2048) {return null;}
|
|
150
|
+
|
|
148
151
|
// Execute the regex pattern on the input string to extract URI components.
|
|
149
152
|
const match = Did.DID_URI_PATTERN.exec(didUri);
|
|
150
153
|
|
|
@@ -44,7 +44,7 @@ export async function pkarrGet({ gatewayUri, publicKeyBytes }: {
|
|
|
44
44
|
// Transmit the Get request to the DID DHT Gateway or Pkarr Relay and get the response.
|
|
45
45
|
let response: Response;
|
|
46
46
|
try {
|
|
47
|
-
response = await fetch(url, { method: 'GET' });
|
|
47
|
+
response = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(30_000) });
|
|
48
48
|
|
|
49
49
|
if (!response.ok) {
|
|
50
50
|
throw new DidError(DidErrorCode.NotFound, `Pkarr record not found for: ${identifier}`);
|
|
@@ -113,7 +113,8 @@ export async function pkarrPut({ gatewayUri, bep44Message }: {
|
|
|
113
113
|
response = await fetch(url, {
|
|
114
114
|
method : 'PUT',
|
|
115
115
|
headers : { 'Content-Type': 'application/octet-stream' },
|
|
116
|
-
body
|
|
116
|
+
body,
|
|
117
|
+
signal : AbortSignal.timeout(30_000),
|
|
117
118
|
});
|
|
118
119
|
|
|
119
120
|
} catch (error: any) {
|
package/src/methods/did-ion.ts
CHANGED
|
@@ -600,7 +600,8 @@ export class DidIon extends DidMethod {
|
|
|
600
600
|
method : 'POST',
|
|
601
601
|
mode : 'cors',
|
|
602
602
|
headers : { 'Content-Type': 'application/json' },
|
|
603
|
-
body : JSON.stringify(createOperation)
|
|
603
|
+
body : JSON.stringify(createOperation),
|
|
604
|
+
signal : AbortSignal.timeout(30_000),
|
|
604
605
|
});
|
|
605
606
|
|
|
606
607
|
// Return the result of processing the Create operation, including the updated DID metadata
|
|
@@ -681,7 +682,7 @@ export class DidIon extends DidMethod {
|
|
|
681
682
|
});
|
|
682
683
|
|
|
683
684
|
// Attempt to retrieve the DID document and metadata from the Sidetree node.
|
|
684
|
-
const response = await fetch(resolutionUrl);
|
|
685
|
+
const response = await fetch(resolutionUrl, { signal: AbortSignal.timeout(30_000) });
|
|
685
686
|
|
|
686
687
|
// If the DID document was not found, return an error.
|
|
687
688
|
if (!response.ok) {
|
package/src/methods/did-web.ts
CHANGED
|
@@ -4,6 +4,46 @@ import { Did } from '../did.js';
|
|
|
4
4
|
import { DidMethod } from './did-method.js';
|
|
5
5
|
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';
|
|
6
6
|
|
|
7
|
+
/** Default fetch timeout for DID document retrieval (30 seconds). */
|
|
8
|
+
const FETCH_TIMEOUT_MS = 30_000;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns `true` when the hostname is a private, loopback, or link-local
|
|
12
|
+
* address. Used to block SSRF via crafted `did:web` identifiers such as
|
|
13
|
+
* `did:web:169.254.169.254` or `did:web:localhost`.
|
|
14
|
+
*/
|
|
15
|
+
function isPrivateHostname(hostname: string): boolean {
|
|
16
|
+
const h = hostname.toLowerCase();
|
|
17
|
+
|
|
18
|
+
if (h === 'localhost' || h === 'localhost.') { return true; }
|
|
19
|
+
|
|
20
|
+
// IPv4 literal check
|
|
21
|
+
const parts = h.split('.');
|
|
22
|
+
if (parts.length === 4) {
|
|
23
|
+
const octets = parts.map(Number);
|
|
24
|
+
if (octets.every((o) => !Number.isNaN(o) && o >= 0 && o <= 255)) {
|
|
25
|
+
const [a, b] = octets;
|
|
26
|
+
if (a === 10) { return true; } // 10.0.0.0/8
|
|
27
|
+
if (a === 172 && b >= 16 && b <= 31) { return true; } // 172.16.0.0/12
|
|
28
|
+
if (a === 192 && b === 168) { return true; } // 192.168.0.0/16
|
|
29
|
+
if (a === 127) { return true; } // 127.0.0.0/8
|
|
30
|
+
if (a === 169 && b === 254) { return true; } // 169.254.0.0/16
|
|
31
|
+
if (a === 0) { return true; } // 0.0.0.0/8
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// IPv6 literal check (bracket-wrapped by URL parser)
|
|
36
|
+
let v6 = h;
|
|
37
|
+
if (v6.startsWith('[') && v6.endsWith(']')) { v6 = v6.slice(1, -1); }
|
|
38
|
+
if (v6.includes(':')) {
|
|
39
|
+
if (v6 === '::1' || v6 === '::' || v6 === '::0') { return true; }
|
|
40
|
+
if (v6.startsWith('fe80:') || v6.startsWith('fe80%')) { return true; }
|
|
41
|
+
if (v6.startsWith('fc') || v6.startsWith('fd')) { return true; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
7
47
|
/**
|
|
8
48
|
* The `DidWeb` class provides an implementation of the `did:web` DID method.
|
|
9
49
|
*
|
|
@@ -71,8 +111,19 @@ export class DidWeb extends DidMethod {
|
|
|
71
111
|
`${baseUrl}/.well-known/did.json`;
|
|
72
112
|
|
|
73
113
|
try {
|
|
114
|
+
// Block requests to private/loopback/link-local addresses (SSRF protection).
|
|
115
|
+
const parsedUrl = new URL(didDocumentUrl);
|
|
116
|
+
if (isPrivateHostname(parsedUrl.hostname)) {
|
|
117
|
+
return {
|
|
118
|
+
...EMPTY_DID_RESOLUTION_RESULT,
|
|
119
|
+
didResolutionMetadata: { error: 'notFound' }
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
74
123
|
// Perform an HTTP GET request to obtain the DID document.
|
|
75
|
-
const response = await fetch(didDocumentUrl
|
|
124
|
+
const response = await fetch(didDocumentUrl, {
|
|
125
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
126
|
+
});
|
|
76
127
|
|
|
77
128
|
// If the response status code is not 200, return an error.
|
|
78
129
|
if (!response.ok) {throw new Error('HTTP error status code returned');}
|
|
@@ -87,6 +87,16 @@ export class DidResolverCacheLevel implements DidResolverCache {
|
|
|
87
87
|
this.ttl = ms(ttl);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Opens the underlying LevelDB store.
|
|
92
|
+
* Calling `open()` on an already-open store is a safe no-op.
|
|
93
|
+
*
|
|
94
|
+
* @returns A promise that resolves when the store is ready for use.
|
|
95
|
+
*/
|
|
96
|
+
open(): Promise<void> {
|
|
97
|
+
return this.cache.open();
|
|
98
|
+
}
|
|
99
|
+
|
|
90
100
|
/**
|
|
91
101
|
* Retrieves a DID resolution result from the cache.
|
|
92
102
|
*
|
|
@@ -26,6 +26,13 @@ export class DidResolverCacheMemory implements DidResolverCache {
|
|
|
26
26
|
this.cache = new TtlCache({ ttl: ms(ttl) });
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* This method is a no-op since in-memory stores are always ready.
|
|
31
|
+
*/
|
|
32
|
+
public async open(): Promise<void> {
|
|
33
|
+
// No-op since there is no underlying store to open.
|
|
34
|
+
}
|
|
35
|
+
|
|
29
36
|
/**
|
|
30
37
|
* Retrieves a DID resolution result from the cache.
|
|
31
38
|
*
|
|
@@ -8,6 +8,9 @@ import type { DidResolverCache } from '../types/did-resolution.js';
|
|
|
8
8
|
* potential for this library to be used in as many JS runtimes as possible.
|
|
9
9
|
*/
|
|
10
10
|
export const DidResolverCacheNoop: DidResolverCache = {
|
|
11
|
+
open(): Promise<void> {
|
|
12
|
+
return Promise.resolve();
|
|
13
|
+
},
|
|
11
14
|
get(_key: string): Promise<DidResolutionResult | void> {
|
|
12
15
|
return Promise.resolve(undefined);
|
|
13
16
|
},
|
|
@@ -86,6 +86,22 @@ export class UniversalResolver implements DidResolver, DidUrlDereferencer {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Opens the resolver's cache, acquiring any resources needed.
|
|
91
|
+
* Must be called before resolving DIDs if using a cache that requires initialization (e.g., LevelDB).
|
|
92
|
+
*/
|
|
93
|
+
public async open(): Promise<void> {
|
|
94
|
+
await this.cache.open();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Closes the resolver's cache, releasing any resources held.
|
|
99
|
+
* Should be called during application shutdown.
|
|
100
|
+
*/
|
|
101
|
+
public async close(): Promise<void> {
|
|
102
|
+
await this.cache.close();
|
|
103
|
+
}
|
|
104
|
+
|
|
89
105
|
/**
|
|
90
106
|
* Resolves a DID to a DID Resolution Result.
|
|
91
107
|
*
|