UNPKG

@dmno/nextjs-integration

Version:

tools for integrating dmno into nextjs

737 lines (735 loc) 30.1 kB
import { fileURLToPath } from 'url'; import path from 'path'; import fs from 'fs'; import zlib from 'zlib'; import { ServerResponse } from 'http'; import { resolve } from 'import-meta-resolve'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var getFilename = /* @__PURE__ */ __name(() => fileURLToPath(import.meta.url), "getFilename"); var getDirname = /* @__PURE__ */ __name(() => path.dirname(getFilename()), "getDirname"); var __dirname = /* @__PURE__ */ getDirname(); var __defProp2 = Object.defineProperty; var __name2 = /* @__PURE__ */ __name((target, value) => __defProp2(target, "name", { value, configurable: true }), "__name"); var FORCE_COLOR; var NODE_DISABLE_COLORS; var NO_COLOR; var TERM; var isTTY = true; if (typeof process !== "undefined") { ({ FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env || {}); isTTY = process.stdout && process.stdout.isTTY; } var $ = { enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== "dumb" && (FORCE_COLOR != null && FORCE_COLOR !== "0" || isTTY), // modifiers reset: init(0, 0), bold: init(1, 22), dim: init(2, 22), italic: init(3, 23), underline: init(4, 24), inverse: init(7, 27), hidden: init(8, 28), strikethrough: init(9, 29), // colors black: init(30, 39), red: init(31, 39), green: init(32, 39), yellow: init(33, 39), blue: init(34, 39), magenta: init(35, 39), cyan: init(36, 39), white: init(37, 39), gray: init(90, 39), grey: init(90, 39), // background colors bgBlack: init(40, 49), bgRed: init(41, 49), bgGreen: init(42, 49), bgYellow: init(43, 49), bgBlue: init(44, 49), bgMagenta: init(45, 49), bgCyan: init(46, 49), bgWhite: init(47, 49) }; function run(arr, str) { let i = 0, tmp, beg = "", end = ""; for (; i < arr.length; i++) { tmp = arr[i]; beg += tmp.open; end += tmp.close; if (!!~str.indexOf(tmp.close)) { str = str.replace(tmp.rgx, tmp.close + tmp.open); } } return beg + str + end; } __name(run, "run"); __name2(run, "run"); function chain(has, keys) { let ctx = { has, keys }; ctx.reset = $.reset.bind(ctx); ctx.bold = $.bold.bind(ctx); ctx.dim = $.dim.bind(ctx); ctx.italic = $.italic.bind(ctx); ctx.underline = $.underline.bind(ctx); ctx.inverse = $.inverse.bind(ctx); ctx.hidden = $.hidden.bind(ctx); ctx.strikethrough = $.strikethrough.bind(ctx); ctx.black = $.black.bind(ctx); ctx.red = $.red.bind(ctx); ctx.green = $.green.bind(ctx); ctx.yellow = $.yellow.bind(ctx); ctx.blue = $.blue.bind(ctx); ctx.magenta = $.magenta.bind(ctx); ctx.cyan = $.cyan.bind(ctx); ctx.white = $.white.bind(ctx); ctx.gray = $.gray.bind(ctx); ctx.grey = $.grey.bind(ctx); ctx.bgBlack = $.bgBlack.bind(ctx); ctx.bgRed = $.bgRed.bind(ctx); ctx.bgGreen = $.bgGreen.bind(ctx); ctx.bgYellow = $.bgYellow.bind(ctx); ctx.bgBlue = $.bgBlue.bind(ctx); ctx.bgMagenta = $.bgMagenta.bind(ctx); ctx.bgCyan = $.bgCyan.bind(ctx); ctx.bgWhite = $.bgWhite.bind(ctx); return ctx; } __name(chain, "chain"); __name2(chain, "chain"); function init(open, close) { let blk = { open: `\x1B[${open}m`, close: `\x1B[${close}m`, rgx: new RegExp(`\\x1b\\[${close}m`, "g") }; return function(txt) { if (this !== void 0 && this.has !== void 0) { !!~this.has.indexOf(open) || (this.has.push(open), this.keys.push(blk)); return txt === void 0 ? this : $.enabled ? run(this.keys, txt + "") : txt + ""; } return txt === void 0 ? chain([open], [blk]) : $.enabled ? run([blk], txt + "") : txt + ""; }; } __name(init, "init"); __name2(init, "init"); var kleur_default = $; var UNMASK_STR = "\u{1F441}"; function buildRedactionRegex(lookup) { if (!lookup || !Object.keys(lookup).length) return; const redactionMap = {}; for (const key in lookup) redactionMap[lookup[key].value] = lookup[key].redacted; const findRegex = new RegExp( [ `(${UNMASK_STR})?`, "(", Object.keys(redactionMap).map((s) => s.replace(/[()[\]{}*+?^$|#.,/\\\s-]/g, "\\$&")).sort((a, b) => b.length - a.length).join("|"), ")", `(${UNMASK_STR})?` ].join(""), "g" ); const replaceFn = /* @__PURE__ */ __name2((match, pre, val, post) => { if (pre && post) return val; return redactionMap[val]; }, "replaceFn"); return { findRegex, replaceFn }; } __name(buildRedactionRegex, "buildRedactionRegex"); __name2(buildRedactionRegex, "buildRedactionRegex"); function resetSensitiveConfigRedactor() { globalThis._dmnoRedactorData = buildRedactionRegex(globalThis._DMNO_SENSITIVE_LOOKUP); } __name(resetSensitiveConfigRedactor, "resetSensitiveConfigRedactor"); __name2(resetSensitiveConfigRedactor, "resetSensitiveConfigRedactor"); var LOG_METHODS = ["trace", "debug", "info", "log", "info", "warn", "error"]; function patchGlobalConsoleToRedactSensitiveLogs() { const redactor = globalThis._dmnoRedactorData; if (!redactor) return; const kWriteToConsoleSymbol = Object.getOwnPropertySymbols(globalThis.console).find((s) => s.description === "kWriteToConsole"); globalThis._dmnoOrigWriteToConsoleFn ||= globalThis.console[kWriteToConsoleSymbol]; globalThis.console[kWriteToConsoleSymbol] = function() { globalThis._dmnoOrigWriteToConsoleFn.apply(this, [ arguments[0], redactSensitiveConfig(arguments[1]), arguments[2] ]); }; if ( // !console.log.toString().includes('[native code]') && !console.log._dmnoPatchedFn ) { for (const logMethodName of LOG_METHODS) { const originalLogMethod = globalThis.console[logMethodName]; const patchedFn = /* @__PURE__ */ __name2(function() { originalLogMethod.apply(this, Array.from(arguments).map(redactSensitiveConfig)); }, "patchedFn"); patchedFn._dmnoPatchedFn = true; globalThis.console[logMethodName] = patchedFn; } } } __name(patchGlobalConsoleToRedactSensitiveLogs, "patchGlobalConsoleToRedactSensitiveLogs"); __name2(patchGlobalConsoleToRedactSensitiveLogs, "patchGlobalConsoleToRedactSensitiveLogs"); function unpatchGlobalConsoleSensitiveLogRedaction() { if (!globalThis._dmnoOrigWriteToConsoleFn) return; const kWriteToConsoleSymbol = Object.getOwnPropertySymbols(globalThis.console).find((s) => s.description === "kWriteToConsole"); globalThis.console[kWriteToConsoleSymbol] = globalThis._dmnoOrigWriteToConsoleFn; delete globalThis._dmnoOrigWriteToConsoleFn; } __name(unpatchGlobalConsoleSensitiveLogRedaction, "unpatchGlobalConsoleSensitiveLogRedaction"); __name2(unpatchGlobalConsoleSensitiveLogRedaction, "unpatchGlobalConsoleSensitiveLogRedaction"); function redactSensitiveConfig(o) { const redactor = globalThis._dmnoRedactorData; if (!redactor) return o; if (!o) return o; if (Array.isArray(o)) { return o.map(redactSensitiveConfig); } if (o && typeof o === "object" && Object.getPrototypeOf(o) === Object.prototype) { try { return JSON.parse(redactSensitiveConfig(JSON.stringify(o))); } catch (err) { return o; } } const type = typeof o; if (type === "string" || type === "object" && Object.prototype.toString.call(o) === "[object String]") { return o.replaceAll(redactor.findRegex, redactor.replaceFn); } return o; } __name(redactSensitiveConfig, "redactSensitiveConfig"); __name2(redactSensitiveConfig, "redactSensitiveConfig"); function unredact(secretStr) { if (!globalThis._dmnoOrigWriteToConsoleFn) return secretStr; return `${UNMASK_STR}${secretStr}${UNMASK_STR}`; } __name(unredact, "unredact"); __name2(unredact, "unredact"); function redactString(valStr, mode, hideLength = true) { if (!valStr) return valStr; const hiddenLength = hideLength ? 5 : valStr.length - 2; const hiddenStr = "\u2592".repeat(hiddenLength); if (mode === "show_last_2") { return `${hiddenStr}${valStr.substring(valStr.length - 2, valStr.length)}`; } else if (mode === "show_first_last") { return `${valStr.substring(0, 1)}${hiddenStr}${valStr.substring(valStr.length - 1, valStr.length)}`; } else { return `${valStr.substring(0, 2)}${hiddenStr}`; } } __name(redactString, "redactString"); __name2(redactString, "redactString"); var splitPatternRegex = /(?:(.*):\/\/)?([^/]+)(\/.*)?/; var patternRegexCache = {}; function buildRegexFromDomainPattern(pattern) { const patternParts = pattern.match(splitPatternRegex); if (!patternParts) { throw new Error(`Unable to parse domain pattern - ${pattern}`); } let [, protocol, domain, path2] = patternParts; if (protocol === "*" || !protocol) { protocol = "[a-z+]+"; } domain = domain.replaceAll("*", "[^/]+"); if (!path2) path2 = "(/.*)?"; else path2 = path2.replaceAll("*", ".*"); path2 += "(\\?.*)?"; const regexString = `${protocol}://${domain}${path2}`; return new RegExp(regexString, "i"); } __name(buildRegexFromDomainPattern, "buildRegexFromDomainPattern"); __name2(buildRegexFromDomainPattern, "buildRegexFromDomainPattern"); function checkUrlMatchesPattern(url, allowPattern) { if (allowPattern === "*") return true; const patternRegex = patternRegexCache[allowPattern] || buildRegexFromDomainPattern(allowPattern); return patternRegex.test(url); } __name(checkUrlMatchesPattern, "checkUrlMatchesPattern"); __name2(checkUrlMatchesPattern, "checkUrlMatchesPattern"); function checkUrlInAllowList(url, allowedList) { for (const allowedListItem of allowedList) { if (url.includes(allowedListItem)) return true; if (checkUrlMatchesPattern(url, allowedListItem)) return true; } return false; } __name(checkUrlInAllowList, "checkUrlInAllowList"); __name2(checkUrlInAllowList, "checkUrlInAllowList"); function buildSensitiveValuesLoookup(lookup) { const valueLookup = {}; if (!lookup || !Object.keys(lookup).length) return { regex: false, lookup: {} }; for (const key in lookup) { valueLookup[lookup[key].value] = { key, allowedDomains: lookup[key].allowedDomains || [] }; } const findRegex = new RegExp( Object.keys(valueLookup).map((s) => s.replace(/[()[\]{}*+?^$|#.,/\\\s-]/g, "\\$&")).sort((a, b) => b.length - a.length).join("|"), "g" ); return { regex: findRegex, lookup: valueLookup }; } __name(buildSensitiveValuesLoookup, "buildSensitiveValuesLoookup"); __name2(buildSensitiveValuesLoookup, "buildSensitiveValuesLoookup"); function dmnoPatchedFetch(...args) { if (dmnoPatchedFetch._dmnoSensitiveRegex) { const [urlOrFetchOpts, fetchOptsArg] = args; const fetchOpts = (typeof urlOrFetchOpts === "object" ? urlOrFetchOpts : fetchOptsArg) || {}; const fetchUrl = (typeof urlOrFetchOpts === "object" ? urlOrFetchOpts.url : urlOrFetchOpts).toString(); const objToCheckAsString = JSON.stringify(fetchOpts); const matches = objToCheckAsString.match(dmnoPatchedFetch._dmnoSensitiveRegex); for (const match of matches || []) { const matchedItem = dmnoPatchedFetch._dmnoSensitiveLookup[match]; if (checkUrlInAllowList(fetchUrl, matchedItem.allowedDomains)) continue; console.error([ "", `\u{1F6D1} ${kleur_default.bgRed(" SENSITIVE CONFIG LEAK INTERCEPTED ")} \u{1F6D1}`, ` > request url: ${kleur_default.green(fetchUrl)}`, ` > config key: ${kleur_default.blue(matchedItem.key)}`, matchedItem.allowedDomains.length ? ` > allowed domains: ${kleur_default.magenta(matchedItem.allowedDomains.join(", "))}` : ` > allowed domains: ${kleur_default.gray().italic("none")}`, "" ].join("\n")); throw new Error(`\u{1F6D1} SECRET LEAK DETECTED! - ${matchedItem.key} was stopped from being sent to ${fetchUrl}`); } } return dmnoPatchedFetch._unpatchedFetch.apply(this, args); } __name(dmnoPatchedFetch, "dmnoPatchedFetch"); __name2(dmnoPatchedFetch, "dmnoPatchedFetch"); function enableHttpInterceptor() { const { regex, lookup } = buildSensitiveValuesLoookup(globalThis._DMNO_SENSITIVE_LOOKUP); const fetchAlreadyPatched = Object.getOwnPropertyDescriptor(globalThis.fetch, "_patchedByDmno"); if (fetchAlreadyPatched) ; else { const unpatchedFetch = globalThis.fetch; dmnoPatchedFetch._unpatchedFetch = unpatchedFetch; dmnoPatchedFetch._patchedByDmno = true; Object.defineProperty(dmnoPatchedFetch, "_patchedByDmno", { value: true }); globalThis.fetch = dmnoPatchedFetch; } dmnoPatchedFetch._dmnoSensitiveRegex = regex; dmnoPatchedFetch._dmnoSensitiveLookup = lookup; } __name(enableHttpInterceptor, "enableHttpInterceptor"); __name2(enableHttpInterceptor, "enableHttpInterceptor"); function disableHttpInterceptor() { if (globalThis.fetch._unpatchedFetch) { globalThis.fetch = globalThis.fetch._unpatchedFetch; } } __name(disableHttpInterceptor, "disableHttpInterceptor"); __name2(disableHttpInterceptor, "disableHttpInterceptor"); var patchedByDmnoKey = "_patchedByDmno"; function patchServerResponseToPreventClientLeaks() { if (Object.getOwnPropertyDescriptor(ServerResponse.prototype, patchedByDmnoKey)) { return; } Object.defineProperty(ServerResponse.prototype, patchedByDmnoKey, { value: true }); const serverResponseWrite = ServerResponse.prototype.write; ServerResponse.prototype.write = /* @__PURE__ */ __name2(/* @__PURE__ */ __name(function dmnoPatchedServerResponseWrite(...args) { const rawChunk = args[0]; const contentType = this.getHeader("content-type")?.toString() || ""; const runScan = contentType.startsWith("text/") || contentType.startsWith("application/json"); if (!runScan) { return serverResponseWrite.apply(this, args); } const compressionType = this.getHeader("Content-Encoding"); let chunkStr; if (typeof rawChunk === "string") { chunkStr = rawChunk; } else if (!compressionType) { const decoder = new TextDecoder(); chunkStr = decoder.decode(rawChunk); } else if (compressionType === "gzip") { if (!this._zlibChunks) { this._zlibChunks = [rawChunk]; } else { this._zlibChunks?.push(rawChunk); try { const unzippedChunk = zlib.unzipSync(Buffer.concat(this._zlibChunks || []), { flush: zlib.constants.Z_SYNC_FLUSH, finishFlush: zlib.constants.Z_SYNC_FLUSH }); chunkStr = unzippedChunk.toString("utf-8"); } catch (err) { } } } if (chunkStr) { globalThis._dmnoLeakScan(chunkStr, { method: "patched ServerResponse.write" }); } return serverResponseWrite.apply(this, args); }, "dmnoPatchedServerResponseWrite"), "dmnoPatchedServerResponseWrite"); const serverResponseEnd = ServerResponse.prototype.end; ServerResponse.prototype.end = /* @__PURE__ */ __name2(/* @__PURE__ */ __name(function patchedServerResponseEnd(...args) { const endChunk = args[0]; if (endChunk && typeof endChunk === "string") { globalThis._dmnoLeakScan(endChunk, { method: "patched ServerResponse.end" }); } return serverResponseEnd.apply(this, args); }, "patchedServerResponseEnd"), "patchedServerResponseEnd"); } __name(patchServerResponseToPreventClientLeaks, "patchServerResponseToPreventClientLeaks"); __name2(patchServerResponseToPreventClientLeaks, "patchServerResponseToPreventClientLeaks"); var processExists = !!globalThis.process; var originalProcessEnv = {}; if (processExists) { try { originalProcessEnv = structuredClone(globalThis.process.env); } catch (err) { } } var IGNORED_PROXY_KEYS = [ "__v_isRef" // vue - see https://github.com/vuejs/core/blob/70773d00985135a50556c61fb9855ed6b930cb82/packages/reactivity/src/ref.ts#L101 ]; function injectDmnoGlobals(opts) { const sensitiveValueLookup = {}; const dynamicKeys2 = []; const sensitiveKeys = []; const publicDynamicKeys = []; const publicDynamicObj = {}; if (opts?.injectedConfig) { globalThis._DMNO_INJECTED_ENV = opts?.injectedConfig; } let injectedDmnoEnv2 = opts?.injectedConfig; if (!injectedDmnoEnv2) { if (globalThis.process?.env.DMNO_INJECTED_ENV) { injectedDmnoEnv2 = JSON.parse(globalThis.process?.env.DMNO_INJECTED_ENV); } else if (globalThis._DMNO_INJECTED_ENV) { injectedDmnoEnv2 = globalThis._DMNO_INJECTED_ENV; } } if (!injectedDmnoEnv2) { throw new Error("Unable to find `process.env.DMNO_INJECTED_ENV` - run this command via `dmno run` - see https://dmno.dev/docs/reference/cli/run for more info"); } if (processExists) { globalThis.process.env = { ...originalProcessEnv }; } const rawConfigObj = {}; const rawPublicConfigObj = {}; const staticReplacements2 = { dmnoConfig: {}, dmnoPublicConfig: {} }; for (const itemKey in injectedDmnoEnv2) { if (itemKey === "$SETTINGS") continue; const injectedItem = injectedDmnoEnv2[itemKey]; const val = injectedItem.value; if (processExists) { if (val === void 0 || val === null) { globalThis.process.env[itemKey] ||= ""; } else { globalThis.process.env[itemKey] ||= val.toString(); } } if (!injectedItem.sensitive) { rawPublicConfigObj[itemKey] = "*"; rawConfigObj[itemKey] = "*"; } else { sensitiveKeys.push(itemKey); rawConfigObj[itemKey] = "*"; if (val) { const valStr = injectedItem.value.toString(); sensitiveValueLookup[itemKey] = { value: valStr, redacted: redactString(valStr, injectedItem.redactMode) || "", allowedDomains: injectedItem.allowedDomains }; } } if (injectedItem.dynamic) { dynamicKeys2.push(itemKey); if (!injectedItem.sensitive) { publicDynamicKeys.push(itemKey); publicDynamicObj[itemKey] = injectedItem.value; } } if (!injectedItem.dynamic) { if (!injectedItem.sensitive) { staticReplacements2.dmnoPublicConfig[`DMNO_PUBLIC_CONFIG.${itemKey}`] = JSON.stringify(injectedItem.value); } staticReplacements2.dmnoConfig[`DMNO_CONFIG.${itemKey}`] = JSON.stringify(injectedItem.value); } } globalThis.DMNO_CONFIG = new Proxy(rawConfigObj, { get(o, key) { if (typeof key === "symbol") return; if (IGNORED_PROXY_KEYS.includes(key)) return; if (opts?.trackingObject) opts.trackingObject[key] = true; if (key in injectedDmnoEnv2) { if (opts?.onItemAccess) opts.onItemAccess(injectedDmnoEnv2[key]); return injectedDmnoEnv2[key].value; } throw new Error(`\u274C ${key} is not a config item (1)`); } }); globalThis.DMNO_PUBLIC_CONFIG = new Proxy(rawPublicConfigObj, { get(o, key) { const keyStr = key.toString(); if (IGNORED_PROXY_KEYS.includes(keyStr)) return; if (opts?.trackingObject) opts.trackingObject[keyStr] = true; if (injectedDmnoEnv2[keyStr]?.sensitive) { throw new Error(`\u274C ${keyStr} is not a public config item! Use \`DMNO_CONFIG.${keyStr}\` instead`); } if (key in injectedDmnoEnv2) { if (opts?.onItemAccess) opts.onItemAccess(injectedDmnoEnv2[keyStr]); return injectedDmnoEnv2[keyStr].value; } throw new Error(`\u274C ${keyStr} is not a config item (2)`); } }); const serviceSettings2 = injectedDmnoEnv2.$SETTINGS; globalThis._DMNO_SERVICE_SETTINGS = serviceSettings2; globalThis._DMNO_PUBLIC_DYNAMIC_KEYS = publicDynamicKeys; globalThis._DMNO_PUBLIC_DYNAMIC_OBJ = publicDynamicObj; globalThis._DMNO_SENSITIVE_LOOKUP = sensitiveValueLookup; globalThis._dmnoRepatchGlobals = repatchGlobals; repatchGlobals(); const injectionResult = { staticReplacements: staticReplacements2, dynamicKeys: dynamicKeys2, publicDynamicKeys, sensitiveKeys, sensitiveValueLookup, serviceSettings: serviceSettings2, injectedDmnoEnv: injectedDmnoEnv2 }; return injectionResult; } __name(injectDmnoGlobals, "injectDmnoGlobals"); __name2(injectDmnoGlobals, "injectDmnoGlobals"); function repatchGlobals() { const serviceSettings2 = globalThis._DMNO_SERVICE_SETTINGS; resetSensitiveConfigRedactor(); if (serviceSettings2.redactSensitiveLogs) { patchGlobalConsoleToRedactSensitiveLogs(); } else { unpatchGlobalConsoleSensitiveLogRedaction(); } if (serviceSettings2.interceptSensitiveLeakRequests) { enableHttpInterceptor(); } else { disableHttpInterceptor(); } if (serviceSettings2.preventClientLeaks) { { patchServerResponseToPreventClientLeaks(); } } } __name(repatchGlobals, "repatchGlobals"); __name2(repatchGlobals, "repatchGlobals"); function isString(s) { return Object.prototype.toString.call(s) === "[object String]"; } __name(isString, "isString"); __name2(isString, "isString"); globalThis._dmnoRedact = redactSensitiveConfig; globalThis._dmnoLeakScan = /* @__PURE__ */ __name2(/* @__PURE__ */ __name(function _dmnoLeakScan(toScan, meta) { function scanStrForLeaks(strToScan) { const sensitiveLookup = globalThis._DMNO_SENSITIVE_LOOKUP; for (const itemKey in sensitiveLookup) { if (strToScan.includes(sensitiveLookup[itemKey].value)) { console.error([ // eslint-disable-line no-console "", `\u{1F6A8} ${kleur_default.bgRed(" DETECTED LEAKED SENSITIVE CONFIG ")} \u{1F6A8}`, `> Config item key: ${kleur_default.blue(itemKey)}`, ...meta?.method ? [`> Scan method: ${meta.method}`] : [], ...meta?.file ? [`> File: ${meta.file}`] : [], "" ].join("\n")); throw new Error(`\u{1F6A8} DETECTED LEAKED SENSITIVE CONFIG - ${itemKey}`); } } } __name(scanStrForLeaks, "scanStrForLeaks"); __name2(scanStrForLeaks, "scanStrForLeaks"); if (isString(toScan)) { scanStrForLeaks(toScan); return toScan; } else if (toScan instanceof ReadableStream) { if (toScan.locked) { return toScan; } const chunkDecoder = new TextDecoder(); return toScan.pipeThrough( new TransformStream({ transform(chunk, controller) { const chunkStr = chunkDecoder.decode(chunk); scanStrForLeaks(chunkStr); controller.enqueue(chunk); } }) ); } return toScan; }, "_dmnoLeakScan"), "_dmnoLeakScan"); var { staticReplacements, dynamicKeys, injectedDmnoEnv, serviceSettings } = injectDmnoGlobals(); function patchFsWriteFileToPreventClientLeaks() { const origPromisesWriteFileFn = fs.promises.writeFile; fs.promises.writeFile = /* @__PURE__ */ __name(function dmnoPatchedWriteFile() { const [filePath, fileContents] = arguments; if (filePath.endsWith(".html") || filePath.endsWith(".rsc") || filePath.endsWith(".body")) { globalThis._dmnoLeakScan(fileContents, { method: "nextjs fs.writeFile", file: filePath }); } return origPromisesWriteFileFn.call(this, ...Array.from(arguments)); }, "dmnoPatchedWriteFile"); } __name(patchFsWriteFileToPreventClientLeaks, "patchFsWriteFileToPreventClientLeaks"); var WEBPACK_PLUGIN_NAME = "DmnoNextWebpackPlugin"; function getCjsModuleSource(moduleName) { const modulePath = fileURLToPath(resolve(moduleName, import.meta.url)).replace(".js", ".cjs"); const moduleSrc = fs.readFileSync(modulePath, "utf8"); return moduleSrc; } __name(getCjsModuleSource, "getCjsModuleSource"); function dmnoNextConfigPlugin(dmnoOptions) { if (serviceSettings?.preventClientLeaks) { patchFsWriteFileToPreventClientLeaks(); } const injectResolvedConfigAtBuildTime = process.env.__VERCEL_BUILD_RUNNING || process.env.VERCEL || process.env.NETLIFY || process.env.NETLIFY_LOCAL && !process.env.NETLIFY_DEV || process.env.CF_PAGES || dmnoOptions?.injectResolvedConfigAtBuildTime; return (nextConfig) => { return async (phase, defaults) => { let resolvedNextConfig; if (typeof nextConfig === "function") { const nextConfigFnResult = nextConfig(phase, defaults); resolvedNextConfig = await nextConfigFnResult; } else { resolvedNextConfig = nextConfig; } if (resolvedNextConfig.output === "export" && dynamicKeys.length) { console.error([ 'Dynamic config is not supported in static builds (next.config output="export")', 'Set `settings.dynamicConfig` to "only_static" in your .dmno/config.mts file', `Dynamic config items: ${dynamicKeys.join(", ")}` ].join("\n")); throw new Error("Dynamic config not compatible with static builds"); } return { ...resolvedNextConfig, webpack(webpackConfig, options) { const { isServer } = options; const webpack = options.webpack; if (resolvedNextConfig.webpack) { webpackConfig = resolvedNextConfig.webpack(webpackConfig, options); } const originalEntry = webpackConfig.entry; webpackConfig.entry = async () => { const entries = await originalEntry(); function injectEntry(entryKey, injectedPath) { if (entries[entryKey]) { if (!Array.isArray(entries[entryKey])) { entries[entryKey] = [entries[entryKey]]; } if (!entries[entryKey].includes(injectedPath)) { entries[entryKey].unshift(injectedPath); } } } __name(injectEntry, "injectEntry"); if (!isServer) { const injectDmnoClientFilePath = `${__dirname}/inject-dmno-client.js`; injectEntry("main-app", injectDmnoClientFilePath); injectEntry("main", injectDmnoClientFilePath); injectEntry("amp", injectDmnoClientFilePath); } return entries; }; webpackConfig.plugins.push(new webpack.DefinePlugin({ ...staticReplacements.dmnoPublicConfig, ...isServer && staticReplacements.dmnoConfig })); function updateServerRuntimeToInjectDmno(edgeRuntime = false) { return function(origSource) { const origSourceStr = origSource.source(); const injectorSrc = getCjsModuleSource(`dmno/injector-standalone${edgeRuntime ? "/edge" : ""}`); const updatedSourceStr = [ // we use `headers()` to force next into dynamic rendering mode, but on the edge runtime it's always dynamic // (see below for where headers is used) !edgeRuntime ? 'const { headers } = require("next/headers");' : "", // code built for edge runtime does not have `module.exports` or `exports` but we are inlining some already built common-js code // so we just create them. It's not needed since it is inlined and we call the function right away edgeRuntime ? "const module = { exports: {} }; const exports = {}" : "", // inline the dmno injector code and then call it injectorSrc, "injectDmnoGlobals({", injectResolvedConfigAtBuildTime ? `injectedConfig: ${JSON.stringify(injectedDmnoEnv)},` : "", // attempts to force the route into dynamic rendering mode so it wont put our our dynamic value into a pre-rendered page // however we have to wrap in try/catch because you can only call headers() within certain parts of the page... so it's not 100% foolproof !edgeRuntime ? ` onItemAccess: async (item) => { if (item.dynamic) { try { headers(); } catch (err) {} } },` : "", "});", origSourceStr ].join("\n"); return new webpack.sources.RawSource(updatedSourceStr); }; } __name(updateServerRuntimeToInjectDmno, "updateServerRuntimeToInjectDmno"); webpackConfig.plugins.push({ apply(compiler) { compiler.hooks.thisCompilation.tap(WEBPACK_PLUGIN_NAME, (compilation) => { compilation.hooks.processAssets.tap( { name: WEBPACK_PLUGIN_NAME, stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS }, () => { if (compilation.getAsset("webpack-runtime.js")) { compilation.updateAsset("webpack-runtime.js", updateServerRuntimeToInjectDmno()); } if (compilation.getAsset("../webpack-runtime.js")) { compilation.updateAsset("../webpack-runtime.js", updateServerRuntimeToInjectDmno()); } if (compilation.getAsset("webpack-api-runtime.js")) { compilation.updateAsset("webpack-api-runtime.js", updateServerRuntimeToInjectDmno()); } if (compilation.getAsset("../webpack-api-runtime.js")) { compilation.updateAsset("../webpack-api-runtime.js", updateServerRuntimeToInjectDmno()); } if (compilation.getAsset("edge-runtime-webpack.js")) { compilation.updateAsset("edge-runtime-webpack.js", updateServerRuntimeToInjectDmno(true)); } } ); }); if (serviceSettings?.preventClientLeaks) { compiler.hooks.assetEmitted.tap( WEBPACK_PLUGIN_NAME, (file, assetDetails) => { const { content, targetPath } = assetDetails; if (targetPath.includes("/.next/static/chunks/")) { globalThis._dmnoLeakScan(content, { method: "nextjs webpack plugin - static chunks", file: targetPath }); } } ); } } }); return webpackConfig; } }; }; }; } __name(dmnoNextConfigPlugin, "dmnoNextConfigPlugin"); export { dmnoNextConfigPlugin }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map