an illustrative note on the relationship logic between Enterprise and OSS based on a specific issue
If you've landed on this page, it's likely that your codebase relies on a widely deployed package that is currently struggling to maintain and is therefore vulnerable to certain scenarios. You have several strategies to follow:
- Ask the author for a fix
- Suggest a fix
- Apply patches locally
- Override internal registry versions
- Consider migrating to other solutions
- Just live with it
It's always a question of rational choice and cost of effort. In our case, the dependency tree profile required too much cascading fixes including external components, so we decided to take matters into our own hands — create a fully compatible alternative and apply it to our systems.
We found that the address classification error (private/public
) was due to a lack of strictness in the parser implementation, and the format error was interpreted as a sign of being in (or out of) the range. An additional complication was that the logic was divided into several auxiliary functions, but was combined differently in the compositions of parsing addresses of variouus notations.
Algorithm
In the first attempt, we simply made the pattern conditions strict, but as expected, we encountered the problem of reduced performance. It became immediately clear that stricter conditions would require a different algorithmic approach.
Here we moved step by step: from split()
to a string traverser, until we were satisfied with the ops/s metric.
The second significant change was related to the internal structure of stored addresses: Buffers
have been replaced with bigints
, which allowed us to greatly simplify and speed up the logic of comparisons and format translation. So it became easy to implement proper and full support for IPv6 for mask()
and subnet()
methods.
Address.from('0.0.0.0').big // 0n
The third major aspect of algorithmic improvements is related to the refactoring of special IP address ranges declarations. Instead of a set of magic conditions in helpers, a single source of truth is allocated now, and productive predicate functions are preinitialized.
const SPECIALS: Record<Special, string[]> = {
loopback: [
'127.0.0.0/8', // IPv4 loopback
'::1/128', // IPv6 loopback
],
// ...
]
const SPECIAL_MATCHERS: ((addr: Address) => Special | undefined)[] = []
for (const [cat, cidrs] of Object.entries(SPECIALS)) {
for (const cidr of cidrs) {
for (const x of ipv6fySubnet(cidr)) {
const subnet = Address.cidrSubnet(x)
SPECIAL_MATCHERS.push((addr: Address) => addr.family === subnet.family && subnet.contains(addr) ? (cat as Special) : undefined)
}
}
}
Compatibility
The key point was to keep backward compatibility. To ensure this, we completely borrowed the test suite from the upstream repo. For clarity, we also additionally investigated how similar other IP tools are in their operating logic. The conclusion is obvious: without additional modifications, it is impossible to achieve equivalent operating results in all scenarios. It was also important for us to make sure that the library functionality would not differ in different versions of runtimes and would be compatible with the browser environment. Polyfills and an extensive palette of smoke tests are right for this.
- https://github.com/webpod/ip/pull/4
- https://github.com/webpod/ip/blob/main/.github/workflows/ci.yaml
Drop-in
As we noted at the beginning, the process of cascading dependency updates is expensive, and some tools of the JS ecosystem provide workarounds for this. And the package should strive for seamlessness: so we added an additional pkg entry point to support legacy IDE-hinted require('ip/lib/ip.js)
statements.
- const ip = require('ip')
+ const ip = require('@webpod/ip')
Temporary workaround to avoid refactoring is using overrides
/ resolutions
in your package.json
:
{
"overrides": {
"ip": "@webpod/ip"
}
}
Next steps
There is no discussion here. The only correct way is to push changes to upstream, reduce the heterogeneity of dependencies. I hope that maintainers, when they have enough time, will support the community's efforts in this direction. Until that happens, there is @webpod/ip
Top comments (1)
Great write-up! 🚀 I like how you broke down the practical strategies — especially the part about balancing strictness vs. performance in the parsing logic. The bigint switch for comparisons is a really elegant improvement. Thanks for sharing the thought process behind the fixes!