Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"LICENSE",
"package-main.json"
],
"limit": "60.40 kB",
"limit": "61.052 kB",
"brotli": false,
"gzip": false
},
Expand All @@ -18,7 +18,7 @@
"path": [
"target/*/core.*"
],
"limit": "41.25 kB",
"limit": "41.55 kB",
"brotli": false,
"gzip": false
},
Expand All @@ -32,15 +32,15 @@
"LICENSE",
"package-main.json"
],
"limit": "18.30 kB",
"limit": "18.55 kB",
"gzip": true
},
{
"name": "cjs",
"path": [
"target/cjs"
],
"limit": "25.40 kB",
"limit": "25.55 kB",
"brotli": false,
"gzip": false
},
Expand All @@ -49,14 +49,14 @@
"path": [
"target/esm"
],
"limit": "21.00 kB",
"limit": "21.15 kB",
"brotli": false,
"gzip": false
},
{
"name": "libdefs",
"path": "target/dts",
"limit": "7.15 kB",
"limit": "7.16 kB",
"brotli": false,
"gzip": false
}
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ Temporary workaround to avoid refactoring is using `overrides` / `resolutions` i

Browser-compatible core build is available as `@webpod/ip/core`: it omits `node:os` dependency and polyfills the `Buffer` API.

### Strict mode
By default, the library is in the `strict` mode that rejects non-canonical embedded IPv4 in IPv6. Switch to legacy flow if needed:

```ts
import { Address } from '@webpod/ip'

Address.from('::ffff:5.6.7.8') // ok
Address.from('1:2:3:4::5.6.7.8') // throws

Address.strict = false
Address.from('1:2:3:4::5.6.7.8') // now it's fine
```

## Usage
The API is fully compatible with the latest `ip@2.0.1` but enforces stricter validations. See [coherence.md](./COHERENCE.md) for details.

Expand Down
23 changes: 13 additions & 10 deletions src/main/ts/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ export class Address {
return o
}

static strict = true

static from(raw: Raw): Address {
if (raw instanceof Address) return this.create(raw.big, raw.family, raw.raw)
if (typeof raw === 'string') return this.fromString(raw.toLowerCase())
Expand Down Expand Up @@ -334,18 +336,18 @@ export class Address {
if (addr === '0') return this.create(0n, 4, addr)

return addr.includes(':')
? this.fromIPv6(addr)
? this.fromIPv6(addr, this.strict)
: this.fromIPv4(addr)
}

private static fromIPv6(addr: string): Address {
private static fromIPv6(addr: string, strict?: boolean): Address {
const al = addr.length
const sep = addr.indexOf('::')
if (
al > IPV6_LEN_LIM ||
sep !== -1 && addr.indexOf('::', sep + 1) !== -1 // only one '::' allowed
)
throw new Error(`Invalid address: ${addr}`)
throw new Error(`Invalid address0: ${addr}`)

const groups: number[] = []
let p = 0, gc = -1
Expand All @@ -360,15 +362,16 @@ export class Address {
if (sep === -1 || (end !== sep && end !== sep + 1 + +last))
throw new Error(`Invalid address: ${addr}`)
gc = groups.length
} else if (last && v.includes('.')) {
// embedded IPv4
if (
groups.length > 6 ||
} else if (last && v.includes('.')) { // embedded IPv4
if (gc === -1 ? groups.length !== 6 : groups.length > 5)
throw new Error(`Invalid address: ${addr}`)

if (strict && (
gc === groups.length ||
(gc === -1 && groups.length !== 6) ||
groups[groups.length - 1] !== 0xffff ||
groups.slice(0, -1).some(x => x !== 0)
) throw new Error(`Invalid address: ${addr}`)
))
throw new Error(`Invalid address: ${addr}`)

const long = Address.normalizeToLong(v, true)
if (long === -1) throw new Error(`Invalid address: ${addr}`)
Expand All @@ -382,7 +385,7 @@ export class Address {
p = i + 1
}
const offset = 8 - groups.length
if (gc === -1 ? offset !== 0 : offset < 1) throw new Error(`Invalid address: ${addr}`)
if (gc === -1 ? offset !== 0 : offset < 1) throw new Error(`Invalid address4: ${addr}`)

let big = 0n
for (let i = 0; i < 8; i++) {
Expand Down
2 changes: 2 additions & 0 deletions src/test/js/export.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('core', () => {
assert.equal(typeof core.Address, 'function', 'core.Address')
assert.equal(typeof core.Address.fromPrefixLen, 'function', 'core.Address.fromPrefixLen')
assert.equal(typeof core.Address.parseCidr, 'function', 'core.Address.parseCidr')
assert.equal(typeof core.Address.strict, 'boolean', 'core.Address.strict')
assert.equal(typeof core.cidr, 'function', 'core.cidr')
assert.equal(typeof core.cidrSubnet, 'function', 'core.cidrSubnet')
assert.equal(typeof core.fromLong, 'function', 'core.fromLong')
Expand Down Expand Up @@ -40,6 +41,7 @@ describe('index', () => {
assert.equal(typeof index.Address, 'function', 'index.Address')
assert.equal(typeof index.Address.fromPrefixLen, 'function', 'index.Address.fromPrefixLen')
assert.equal(typeof index.Address.parseCidr, 'function', 'index.Address.parseCidr')
assert.equal(typeof index.Address.strict, 'boolean', 'index.Address.strict')
assert.equal(typeof index.address, 'function', 'index.address')
assert.equal(typeof index.addresses, 'function', 'index.addresses')
assert.equal(typeof index.cidr, 'function', 'index.cidr')
Expand Down
Loading