@jini-ai/platform 0.5.7 → 0.5.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # @jini-ai/platform
2
2
 
3
+ ## 0.5.8 — 2026-10-15
4
+
5
+ - SECURITY: the guarded HTTP client forwards only `accept`, `accept-language` and `user-agent` on a cross-origin redirect (4de69b67). It used to strip only Authorization/Cookie, so custom credential headers (`x-api-key`, `X-Shopify-Access-Token`, …) followed a redirect to another origin. Same-origin hops keep all headers.
6
+ - `./web` page fetch applies `maxChars` to the readable text, not the raw HTML (0625844e): raw HTML uses the fixed 1,000,000-char parser ceiling, and the result says `truncated: true` when cut. A page with a large script/style head no longer returns almost no text.
7
+
3
8
  ## 0.5.7 — 2026-10-14
4
9
 
5
10
  - The asset cache's optional `undici` load moves behind `asset-cache.adapter.ts` (64eafe35); injected dispatchers, DNS validation and the MODULE_NOT_FOUND message are unchanged. No API change.
@@ -38,7 +38,7 @@ export function classifyAddress({ ip }) {
38
38
  // it is not globally routable and belongs with RFC 1918 private ranges for egress decisions.
39
39
  return "private";
40
40
  }
41
- const SENSITIVE_HEADERS = new Set(["authorization", "cookie"]);
41
+ const CROSS_ORIGIN_HEADERS = new Set(["accept", "accept-language", "user-agent"]);
42
42
  // HTTP header names are case-insensitive; preserve the caller's explicit User-Agent in any casing.
43
43
  function hasUserAgentHeader(headers) {
44
44
  return Object.keys(headers).some((key) => key.toLowerCase() === "user-agent");
@@ -105,10 +105,10 @@ async function resolvePinnedPeer(url, policy, dns) {
105
105
  tlsServerName: isIP(hostname) === 0 ? url.hostname : undefined,
106
106
  };
107
107
  }
108
- function withStrippedSensitiveHeaders(headers) {
108
+ function withCrossOriginHeaders(headers) {
109
109
  const next = {};
110
110
  for (const [key, value] of Object.entries(headers)) {
111
- if (SENSITIVE_HEADERS.has(key.toLowerCase()))
111
+ if (!CROSS_ORIGIN_HEADERS.has(key.toLowerCase()))
112
112
  continue;
113
113
  next[key] = value;
114
114
  }
@@ -179,8 +179,9 @@ async function sendWithPolicy(transport, policy, request, redirectsFollowed, dns
179
179
  return cappedResponse;
180
180
  const nextUrl = new URL(location, url);
181
181
  // Cross-origin hops must not inherit authorization or cookies entrusted to the original origin.
182
+ // Custom headers can carry credentials too; forward only the explicit noncredential allowlist.
182
183
  const nextHeaders = isCrossOrigin(url, nextUrl)
183
- ? withStrippedSensitiveHeaders(request.headers)
184
+ ? withCrossOriginHeaders(request.headers)
184
185
  : request.headers;
185
186
  return sendWithPolicy(transport, policy, { ...request, url: nextUrl.toString(), headers: nextHeaders }, redirectsFollowed + 1, dns, redirect);
186
187
  }
@@ -245,8 +245,8 @@ function project({ input, ports, url, response }) {
245
245
  const body = decodeBody(response, contentType, isHtml);
246
246
  if (body.includes("\u0000"))
247
247
  throw new ToolInputError({ message: "web_fetch_page: the response is binary, not a text page." });
248
- // Four input characters per output character limits synchronous parsing for small requests too.
249
- const bounded = isHtml ? boundHtml({ html: body }, { maxChars: Math.max(4096, input.maxChars * 4) }) : { html: body, truncated: false };
248
+ // A fixed ceiling limits synchronous parsing without letting a large head consume the output budget.
249
+ const bounded = isHtml ? boundHtml({ html: body }) : { html: body, truncated: false };
250
250
  const facts = isHtml ? readHtmlDocument({ html: bounded.html, pageUrl: finalUrl }) : /xml/.test(mediaType) ? sitemapFacts(body, finalUrl) : EMPTY_FACTS;
251
251
  const rendered = render(input.format, input.format === "raw" ? body : bounded.html, isHtml, finalUrl, ports);
252
252
  const content = clip(rendered, input.maxChars);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/platform",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "Generic OS/platform primitives: process exec and lifecycle, filesystem containment, HTTP readiness polling, toolchain discovery, blob storage, AWS signing, and an SSRF-safe asset cache.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {