x402-surface-check 0.2.21 → 0.2.23
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 +7 -3
- package/bin/x402-surface-check.mjs +15 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,12 +5,14 @@ No-payment CLI for checking x402 launch surfaces before a real agent spends.
|
|
|
5
5
|
It accepts an x402 manifest or OpenAPI URL, derives public endpoints, sends no-payment probes, checks browser preflight behavior, and returns a Markdown patch queue. It never sends `X-PAYMENT`, never signs, and never attempts a paid call.
|
|
6
6
|
|
|
7
7
|
npm: https://www.npmjs.com/package/x402-surface-check
|
|
8
|
+
Attack-map field note: https://tateprograms.com/x402-attack-map-2026.html
|
|
8
9
|
|
|
9
10
|
```bash
|
|
10
11
|
npx --yes x402-surface-check https://api.example.com/.well-known/x402
|
|
11
12
|
npx --yes x402-surface-check https://api.example.com/openapi.json report.md
|
|
12
13
|
npx --yes x402-surface-check --endpoint --method POST https://x402.rpc.ankr.com/eth
|
|
13
14
|
npx --yes x402-surface-check --endpoint --method POST --body '{"prompt":"price CPI"}' https://api.example.com/paid-post
|
|
15
|
+
npx --yes x402-surface-check --strict-cache https://api.example.com/openapi.json
|
|
14
16
|
```
|
|
15
17
|
|
|
16
18
|
## What It Checks
|
|
@@ -30,9 +32,9 @@ npx --yes x402-surface-check --endpoint --method POST --body '{"prompt":"price C
|
|
|
30
32
|
- Testnet or staging rails such as Base Sepolia and Solana devnet
|
|
31
33
|
- HTTPS resource URLs and stable resource metadata
|
|
32
34
|
- Browser CORS allowance for the requesting origin and `X-PAYMENT`, including the actual 402 challenge response
|
|
33
|
-
- Cache-Control posture on no-payment challenge responses, with warnings for explicitly cacheable payment gates
|
|
35
|
+
- Cache-Control posture on no-payment challenge responses, with warnings for explicitly cacheable payment gates and optional strict-cache findings for missing policy headers
|
|
34
36
|
- Grouped finding summaries for repeated route-wide issues, so large manifests keep the patch order readable
|
|
35
|
-
- Contextual reference guides for CORS, cache policy, Worker gates, resource echo, validation/auth ordering, and
|
|
37
|
+
- Contextual reference guides for CORS, cache policy, Worker gates, resource echo, validation/auth ordering, and the May 2026 x402 attack-control map
|
|
36
38
|
- Over-broad public method surfaces
|
|
37
39
|
- Auth, validation, and free/trial responses that appear before a payment challenge, without piling on missing-field findings when no challenge was actually returned
|
|
38
40
|
- Operational health/status endpoints, without treating expected free health checks as paid-route failures
|
|
@@ -48,7 +50,7 @@ Recent public no-payment checks have found and verified real launch fixes:
|
|
|
48
50
|
- UZPROOF: schemes-style Solana x402 challenge and browser payment-header behavior verified clean. https://github.com/solana-foundation/pay-skills/pull/28#issuecomment-4455613892
|
|
49
51
|
- HYRE Agent: OpenAPI-declared prices found 10x below live 402 challenge prices. https://github.com/solana-foundation/pay-skills/pull/19#issuecomment-4455641258
|
|
50
52
|
- anchor-x402: multi-rail x402 challenges verified, with browser preflight blockers isolated before merge. https://github.com/solana-foundation/pay-skills/pull/47#issuecomment-4455678163
|
|
51
|
-
- Agent Trust Bench:
|
|
53
|
+
- Agent Trust Bench: three no-payment passes converged on zero findings after discovery, browser preflight, cache, and resource-echo fixes. https://github.com/solana-foundation/pay-skills/pull/23#issuecomment-4467597309
|
|
52
54
|
- Solrouter: private LLM inference route verified with HTTPS resource-binding and price-alignment notes. https://github.com/solana-foundation/pay-skills/pull/39#issuecomment-4455800060
|
|
53
55
|
- Tetrac: Solana market-data payment gates verified, with browser payment-header preflight blocker isolated. https://github.com/solana-foundation/pay-skills/pull/32#issuecomment-4455923744
|
|
54
56
|
|
|
@@ -66,6 +68,7 @@ x402-surface-check --endpoint --method POST <paid-endpoint-url> [output.md]
|
|
|
66
68
|
--body-file <p> Read JSON request body for direct endpoint mode from a file
|
|
67
69
|
--origin <url> Origin to use for browser-style CORS preflight
|
|
68
70
|
--limit <n> Maximum endpoints to probe, default 6
|
|
71
|
+
--strict-cache Flag missing Cache-Control on no-payment 402 responses
|
|
69
72
|
--json Print JSON instead of Markdown
|
|
70
73
|
--help Show usage
|
|
71
74
|
--version Show package version
|
|
@@ -76,6 +79,7 @@ Environment variables are also supported:
|
|
|
76
79
|
```bash
|
|
77
80
|
X402_CHECK_ORIGIN=https://example.com x402-surface-check https://api.example.com/openapi.json
|
|
78
81
|
X402_CHECK_LIMIT=12 x402-surface-check https://api.example.com/.well-known/x402
|
|
82
|
+
X402_STRICT_CACHE=1 x402-surface-check https://api.example.com/.well-known/x402
|
|
79
83
|
```
|
|
80
84
|
|
|
81
85
|
## Scope
|
|
@@ -22,6 +22,7 @@ Options:
|
|
|
22
22
|
--body-file <p> Read JSON request body for direct endpoint mode from a file
|
|
23
23
|
--origin <url> Origin to use for browser-style CORS preflight
|
|
24
24
|
--limit <n> Maximum endpoints to probe, default ${defaultLimit}
|
|
25
|
+
--strict-cache Flag missing Cache-Control on no-payment 402 responses
|
|
25
26
|
--json Print JSON instead of Markdown
|
|
26
27
|
--help Show this help
|
|
27
28
|
--version Show package version
|
|
@@ -38,6 +39,7 @@ function parseArgs(argv) {
|
|
|
38
39
|
body: process.env.X402_CHECK_BODY,
|
|
39
40
|
bodyFile: process.env.X402_CHECK_BODY_FILE,
|
|
40
41
|
outputPath: '',
|
|
42
|
+
strictCache: process.env.X402_STRICT_CACHE === '1',
|
|
41
43
|
url: '',
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -55,6 +57,9 @@ function parseArgs(argv) {
|
|
|
55
57
|
else if (arg === '--endpoint') {
|
|
56
58
|
args.endpoint = true
|
|
57
59
|
}
|
|
60
|
+
else if (arg === '--strict-cache') {
|
|
61
|
+
args.strictCache = true
|
|
62
|
+
}
|
|
58
63
|
else if (arg === '--method') {
|
|
59
64
|
args.method = String(argv[index + 1] ?? '').toUpperCase()
|
|
60
65
|
index += 1
|
|
@@ -721,7 +726,7 @@ function looksLikeOperationalHealthEndpoint(result) {
|
|
|
721
726
|
return /(^|[/_\s-])(health|healthz|ready|readiness|live|liveness|status)([/_\s-]|$)/.test(value)
|
|
722
727
|
}
|
|
723
728
|
|
|
724
|
-
function findingList(documentResult, challengeResults, preflightResults, entries) {
|
|
729
|
+
function findingList(documentResult, challengeResults, preflightResults, entries, options = {}) {
|
|
725
730
|
const document = documentResult.body.json ?? {}
|
|
726
731
|
const findings = []
|
|
727
732
|
const networks = valueList(document.networks)
|
|
@@ -795,6 +800,9 @@ function findingList(documentResult, challengeResults, preflightResults, entries
|
|
|
795
800
|
if (looksExplicitlyCacheable(result.headers)) {
|
|
796
801
|
findings.push(`P2 - ${result.name} payment challenge response is explicitly cacheable (${cachePolicy(result.headers)}); paid routes should use no-store/private cache policy or bypass shared caches.`)
|
|
797
802
|
}
|
|
803
|
+
else if (options.strictCache && !cachePolicy(result.headers)) {
|
|
804
|
+
findings.push(`P3 - ${result.name} payment challenge response did not expose Cache-Control; for payment-gated routes, document or send no-store/private cache policy and confirm paid 200 responses are never shared-cacheable.`)
|
|
805
|
+
}
|
|
798
806
|
}
|
|
799
807
|
|
|
800
808
|
for (const result of preflightResults) {
|
|
@@ -853,6 +861,9 @@ function groupedFindingLabel(finding) {
|
|
|
853
861
|
if (/challenge advertises placeholder-looking payTo/.test(finding)) {
|
|
854
862
|
return 'P1 - Challenges advertise placeholder-looking payTo recipients.'
|
|
855
863
|
}
|
|
864
|
+
if (/payment challenge response did not expose Cache-Control/.test(finding)) {
|
|
865
|
+
return 'P3 - Payment challenge responses do not expose Cache-Control in strict cache mode.'
|
|
866
|
+
}
|
|
856
867
|
return null
|
|
857
868
|
}
|
|
858
869
|
|
|
@@ -1013,7 +1024,9 @@ async function runCheck(options) {
|
|
|
1013
1024
|
preflights,
|
|
1014
1025
|
sourceDocument,
|
|
1015
1026
|
}
|
|
1016
|
-
report.findings = findingList(document, challenges, preflights, entries
|
|
1027
|
+
report.findings = findingList(document, challenges, preflights, entries, {
|
|
1028
|
+
strictCache: options.strictCache,
|
|
1029
|
+
})
|
|
1017
1030
|
return report
|
|
1018
1031
|
}
|
|
1019
1032
|
|