Skip to content

Commit 5b0598d

Browse files
authored
Merge pull request #1 from jeanpunt/fix-chainid-svm
Fix ChainIdToNetwork mapping for Solana
2 parents 9c08f80 + 456aa11 commit 5b0598d

File tree

3 files changed

+64
-6
lines changed

3 files changed

+64
-6
lines changed

FUTURE_CONTRIBUTION_IDEAS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Future Contribution Ideas
2+
3+
## Honor Proxy Headers in TypeScript Middleware
4+
- **Context:** The Python Flask middleware already respects `X-Original-URI` when reconstructing the protected resource URL, but the TypeScript middlewares for Express, Hono, and Next fall back to the raw request path. Reverse proxies that rewrite URLs (e.g., Nginx, Cloudflare, API gateways) therefore produce paywall links that point at the proxy rather than the original route.
5+
- **Why it matters:** Incorrect resource URLs cascade into malformed payment requirements and broken paywall redirects for downstream integrators hosting behind a proxy.
6+
- **Suggested scope:**
7+
1. Extend the Express, Hono, and Next middleware implementations to read `X-Original-URI` (and `X-Forwarded-Proto` / `X-Forwarded-Host` where available) before falling back to the local request path.
8+
2. Normalize and validate the reconstructed URL to avoid header spoofing.
9+
3. Add focused unit tests in each package to cover the new header-aware branch.
10+
4. Update README snippets or inline docs to mention proxy compatibility.
11+
12+
## Harden Paywall HTML Escaping
13+
- **Context:** `getPaywallHtml` only escapes quotes, backslashes, and whitespace before embedding dynamic values inside `<script>` tags. Strings containing `<`, `>` or `</script>` can therefore smuggle executable markup, enabling content injection when paywall settings come from user-controlled sources.
14+
- **Why it matters:** The facilitator may operate in multi-tenant environments; robust escaping reduces the risk of in-browser script injection.
15+
- **Suggested scope:**
16+
1. Expand `escapeString` to escape `<`, `>`, and `/`, or switch to a whitelist encoder that converts every non-alphanumeric character to `\uXXXX`.
17+
2. Back the change with unit tests that feed representative payloads (`</script><img ...>`, Unicode separators, etc.) and assert the output remains a single JavaScript string literal.
18+
3. Consider documenting the escaping guarantees in the paywall README for developers embedding custom content.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
import {
4+
ChainIdToNetwork,
5+
EvmNetworkToChainId,
6+
SupportedEVMNetworks,
7+
SupportedSVMNetworks,
8+
SvmNetworkToChainId,
9+
} from "./network";
10+
11+
describe("ChainIdToNetwork", () => {
12+
it("maps every supported EVM chain id to its network", () => {
13+
SupportedEVMNetworks.forEach(network => {
14+
const chainId = EvmNetworkToChainId.get(network);
15+
expect(chainId).toBeDefined();
16+
expect(ChainIdToNetwork[chainId!]).toBe(network);
17+
});
18+
});
19+
20+
it("maps every supported SVM chain id to its network", () => {
21+
SupportedSVMNetworks.forEach(network => {
22+
const chainId = SvmNetworkToChainId.get(network);
23+
expect(chainId).toBeDefined();
24+
expect(ChainIdToNetwork[chainId!]).toBe(network);
25+
});
26+
});
27+
});

typescript/packages/x402/src/types/shared/network.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,22 @@ export const SvmNetworkToChainId = new Map<Network, number>([
4949
["solana", 101],
5050
]);
5151

52-
export const ChainIdToNetwork = Object.fromEntries(
53-
[...SupportedEVMNetworks, ...SupportedSVMNetworks].map(network => [
54-
EvmNetworkToChainId.get(network),
55-
network,
56-
]),
57-
) as Record<number, Network>;
52+
const chainIdEntries: Array<[number, Network]> = [];
53+
54+
for (const network of SupportedEVMNetworks) {
55+
const chainId = EvmNetworkToChainId.get(network);
56+
if (chainId === undefined) {
57+
throw new Error(`Missing chain id mapping for EVM network: ${network}`);
58+
}
59+
chainIdEntries.push([chainId, network]);
60+
}
61+
62+
for (const network of SupportedSVMNetworks) {
63+
const chainId = SvmNetworkToChainId.get(network);
64+
if (chainId === undefined) {
65+
throw new Error(`Missing chain id mapping for SVM network: ${network}`);
66+
}
67+
chainIdEntries.push([chainId, network]);
68+
}
69+
70+
export const ChainIdToNetwork = Object.fromEntries(chainIdEntries) as Record<number, Network>;

0 commit comments

Comments
 (0)