@okostream/mcp 0.5.0 → 0.5.1
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 +4 -0
- package/dist/networkPolicy.js +18 -0
- package/dist/server.js +1 -1
- package/dist/tools.js +8 -33
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.1 - 2026-07-10
|
|
4
|
+
|
|
5
|
+
- Replaced hand-maintained remote-image IP ranges with a tested public-unicast policy covering IPv4, IPv6, mapped, reserved, documentation, benchmark, multicast, and private address classes.
|
|
6
|
+
|
|
3
7
|
## 0.5.0 - 2026-07-10
|
|
4
8
|
|
|
5
9
|
- Isolated every API key behind a synthetic Firebase identity with immediate revocation across Firestore, main RTDB, and chat RTDB.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import ipaddr from "ipaddr.js";
|
|
2
|
+
export function normalizeIpLiteralHostname(hostname) {
|
|
3
|
+
return hostname.startsWith("[") && hostname.endsWith("]")
|
|
4
|
+
? hostname.slice(1, -1)
|
|
5
|
+
: hostname;
|
|
6
|
+
}
|
|
7
|
+
export function ipLiteralFamily(address) {
|
|
8
|
+
const normalized = normalizeIpLiteralHostname(address);
|
|
9
|
+
if (!ipaddr.isValid(normalized))
|
|
10
|
+
return 0;
|
|
11
|
+
return ipaddr.parse(normalized).kind() === "ipv4" ? 4 : 6;
|
|
12
|
+
}
|
|
13
|
+
export function isPublicUnicastAddress(address) {
|
|
14
|
+
const normalized = normalizeIpLiteralHostname(address);
|
|
15
|
+
if (!ipaddr.isValid(normalized))
|
|
16
|
+
return false;
|
|
17
|
+
return ipaddr.parse(normalized).range() === "unicast";
|
|
18
|
+
}
|
package/dist/server.js
CHANGED
package/dist/tools.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { lookup } from "node:dns/promises";
|
|
2
2
|
import { stat, readFile, realpath } from "node:fs/promises";
|
|
3
|
-
import { isIP } from "node:net";
|
|
4
3
|
import { homedir } from "node:os";
|
|
5
4
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
5
|
import { randomUUID } from "node:crypto";
|
|
@@ -12,6 +11,7 @@ import { get, endBefore, limitToLast, orderByKey, push, query as dbQuery, ref, r
|
|
|
12
11
|
import { httpsCallable } from "firebase/functions";
|
|
13
12
|
import { ensureAuth, currentScopes } from "./auth.js";
|
|
14
13
|
import { env } from "./env.js";
|
|
14
|
+
import { ipLiteralFamily, isPublicUnicastAddress, normalizeIpLiteralHostname, } from "./networkPolicy.js";
|
|
15
15
|
import { chatRtdb, firestore, functions, rtdb } from "./firebase.js";
|
|
16
16
|
import { csvCell, requireConfirm, safeString, serialize, textResult } from "./utils.js";
|
|
17
17
|
import { detectAndNormalize, extractIframeSrc } from "./videoUrl.js";
|
|
@@ -372,33 +372,6 @@ function isBlockedHostname(hostname) {
|
|
|
372
372
|
const host = hostname.toLowerCase();
|
|
373
373
|
return host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local");
|
|
374
374
|
}
|
|
375
|
-
function isBlockedIp(address) {
|
|
376
|
-
const normalized = address.toLowerCase().startsWith("::ffff:")
|
|
377
|
-
? address.slice(7)
|
|
378
|
-
: address;
|
|
379
|
-
const version = isIP(normalized);
|
|
380
|
-
if (version === 4) {
|
|
381
|
-
const [a = -1, b = -1] = normalized.split(".").map((part) => Number(part));
|
|
382
|
-
return a === 0
|
|
383
|
-
|| a === 10
|
|
384
|
-
|| a === 127
|
|
385
|
-
|| (a === 100 && b >= 64 && b <= 127)
|
|
386
|
-
|| (a === 169 && b === 254)
|
|
387
|
-
|| (a === 172 && b >= 16 && b <= 31)
|
|
388
|
-
|| (a === 192 && b === 168)
|
|
389
|
-
|| (a === 198 && (b === 18 || b === 19))
|
|
390
|
-
|| a >= 224;
|
|
391
|
-
}
|
|
392
|
-
if (version === 6) {
|
|
393
|
-
return normalized === "::"
|
|
394
|
-
|| normalized === "::1"
|
|
395
|
-
|| normalized.startsWith("fc")
|
|
396
|
-
|| normalized.startsWith("fd")
|
|
397
|
-
|| normalized.startsWith("fe80:")
|
|
398
|
-
|| normalized.startsWith("ff");
|
|
399
|
-
}
|
|
400
|
-
return false;
|
|
401
|
-
}
|
|
402
375
|
async function resolveRemoteImageAddress(url) {
|
|
403
376
|
if (url.protocol !== "https:") {
|
|
404
377
|
throw new Error("Image URL must use https://.");
|
|
@@ -409,15 +382,17 @@ async function resolveRemoteImageAddress(url) {
|
|
|
409
382
|
if (isBlockedHostname(url.hostname)) {
|
|
410
383
|
throw new Error("Image URL host is not allowed. Use a public HTTPS image URL.");
|
|
411
384
|
}
|
|
412
|
-
|
|
413
|
-
|
|
385
|
+
const literalAddress = normalizeIpLiteralHostname(url.hostname);
|
|
386
|
+
const literalFamily = ipLiteralFamily(literalAddress);
|
|
387
|
+
if (literalFamily !== 0) {
|
|
388
|
+
if (!isPublicUnicastAddress(literalAddress)) {
|
|
414
389
|
throw new Error("Image URL points to a private/local IP address, which is not allowed.");
|
|
415
390
|
}
|
|
416
|
-
return { address:
|
|
391
|
+
return { address: literalAddress, family: literalFamily };
|
|
417
392
|
}
|
|
418
393
|
const addresses = await lookup(url.hostname, { all: true });
|
|
419
|
-
if (addresses.length === 0 || addresses.some((item) =>
|
|
420
|
-
throw new Error("Image URL resolves to a
|
|
394
|
+
if (addresses.length === 0 || addresses.some((item) => !isPublicUnicastAddress(item.address))) {
|
|
395
|
+
throw new Error("Image URL resolves to a non-public IP address, which is not allowed.");
|
|
421
396
|
}
|
|
422
397
|
const selected = addresses[0];
|
|
423
398
|
if (!selected || (selected.family !== 4 && selected.family !== 6)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@okostream/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "MCP server for managing OKO Stream webinars as an organizer.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
37
|
"dotenv": "^17.2.3",
|
|
38
38
|
"firebase": "^12.16.0",
|
|
39
|
+
"ipaddr.js": "2.4.0",
|
|
39
40
|
"sharp": "^0.35.3",
|
|
40
41
|
"undici": "7.28.0",
|
|
41
42
|
"zod": "^3.25.76"
|