@abtnode/util 1.16.47-beta-20250805-140707-3a4df7fd → 1.16.47-beta-20250808-102837-d10f3b40

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.
@@ -3,6 +3,8 @@ const { toBase64, toBase58, toDid } = require('@ocap/util');
3
3
  const { joinURL } = require('ufo');
4
4
  const pRetry = require('p-retry');
5
5
  const debug = require('debug')('@abtnode/util:did-document');
6
+
7
+ const sleep = require('./sleep');
6
8
  const axios = require('./axios');
7
9
  const { encode: encodeBase32 } = require('./base32');
8
10
 
@@ -60,7 +62,27 @@ const update = ({ id, services, didRegistryUrl, wallet, alsoKnownAs = [], blockl
60
62
  });
61
63
  };
62
64
 
63
- const updateWithRetry = (...args) => pRetry(() => update(...args), { retries: 3 });
65
+ const DEFAULT_RETRY_COUNT = 6;
66
+ const getRetryCount = () => {
67
+ try {
68
+ let retryCount = parseInt(process.env.ABT_NODE_UPDATE_DID_DOCUMENT_RETRY_COUNT, 10);
69
+ retryCount = Number.isNaN(retryCount) ? DEFAULT_RETRY_COUNT : retryCount;
70
+ debug('get retry count', retryCount);
71
+ return retryCount;
72
+ } catch (error) {
73
+ debug('get retry count error', error);
74
+ return DEFAULT_RETRY_COUNT;
75
+ }
76
+ };
77
+
78
+ const updateWithRetry = (...args) =>
79
+ pRetry(() => update(...args), {
80
+ retries: getRetryCount(),
81
+ onFailedAttempt: async (error) => {
82
+ debug('update did document failed', error);
83
+ await sleep(10 * 1000);
84
+ },
85
+ });
64
86
 
