@dmno/nextjs-integration
Version:
tools for integrating dmno into nextjs
737 lines (735 loc) • 30.1 kB
JavaScript
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