@craigruks/x402-server-guard 0.1.1 → 0.1.2
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/README.md +13 -8
- package/SECURITY.md +3 -1
- package/dist/cache.d.ts +6 -2
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +3 -1
- package/dist/cache.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +14 -14
- package/src/cache.ts +6 -2
- package/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# x402-server-guard
|
|
2
2
|
|
|
3
|
-
**Server-side hardening middleware for [x402](https://github.com/
|
|
3
|
+
**Server-side hardening middleware for [x402](https://github.com/x402-foundation/x402)
|
|
4
4
|
payment endpoints.**
|
|
5
5
|
|
|
6
|
+
[](https://www.npmjs.com/package/@craigruks/x402-server-guard)
|
|
6
7
|
[](https://github.com/craigruks/x402-server-guard/actions/workflows/ci.yml)
|
|
7
8
|
[](./LICENSE)
|
|
8
9
|
|
|
@@ -162,13 +163,17 @@ and honesty (is this a strawman, does the reference actually have these gaps, wh
|
|
|
162
163
|
this does not do) are answered in
|
|
163
164
|
[`docs/objection-handling.md`](./docs/objection-handling.md).
|
|
164
165
|
|
|
165
|
-
| Attack class | Status |
|
|
166
|
-
| --- | --- |
|
|
167
|
-
| Duplicate-settlement race | done |
|
|
168
|
-
| Payment replay | done (same nonce reservation) |
|
|
169
|
-
| Cross-resource substitution | done (same nonce reservation, distinct reason) |
|
|
170
|
-
| Grant-before-finality (k-confirmations) | done |
|
|
171
|
-
| Cache leakage of paid content | done |
|
|
166
|
+
| Attack class | Status | Upstream `@x402/core` 2.29.0 |
|
|
167
|
+
| --- | --- | --- |
|
|
168
|
+
| Duplicate-settlement race | done | partly: closed by the opt-in `upfront` flow ([#3053](https://github.com/x402-foundation/x402/pull/3053)), open in the default flow |
|
|
169
|
+
| Payment replay | done (same nonce reservation) | partly, as for the race |
|
|
170
|
+
| Cross-resource substitution | done (same nonce reservation, distinct reason) | partly: `upfront` denies the reuse, nothing binds or flags the resource |
|
|
171
|
+
| Grant-before-finality (k-confirmations) | done | no: success is one inclusion; a hook can now fail it closed ([#3994](https://github.com/x402-foundation/x402/pull/3994)) |
|
|
172
|
+
| Cache leakage of paid content | done | partly: `private` on the paid 200 since 2.21.0 ([#2990](https://github.com/x402-foundation/x402/pull/2990)), no `no-store`, no `Vary` |
|
|
173
|
+
|
|
174
|
+
The upstream column is a survey, not a dependency: the guard does not call
|
|
175
|
+
`@x402/core`. Evidence for each entry is in
|
|
176
|
+
[`docs/hardening.md`](./docs/hardening.md#what-upstream-now-covers).
|
|
172
177
|
|
|
173
178
|
## Development
|
|
174
179
|
|
package/SECURITY.md
CHANGED
|
@@ -47,7 +47,9 @@ published under a separate tag.
|
|
|
47
47
|
|
|
48
48
|
Please report suspected vulnerabilities **privately** via GitHub Security
|
|
49
49
|
Advisories ("Report a vulnerability" on the repository's Security tab), not
|
|
50
|
-
through public issues.
|
|
50
|
+
through public issues. This covers any case where the guard fails to stop an
|
|
51
|
+
attack it claims to mitigate. A public issue that describes a working attack
|
|
52
|
+
discloses it before a fix exists.
|
|
51
53
|
|
|
52
54
|
Include a description, affected version, and a reproduction if possible. Expect an
|
|
53
55
|
acknowledgement within 72 hours. Please allow a reasonable window for a fix before
|
package/dist/cache.d.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A shared cache (CDN or reverse proxy) is keyed on the URL and knows nothing about
|
|
5
5
|
* payment, so a cacheable paid 200 can be stored and served to the next caller, paid
|
|
6
|
-
* or not. The reference x402 adapters
|
|
6
|
+
* or not. The reference x402 adapters send `private` on the paid 200 as of
|
|
7
|
+
* `@x402/core` 2.21.0 (unchanged through 2.29.0), and set no directive at all before
|
|
8
|
+
* it; neither sets `Vary`.
|
|
7
9
|
*
|
|
8
10
|
* The mitigation is a response directive, not a decision about a nonce, so it lives
|
|
9
11
|
* here as a small pure helper the server or adapter applies to every paid response.
|
|
@@ -20,7 +22,9 @@ export interface CacheDirectives {
|
|
|
20
22
|
export interface CacheDirectivesOptions {
|
|
21
23
|
/**
|
|
22
24
|
* The request header the payment travels in, added to `Vary` so a cache that
|
|
23
|
-
* stores despite `no-store` still keys by payer. Defaults to `X-PAYMENT
|
|
25
|
+
* stores despite `no-store` still keys by payer. Defaults to `X-PAYMENT`, the x402
|
|
26
|
+
* v1 header; an x402 v2 client sends `PAYMENT-SIGNATURE`, so pass that behind a v2
|
|
27
|
+
* server.
|
|
24
28
|
*/
|
|
25
29
|
readonly paymentHeader?: string;
|
|
26
30
|
}
|
package/dist/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,wFAAwF;AACxF,MAAM,WAAW,eAAe;IAC9B,6BAA6B;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,+DAA+D;AAC/D,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,sBAA2B,GAAG,eAAe,CAGjG;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAMjF"}
|
package/dist/cache.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A shared cache (CDN or reverse proxy) is keyed on the URL and knows nothing about
|
|
5
5
|
* payment, so a cacheable paid 200 can be stored and served to the next caller, paid
|
|
6
|
-
* or not. The reference x402 adapters
|
|
6
|
+
* or not. The reference x402 adapters send `private` on the paid 200 as of
|
|
7
|
+
* `@x402/core` 2.21.0 (unchanged through 2.29.0), and set no directive at all before
|
|
8
|
+
* it; neither sets `Vary`.
|
|
7
9
|
*
|
|
8
10
|
* The mitigation is a response directive, not a decision about a nonce, so it lives
|
|
9
11
|
* here as a small pure helper the server or adapter applies to every paid response.
|
package/dist/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoBH,+DAA+D;AAC/D,MAAM,UAAU,2BAA2B,CAAC,OAAO,GAA2B,EAAE;IAC9E,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,WAAW,CAAC;IAC3D,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;AACpE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,YAAgC;IACtE,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,EAAE,EAAE,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,UAAU,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;IAC9C,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAC7E,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* race and payment replay is the first mitigation to land.
|
|
13
13
|
*/
|
|
14
14
|
/** Current package version (semver). */
|
|
15
|
-
export const VERSION = "0.1.
|
|
15
|
+
export const VERSION = "0.1.2";
|
|
16
16
|
export { isStorableBySharedCache, paidResponseCacheDirectives, } from "./cache.js";
|
|
17
17
|
export { canonicalNonce, canonicalResource } from "./canonical.js";
|
|
18
18
|
export { guardError } from "./error.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@craigruks/x402-server-guard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Server-side hardening middleware for x402 payment endpoints. Mitigations for settlement races, replay, cross-resource substitution, and cache leakage. Not a security guarantee.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"x402",
|
|
@@ -70,21 +70,21 @@
|
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@arethetypeswrong/cli": "0.18.5",
|
|
73
|
-
"@biomejs/biome": "2.
|
|
74
|
-
"@cloudflare/workers-types": "5.
|
|
75
|
-
"@types/node": "26.
|
|
76
|
-
"@vitest/coverage-v8": "
|
|
77
|
-
"@x402/core": "2.
|
|
78
|
-
"esbuild": "0.28.
|
|
79
|
-
"hono": "4.
|
|
80
|
-
"miniflare": "4.
|
|
81
|
-
"publint": "0.3.
|
|
82
|
-
"tsx": "4.23.
|
|
73
|
+
"@biomejs/biome": "2.6.4",
|
|
74
|
+
"@cloudflare/workers-types": "5.20261104.1",
|
|
75
|
+
"@types/node": "26.6.6",
|
|
76
|
+
"@vitest/coverage-v8": "5.1.0",
|
|
77
|
+
"@x402/core": "2.29.0",
|
|
78
|
+
"esbuild": "0.28.2",
|
|
79
|
+
"hono": "4.13.16",
|
|
80
|
+
"miniflare": "4.20260730.0",
|
|
81
|
+
"publint": "0.3.25",
|
|
82
|
+
"tsx": "4.23.15",
|
|
83
83
|
"typescript": "7.0.2",
|
|
84
|
-
"vitest": "
|
|
84
|
+
"vitest": "5.1.0"
|
|
85
85
|
},
|
|
86
86
|
"allowScripts": {
|
|
87
|
-
"esbuild@0.28.
|
|
88
|
-
"workerd@1.
|
|
87
|
+
"esbuild@0.28.2": true,
|
|
88
|
+
"workerd@1.20260730.1": true
|
|
89
89
|
}
|
|
90
90
|
}
|
package/src/cache.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A shared cache (CDN or reverse proxy) is keyed on the URL and knows nothing about
|
|
5
5
|
* payment, so a cacheable paid 200 can be stored and served to the next caller, paid
|
|
6
|
-
* or not. The reference x402 adapters
|
|
6
|
+
* or not. The reference x402 adapters send `private` on the paid 200 as of
|
|
7
|
+
* `@x402/core` 2.21.0 (unchanged through 2.29.0), and set no directive at all before
|
|
8
|
+
* it; neither sets `Vary`.
|
|
7
9
|
*
|
|
8
10
|
* The mitigation is a response directive, not a decision about a nonce, so it lives
|
|
9
11
|
* here as a small pure helper the server or adapter applies to every paid response.
|
|
@@ -22,7 +24,9 @@ export interface CacheDirectives {
|
|
|
22
24
|
export interface CacheDirectivesOptions {
|
|
23
25
|
/**
|
|
24
26
|
* The request header the payment travels in, added to `Vary` so a cache that
|
|
25
|
-
* stores despite `no-store` still keys by payer. Defaults to `X-PAYMENT
|
|
27
|
+
* stores despite `no-store` still keys by payer. Defaults to `X-PAYMENT`, the x402
|
|
28
|
+
* v1 header; an x402 v2 client sends `PAYMENT-SIGNATURE`, so pass that behind a v2
|
|
29
|
+
* server.
|
|
26
30
|
*/
|
|
27
31
|
readonly paymentHeader?: string;
|
|
28
32
|
}
|