beachviber 1.0.34 → 1.0.35

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.

Potentially problematic release.


This version of beachviber might be problematic. Click here for more details.

@@ -2,7 +2,7 @@ import { mkdirSync, existsSync, readdirSync, statSync, unlinkSync, writeFileSync
2
2
  import { join } from "path";
3
3
  import { tmpdir } from "os";
4
4
  import { randomUUID } from "crypto";
5
- import { resolve as dnsResolve } from "dns/promises";
5
+ import { lookup as dnsLookup } from "dns/promises";
6
6
  const IMAGE_DIR = join(tmpdir(), "beachviber-images");
7
7
  const ALLOWED_EXTENSIONS = new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "heic"]);
8
8
  const ALLOWED_CONTENT_TYPES = new Set([
@@ -59,17 +59,16 @@ export async function downloadImage(url) {
59
59
  throw new Error(`Image download blocked: only HTTPS URLs are allowed (got ${parsed.protocol})`);
60
60
  }
61
61
  // Resolve hostname and block private/reserved IPs to prevent SSRF.
62
- // Pin the resolved IP to prevent DNS rebinding between validation and fetch.
62
+ // Uses dns.lookup (OS resolver) instead of dns.resolve (raw DNS queries)
63
+ // for reliable resolution across all platforms.
63
64
  const hostname = parsed.hostname;
64
- let resolvedIP;
65
65
  try {
66
- const addresses = await dnsResolve(hostname);
67
- for (const addr of addresses) {
68
- if (isPrivateIP(addr)) {
69
- throw new Error(`Image download blocked: hostname "${hostname}" resolves to private IP ${addr}`);
66
+ const results = await dnsLookup(hostname, { all: true });
67
+ for (const { address } of results) {
68
+ if (isPrivateIP(address)) {
69
+ throw new Error(`Image download blocked: hostname "${hostname}" resolves to private IP ${address}`);
70
70
  }
71
71
  }
72
- resolvedIP = addresses[0];
73
72
  }
74
73
  catch (err) {
75
74
  if (err instanceof Error && err.message.startsWith("Image download blocked"))
@@ -80,13 +79,11 @@ export async function downloadImage(url) {
80
79
  const ext = extFromUrl(url);
81
80
  const filename = `${randomUUID()}.${ext}`;
82
81
  const filepath = join(IMAGE_DIR, filename);
83
- // Fetch using the pinned IP to prevent DNS rebinding.
84
- // Replace hostname with the resolved IP and set Host header for TLS/virtual hosting.
85
- const pinnedUrl = new URL(url);
86
- pinnedUrl.hostname = resolvedIP;
87
- const response = await fetch(pinnedUrl.toString(), {
82
+ // Fetch using the original URL — IP pinning is not used because replacing
83
+ // the hostname with a raw IP breaks TLS certificate validation for S3 and
84
+ // other virtual-hosted services.
85
+ const response = await fetch(url, {
88
86
  signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
89
- headers: { Host: hostname },
90
87
  });
91
88
  if (!response.ok) {
92
89
  throw new Error(`Failed to download image: ${response.status} ${response.statusText}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beachviber",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "BeachViber Agent — control Claude Code remotely from your phone",
5
5
  "type": "module",
6
6
  "license": "MIT",