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 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
- findings.push(`P1 - ${result.name} returned ${result.status}, not 402, for a no-payment ${result.method ?? 'POST'} probe.`)
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402-surface-check",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "No-payment x402 public-surface checker for manifests, OpenAPI specs, and HTTP 402 challenges.",
5
5
  "type": "module",
6
6
  "bin": {