x402-surface-check 0.2.17 → 0.2.19

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
@@ -15,7 +15,7 @@ npx --yes x402-surface-check --endpoint --method POST --body '{"prompt":"price C
15
15
 
16
16
  ## What It Checks
17
17
 
18
- - Manifest endpoint discovery from `items[]`, `endpoints[]`, `resources[]`, `x402Endpoints`, category arrays, resource strings, and OpenAPI paths
18
+ - Manifest endpoint discovery from `items[]`, `endpoints[]`, `resources[]`, `x402Endpoints`, category arrays, raw resource URL strings, method-prefixed resource strings, and OpenAPI paths
19
19
  - Linked discovery documents via `discovery_url`, `discoveryUrl`, `resources_url`, `resourcesUrl`, or manifest-level OpenAPI links
20
20
  - OpenAPI `servers[]` base-path preservation, so `/paths` are probed through the documented gateway rather than the domain root
21
21
  - OpenAPI query/path examples, JSON request-body examples, nested request schemas, local `$ref` request schemas, and explicit direct-endpoint bodies for safer no-payment probes
@@ -30,6 +30,7 @@ npx --yes x402-surface-check --endpoint --method POST --body '{"prompt":"price C
30
30
  - Testnet or staging rails such as Base Sepolia and Solana devnet
31
31
  - HTTPS resource URLs and stable resource metadata
32
32
  - Browser CORS allowance for the requesting origin and `X-PAYMENT`, including the actual 402 challenge response
33
+ - Grouped finding summaries for repeated route-wide issues, so large manifests keep the patch order readable
33
34
  - Over-broad public method surfaces
34
35
  - Auth, validation, and free/trial responses that appear before a payment challenge, without piling on missing-field findings when no challenge was actually returned
35
36
  - Operational health/status endpoints, without treating expected free health checks as paid-route failures
@@ -394,8 +394,8 @@ function endpointEntries(document, sourceUrl, limit) {
394
394
  for (const resource of document.resources ?? []) {
395
395
  if (typeof resource === 'string') {
396
396
  const match = resource.match(/^(GET|POST|PUT|PATCH|DELETE)\s+(\S+)/i)
397
- if (!match) continue
398
- const [, method, rawPath] = match
397
+ const method = match?.[1] ?? 'GET'
398
+ const rawPath = match?.[2] ?? resource
399
399
  const url = rawPath.startsWith('http')
400
400
  ? rawPath
401
401
  : new URL(rawPath, document.baseUrl ?? sourceUrl).toString()
@@ -814,6 +814,45 @@ function findingList(documentResult, challengeResults, preflightResults, entries
814
814
  return findings
815
815
  }
816
816
 
817
+ function groupedFindingLabel(finding) {
818
+ if (/402 challenge response does not allow the requesting origin/.test(finding)) {
819
+ return 'P1 - Actual 402 challenge responses do not allow the requesting origin; browser clients cannot read payment requirements.'
820
+ }
821
+ if (/CORS preflight does not allow the requesting origin/.test(finding)) {
822
+ return 'P1 - CORS preflight does not allow the requesting origin.'
823
+ }
824
+ if (/CORS preflight does not allow X-PAYMENT/.test(finding)) {
825
+ return 'P1 - CORS preflight does not allow X-PAYMENT.'
826
+ }
827
+ if (/challenge does not repeat the resource URL/.test(finding)) {
828
+ return 'P2 - Challenge accept legs do not repeat the resource URL for reconciliation.'
829
+ }
830
+ if (/returned validation HTTP \d+ before a payment challenge/.test(finding)) {
831
+ return 'P1 - Routes return validation before a payment challenge.'
832
+ }
833
+ if (/returned auth HTTP \d+ before a payment challenge/.test(finding)) {
834
+ return 'P2 - Routes return auth before a payment challenge.'
835
+ }
836
+ if (/challenge advertises staging\/test network/.test(finding)) {
837
+ return 'P2 - Challenges advertise staging or test networks.'
838
+ }
839
+ if (/challenge advertises placeholder-looking payTo/.test(finding)) {
840
+ return 'P1 - Challenges advertise placeholder-looking payTo recipients.'
841
+ }
842
+ return null
843
+ }
844
+
845
+ function groupedFindingSummary(findings) {
846
+ const counts = new Map()
847
+ for (const finding of findings) {
848
+ const label = groupedFindingLabel(finding)
849
+ if (label) counts.set(label, (counts.get(label) ?? 0) + 1)
850
+ }
851
+ return [...counts.entries()]
852
+ .filter(([, count]) => count > 1)
853
+ .map(([label, count]) => `- ${count} endpoints: ${label}`)
854
+ }
855
+
817
856
  function formatMarkdown(report) {
818
857
  const document = report.document.body.json ?? {}
819
858
  const challengeRows = report.challenges.map(result => {
@@ -823,6 +862,7 @@ function formatMarkdown(report) {
823
862
  const preflightRows = report.preflights.map(result => {
824
863
  return `| ${result.name} | ${result.method ?? 'POST'} | ${result.status} | ${result.headers['access-control-allow-origin'] ?? '-'} | ${result.headers['access-control-allow-headers'] ?? '-'} | ${result.headers['access-control-allow-methods'] ?? '-'} |`
825
864
  })
865
+ const findingSummary = groupedFindingSummary(report.findings)
826
866
 
827
867
  return [
828
868
  '# x402 Public Surface Check',
@@ -856,6 +896,12 @@ function formatMarkdown(report) {
856
896
  '| --- | --- | --- | --- | --- | --- |',
857
897
  ...(preflightRows.length ? preflightRows : ['| - | - | - | - | - | - |']),
858
898
  '',
899
+ ...(findingSummary.length ? [
900
+ '## Finding Summary',
901
+ '',
902
+ ...findingSummary,
903
+ '',
904
+ ] : []),
859
905
  '## Findings',
860
906
  '',
861
907
  ...(report.findings.length ? report.findings.map(item => `- ${item}`) : ['- No obvious launch-readiness findings from the public no-payment probes.']),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402-surface-check",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "description": "No-payment x402 public-surface checker for manifests, OpenAPI specs, and HTTP 402 challenges.",
5
5
  "type": "module",
6
6
  "bin": {