65
87
  const getServerServices = ({ ips, wallet, domain }) => {
66
88
  const records = ips.map((ip) => ({
@@ -148,9 +170,12 @@ const updateBlockletDocument = ({
148
170
  };
149
171
 
150
172
  module.exports = {
173
+ DEFAULT_RETRY_COUNT,
151
174
  updateServerDocument,
152
175
  updateBlockletDocument,
153
176
  getDID,
154
177
  getServerServices,
155
178
  getBlockletServices,
179
+ updateWithRetry,
180
+ getRetryCount,
156
181
  };
package/lib/sanitize.js CHANGED
@@ -12,9 +12,12 @@ const sanitizeTag = (content) => {
12
12
 
13
13
  const sanitize = initSanitize({
14
14
  whiteList: {},
15
- stripIgnoreTag: true,
16
- onIgnoreTag: false,
15
+ stripIgnoreTag: false,
17
16
  stripIgnoreTagBody: [],
17
+ onIgnoreTag: (tag, html) => html.replace(/</g, '&lt;').replace(/>/g, '&gt;'),
18
+ onIgnoreTagAttr: (tag, name, value) => `${name}="${value}"`,
19
+ onTag: (tag, html) => html.replace(/</g, '&lt;').replace(/>/g, '&gt;'),
20
+ escapeHtml: (html) => html.replace(/</g, '&lt;').replace(/>/g, '&gt;'),
18
21
  });
19
22
  return sanitize(content);
20
23
  };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * SSRF Protector
3
+ */
4
+ const isPrivateIP = require('private-ip');
5
+ const isIP = require('is-ip');
6
+ const dns = require('dns');
7
+
8
+ // 允许的协议, 只允许 https
9
+ function isAllowedProtocol(protocol) {
10
+ if (!protocol) {
11
+ return false;
12
+ }
13
+
14
+ const _protocol = protocol.toLowerCase().replace(':', '');
15
+
16
+ return ['https'].includes(_protocol);
17
+ }
18
+
19
+ // 允许的 host, 只能是当前站点
20
+ function isAllowedReferer(referer, host) {
21
+ if (!referer || !host) {
22
+ return false;
23
+ }
24
+
25
+ try {
26
+ const refererUrl = new URL(referer);
27
+
28
+ // 检查referer是否来自当前站点
29
+ return refererUrl.hostname === host || refererUrl.hostname === host.split(':')[0]; // 处理端口号情况
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ const resolveDomain = (domain) => {
36
+ return new Promise((resolve, reject) => {
37
+ dns.lookup(domain, { all: false }, (error, address) => {
38
+ if (!error) {
39
+ resolve(address);
40
+ } else {
41
+ reject(error);
42
+ }
43
+ });
44
+ });
45
+ };
46
+
47
+ /**
48
+ * 解析域名并验证是否为私有IP
49
+ * @param {string} hostname - 要解析的域名
50
+ * @returns {Promise<boolean>} - 如果解析到的IP不是私有IP则返回true,否则返回false
51
+ */
52
+ async function resolveAndValidateHostname(hostname) {
53
+ if (!hostname) {
54
+ return false;
55
+ }
56
+
57
+ try {
58
+ // 使用 lookup 方法解析域名
59
+ const address = await resolveDomain(hostname);
60
+
61
+ // 检查是否为私有IP,返回相反的结果
62
+ return !isPrivateIP(address);
63
+ } catch (error) {
64
+ // DNS解析失败,拒绝访问
65
+ return false;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * 解析URL为IP地址并验证是否为私有IP
71
+ * @param {string} url - 要验证的URL
72
+ * @returns {Promise<boolean>} - 如果IP不是私有IP则返回true,否则返回false
73
+ */
74
+ async function isAllowedURL(url) {
75
+ if (!url) {
76
+ return false;
77
+ }
78
+
79
+ try {
80
+ const { hostname, protocol } = new URL(url);
81
+
82
+ if (!isAllowedProtocol(protocol)) {
83
+ return false;
84
+ }
85
+
86
+ // 如果是IP地址,直接验证
87
+ if (isIP(hostname)) {
88
+ return !isPrivateIP(hostname);
89
+ }
90
+
91
+ // 测试和开发环境不进行 DNS 解析验证
92
+ if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'development') {
93
+ return true;
94
+ }
95
+
96
+ // 如果是域名,进行DNS解析
97
+ const result = await resolveAndValidateHostname(hostname);
98
+
99
+ return result;
100
+ } catch (error) {
101
+ // URL解析失败
102
+ return false;
103
+ }
104
+ }
105
+
106
+ module.exports = {
107
+ isAllowedProtocol,
108
+ isAllowedReferer,
109
+ isAllowedURL,
110
+ resolveAndValidateHostname,
111
+ };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.47-beta-20250805-140707-3a4df7fd",
6
+ "version": "1.16.47-beta-20250808-102837-d10f3b40",
7
7
  "description": "ArcBlock's JavaScript utility",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -18,18 +18,18 @@
18
18
  "author": "polunzh <polunzh@gmail.com> (http://github.com/polunzh)",
19
19
  "license": "Apache-2.0",
20
20
  "dependencies": {
21
- "@abtnode/constant": "1.16.47-beta-20250805-140707-3a4df7fd",
22
- "@abtnode/db-cache": "1.16.47-beta-20250805-140707-3a4df7fd",
23
- "@arcblock/did": "1.21.0",
21
+ "@abtnode/constant": "1.16.47-beta-20250808-102837-d10f3b40",
22
+ "@abtnode/db-cache": "1.16.47-beta-20250808-102837-d10f3b40",
23
+ "@arcblock/did": "1.21.2",
24
24
  "@arcblock/pm2": "^6.0.12",
25
- "@blocklet/constant": "1.16.47-beta-20250805-140707-3a4df7fd",
25
+ "@blocklet/constant": "1.16.47-beta-20250808-102837-d10f3b40",
26
26
  "@blocklet/error": "^0.2.5",
27
- "@blocklet/meta": "1.16.47-beta-20250805-140707-3a4df7fd",
27
+ "@blocklet/meta": "1.16.47-beta-20250808-102837-d10f3b40",
28
28
  "@blocklet/xss": "^0.2.3",
29
- "@ocap/client": "1.21.0",
30
- "@ocap/mcrypto": "1.21.0",
31
- "@ocap/util": "1.21.0",
32
- "@ocap/wallet": "1.21.0",
29
+ "@ocap/client": "1.21.2",
30
+ "@ocap/mcrypto": "1.21.2",
31
+ "@ocap/util": "1.21.2",
32
+ "@ocap/wallet": "1.21.2",
33
33
  "archiver": "^7.0.1",
34
34
  "axios": "^1.7.9",
35
35
  "axios-mock-adapter": "^2.1.0",
@@ -63,6 +63,7 @@
63
63
  "p-retry": "^4.6.2",
64
64
  "p-wait-for": "^3.2.0",
65
65
  "parallel-transform": "^1.2.0",
66
+ "private-ip": "^2.3.4",
66
67
  "public-ip": "^4.0.4",
67
68
  "pump": "^3.0.0",
68
69
  "request-ip": "^3.3.0",
@@ -89,5 +90,5 @@
89
90
  "fs-extra": "^11.2.0",
90
91
  "jest": "^29.7.0"
91
92
  },
92
- "gitHead": "eee7c229e99cc764a20be2ce0347d6895766a8a0"
93
+ "gitHead": "545f4b619e4e872f3cb6645aa95a0c22c06b58d0"
93
94
  }