@tonnode/mcp
Advanced tools
+26
-4
@@ -38,3 +38,10 @@ // Hosted mode: the same TON MCP server over Streamable HTTP. | ||
| .filter((e) => typeof e?.key === "string" && e.key.length > 0) | ||
| .map((e) => [e.key, { rpm: e.rpm, expires: e.expires, label: e.label }])); | ||
| // `ips` was missing from this projection, so the allowlist below | ||
| // could never fire for a key loaded from the file — which is every | ||
| // key in production. The billing service validates the addresses, | ||
| // writes them into the registry and the console shows the key as | ||
| // restricted; the server then dropped the field on load and served | ||
| // the key from anywhere. Every field the auth path reads has to | ||
| // survive this map. | ||
| .map((e) => [e.key, { rpm: e.rpm, expires: e.expires, label: e.label, ips: e.ips }])); | ||
| } | ||
@@ -88,6 +95,21 @@ catch (err) { | ||
| function clientIp(req) { | ||
| // The LAST entry, not the first. | ||
| // | ||
| // This address decides whether a key with an allowlist is accepted, so the | ||
| // question is which part of the header an attacker controls. Caddy as | ||
| // configured today REPLACES X-Forwarded-For with the real peer, so there is | ||
| // exactly one entry and either end works — verified by proxying a spoofed | ||
| // header through it. | ||
| // | ||
| // But that safety comes from the proxy, not from here. Put any appending | ||
| // proxy in front and the header becomes "<whatever the client typed>, <real | ||
| // client>": the first entry is then attacker-controlled and the allowlist | ||
| // is bypassed by sending one header. The last entry is always the one our | ||
| // own nearest proxy wrote. If that ever stops matching a customer's rule the | ||
| // failure is a refused request, not a granted one. | ||
| const fwd = req.headers["x-forwarded-for"]; | ||
| const raw = Array.isArray(fwd) ? fwd[0] : fwd; | ||
| const first = (raw ?? "").split(",")[0].trim(); | ||
| const addr = first || req.socket.remoteAddress || ""; | ||
| const raw = Array.isArray(fwd) ? fwd[fwd.length - 1] : fwd; | ||
| const parts = (raw ?? "").split(",").map((p) => p.trim()).filter(Boolean); | ||
| const nearest = parts.length > 0 ? parts[parts.length - 1] : ""; | ||
| const addr = nearest || req.socket.remoteAddress || ""; | ||
| // ::ffff:1.2.3.4 → 1.2.3.4 | ||
@@ -94,0 +116,0 @@ return addr.startsWith("::ffff:") ? addr.slice(7) : addr; |
+4
-3
| { | ||
| "name": "@tonnode/mcp", | ||
| "mcpName": "io.github.tonnode/mcp", | ||
| "version": "0.9.0", | ||
| "description": "Liteserver access to TON: balances, account state, transactions, get-methods over native ADNL — plus non-custodial DEX swaps, cross-chain swaps, and TON wallet generation.", | ||
| "version": "0.9.1", | ||
| "description": "Liteserver access to TON: balances, account state, transactions, get-methods over native ADNL \u2014 plus non-custodial DEX swaps, cross-chain swaps, and TON wallet generation.", | ||
| "type": "module", | ||
@@ -20,3 +20,4 @@ "bin": { | ||
| "prepublishOnly": "npm run build", | ||
| "start": "node dist/index.js" | ||
| "start": "node dist/index.js", | ||
| "test": "node --test --test-force-exit \"test/*.test.mjs\"" | ||
| }, | ||
@@ -23,0 +24,0 @@ "keywords": [ |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
135740
1.17%2126
1.05%1
-50%