@blamejs/core 0.5.4 → 0.5.5
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.
- package/CHANGELOG.md +1 -0
- package/lib/middleware/security-headers.js +7 -1
- package/lib/ssrf-guard.js +31 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,7 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.5.x
|
|
10
10
|
|
|
11
|
+
- **0.5.4** (2026-04-30) — close SSRF DNS-rebinding window with pinned outbound DNS
|
|
11
12
|
- **0.5.3** (2026-04-30) — security cleanup: trustProxy primitive + Vary merge + HSTS gate
|
|
12
13
|
- **0.5.2** (2026-04-30) — b.breakGlass: passkey factor + service-account bypass + admin tools
|
|
13
14
|
- **0.5.1** (2026-04-30) — b.breakGlass: per-cell encryption + context binding + migrate
|
|
@@ -46,10 +46,16 @@ var DEFAULT_PERMISSIONS = [
|
|
|
46
46
|
"screen-wake-lock=()", "sync-xhr=()", "usb=()", "web-share=()", "xr-spatial-tracking=()",
|
|
47
47
|
];
|
|
48
48
|
|
|
49
|
+
// Strict CSP — no 'unsafe-inline' on script-src OR style-src. Operators
|
|
50
|
+
// with inline styles / scripts wire `b.middleware.cspNonce()` and use
|
|
51
|
+
// `{{ cspNonce }}` in their views (or req.cspNonce in handlers); the
|
|
52
|
+
// nonce hooks into the policy via the `csp` opt or a custom value.
|
|
53
|
+
// Operators in transitional environments who genuinely need inline
|
|
54
|
+
// styles set `csp` explicitly to the loose form.
|
|
49
55
|
var DEFAULT_CSP =
|
|
50
56
|
"default-src 'self'; " +
|
|
51
57
|
"script-src 'self'; " +
|
|
52
|
-
"style-src 'self'
|
|
58
|
+
"style-src 'self'; " +
|
|
53
59
|
"img-src 'self' data:; " +
|
|
54
60
|
"font-src 'self'; " +
|
|
55
61
|
"connect-src 'self'; " +
|
package/lib/ssrf-guard.js
CHANGED
|
@@ -94,6 +94,23 @@ var IPV6_PRIVATE_PREFIX = _ipv6ToBytes("fc00::");
|
|
|
94
94
|
var IPV6_LINK_LOCAL_PREFIX = _ipv6ToBytes("fe80::");
|
|
95
95
|
var IPV6_DOC_PREFIX = _ipv6ToBytes("2001:db8::"); // documentation
|
|
96
96
|
var IPV6_V4_MAPPED_PREFIX = _ipv6ToBytes("::ffff:0:0"); // IPv4-mapped (::ffff:0:0/96)
|
|
97
|
+
// Multicast ff00::/8 — RFC 4291 §2.7. Refused for outbound HTTP same as
|
|
98
|
+
// IPv4 multicast 224/4 — there's no legitimate fetch from a multicast
|
|
99
|
+
// destination on a production gateway.
|
|
100
|
+
var IPV6_MULTICAST_PREFIX = _ipv6ToBytes("ff00::");
|
|
101
|
+
// NAT64 well-known prefix 64:ff9b::/96 — RFC 6052. Translates to IPv4
|
|
102
|
+
// embedded in the lower 32 bits; the framework refuses since the
|
|
103
|
+
// underlying v4 address is what gets contacted and might be private.
|
|
104
|
+
// (Operators with NAT64 deployments flip allowInternal.)
|
|
105
|
+
var IPV6_NAT64_PREFIX = _ipv6ToBytes("64:ff9b::");
|
|
106
|
+
// 6to4 2002::/16 — RFC 3056. Carries an embedded IPv4 in bytes 2-5;
|
|
107
|
+
// hostile use is to tunnel through to a v4 destination the v4 guard
|
|
108
|
+
// would have refused. Refused.
|
|
109
|
+
var IPV6_6TO4_PREFIX = _ipv6ToBytes("2002::");
|
|
110
|
+
// Discard prefix 100::/64 — RFC 6666. Dropped by routers; fetching
|
|
111
|
+
// from it is operationally meaningless and a likely sign of mis-config
|
|
112
|
+
// or attempted exfil to a sinkhole.
|
|
113
|
+
var IPV6_DISCARD_PREFIX = _ipv6ToBytes("100::");
|
|
97
114
|
|
|
98
115
|
// ---- Cloud metadata addresses (string-equality, exact match) ----
|
|
99
116
|
var CLOUD_METADATA_IPS = [
|
|
@@ -235,6 +252,20 @@ function classify(ip) {
|
|
|
235
252
|
if (_ipv6PrefixMatch(IPV6_LINK_LOCAL_PREFIX, 10, bytes)) return "link-local";
|
|
236
253
|
if (_ipv6PrefixMatch(IPV6_PRIVATE_PREFIX, 7, bytes)) return "private";
|
|
237
254
|
if (_ipv6PrefixMatch(IPV6_DOC_PREFIX, 32, bytes)) return "reserved";
|
|
255
|
+
if (_ipv6PrefixMatch(IPV6_MULTICAST_PREFIX, 8, bytes)) return "reserved";
|
|
256
|
+
if (_ipv6PrefixMatch(IPV6_DISCARD_PREFIX, 64, bytes)) return "reserved";
|
|
257
|
+
// 6to4 (2002::/16) embeds a v4 address in bytes 2–5; classify the
|
|
258
|
+
// embedded v4 so a 6to4-wrapped private/metadata address is refused
|
|
259
|
+
// for the same reason its v4 form would be.
|
|
260
|
+
if (_ipv6PrefixMatch(IPV6_6TO4_PREFIX, 16, bytes)) {
|
|
261
|
+
var v4From6to4 = bytes[2] + "." + bytes[3] + "." + bytes[4] + "." + bytes[5];
|
|
262
|
+
return classify(v4From6to4) || "reserved";
|
|
263
|
+
}
|
|
264
|
+
// NAT64 well-known prefix (64:ff9b::/96) embeds v4 in bytes 12–15.
|
|
265
|
+
if (_ipv6PrefixMatch(IPV6_NAT64_PREFIX, 96, bytes)) {
|
|
266
|
+
var v4FromNat64 = bytes[12] + "." + bytes[13] + "." + bytes[14] + "." + bytes[15];
|
|
267
|
+
return classify(v4FromNat64) || "reserved";
|
|
268
|
+
}
|
|
238
269
|
// IPv4-mapped addresses (::ffff:a.b.c.d/96): re-classify the v4 portion.
|
|
239
270
|
if (_ipv6PrefixMatch(IPV6_V4_MAPPED_PREFIX, 96, bytes)) {
|
|
240
271
|
var mappedV4 = bytes[12] + "." + bytes[13] + "." + bytes[14] + "." + bytes[15];
|