@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.
- package/README.md +4 -2
- package/dist/auth/crypto.d.ts +10 -0
- package/dist/auth/crypto.js +30 -6
- package/dist/auth/crypto.js.map +1 -1
- package/dist/auth/handoff.js +20 -1
- package/dist/auth/handoff.js.map +1 -1
- package/dist/auth/manager.d.ts +1 -0
- package/dist/auth/manager.js +35 -9
- package/dist/auth/manager.js.map +1 -1
- package/dist/capture/monitor.js +4 -0
- package/dist/capture/monitor.js.map +1 -1
- package/dist/capture/scrubber.js +10 -0
- package/dist/capture/scrubber.js.map +1 -1
- package/dist/capture/session.js +7 -17
- package/dist/capture/session.js.map +1 -1
- package/dist/cli.js +74 -17
- package/dist/cli.js.map +1 -1
- package/dist/discovery/fetch.js +3 -3
- package/dist/discovery/fetch.js.map +1 -1
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.js +59 -33
- package/dist/mcp.js.map +1 -1
- package/dist/native-host.js +2 -2
- package/dist/native-host.js.map +1 -1
- package/dist/orchestration/browse.js +13 -4
- package/dist/orchestration/browse.js.map +1 -1
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +14 -4
- package/dist/plugin.js.map +1 -1
- package/dist/read/decoders/reddit.js +4 -0
- package/dist/read/decoders/reddit.js.map +1 -1
- package/dist/replay/engine.js +60 -17
- package/dist/replay/engine.js.map +1 -1
- package/dist/serve.d.ts +2 -0
- package/dist/serve.js +8 -1
- package/dist/serve.js.map +1 -1
- package/dist/skill/generator.d.ts +5 -0
- package/dist/skill/generator.js +30 -4
- package/dist/skill/generator.js.map +1 -1
- package/dist/skill/search.js +1 -1
- package/dist/skill/search.js.map +1 -1
- package/dist/skill/signing.js +19 -1
- package/dist/skill/signing.js.map +1 -1
- package/dist/skill/ssrf.js +71 -2
- package/dist/skill/ssrf.js.map +1 -1
- package/dist/skill/store.d.ts +2 -0
- package/dist/skill/store.js +23 -10
- package/dist/skill/store.js.map +1 -1
- package/dist/skill/validate.d.ts +10 -0
- package/dist/skill/validate.js +106 -0
- package/dist/skill/validate.js.map +1 -0
- package/package.json +1 -1
- package/src/auth/crypto.ts +14 -6
- package/src/auth/handoff.ts +19 -1
- package/src/auth/manager.ts +22 -5
- package/src/capture/monitor.ts +4 -0
- package/src/capture/scrubber.ts +12 -0
- package/src/capture/session.ts +5 -14
- package/src/cli.ts +215 -12
- package/src/discovery/fetch.ts +2 -2
- package/src/index/reader.ts +65 -0
- package/src/mcp.ts +64 -31
- package/src/native-host.ts +29 -2
- package/src/orchestration/browse.ts +13 -4
- package/src/plugin.ts +17 -5
- package/src/read/decoders/reddit.ts +3 -3
- package/src/replay/engine.ts +65 -15
- package/src/serve.ts +10 -1
- package/src/skill/generator.ts +32 -4
- package/src/skill/search.ts +1 -1
- package/src/skill/signing.ts +20 -1
- package/src/skill/ssrf.ts +69 -2
- package/src/skill/store.ts +29 -11
- package/src/skill/validate.ts +48 -0
package/src/skill/validate.ts
CHANGED
|
@@ -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;
|