w-ipproxy 1.0.9 → 1.0.11

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.
@@ -709,7 +709,7 @@
709
709
  <br class="clear">
710
710
 
711
711
  <footer>
712
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 13:48:35 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
712
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 13:44:53 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
713
713
  </footer>
714
714
 
715
715
  <script>prettyPrint();</script>
@@ -185,6 +185,40 @@ function WIpProxy(opt = {}) {
185
185
  let prxsRaw = []
186
186
  let kpPrx = {}
187
187
 
188
+ //_normalizeIp: 正規化單一IP字串, 去除[]包裹, ipv4之port, 及ipv4-mapped-ipv6之'::ffff:'前綴
189
+ function _normalizeIp(v) {
190
+ let s = trim(v)
191
+ if (s === '') {
192
+ return ''
193
+ }
194
+ if (s[0] === '[') {
195
+ //形如'[::1]:8080'或'[2001:db8::1]'
196
+ let k = s.indexOf(']')
197
+ if (k > 0) {
198
+ s = s.slice(1, k)
199
+ }
200
+ }
201
+ else if (s.split(':').length === 2) {
202
+ //僅單一冒號才視為'1.2.3.4:port', 多冒號為ipv6不可截斷
203
+ s = s.split(':')[0]
204
+ }
205
+ //ipv4-mapped-ipv6, 如'::ffff:1.2.3.4', 統一還原為ipv4
206
+ let m = s.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/i)
207
+ if (m) {
208
+ s = m[1]
209
+ }
210
+ return s
211
+ }
212
+
213
+ //_originHasIp: 檢查origin(可能為'ip1, ip2, ...')逐段正規化後是否含有指定IP
214
+ function _originHasIp(origin, ip) {
215
+ if (!isestr(ip)) {
216
+ return false
217
+ }
218
+ let vs = map(sep(origin, ','), (v) => _normalizeIp(v))
219
+ return vs.indexOf(ip) >= 0
220
+ }
221
+
188
222
  //myRealIp: 本機真實公網IP, 用於匿名性檢測時比對代理egress是否洩漏
189
223
  let myRealIp = ''
190
224
  let myRealIpReady = false
@@ -199,8 +233,8 @@ function WIpProxy(opt = {}) {
199
233
  let res = await axios.get('https://httpbin.org/ip', { timeout: 10000 })
200
234
  let origin = get(res, 'data.origin', '')
201
235
  if (isestr(origin)) {
202
- //origin可能為"1.2.3.4""1.2.3.4:port", 取首個逗號前且去port
203
- myRealIp = origin.split(',')[0].trim().split(':')[0]
236
+ //origin可能為"1.2.3.4", "1.2.3.4:port", 或"::ffff:1.2.3.4"(部分環境如雲端runner為ipv4-mapped-ipv6), 取首個逗號前再正規化
237
+ myRealIp = _normalizeIp(sep(origin, ',')[0])
204
238
  }
205
239
  }
