@apitap/core 1.4.0 → 1.4.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.
Files changed (74) hide show
  1. package/README.md +4 -2
  2. package/dist/auth/crypto.d.ts +10 -0
  3. package/dist/auth/crypto.js +30 -6
  4. package/dist/auth/crypto.js.map +1 -1
  5. package/dist/auth/handoff.js +20 -1
  6. package/dist/auth/handoff.js.map +1 -1
  7. package/dist/auth/manager.d.ts +1 -0
  8. package/dist/auth/manager.js +35 -9
  9. package/dist/auth/manager.js.map +1 -1
  10. package/dist/capture/monitor.js +4 -0
  11. package/dist/capture/monitor.js.map +1 -1
  12. package/dist/capture/scrubber.js +10 -0
  13. package/dist/capture/scrubber.js.map +1 -1
  14. package/dist/capture/session.js +7 -17
  15. package/dist/capture/session.js.map +1 -1
  16. package/dist/cli.js +74 -17
  17. package/dist/cli.js.map +1 -1
  18. package/dist/discovery/fetch.js +3 -3
  19. package/dist/discovery/fetch.js.map +1 -1
  20. package/dist/mcp.d.ts +2 -0
  21. package/dist/mcp.js +59 -33
  22. package/dist/mcp.js.map +1 -1
  23. package/dist/native-host.js +2 -2
  24. package/dist/native-host.js.map +1 -1
  25. package/dist/orchestration/browse.js +13 -4
  26. package/dist/orchestration/browse.js.map +1 -1
  27. package/dist/plugin.d.ts +1 -1
  28. package/dist/plugin.js +14 -4
  29. package/dist/plugin.js.map +1 -1
  30. package/dist/read/decoders/reddit.js +4 -0
  31. package/dist/read/decoders/reddit.js.map +1 -1
  32. package/dist/replay/engine.js +60 -17
  33. package/dist/replay/engine.js.map +1 -1
  34. package/dist/serve.d.ts +2 -0
  35. package/dist/serve.js +8 -1
  36. package/dist/serve.js.map +1 -1
  37. package/dist/skill/generator.d.ts +5 -0
  38. package/dist/skill/generator.js +30 -4
  39. package/dist/skill/generator.js.map +1 -1
  40. package/dist/skill/search.js +1 -1
  41. package/dist/skill/search.js.map +1 -1
  42. package/dist/skill/signing.js +19 -1
  43. package/dist/skill/signing.js.map +1 -1
  44. package/dist/skill/ssrf.js +71 -2
  45. package/dist/skill/ssrf.js.map +1 -1
  46. package/dist/skill/store.d.ts +2 -0
  47. package/dist/skill/store.js +23 -10
  48. package/dist/skill/store.js.map +1 -1
  49. package/dist/skill/validate.d.ts +10 -0
  50. package/dist/skill/validate.js +106 -0
  51. package/dist/skill/validate.js.map +1 -0
  52. package/package.json +1 -1
  53. package/src/auth/crypto.ts +14 -6
  54. package/src/auth/handoff.ts +19 -1
  55. package/src/auth/manager.ts +22 -5
  56. package/src/capture/monitor.ts +4 -0
  57. package/src/capture/scrubber.ts +12 -0
  58. package/src/capture/session.ts +5 -14
  59. package/src/cli.ts +215 -12
  60. package/src/discovery/fetch.ts +2 -2
  61. package/src/index/reader.ts +65 -0
  62. package/src/mcp.ts +64 -31
  63. package/src/native-host.ts +29 -2
  64. package/src/orchestration/browse.ts +13 -4
  65. package/src/plugin.ts +17 -5
  66. package/src/read/decoders/reddit.ts +3 -3
  67. package/src/replay/engine.ts +65 -15
  68. package/src/serve.ts +10 -1
  69. package/src/skill/generator.ts +32 -4
  70. package/src/skill/search.ts +1 -1
  71. package/src/skill/signing.ts +20 -1
  72. package/src/skill/ssrf.ts +69 -2
  73. package/src/skill/store.ts +29 -11
  74. package/src/skill/validate.ts +48 -0
