UNPKG

@nocobase/flow-engine

Version:

A standalone flow engine for NocoBase, managing workflows, models, and actions.

180 lines (178 loc) 6.8 kB
/** * This file is part of the NocoBase (R) project. * Copyright (c) 2020-2024 NocoBase Co., Ltd. * Authors: NocoBase Team. * * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. * For more information, please refer to: https://www.nocobase.com/agreement. */ var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var safeGlobals_exports = {}; __export(safeGlobals_exports, { createSafeDocument: () => createSafeDocument, createSafeWindow: () => createSafeWindow }); module.exports = __toCommonJS(safeGlobals_exports); function createSafeWindow(extra) { const getSafeBaseHref = /* @__PURE__ */ __name(() => `${window.location.origin}${window.location.pathname}`, "getSafeBaseHref"); const safeOpen = /* @__PURE__ */ __name((url, target, features) => { const isSafeUrl = /* @__PURE__ */ __name((u) => { try { const parsed = new URL(u, getSafeBaseHref()); const protocol = parsed.protocol.toLowerCase(); if (protocol === "about:") return parsed.href === "about:blank"; return protocol === "http:" || protocol === "https:"; } catch { return false; } }, "isSafeUrl"); if (!isSafeUrl(url)) { throw new Error("Unsafe URL: window.open only allows http/https/about:blank."); } const sanitizedTarget = "_blank"; const enforceFeatures = /* @__PURE__ */ __name((f) => { const set = /* @__PURE__ */ new Set(); if (f) { f.split(",").map((s) => s.trim()).filter(Boolean).forEach((part) => { const key = part.split("=")[0].trim().toLowerCase(); if (key !== "noopener" && key !== "noreferrer") set.add(part); }); } set.add("noopener"); set.add("noreferrer"); return Array.from(set).join(","); }, "enforceFeatures"); const sanitizedFeatures = enforceFeatures(features); const newWin = window.open.call(window, url, sanitizedTarget, sanitizedFeatures); if (newWin && "opener" in newWin) { try { newWin.opener = null; } catch { } } return newWin; }, "safeOpen"); const guardedNavigate = /* @__PURE__ */ __name((rawUrl, opts) => { const parsed = new URL(rawUrl, getSafeBaseHref()); const protocol = parsed.protocol.toLowerCase(); const isAboutBlank = protocol === "about:" && parsed.href === "about:blank"; const isHttp = protocol === "http:" || protocol === "https:"; if (!isHttp && !isAboutBlank) { throw new Error("Unsafe URL: only http/https/about:blank are allowed."); } if (isAboutBlank) { return (opts == null ? void 0 : opts.replace) ? window.location.replace("about:blank") : window.location.assign("about:blank"); } const sameOrigin = parsed.protocol === window.location.protocol && parsed.hostname === window.location.hostname && parsed.port === window.location.port; if (sameOrigin) { return (opts == null ? void 0 : opts.replace) ? window.location.replace(parsed.href) : window.location.assign(parsed.href); } const win = safeOpen(parsed.href); if (!win) throw new Error("Popup blocked: cross-origin navigation is opened in a new tab."); }, "guardedNavigate"); const safeLocation = new Proxy( {}, { get(_t, prop) { switch (prop) { case "origin": return window.location.origin; case "protocol": return window.location.protocol; case "host": return window.location.host; case "hostname": return window.location.hostname; case "port": return window.location.port; case "pathname": return window.location.pathname; case "assign": return (u) => guardedNavigate(u, { replace: false }); case "replace": return (u) => guardedNavigate(u, { replace: true }); case "reload": throw new Error("Access to location.reload is not allowed."); case "href": throw new Error("Reading location.href is not allowed."); default: throw new Error(`Access to location property "${prop}" is not allowed.`); } }, set(_t, prop, value) { if (prop === "href") { guardedNavigate(String(value), { replace: false }); return true; } throw new Error("Mutation on location is not allowed."); } } ); const allowedGlobals = { // 需绑定到原始 window,避免严格模式下触发 Illegal invocation setTimeout: window.setTimeout.bind(window), clearTimeout: window.clearTimeout.bind(window), setInterval: window.setInterval.bind(window), clearInterval: window.clearInterval.bind(window), console, Math, Date, // 事件侦听仅绑定到真实 window,便于少量需要的全局监听 addEventListener: addEventListener.bind(window), // 安全的 window.open 代理 open: safeOpen, // 安全的 location 代理 location: safeLocation, ...extra || {} }; return new Proxy( {}, { get(_target, prop) { if (prop in allowedGlobals) return allowedGlobals[prop]; throw new Error(`Access to global property "${prop}" is not allowed.`); } } ); } __name(createSafeWindow, "createSafeWindow"); function createSafeDocument(extra) { const allowed = { createElement: document.createElement.bind(document), querySelector: document.querySelector.bind(document), querySelectorAll: document.querySelectorAll.bind(document), ...extra || {} }; return new Proxy( {}, { get(_target, prop) { if (prop in allowed) return allowed[prop]; throw new Error(`Access to document property "${prop}" is not allowed.`); } } ); } __name(createSafeDocument, "createSafeDocument"); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createSafeDocument, createSafeWindow });