@sproutboat/runtime 0.10.0 → 0.11.0
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 +6 -0
- package/package.json +1 -1
- package/src/client-ip.test.js +20 -1
- package/src/native-fetch-prelude.js +21 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @sproutboat/runtime
|
|
2
2
|
|
|
3
|
+
## 0.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d0e2c05: `request.cf` gains `httpProtocol`, `tlsVersion`, and `tlsCipher` (baronunread/sproutboat#128). Like `clientIp` (#163), these are only populated when the direct peer is listed in `SB_TRUSTED_PROXIES` and it forwarded the corresponding `x-sb-http-protocol` / `x-sb-tls-version` / `x-sb-tls-cipher` header — otherwise they stay absent rather than a guessed or faked value. `colo`, ASN, and geo fields remain out of scope (no GeoIP database on the box).
|
|
8
|
+
|
|
3
9
|
## 0.10.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/package.json
CHANGED
package/src/client-ip.test.js
CHANGED
|
@@ -17,7 +17,7 @@ const load = new Function(
|
|
|
17
17
|
"__envMap",
|
|
18
18
|
`${src.slice(start, end)}
|
|
19
19
|
const __sbEnv = (k) => __envMap[k];
|
|
20
|
-
return { __sbNormalizeIp, __sbIpInCidr, __sbIpTrusted, __sbClientIp, __sbSplitList };`,
|
|
20
|
+
return { __sbNormalizeIp, __sbIpInCidr, __sbIpTrusted, __sbClientIp, __sbSplitList, __sbTrustedProxyHeader };`,
|
|
21
21
|
);
|
|
22
22
|
const H = load(new Proxy({}, { get: (_, k) => env[k] }));
|
|
23
23
|
|
|
@@ -64,3 +64,22 @@ test("__sbClientIp: an untrusted peer cannot use XFF even if it sends one", () =
|
|
|
64
64
|
env = { SB_TRUSTED_PROXIES: "10.0.0.0/8" };
|
|
65
65
|
expect(H.__sbClientIp(req({ "x-sb-remote-addr": "198.51.100.9", "x-forwarded-for": "1.1.1.1" }))).toBe("198.51.100.9");
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
test("__sbTrustedProxyHeader: no trusted proxies configured means the header is ignored", () => {
|
|
69
|
+
env = {};
|
|
70
|
+
const r = req({ "x-sb-remote-addr": "127.0.0.1", "x-sb-http-protocol": "HTTP/2" });
|
|
71
|
+
expect(H.__sbTrustedProxyHeader(r, "x-sb-http-protocol")).toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("__sbTrustedProxyHeader: an untrusted direct peer cannot fake it", () => {
|
|
75
|
+
env = { SB_TRUSTED_PROXIES: "10.0.0.0/8" };
|
|
76
|
+
const r = req({ "x-sb-remote-addr": "198.51.100.9", "x-sb-tls-version": "TLSv1.3" });
|
|
77
|
+
expect(H.__sbTrustedProxyHeader(r, "x-sb-tls-version")).toBeUndefined();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("__sbTrustedProxyHeader: a trusted peer's value passes through, absent stays undefined", () => {
|
|
81
|
+
env = { SB_TRUSTED_PROXIES: "127.0.0.0/8" };
|
|
82
|
+
const r = req({ "x-sb-remote-addr": "127.0.0.1", "x-sb-http-protocol": "HTTP/2" });
|
|
83
|
+
expect(H.__sbTrustedProxyHeader(r, "x-sb-http-protocol")).toBe("HTTP/2");
|
|
84
|
+
expect(H.__sbTrustedProxyHeader(r, "x-sb-tls-cipher")).toBeUndefined();
|
|
85
|
+
});
|
|
@@ -1739,6 +1739,17 @@ function __sbClientIp(request) {
|
|
|
1739
1739
|
}
|
|
1740
1740
|
return peer;
|
|
1741
1741
|
}
|
|
1742
|
+
// #128 — httpProtocol/tlsVersion/tlsCipher: only a trusted front proxy can set
|
|
1743
|
+
// these (an untrusted direct peer could otherwise fake any value), so this
|
|
1744
|
+
// reuses the same SB_TRUSTED_PROXIES chain #163 already resolves clientIp
|
|
1745
|
+
// against. Undefined when no trusted proxy is configured or it sent nothing —
|
|
1746
|
+
// omitted, not faked, matching how clientIp itself falls back to the raw peer.
|
|
1747
|
+
function __sbTrustedProxyHeader(request, name) {
|
|
1748
|
+
const peer = __sbNormalizeIp(request.headers.get("x-sb-remote-addr") || "");
|
|
1749
|
+
const trusted = __sbSplitList(__sbEnv("SB_TRUSTED_PROXIES"));
|
|
1750
|
+
if (!trusted.length || !__sbIpTrusted(peer, trusted)) return undefined;
|
|
1751
|
+
return request.headers.get(name) || undefined;
|
|
1752
|
+
}
|
|
1742
1753
|
|
|
1743
1754
|
// #57 — ctx.waitUntil: work that must run before the turn completes but must
|
|
1744
1755
|
// not block the response the handler already built. `waitUntil(p)` just
|
|
@@ -1824,9 +1835,18 @@ async function __sbQueueWithDrain(result, tasks) {
|
|
|
1824
1835
|
globalThis.__sbEntry = function (handlers, request) {
|
|
1825
1836
|
const trigger = request.headers.get("x-sb-trigger");
|
|
1826
1837
|
if (!trigger) {
|
|
1827
|
-
// #163 — expose the
|
|
1838
|
+
// #163/#128 — expose the request metadata the edge already has, the
|
|
1839
|
+
// Workers way, before the handler runs. httpProtocol/tlsVersion/tlsCipher
|
|
1840
|
+
// are only set when a trusted front proxy forwarded them; colo, ASN and
|
|
1841
|
+
// geo fields stay absent everywhere (#128 explicitly rejects faking them).
|
|
1828
1842
|
const __cf = request.cf || {};
|
|
1829
1843
|
__cf.clientIp = __sbClientIp(request);
|
|
1844
|
+
const __httpProtocol = __sbTrustedProxyHeader(request, "x-sb-http-protocol");
|
|
1845
|
+
if (__httpProtocol) __cf.httpProtocol = __httpProtocol;
|
|
1846
|
+
const __tlsVersion = __sbTrustedProxyHeader(request, "x-sb-tls-version");
|
|
1847
|
+
if (__tlsVersion) __cf.tlsVersion = __tlsVersion;
|
|
1848
|
+
const __tlsCipher = __sbTrustedProxyHeader(request, "x-sb-tls-cipher");
|
|
1849
|
+
if (__tlsCipher) __cf.tlsCipher = __tlsCipher;
|
|
1830
1850
|
request.cf = __cf;
|
|
1831
1851
|
// #28 — per-invocation CPU time. One fetch turn per process (serial), so the
|
|
1832
1852
|
// process CPU delta across the handler is this invocation's CPU.
|