@apitap/core 1.0.11 → 1.0.13

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.
@@ -7,24 +7,40 @@ import { refreshTokens } from '../auth/refresh.js';
7
7
  import { truncateResponse } from './truncate.js';
8
8
  import { resolveAndValidateUrl } from '../skill/ssrf.js';
9
9
 
10
- // Header security: prevent header injection from skill files
11
- const ALLOWED_SKILL_HEADERS = new Set([
12
- 'accept', 'accept-language', 'accept-encoding',
13
- 'content-type', 'content-length',
14
- 'x-requested-with', 'x-api-key',
15
- 'origin', 'referer',
16
- 'user-agent',
17
- // Auth headers are injected separately from encrypted storage, not from skill file
18
- ]);
19
-
20
- const BLOCKED_HEADERS = new Set([
21
- 'host', 'x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto',
22
- 'x-real-ip', 'forwarded', 'via',
23
- 'cookie', 'set-cookie',
24
- 'authorization', // Must come from auth manager, not skill file
10
+ // Header security: block dangerous headers from skill files (blocklist approach).
11
+ // All other headers — including custom API headers like Client-ID — pass through.
12
+ const BLOCKED_REPLAY_HEADERS = new Set([
13
+ // Connection control
14
+ 'host',
15
+ 'connection',
16
+ 'keep-alive',
17
+ 'transfer-encoding',
18
+ 'upgrade',
19
+ 'te',
20
+ 'trailer',
21
+ // Proxy/forwarding
22
+ 'x-forwarded-for',
23
+ 'x-forwarded-host',
24
+ 'x-forwarded-proto',
25
+ 'x-forwarded-port',
26
+ 'x-real-ip',
27
+ 'forwarded',
28
+ 'via',
25
29
  'proxy-authorization',
26
- 'transfer-encoding', 'te', 'trailer',
27
- 'connection', 'upgrade',
30
+ 'proxy-connection',
31
+ // Cookie/auth (managed separately)
32
+ 'cookie',
33
+ 'set-cookie',
34
+ 'authorization', // Must come from auth manager, not skill file
35
+ // Browser-internal (Sec-* headers)
36
+ 'sec-ch-ua',
37
+ 'sec-ch-ua-mobile',
38
+ 'sec-ch-ua-platform',
39
+ 'sec-ch-ua-full-version-list',
40
+ 'sec-fetch-dest',
41
+ 'sec-fetch-mode',
42
+ 'sec-fetch-site',
43
+ 'sec-fetch-user',
28
44
  ]);
29
45
 
30
46
  export interface ReplayOptions {
@@ -205,7 +221,7 @@ export async function replayEndpoint(
205
221
  // Filter headers from skill file — block dangerous headers
206
222
  for (const key of Object.keys(headers)) {
207
223
  const lower = key.toLowerCase();
208
- if (BLOCKED_HEADERS.has(lower) || (!ALLOWED_SKILL_HEADERS.has(lower) && !lower.startsWith('x-'))) {
224
+ if (BLOCKED_REPLAY_HEADERS.has(lower) || lower.startsWith('sec-')) {
209
225
  delete headers[key];
210
226
  }
211
227
  }
@@ -10,13 +10,39 @@ import { isLikelyToken } from '../capture/entropy.js';
10
10
  import { isOAuthTokenRequest, type OAuthInfo } from '../capture/oauth-detector.js';
11
11
  import { diffBodies } from '../capture/body-diff.js';
12
12
 
13
- const KEEP_HEADERS = new Set([
14
- 'authorization',
15
- 'content-type',
16
- 'accept',
17
- 'x-api-key',
18
- 'x-csrf-token',
19
- 'x-requested-with',
13
+ /** Headers to strip (connection control, forwarding, browser-internal, encoding) */
14
+ const STRIP_HEADERS = new Set([
15
+ // Connection control
16
+ 'host',
17
+ 'connection',
18
+ 'keep-alive',
19
+ 'transfer-encoding',
20
+ 'upgrade',
21
+ 'te',
22
+ 'trailer',
23
+ // Proxy/forwarding
24
+ 'x-forwarded-for',
25
+ 'x-forwarded-host',
26
+ 'x-forwarded-proto',
27
+ 'x-forwarded-port',
28
+ 'x-real-ip',
29
+ 'forwarded',
30
+ 'via',
31
+ 'proxy-authorization',
32
+ 'proxy-connection',
33
+ // Browser-internal (Sec-* headers)
34
+ 'sec-ch-ua',
35
+ 'sec-ch-ua-mobile',
36
+ 'sec-ch-ua-platform',
37
+ 'sec-ch-ua-full-version-list',
38
+ 'sec-fetch-dest',
39
+ 'sec-fetch-mode',
40
+ 'sec-fetch-site',
41
+ 'sec-fetch-user',
42
+ // Encoding (handled automatically by fetch)
43
+ 'accept-encoding',
44
+ // Cookie (stored separately via AuthManager)
45
+ 'cookie',
20
46
  ]);
21
47
 
22
48
  const AUTH_HEADERS = new Set([
@@ -33,7 +59,7 @@ function filterHeaders(headers: Record<string, string>): Record<string, string>
33
59
  const filtered: Record<string, string> = {};
34
60
  for (const [key, value] of Object.entries(headers)) {
35
61
  const lower = key.toLowerCase();
36
- if (KEEP_HEADERS.has(lower) || (lower.startsWith('x-') && !lower.startsWith('x-forwarded'))) {
62
+ if (!STRIP_HEADERS.has(lower) && !lower.startsWith('sec-')) {
37
63
  filtered[key] = value;
38
64
  }
39
65
  }