browserclaw 0.5.0 → 0.5.1

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/dist/index.cjs CHANGED
@@ -1524,6 +1524,22 @@ function extractEmbeddedIpv4FromIpv6(v6, opts) {
1524
1524
  return true;
1525
1525
  }
1526
1526
  }
1527
+ if (parts[0] === 0 && parts[1] === 0 && parts[2] === 0 && parts[3] === 0 && parts[4] === 0 && parts[5] === 0) {
1528
+ const ip4str = `${parts[6] >> 8 & 255}.${parts[6] & 255}.${parts[7] >> 8 & 255}.${parts[7] & 255}`;
1529
+ try {
1530
+ return isBlockedSpecialUseIpv4Address(ipaddr__namespace.IPv4.parse(ip4str), opts);
1531
+ } catch {
1532
+ return true;
1533
+ }
1534
+ }
1535
+ if ((parts[4] & 65023) === 0 && parts[5] === 24318) {
1536
+ const ip4str = `${parts[6] >> 8 & 255}.${parts[6] & 255}.${parts[7] >> 8 & 255}.${parts[7] & 255}`;
1537
+ try {
1538
+ return isBlockedSpecialUseIpv4Address(ipaddr__namespace.IPv4.parse(ip4str), opts);
1539
+ } catch {
1540
+ return true;
1541
+ }
1542
+ }
1527
1543
  return null;
1528
1544
  }
1529
1545
  function isPrivateIpAddress(address, opts) {
@@ -1546,6 +1562,30 @@ function isPrivateIpAddress(address, opts) {
1546
1562
  if (normalized.includes(":")) return true;
1547
1563
  return false;
1548
1564
  }
1565
+ function normalizeHostnameSet(values) {
1566
+ if (!values || values.length === 0) return /* @__PURE__ */ new Set();
1567
+ return new Set(values.map((v) => normalizeHostname(v)).filter(Boolean));
1568
+ }
1569
+ function normalizeHostnameAllowlist(values) {
1570
+ if (!values || values.length === 0) return [];
1571
+ return Array.from(
1572
+ new Set(
1573
+ values.map((v) => normalizeHostname(v)).filter((v) => v !== "*" && v !== "*." && v.length > 0)
1574
+ )
1575
+ );
1576
+ }
1577
+ function isHostnameAllowedByPattern(hostname, pattern) {
1578
+ if (pattern.startsWith("*.")) {
1579
+ const suffix = pattern.slice(2);
1580
+ if (!suffix || hostname === suffix) return false;
1581
+ return hostname.endsWith(`.${suffix}`);
1582
+ }
1583
+ return hostname === pattern;
1584
+ }
1585
+ function matchesHostnameAllowlist(hostname, allowlist) {
1586
+ if (allowlist.length === 0) return true;
1587
+ return allowlist.some((pattern) => isHostnameAllowedByPattern(hostname, pattern));
1588
+ }
1549
1589
  function dedupeAndPreferIpv4(results) {
1550
1590
  const seen = /* @__PURE__ */ new Set();
1551
1591
  const ipv4 = [];
@@ -1591,12 +1631,15 @@ async function resolvePinnedHostnameWithPolicy(hostname, params = {}) {
1591
1631
  const normalized = normalizeHostname(hostname);
1592
1632
  if (!normalized) throw new InvalidBrowserNavigationUrlError(`Invalid hostname: "${hostname}"`);
1593
1633
  const allowPrivateNetwork = isPrivateNetworkAllowedByPolicy(params.policy);
1594
- const allowedHostnames = [
1595
- ...params.policy?.allowedHostnames ?? [],
1596
- ...params.policy?.hostnameAllowlist ?? []
1597
- ].map((h) => normalizeHostname(h));
1598
- const isExplicitlyAllowed = allowedHostnames.some((h) => h === normalized);
1634
+ const allowedHostnames = normalizeHostnameSet(params.policy?.allowedHostnames);
1635
+ const hostnameAllowlist = normalizeHostnameAllowlist(params.policy?.hostnameAllowlist);
1636
+ const isExplicitlyAllowed = allowedHostnames.has(normalized);
1599
1637
  const skipPrivateNetworkChecks = allowPrivateNetwork || isExplicitlyAllowed;
1638
+ if (!matchesHostnameAllowlist(normalized, hostnameAllowlist)) {
1639
+ throw new InvalidBrowserNavigationUrlError(
1640
+ `Navigation blocked: hostname "${hostname}" is not in the allowlist.`
1641
+ );
1642
+ }
1600
1643
  if (!skipPrivateNetworkChecks) {
1601
1644
  if (isBlockedHostnameNormalized(normalized)) {
1602
1645
  throw new InvalidBrowserNavigationUrlError(
@@ -1663,11 +1706,9 @@ async function assertBrowserNavigationAllowed(opts) {
1663
1706
  "Navigation blocked: strict browser SSRF policy cannot be enforced while env proxy variables are set"
1664
1707
  );
1665
1708
  }
1666
- const policy = opts.ssrfPolicy;
1667
- if (policy?.dangerouslyAllowPrivateNetwork ?? policy?.allowPrivateNetwork ?? true) return;
1668
1709
  await resolvePinnedHostnameWithPolicy(parsed.hostname, {
1669
1710
  lookupFn: opts.lookupFn,
1670
- policy
1711
+ policy: opts.ssrfPolicy
1671
1712
  });
1672
1713
  }
1673
1714
  async function assertSafeOutputPath(path2, allowedRoots) {