• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

deno-libs / proxy_addr / 5756589196

pending completion
5756589196

push

github

talentlessguy
use ipaddrjs deno lib instead of esm.sh

11 of 23 branches covered (47.83%)

Branch coverage included in aggregate %.

20 of 20 new or added lines in 2 files covered. (100.0%)

87 of 165 relevant lines covered (52.73%)

1.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

50.28
/mod.ts
1
import { forwarded, isValid, parse } from './deps.ts'
1✔
2
import type { IPv4, IPv6, RequestWithConnection } from './deps.ts'
3
export { RequestWithConnection }
4

5
type Trust = ((addr: string, i?: number) => boolean) | string[] | string
6

7
const DIGIT_REGEXP = /^[0-9]+$/
1✔
8
/**
1✔
9
 * Pre-defined IP ranges.
10
 */
1✔
11
const IP_RANGES: Record<string, string[]> = {
1✔
12
  linklocal: ['169.254.0.0/16', 'fe80::/10'],
4✔
13
  loopback: ['127.0.0.1/8', '::1/128'],
4✔
14
  uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7'],
6✔
15
}
1✔
16
/**
1✔
17
 * Get all addresses in the request, optionally stopping
18
 * at the first untrusted.
19
 *
20
 * @param request
21
 * @param trust
22
 */
1✔
23
function alladdrs(req: RequestWithConnection, trust?: Trust) {
1✔
24
  // get addresses
6✔
25

26
  const addrs = forwarded(req)
6✔
27

28
  if (!trust) return addrs
6✔
29

30
  if (typeof trust !== 'function') trust = compile(trust)
8✔
31

32
  for (let i = 0; i < addrs.length - 1; i++) {
6✔
33
    if (trust(addrs[i], i)) continue
9✔
34
    addrs.length = i + 1
11✔
35
  }
11✔
36
  return addrs
8✔
37
}
6✔
38
/**
1✔
39
 * Compile argument into trust function.
40
 *
41
 * @param  val
42
 */
1✔
43
function compile(val: string | string[]) {
1✔
44
  let trust
3✔
45
  if (typeof val === 'string') trust = [val]
11✔
46
  else if (Array.isArray(val)) trust = val.slice()
4!
47
  else throw new TypeError('unsupported trust argument')
×
48

49
  for (let i = 0; i < trust.length; i++) {
3✔
50
    val = trust[i]
4✔
51

52
    if (!Object.prototype.hasOwnProperty.call(IP_RANGES, val)) {
4✔
53
      continue
4✔
54
    }
4!
55

56
    // Splice in pre-defined range
×
57
    val = IP_RANGES[val]
×
58
    trust.splice.apply(trust, [i, 1, ...val])
×
59
    i += val.length - 1
×
60
  }
×
61

62
  return compileTrust(compileRangeSubnets(trust))
3✔
63
}
3✔
64
/**
1✔
65
 * Compile `arr` elements into range subnets.
66
 */
1✔
67
function compileRangeSubnets(arr: string[]) {
1✔
68
  const rangeSubnets = new Array(arr.length)
3✔
69
  for (let i = 0; i < arr.length; i++) {
3✔
70
    rangeSubnets[i] = parseIPNotation(arr[i])
4✔
71
  }
4✔
72

73
  return rangeSubnets
3✔
74
}
3✔
75
/**
1✔
76
 * Compile range subnet array into trust function.
77
 *
78
 * @param rangeSubnets
79
 */
1✔
80
function compileTrust(rangeSubnets: (IPv4 | IPv6)[][]) {
1✔
81
  // Return optimized function based on length
3✔
82
  const len = rangeSubnets.length
3✔
83
  return len === 0
×
84
    ? trustNone
×
85
    : len === 1
×
86
    ? trustSingle(rangeSubnets[0])
×
87
    : trustMulti(rangeSubnets)
×
88
}
3✔
89
/**
1✔
90
 * Parse IP notation string into range subnet.
91
 *
92
 * @param {String} note
93
 * @private
94
 */
1✔
95
export function parseIPNotation(note: string) {
1✔
96
  const pos = note.lastIndexOf('/')
2✔
97
  const str = pos !== -1 ? note.substring(0, pos) : note
×
98

99
  if (!isValid(str)) throw new TypeError('invalid IP address: ' + str)
×
100

101
  let ip = parse(str) as IPv4 | IPv6
2✔
102

103
  if (pos === -1 && ip.kind() === 'ipv6') {
×
104
    ip = ip as IPv6
×
105

106
    if (ip.isIPv4MappedAddress()) ip = ip.toIPv4Address()
×
107
  }
×
108

109
  const max = ip.kind() === 'ipv6' ? 128 : 32
×
110

111
  let range: string | number | null =
×
112
    pos !== -1 ? note.substring(pos + 1, note.length) : null
×
113

114
  if (range === null) range = max
2!
115
  else if (DIGIT_REGEXP.test(range)) range = parseInt(range, 10)
×
116
  else if (ip.kind() === 'ipv4' && isValid(range)) range = parseNetmask(range)
×
117
  else range = null
×
118

119
  if (range && typeof range === 'number' && (range <= 0 || range > max)) {
×
120
    throw new TypeError('invalid range on address: ' + note)
×
121
  }
×
122

123
  return [ip, range]
8✔
124
}
2✔
125
/**
1✔
126
 * Parse netmask string into CIDR range.
127
 *
128
 * @param netmask
129
 * @private
130
 */
×
131
function parseNetmask(netmask: string) {
×
132
  const ip = parse(netmask)
×
133
  return ip.kind() === 'ipv4' ? ip.prefixLengthFromSubnetMask() : null
×
134
}
×
135
/**
1✔
136
 * Determine address of proxied request.
137
 *
138
 * @param request
139
 * @param trust
140
 * @public
141
 */
×
142
export function proxyaddr(req: RequestWithConnection, trust?: Trust) {
×
143
  const addrs = alladdrs(req, trust)
×
144

145
  return addrs[addrs.length - 1]
×
146
}
×
147
/**
1✔
148
 * Static trust function to trust nothing.
149
 */
1✔
150
const trustNone = () => false
1✔
151
/**
1✔
152
 * Compile trust function for multiple subnets.
153
 */
×
154
function trustMulti(subnets: (IPv4 | IPv6)[][]) {
×
155
  return function trust(addr: string) {
×
156
    if (!isValid(addr)) return false
×
157
    const ip = parse(addr)
×
158
    let ipconv
×
159
    const kind = ip.kind()
×
160
    for (let i = 0; i < subnets.length; i++) {
×
161
      const subnet = subnets[i]
×
162
      const subnetip = subnet[0]
×
163
      const subnetkind = subnetip.kind()
×
164
      const subnetrange = subnet[1]
×
165
      let trusted = ip
×
166
      if (kind !== subnetkind) {
×
167
        if (subnetkind === 'ipv4' && !(ip as IPv6).isIPv4MappedAddress()) {
×
168
          continue
×
169
        }
×
170

171
        if (!ipconv) {
×
172
          ipconv =
×
173
            subnetkind === 'ipv4'
×
174
              ? (ip as IPv6).toIPv4Address()
×
175
              : (ip as IPv6).toIPv4MappedAddress()
×
176
        }
×
177

178
        trusted = ipconv
×
179
      }
×
180
      // @ts-ignore types
×
181
      if (trusted.match(subnetip, subnetrange)) return true
×
182
    }
×
183
    return false
×
184
  }
×
185
}
×
186
/**
1✔
187
 * Compile trust function for single subnet.
188
 *
189
 * @param subnet
190
 */
1✔
191
function trustSingle(subnet: (IPv4 | IPv6)[]) {
1✔
192
  const subnetip = subnet[0]
2✔
193
  const subnetkind = subnetip.kind()
2✔
194
  const subnetisipv4 = subnetkind === 'ipv4'
2✔
195
  const subnetrange = subnet[1]
2✔
196

197
  return function trust(addr: string) {
2✔
198
    if (!isValid(addr)) return false
×
199

200
    let ip = parse(addr)
4✔
201
    const kind = ip.kind()
4✔
202

203
    if (kind !== subnetkind) {
×
204
      if (subnetisipv4 && !(ip as IPv6).isIPv4MappedAddress()) {
×
205
        // Incompatible IP addresses
×
206
        return false
×
207
      }
×
208

209
      // Convert IP to match subnet IP kind
×
210
      ip = subnetisipv4
×
211
        ? (ip as IPv6).toIPv4Address()
×
212
        : (ip as IPv6).toIPv4MappedAddress()
×
213
    }
×
214

215
    // @ts-ignore types
4✔
216
    return ip.match(subnetip, subnetrange)
4✔
217
  }
2✔
218
}
2✔
219

220
export { alladdrs as all }
1✔
221
export { compile }
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc