x402-surface-check 0.2.3 → 0.2.5
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 +1 -0
- package/bin/x402-surface-check.mjs +24 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ npx --yes x402-surface-check --endpoint --method POST https://x402.rpc.ankr.com/
|
|
|
24
24
|
- HTTPS resource URLs and stable resource metadata
|
|
25
25
|
- Browser CORS allowance for `X-PAYMENT`
|
|
26
26
|
- Over-broad public method surfaces
|
|
27
|
+
- Auth, validation, and free/trial responses that appear before a payment challenge, without piling on missing-field findings when no challenge was actually returned
|
|
27
28
|
|
|
28
29
|
## Options
|
|
29
30
|
|
|
@@ -78,6 +78,7 @@ function parseArgs(argv) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function moneyFromAtomic(amount, decimals = 6) {
|
|
81
|
+
if (amount === '' || amount === null || amount === undefined) return ''
|
|
81
82
|
const numeric = Number(amount)
|
|
82
83
|
if (!Number.isFinite(numeric)) return String(amount ?? '')
|
|
83
84
|
const value = numeric / (10 ** decimals)
|
|
@@ -373,6 +374,11 @@ function challengeAccepts(result) {
|
|
|
373
374
|
return Array.isArray(result.body.json?.accepts) ? result.body.json.accepts : []
|
|
374
375
|
}
|
|
375
376
|
|
|
377
|
+
function hasPaymentChallenge(result) {
|
|
378
|
+
const challenge = result.body.json
|
|
379
|
+
return challengeAccepts(result).length > 0 || Boolean(challenge?.resource || challenge?.payment || result.headers?.['www-authenticate'])
|
|
380
|
+
}
|
|
381
|
+
|
|
376
382
|
function challengeSummary(result) {
|
|
377
383
|
const challenge = result.body.json
|
|
378
384
|
const firstAccept = challenge?.accepts?.[0] ?? {}
|
|
@@ -427,10 +433,27 @@ function findingList(documentResult, challengeResults, preflightResults, entries
|
|
|
427
433
|
for (const result of challengeResults) {
|
|
428
434
|
const summary = challengeSummary(result)
|
|
429
435
|
if (summary.network) challengeNetworks.add(summary.network)
|
|
436
|
+
const hasChallenge = hasPaymentChallenge(result)
|
|
430
437
|
|
|
431
438
|
if (result.status !== 402) {
|
|
432
|
-
|
|
439
|
+
if (result.status >= 200 && result.status < 300) {
|
|
440
|
+
findings.push(`P3 - ${result.name} returned ${result.status} without a payment challenge for a no-payment ${result.method ?? 'POST'} probe; document this as free/trial access or move the 402 challenge before content.`)
|
|
441
|
+
}
|
|
442
|
+
else if (result.status === 400 || result.status === 422) {
|
|
443
|
+
findings.push(`P1 - ${result.name} returned validation HTTP ${result.status} before a payment challenge for a no-payment ${result.method ?? 'POST'} probe.`)
|
|
444
|
+
}
|
|
445
|
+
else if (result.status === 401 || result.status === 403) {
|
|
446
|
+
findings.push(`P2 - ${result.name} returned auth HTTP ${result.status} before a payment challenge for a no-payment ${result.method ?? 'POST'} probe; document the auth/free-tier order if this is intentional.`)
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
findings.push(`P1 - ${result.name} returned ${result.status}, not 402, for a no-payment ${result.method ?? 'POST'} probe.`)
|
|
450
|
+
}
|
|
433
451
|
}
|
|
452
|
+
|
|
453
|
+
if (!hasChallenge) {
|
|
454
|
+
continue
|
|
455
|
+
}
|
|
456
|
+
|
|
434
457
|
if (summary.resourceUrl.startsWith('http://') || summary.extraResource.startsWith('http://')) {
|
|
435
458
|
findings.push(`P1 - ${result.name} challenge uses a non-HTTPS resource URL: ${summary.resourceUrl || summary.extraResource}.`)
|
|
436
459
|
}
|