@@ -25,14 +25,26 @@ export function validateSkillFile(raw: unknown, options?: { checkSsrf?: boolean
25
25
  if (typeof obj.baseUrl !== 'string') {
26
26
  throw new Error('Missing baseUrl');
27
27
  }
28
+ let baseUrlHostname: string;
28
29
  try {
29
30
  const url = new URL(obj.baseUrl);
30
31
  if (url.protocol !== 'http:' && url.protocol !== 'https:') {
31
32
  throw new Error('non-HTTP scheme');
32
33
  }
34
+ baseUrlHostname = url.hostname;
33
35
  } catch {
34
36
  throw new Error(`Invalid baseUrl: must be a valid HTTP(S) URL`);
35
37
  }
38
+
39
+ // Domain-lock: baseUrl hostname must match or be a subdomain of domain (C1 fix)
40
+ const domainStr = obj.domain as string;
41
+ if (baseUrlHostname !== domainStr && !baseUrlHostname.endsWith('.' + domainStr)) {
42
+ throw new Error(
43
+ `baseUrl hostname "${baseUrlHostname}" does not match domain "${domainStr}". ` +
44
+ `Skill files cannot redirect requests to unrelated hosts.`
45
+ );
46
+ }
47
+
36
48
  if (options?.checkSsrf) {
37
49
  const ssrf = validateUrl(obj.baseUrl);
38
50
  if (!ssrf.safe) {
@@ -66,6 +78,42 @@ export function validateSkillFile(raw: unknown, options?: { checkSsrf?: boolean
66
78
  if (e.path.length > 2000) {
67
79
  throw new Error(`Endpoint ${i}: path exceeds 2000 characters`);
68
80
  }
81
+
82
+ // M11: Deep type validation on nested structures
83
+ if ('headers' in e && e.headers !== undefined) {
84
+ if (typeof e.headers !== 'object' || e.headers === null || Array.isArray(e.headers)) {
85
+ throw new Error(`Endpoint ${i}: headers must be an object`);
86
+ }
87
+ for (const [hk, hv] of Object.entries(e.headers as Record<string, unknown>)) {
88
+ if (typeof hv !== 'string') {
89
+ throw new Error(`Endpoint ${i}: header "${hk}" value must be a string`);
90
+ }
91
+ }
92
+ }
93
+
94
+ if ('queryParams' in e && e.queryParams !== undefined) {
95
+ if (typeof e.queryParams !== 'object' || e.queryParams === null || Array.isArray(e.queryParams)) {
96
+ throw new Error(`Endpoint ${i}: queryParams must be an object`);
97
+ }
98
+ for (const [qk, qv] of Object.entries(e.queryParams as Record<string, unknown>)) {
99
+ if (typeof qv !== 'object' || qv === null || typeof (qv as any).example !== 'string') {
100
+ throw new Error(`Endpoint ${i}: queryParam "${qk}" must have a string example`);
101
+ }
102
+ }
103
+ }
104
+
105
+ if ('requestBody' in e && e.requestBody !== undefined) {
106
+ const rb = e.requestBody as Record<string, unknown>;
107
+ if (typeof rb !== 'object' || rb === null) {
108
+ throw new Error(`Endpoint ${i}: requestBody must be an object`);
109
+ }
110
+ if (typeof rb.contentType !== 'string') {
111
+ throw new Error(`Endpoint ${i}: requestBody.contentType must be a string`);
112
+ }
113
+ if (rb.variables !== undefined && !Array.isArray(rb.variables)) {
114
+ throw new Error(`Endpoint ${i}: requestBody.variables must be an array`);
115
+ }
116
+ }
69
117
  }
70
118
 
71
119
  return raw as SkillFile;