diff --git a/.changeset/deep-nails-behave.md b/.changeset/deep-nails-behave.md new file mode 100644 index 00000000..4c95a8b8 --- /dev/null +++ b/.changeset/deep-nails-behave.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": patch +--- + +fix: escape shell arguments when populating the cache diff --git a/packages/cloudflare/src/cli/commands/populate-cache.ts b/packages/cloudflare/src/cli/commands/populate-cache.ts index 2a0c89e2..5d34d2e6 100644 --- a/packages/cloudflare/src/cli/commands/populate-cache.ts +++ b/packages/cloudflare/src/cli/commands/populate-cache.ts @@ -122,7 +122,7 @@ async function populateR2IncrementalCache( runWrangler( options, - ["r2 object put", JSON.stringify(path.join(bucket, cacheKey)), `--file ${JSON.stringify(fullPath)}`], + ["r2 object put", quoteShellMeta(path.join(bucket, cacheKey)), `--file ${quoteShellMeta(fullPath)}`], // NOTE: R2 does not support the environment flag and results in the following error: // Incorrect type for the 'cacheExpiry' field on 'HttpMetadata': the provided value is not of type 'date'. { target: populateCacheOptions.target, excludeRemoteFlag: true, logging: "error" } @@ -168,11 +168,10 @@ async function populateKVIncrementalCache( writeFileSync(chunkPath, JSON.stringify(kvMapping)); - runWrangler( - options, - ["kv bulk put", JSON.stringify(chunkPath), `--binding ${JSON.stringify(KV_CACHE_BINDING_NAME)}`], - { ...populateCacheOptions, logging: "error" } - ); + runWrangler(options, ["kv bulk put", quoteShellMeta(chunkPath), `--binding ${KV_CACHE_BINDING_NAME}`], { + ...populateCacheOptions, + logging: "error", + }); rmSync(chunkPath); } @@ -197,7 +196,7 @@ function populateD1TagCache( options, [ "d1 execute", - JSON.stringify(D1_TAG_BINDING_NAME), + D1_TAG_BINDING_NAME, `--command "CREATE TABLE IF NOT EXISTS revalidations (tag TEXT NOT NULL, revalidatedAt INTEGER NOT NULL, UNIQUE(tag) ON CONFLICT REPLACE);"`, ], { ...populateCacheOptions, logging: "error" } @@ -258,3 +257,23 @@ export async function populateCache( } } } + +/** + * Escape shell metacharacters. + * + * When `spawnSync` is invoked with `shell: true`, metacharacters need to be escaped. + * + * Based on https://github.com/ljharb/shell-quote/blob/main/quote.js + * + * @param arg + * @returns escaped arg + */ +function quoteShellMeta(arg: string) { + if (/["\s]/.test(arg) && !/'/.test(arg)) { + return `'${arg.replace(/(['\\])/g, "\\$1")}'`; + } + if (/["'\s]/.test(arg)) { + return `"${arg.replace(/(["\\$`!])/g, "\\$1")}"`; + } + return arg.replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, "$1\\$2"); +}