206
240
  catch (err) {
@@ -239,7 +273,7 @@ function WIpProxy(opt = {}) {
239
273
  if (!isestr(origin)) {
240
274
  return { ok: false, reason: 'anonymity non-json' }
241
275
  }
242
- if (origin.indexOf(myRealIp) >= 0) {
276
+ if (_originHasIp(origin, myRealIp)) {
243
277
  return { ok: false, reason: `leaks real IP` }
244
278
  }
245
279
  }
@@ -541,7 +575,7 @@ export default WIpProxy
541
575
  <br class="clear">
542
576
 
543
577
  <footer>
544
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 13:48:35 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
578
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 13:44:53 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
545
579
  </footer>
546
580
 
547
581
  <script>prettyPrint();</script>
package/docs/index.html CHANGED
@@ -71,7 +71,7 @@
71
71
  <br class="clear">
72
72
 
73
73
  <footer>
74
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 13:48:35 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
74
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 18 2026 13:44:53 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
75
75
  </footer>
76
76
 
77
77
  <script>prettyPrint();</script>
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "w-ipproxy",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "main": "dist/w-ipproxy.umd.js",
5
5
  "dependencies": {
6
6
  "@hapi/hapi": "^21.4.8",
7
7
  "axios": "^1.13.6",
8
8
  "cheerio": "^1.2.0",
9
- "wsemi": "^1.8.70"
9
+ "wsemi": "^1.8.75"
10
10
  },
11
11
  "devDependencies": {
12
12
  "w-package-tools": "^1.1.12"
package/src/WIpProxy.mjs CHANGED
@@ -138,6 +138,40 @@ function WIpProxy(opt = {}) {
138
138
  let prxsRaw = []
139
139
  let kpPrx = {}
140
140
 
141
+ //_normalizeIp: 正規化單一IP字串, 去除[]包裹, ipv4之port, 及ipv4-mapped-ipv6之'::ffff:'前綴
142
+ function _normalizeIp(v) {
143
+ let s = trim(v)
144
+ if (s === '') {
145
+ return ''
146
+ }
147
+ if (s[0] === '[') {
148
+ //形如'[::1]:8080'或'[2001:db8::1]'
149
+ let k = s.indexOf(']')
150
+ if (k > 0) {
151
+ s = s.slice(1, k)
152
+ }
153
+ }
154
+ else if (s.split(':').length === 2) {
155
+ //僅單一冒號才視為'1.2.3.4:port', 多冒號為ipv6不可截斷
156
+ s = s.split(':')[0]
157
+ }
158
+ //ipv4-mapped-ipv6, 如'::ffff:1.2.3.4', 統一還原為ipv4
159
+ let m = s.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/i)
160
+ if (m) {
161
+ s = m[1]
162
+ }
163
+ return s
164
+ }
165
+
166
+ //_originHasIp: 檢查origin(可能為'ip1, ip2, ...')逐段正規化後是否含有指定IP
167
+ function _originHasIp(origin, ip) {
168
+ if (!isestr(ip)) {
169
+ return false
170
+ }
171
+ let vs = map(sep(origin, ','), (v) => _normalizeIp(v))
172
+ return vs.indexOf(ip) >= 0
173
+ }
174
+
141
175
  //myRealIp: 本機真實公網IP, 用於匿名性檢測時比對代理egress是否洩漏
142
176
  let myRealIp = ''
143
177
  let myRealIpReady = false
@@ -152,8 +186,8 @@ function WIpProxy(opt = {}) {
152
186
  let res = await axios.get('https://httpbin.org/ip', { timeout: 10000 })
153
187
  let origin = get(res, 'data.origin', '')
154
188
  if (isestr(origin)) {
155
- //origin可能為"1.2.3.4""1.2.3.4:port", 取首個逗號前且去port
156
- myRealIp = origin.split(',')[0].trim().split(':')[0]
189
+ //origin可能為"1.2.3.4", "1.2.3.4:port", 或"::ffff:1.2.3.4"(部分環境如雲端runner為ipv4-mapped-ipv6), 取首個逗號前再正規化
190
+ myRealIp = _normalizeIp(sep(origin, ',')[0])
157
191
  }
158
192
  }
159
193
  catch (err) {
@@ -192,7 +226,7 @@ function WIpProxy(opt = {}) {
192
226
  if (!isestr(origin)) {
193
227
  return { ok: false, reason: 'anonymity non-json' }
194
228
  }
195
- if (origin.indexOf(myRealIp) >= 0) {
229
+ if (_originHasIp(origin, myRealIp)) {
196
230
  return { ok: false, reason: `leaks real IP` }
197
231
  }
198
232
  }
@@ -4,6 +4,37 @@ import https from 'https'
4
4
  import wip from '../src/WIpProxy.mjs'
5
5
 
6
6
 
7
+ //正規化單一IP字串, 去除[]包裹, ipv4之port, 及ipv4-mapped-ipv6之'::ffff:'前綴
8
+ function normalizeIp(v) {
9
+ let s = String(v || '').trim()
10
+ if (s === '') {
11
+ return ''
12
+ }
13
+ if (s[0] === '[') {
14
+ let k = s.indexOf(']')
15
+ if (k > 0) {
16
+ s = s.slice(1, k)
17
+ }
18
+ }
19
+ else if (s.split(':').length === 2) {
20
+ s = s.split(':')[0]
21
+ }
22
+ let m = s.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/i)
23
+ if (m) {
24
+ s = m[1]
25
+ }
26
+ return s
27
+ }
28
+
29
+ //檢查origin(可能為'ip1, ip2, ...')逐段正規化後是否含有指定IP
30
+ function originHasIp(origin, ip) {
31
+ if (ip === '') {
32
+ return false
33
+ }
34
+ return String(origin).split(',').map((v) => normalizeIp(v)).indexOf(ip) >= 0
35
+ }
36
+
37
+
7
38
  describe('anonymity (elite + anonymityCheck)', function() {
8
39
  this.timeout(240 * 1000) //4min, 因為getProxiesOnce加上獨立leak驗證本身就耗時
9
40
 
@@ -12,10 +43,10 @@ describe('anonymity (elite + anonymityCheck)', function() {
12
43
 
13
44
  before(async function() {
14
45
 
15
- //取本機真實公網IP
46
+ //取本機真實公網IP, 各環境格式不一(如雲端runner為'::ffff:1.2.3.4'), 須正規化後才可比對
16
47
  try {
17
48
  let r = await axios.get('https://httpbin.org/ip', { timeout: 10000 })
18
- myIp = r.data?.origin || ''
49
+ myIp = normalizeIp(String(r.data?.origin || '').split(',')[0])
19
50
  }
20
51
  catch (err) {
21
52
  this.skip()
@@ -57,7 +88,7 @@ describe('anonymity (elite + anonymityCheck)', function() {
57
88
  return { proxy: p.proxy, ok: true, reason: 'non-json, ignored' }
58
89
  }
59
90
  //回傳origin含本機真實IP即代表該代理洩漏
60
- let leaks = origin.indexOf(myIp) >= 0
91
+ let leaks = originHasIp(origin, myIp)
61
92
  return { proxy: p.proxy, ok: !leaks, origin }
62
93
  }
63
94
  catch (err) {
@@ -112,7 +143,7 @@ describe('anonymity (elite + anonymityCheck)', function() {
112
143
  }
113
144
  let bad = []
114
145
  for (let k of LEAK_IP_HEADERS) {
115
- if (k in hs && hs[k].indexOf(myIp) >= 0) {
146
+ if (k in hs && originHasIp(hs[k], myIp)) {
116
147
  bad.push(`${k}=${hs[k]}`)
117
148
  }
118
149
  }