fix security issues
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
* served from Shopify's CDN so re-fetching is cheap, but caching avoids
|
||||
* hammering the network when regenerating an invoice multiple times.
|
||||
*/
|
||||
import { safeFetch, SafeFetchError, SHOPIFY_CDN_HOSTS } from "./safeFetch.server";
|
||||
|
||||
const MAX_BYTES = 2 * 1024 * 1024; // 2 MB cap per image
|
||||
const CACHE_MAX_ENTRIES = 200;
|
||||
|
||||
@@ -40,25 +42,34 @@ export async function fetchProductImageDataUrl(url: string): Promise<string | un
|
||||
? `${url}${url.includes("?") ? "&" : "?"}width=128`
|
||||
: url;
|
||||
|
||||
let res: Response;
|
||||
let res: Awaited<ReturnType<typeof safeFetch>>;
|
||||
try {
|
||||
res = await fetch(requestUrl);
|
||||
res = await safeFetch(requestUrl, {
|
||||
maxBytes: MAX_BYTES,
|
||||
accept: "image/*",
|
||||
// Lock product images to Shopify's CDN — line item image URLs come
|
||||
// from the Admin API and should never point anywhere else.
|
||||
allowedHosts: SHOPIFY_CDN_HOSTS,
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn(`Product image fetch failed for ${url}:`, err);
|
||||
if (err instanceof SafeFetchError) {
|
||||
console.warn(`Product image refused (${err.code}) for ${url}: ${err.message}`);
|
||||
} else {
|
||||
console.warn(`Product image fetch failed for ${url}:`, err);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (!res.ok) {
|
||||
if (res.status < 200 || res.status >= 300) {
|
||||
console.warn(`Product image HTTP ${res.status} for ${url}`);
|
||||
return undefined;
|
||||
}
|
||||
const buf = await res.arrayBuffer();
|
||||
if (buf.byteLength === 0 || buf.byteLength > MAX_BYTES) return undefined;
|
||||
if (res.bytesRead === 0) return undefined;
|
||||
|
||||
const contentType = guessContentType(url, res.headers.get("content-type"));
|
||||
const contentType = guessContentType(url, res.contentType);
|
||||
// @react-pdf supports png/jpeg natively; webp/gif are unreliable. Skip those.
|
||||
if (contentType !== "image/png" && contentType !== "image/jpeg") return undefined;
|
||||
|
||||
const b64 = Buffer.from(buf).toString("base64");
|
||||
const b64 = Buffer.from(res.bytes).toString("base64");
|
||||
const dataUrl = `data:${contentType};base64,${b64}`;
|
||||
rememberInCache(url, dataUrl);
|
||||
return dataUrl;
|
||||
|
||||
Reference in New Issue
Block a user