nuxt-og-image 6.7.8 → 6.9.0
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/chunks/tw4.mjs +2 -2
- package/dist/chunks/uno.mjs +4 -4
- package/dist/cli.mjs +1 -1
- package/dist/devtools/nuxt.config.ts +4 -1
- package/dist/module.d.mts +10 -0
- package/dist/module.d.ts +10 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -3
- package/dist/runtime/app/client-utils.js +4 -3
- package/dist/runtime/app/utils.js +3 -2
- package/dist/runtime/server/nitro.d.ts +9 -0
- package/dist/runtime/server/nitro.js +10 -0
- package/dist/runtime/server/og-image/bindings/fetch/node.d.ts +2 -0
- package/dist/runtime/server/og-image/bindings/fetch/node.js +42 -0
- package/dist/runtime/server/og-image/bindings/fetch/web.d.ts +7 -0
- package/dist/runtime/server/og-image/bindings/fetch/web.js +3 -0
- package/dist/runtime/server/og-image/bindings/font-assets/external-url.d.ts +0 -11
- package/dist/runtime/server/og-image/bindings/font-assets/external-url.js +2 -19
- package/dist/runtime/server/og-image/bindings/satori/harfbuzz.d.ts +4 -0
- package/dist/runtime/server/og-image/bindings/satori/harfbuzz.js +33 -0
- package/dist/runtime/server/og-image/browser/screenshot.js +4 -1
- package/dist/runtime/server/og-image/context.js +9 -1
- package/dist/runtime/server/og-image/font-subsets.d.ts +3 -11
- package/dist/runtime/server/og-image/font-subsets.js +50 -27
- package/dist/runtime/server/og-image/fonts.d.ts +1 -1
- package/dist/runtime/server/routes/resolve.js +2 -2
- package/dist/runtime/server/util/cloudflare.js +2 -2
- package/dist/runtime/server/util/eventHandlers.js +3 -3
- package/dist/runtime/server/util/ssrf.d.ts +31 -0
- package/dist/runtime/server/util/ssrf.js +64 -5
- package/dist/runtime/server/utils.d.ts +1 -1
- package/dist/runtime/server/utils.js +3 -3
- package/dist/runtime/shared.d.ts +10 -1
- package/dist/runtime/shared.js +2 -2
- package/dist/runtime/types.d.ts +5 -1
- package/dist/shared/{nuxt-og-image.BMHzg1Vz.mjs → nuxt-og-image.CTD0ZPNd.mjs} +634 -468
- package/package.json +70 -66
- package/types/virtual.d.ts +26 -0
|
@@ -10,6 +10,36 @@ export declare function expandIPv6(addr: string): number[] | null;
|
|
|
10
10
|
* blocked outright.
|
|
11
11
|
*/
|
|
12
12
|
export declare function isBlockedUrl(url: string): boolean;
|
|
13
|
+
/** True when a resolved IP address is internal, reserved, or unparseable. */
|
|
14
|
+
export declare function isBlockedAddress(address: string): boolean;
|
|
15
|
+
declare const sameOriginPathBrand: unique symbol;
|
|
16
|
+
/**
|
|
17
|
+
* A path that stays on the current origin when resolved against any base URL.
|
|
18
|
+
* Only `parseSameOriginPath` creates one, so a value of this type is safe to
|
|
19
|
+
* pass to a same-origin fetch or a headless browser navigation.
|
|
20
|
+
*/
|
|
21
|
+
export type SameOriginPath = string & {
|
|
22
|
+
readonly [sameOriginPathBrand]: true;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Parse an untrusted path (for example the `_path` URL option) into a
|
|
26
|
+
* same-origin path. Returns `undefined` for anything that could leave the
|
|
27
|
+
* origin: absolute URLs, other schemes, protocol-relative `//host`,
|
|
28
|
+
* backslash variants, control characters, and paths without a leading `/`
|
|
29
|
+
* (which a string concatenation with the origin turns into `origin@host`).
|
|
30
|
+
*
|
|
31
|
+
* GHSA-h5qf-97cw-2h86: an absolute `_path` navigated the browser renderer to
|
|
32
|
+
* an internal host and reflected the response into the image.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseSameOriginPath(input: unknown): SameOriginPath | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Resolve `path` against `origin` and return the href, but only when the result
|
|
37
|
+
* stays on that origin. Returns null for a missing or unparseable origin, and for
|
|
38
|
+
* anything that resolves elsewhere. The WHATWG parser normalizes leading
|
|
39
|
+
* controls, tabs, and backslashes first, so " //127.0.0.1" resolves cross-origin
|
|
40
|
+
* and is rejected (GHSA-q8hw-4fvp-9rwv).
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveSameOriginUrl(path: string, origin: string | undefined): string | null;
|
|
13
43
|
export interface SafeFetchOptions {
|
|
14
44
|
timeout: number;
|
|
15
45
|
headers?: Record<string, string>;
|
|
@@ -35,3 +65,4 @@ export interface SafeFetchOptions {
|
|
|
35
65
|
* usual missing-asset behaviour.
|
|
36
66
|
*/
|
|
37
67
|
export declare function fetchWithRedirectValidation(initialUrl: string, opts: SafeFetchOptions): Promise<ArrayBuffer | null>;
|
|
68
|
+
export {};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { guardedFetch } from "#og-image/bindings/fetch";
|
|
1
2
|
const RE_IPV6_BRACKETS = /^\[|\]$/g;
|
|
3
|
+
const RE_TRAILING_DOTS = /\.+$/;
|
|
2
4
|
const RE_DIGIT_ONLY = /^\d+$/;
|
|
3
5
|
const RE_INT_IP = /^(?:0x[\da-f]+|\d+)$/i;
|
|
4
6
|
const RE_HEX_GROUP = /^[0-9a-f]{1,4}$/i;
|
|
@@ -18,6 +20,12 @@ function isPrivateIPv4(a, b) {
|
|
|
18
20
|
return true;
|
|
19
21
|
if (a === 0)
|
|
20
22
|
return true;
|
|
23
|
+
if (a === 100 && b >= 64 && b <= 127)
|
|
24
|
+
return true;
|
|
25
|
+
if (a === 198 && (b === 18 || b === 19))
|
|
26
|
+
return true;
|
|
27
|
+
if (a >= 224)
|
|
28
|
+
return true;
|
|
21
29
|
return false;
|
|
22
30
|
}
|
|
23
31
|
export function expandIPv6(addr) {
|
|
@@ -88,6 +96,10 @@ function isPrivateIPv6(groups) {
|
|
|
88
96
|
}
|
|
89
97
|
if (g0 === 100 && g1 === 65435 && g2 === 1)
|
|
90
98
|
return true;
|
|
99
|
+
if (g0 === 256 && g1 === 0 && g2 === 0 && g3 === 0)
|
|
100
|
+
return true;
|
|
101
|
+
if (g0 === 8193 && g1 === 0)
|
|
102
|
+
return true;
|
|
91
103
|
if ((g0 & 65024) === 64512)
|
|
92
104
|
return true;
|
|
93
105
|
if ((g0 & 65472) === 65152)
|
|
@@ -114,7 +126,7 @@ export function isBlockedUrl(url) {
|
|
|
114
126
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
115
127
|
return true;
|
|
116
128
|
const hostname = parsed.hostname.toLowerCase();
|
|
117
|
-
const bare = hostname.replace(RE_IPV6_BRACKETS, "");
|
|
129
|
+
const bare = hostname.replace(RE_IPV6_BRACKETS, "").replace(RE_TRAILING_DOTS, "");
|
|
118
130
|
if (bare === "localhost" || bare.endsWith(".localhost"))
|
|
119
131
|
return true;
|
|
120
132
|
const dottedParts = bare.split(".");
|
|
@@ -137,6 +149,50 @@ export function isBlockedUrl(url) {
|
|
|
137
149
|
}
|
|
138
150
|
return false;
|
|
139
151
|
}
|
|
152
|
+
export function isBlockedAddress(address) {
|
|
153
|
+
return isBlockedUrl(`http://${address.includes(":") ? `[${address}]` : address}/`);
|
|
154
|
+
}
|
|
155
|
+
const PROBE_ORIGIN = "http://og-image.invalid";
|
|
156
|
+
function hasUnsafePathChar(input) {
|
|
157
|
+
for (let i = 0; i < input.length; i++) {
|
|
158
|
+
const code = input.charCodeAt(i);
|
|
159
|
+
if (code <= 31 || code === 127 || code === 92)
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
export function parseSameOriginPath(input) {
|
|
165
|
+
if (typeof input !== "string" || input[0] !== "/" || input[1] === "/")
|
|
166
|
+
return void 0;
|
|
167
|
+
if (hasUnsafePathChar(input))
|
|
168
|
+
return void 0;
|
|
169
|
+
let resolved;
|
|
170
|
+
try {
|
|
171
|
+
resolved = new URL(input, PROBE_ORIGIN);
|
|
172
|
+
} catch {
|
|
173
|
+
return void 0;
|
|
174
|
+
}
|
|
175
|
+
if (resolved.origin !== PROBE_ORIGIN)
|
|
176
|
+
return void 0;
|
|
177
|
+
return input;
|
|
178
|
+
}
|
|
179
|
+
export function resolveSameOriginUrl(path, origin) {
|
|
180
|
+
if (!origin)
|
|
181
|
+
return null;
|
|
182
|
+
let base;
|
|
183
|
+
try {
|
|
184
|
+
base = new URL(origin).origin;
|
|
185
|
+
} catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
let target;
|
|
189
|
+
try {
|
|
190
|
+
target = new URL(path, base);
|
|
191
|
+
} catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return target.origin === base ? target.href : null;
|
|
195
|
+
}
|
|
140
196
|
export async function fetchWithRedirectValidation(initialUrl, opts) {
|
|
141
197
|
const controller = new AbortController();
|
|
142
198
|
const externalAbort = opts.signal;
|
|
@@ -161,20 +217,23 @@ export async function fetchWithRedirectValidation(initialUrl, opts) {
|
|
|
161
217
|
}
|
|
162
218
|
if (!isTrusted && isBlockedUrl(url))
|
|
163
219
|
return null;
|
|
164
|
-
const res = await
|
|
165
|
-
redirect: "manual",
|
|
220
|
+
const res = await guardedFetch(url, {
|
|
166
221
|
signal: controller.signal,
|
|
167
|
-
headers: opts.headers
|
|
222
|
+
headers: opts.headers,
|
|
223
|
+
isBlockedAddress: isTrusted ? void 0 : isBlockedAddress
|
|
168
224
|
});
|
|
169
225
|
if (res.status >= 300 && res.status < 400) {
|
|
226
|
+
await res.body?.cancel();
|
|
170
227
|
const loc = res.headers.get("location");
|
|
171
228
|
if (!loc)
|
|
172
229
|
return null;
|
|
173
230
|
url = new URL(loc, url).toString();
|
|
174
231
|
continue;
|
|
175
232
|
}
|
|
176
|
-
if (!res.ok)
|
|
233
|
+
if (!res.ok) {
|
|
234
|
+
await res.body?.cancel();
|
|
177
235
|
return null;
|
|
236
|
+
}
|
|
178
237
|
return await res.arrayBuffer();
|
|
179
238
|
}
|
|
180
239
|
return null;
|
|
@@ -4,5 +4,5 @@ export interface GetOgImagePathResult {
|
|
|
4
4
|
path: string;
|
|
5
5
|
hash?: string;
|
|
6
6
|
}
|
|
7
|
-
export declare function getOgImagePath(_pagePath: string, _options?: Partial<OgImageOptionsInternal>): GetOgImagePathResult;
|
|
7
|
+
export declare function getOgImagePath(event: H3Event, _pagePath: string, _options?: Partial<OgImageOptionsInternal>): GetOgImagePathResult;
|
|
8
8
|
export declare function useOgImageRuntimeConfig(e?: H3Event): OgImageRuntimeConfig;
|
|
@@ -3,9 +3,9 @@ import { useRuntimeConfig } from "#nuxtseo/nitro";
|
|
|
3
3
|
import { componentNames } from "#og-image-virtual/component-names.mjs";
|
|
4
4
|
import { buildOgImageUrl } from "../shared.js";
|
|
5
5
|
import { getCloudflareEnv } from "./util/cloudflare.js";
|
|
6
|
-
export function getOgImagePath(_pagePath, _options) {
|
|
7
|
-
const baseURL = useRuntimeConfig().app.baseURL;
|
|
8
|
-
const { defaults, security } = useOgImageRuntimeConfig();
|
|
6
|
+
export function getOgImagePath(event, _pagePath, _options) {
|
|
7
|
+
const baseURL = useRuntimeConfig(event).app.baseURL;
|
|
8
|
+
const { defaults, security } = useOgImageRuntimeConfig(event);
|
|
9
9
|
const extension = _options?.extension || defaults.extension;
|
|
10
10
|
const isStatic = import.meta.prerender && !(security?.secret && security?.strict);
|
|
11
11
|
const options = { ..._options, _path: _pagePath };
|
package/dist/runtime/shared.d.ts
CHANGED
|
@@ -2,7 +2,16 @@ import type { ResolvableMeta } from '@unhead/vue';
|
|
|
2
2
|
import type { OgImageOptions, OgImagePrebuilt } from './types.js';
|
|
3
3
|
export { extractSocialPreviewTags, toBase64Image } from './pure.js';
|
|
4
4
|
export { buildOgImageUrl, decodeOgImageParams, encodeOgImageParams, extractEncodedSegment, hashOgImageOptions, parseOgImageUrl, signEncodedParams, verifyOgImageSignature } from './shared/urlEncoding.js';
|
|
5
|
-
export
|
|
5
|
+
export interface GenerateMetaOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Emit Twitter card meta tags (`twitter:card`, `twitter:image`, etc) alongside
|
|
8
|
+
* the Open Graph image tags.
|
|
9
|
+
*
|
|
10
|
+
* @default true
|
|
11
|
+
*/
|
|
12
|
+
includeTwitter?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function generateMeta(url: OgImagePrebuilt['url'] | string, resolvedOptions: OgImageOptions | OgImagePrebuilt, metaOptions?: GenerateMetaOptions): ResolvableMeta[];
|
|
6
15
|
export declare function isInternalRoute(path: string): boolean;
|
|
7
16
|
export declare function separateProps(options: OgImageOptions | undefined, ignoreKeys?: string[]): OgImageOptions;
|
|
8
17
|
/**
|
package/dist/runtime/shared.js
CHANGED
|
@@ -3,10 +3,10 @@ import { toValue } from "vue";
|
|
|
3
3
|
export { extractSocialPreviewTags, toBase64Image } from "./pure.js";
|
|
4
4
|
export { buildOgImageUrl, decodeOgImageParams, encodeOgImageParams, extractEncodedSegment, hashOgImageOptions, parseOgImageUrl, signEncodedParams, verifyOgImageSignature } from "./shared/urlEncoding.js";
|
|
5
5
|
const RE_KEBAB_CASE = /-([a-z])/g;
|
|
6
|
-
export function generateMeta(url, resolvedOptions) {
|
|
6
|
+
export function generateMeta(url, resolvedOptions, metaOptions = {}) {
|
|
7
7
|
const key = resolvedOptions.key || "og";
|
|
8
8
|
const isTwitterOnly = key === "twitter";
|
|
9
|
-
const includeTwitter = key === "og" || key === "twitter";
|
|
9
|
+
const includeTwitter = (key === "og" || key === "twitter") && metaOptions.includeTwitter !== false;
|
|
10
10
|
const meta = [];
|
|
11
11
|
if (includeTwitter) {
|
|
12
12
|
meta.push({ name: "twitter:card", content: "summary_large_image" });
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -4,12 +4,13 @@ import type { SatoriOptions } from 'satori';
|
|
|
4
4
|
import type { JpegOptions, SharpOptions } from 'sharp';
|
|
5
5
|
import type { MaybeRefOrGetter, Ref } from 'vue';
|
|
6
6
|
import type { H3Error, H3Event } from '#nuxtseo/h3';
|
|
7
|
+
import type { SameOriginPath } from './server/util/ssrf.js';
|
|
7
8
|
import type { Timings } from './server/util/timings.js';
|
|
8
9
|
export interface OgImageRenderEventContext {
|
|
9
10
|
e: H3Event;
|
|
10
11
|
extension: 'png' | 'jpeg' | 'jpg' | 'webp' | 'svg' | 'html' | 'json';
|
|
11
12
|
key: string;
|
|
12
|
-
basePath:
|
|
13
|
+
basePath: SameOriginPath;
|
|
13
14
|
renderer: Renderer;
|
|
14
15
|
options: OgImageOptionsInternal;
|
|
15
16
|
isDevToolsContextRequest: boolean;
|
|
@@ -37,6 +38,8 @@ export interface OgImageRuntimeConfig {
|
|
|
37
38
|
publicStoragePath: string;
|
|
38
39
|
defaults: Omit<OgImageOptions, 'component' | 'renderer' | 'props' | 'url' | 'html' | 'key' | 'cacheKey' | '_query' | '_hash' | 'socialPreview'>;
|
|
39
40
|
debug: boolean;
|
|
41
|
+
/** Whether twitter:* meta tags are emitted alongside og:image tags */
|
|
42
|
+
includeTwitter: boolean;
|
|
40
43
|
baseCacheKey: string;
|
|
41
44
|
hasNuxtIcon: boolean;
|
|
42
45
|
hasNuxtContent?: boolean;
|
|
@@ -239,6 +242,7 @@ export interface RuntimeCompatibilitySchema {
|
|
|
239
242
|
takumi: 'node' | 'node-dev' | 'wasm' | false;
|
|
240
243
|
sharp: 'node' | false;
|
|
241
244
|
emoji?: 'local' | 'fetch';
|
|
245
|
+
fetch?: 'node' | 'web';
|
|
242
246
|
wasm?: any;
|
|
243
247
|
}
|
|
244
248
|
export interface RuntimeCompatibilityMeta {
|