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.
- package/dist/image-download.js +11 -14
- package/package.json +1 -1
package/dist/image-download.js
CHANGED
|
@@ -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 {
|
|
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
|
-
//
|
|
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
|
|
67
|
-
for (const
|
|
68
|
-
if (isPrivateIP(
|
|
69
|
-
throw new Error(`Image download blocked: hostname "${hostname}" resolves to private IP ${
|
|
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
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
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}`);
|