release: v4.8.9 security hardening patch
CodeQL Analysis / Analyze CodeQL (push) Has been cancelled
Deploy Application / deploy (push) Has been cancelled
Mirror to Codeberg / mirror (push) Has been cancelled
Mirror to PrivacyGuides / mirror (push) Has been cancelled

- upgrade DOMPurify to patched release (fixes high-severity XSS GHSA-87xg-pxx2-7hvx)
- upgrade esbuild build dependency; npm audit now reports 0 vulnerabilities
- stop tracking config/ice-servers.js (TURN credentials); add example template
- production logger no longer prints error context or info/debug output
- bump version to 4.8.9 across header, manifest, README, init message
- update SECURITY.md supported-release table to v4.8.x
This commit is contained in:
lockbitchat
2026-06-15 15:08:03 -04:00
parent d11f250257
commit 366f080128
21 changed files with 691 additions and 347 deletions
+6
View File
@@ -4,3 +4,9 @@ node_modules/
# Local environment noise # Local environment noise
.npm/ .npm/
npm-debug.log* npm-debug.log*
.DS_Store
**/.DS_Store
# Operator ICE override holds TURN credentials — never commit it.
# Use config/ice-servers.example.js as the template.
config/ice-servers.js
+16
View File
@@ -1,5 +1,21 @@
# Changelog # Changelog
## v4.8.9 — Security hardening patch
This release closes a vulnerable dependency, removes committed TURN credentials, and tightens production logging.
### Security
- Upgraded DOMPurify from 3.4.4 to a patched release, resolving a high-severity XSS advisory (GHSA-87xg-pxx2-7hvx) in the incoming-message sanitizer.
- Upgraded the `esbuild` build dependency to clear a high-severity advisory in the toolchain. `npm audit` now reports zero vulnerabilities.
- Stopped tracking `config/ice-servers.js` (operator TURN credentials) in Git and added `config/ice-servers.example.js` as a template. Operators must rotate any previously committed credentials.
- Removed temporary debug branches from the production logger so it no longer prints error context or info/debug payloads — only an opaque error code.
### Documentation
- Updated the supported-release table in `SECURITY.md` to the v4.8.x line.
- Synchronized the version string across the header, manifest, README, and in-app initialization message.
## v4.8.8 — File transfer consent fix ## v4.8.8 — File transfer consent fix
This patch completes the mandatory receiver-consent gate for incoming file transfers and resolves a callback ownership conflict that caused every incoming file request to be silently auto-rejected. This patch completes the mandatory receiver-consent gate for incoming file transfers and resolves a callback ownership conflict that caused every incoming file request to be silently auto-rejected.
+10 -6
View File
@@ -1,4 +1,4 @@
# SecureBit.chat v4.8.7 # SecureBit.chat v4.8.9
SecureBit.chat is a browser-based peer-to-peer chat application built on WebRTC and Web Crypto APIs. It is designed for direct encrypted communication, explicit peer verification, and a small operational footprint without account registration or server-side message storage. SecureBit.chat is a browser-based peer-to-peer chat application built on WebRTC and Web Crypto APIs. It is designed for direct encrypted communication, explicit peer verification, and a small operational footprint without account registration or server-side message storage.
@@ -15,13 +15,17 @@ SecureBit.chat uses:
A session is not treated as verified until both peers complete the interactive SAS flow. Each user must compare the displayed code with the peer through an out-of-band channel and enter the matching code manually. Three failed SAS attempts terminate the session. A session is not treated as verified until both peers complete the interactive SAS flow. Each user must compare the displayed code with the peer through an out-of-band channel and enter the matching code manually. Three failed SAS attempts terminate the session.
## Highlights in v4.8.7 ## Highlights in v4.8.9
- Manual WebRTC setup now preserves pending offer/answer state during slow out-of-band exchange. - Patched a high-severity XSS advisory in the DOMPurify dependency (the message sanitizer) by upgrading to a fixed release.
- Operator TURN credentials are no longer committed to the repository; use `config/ice-servers.example.js` as a template.
- The production logger no longer prints error context or info/debug output, only opaque error codes.
This patch release builds on the earlier hardening pass:
- Manual WebRTC setup preserves pending offer/answer state during slow out-of-band exchange.
- TURN relay fallback can be configured through `config/ice-servers.js` for restrictive networks. - TURN relay fallback can be configured through `config/ice-servers.js` for restrictive networks.
- ICE diagnostics now identify mDNS-only candidate failures without exposing full peer IPs. - ICE diagnostics identify mDNS-only candidate failures without exposing full peer IPs.
This patch release strengthens the existing security model with a focused hardening pass:
- SAS verification is bound to the actual DTLS fingerprint strings of both peers - SAS verification is bound to the actual DTLS fingerprint strings of both peers
- chat sanitization uses DOMPurify-backed text-only output - chat sanitization uses DOMPurify-backed text-only output
+2 -1
View File
@@ -4,7 +4,8 @@
| Release | Status | Protocol | | Release | Status | Protocol |
| --- | --- | --- | | --- | --- | --- |
| v4.1.x | Supported | 4.1 | | v4.8.x | Supported | 4.1 |
| v4.1.x v4.7.x | Unsupported | 4.1 |
| earlier releases | Unsupported | legacy | | earlier releases | Unsupported | legacy |
Users should run the current supported release line to receive the latest verification, storage, and file-transfer protections. Users should run the current supported release line to receive the latest verification, storage, and file-transfer protections.
+1 -1
View File
@@ -22,6 +22,6 @@ SecureBit.chat is intended for legitimate private communication, journalism, res
## Current release ## Current release
- Product release: `v4.8.5` - Product release: `v4.8.9`
- Protocol version: `4.1` - Protocol version: `4.1`
- Last updated: May 17, 2026 - Last updated: May 17, 2026
+21
View File
@@ -0,0 +1,21 @@
// SecureBit.chat operator ICE server override — TEMPLATE.
//
// Copy this file to `config/ice-servers.js` and fill in your own TURN/STUN
// servers. The real `config/ice-servers.js` is git-ignored on purpose:
// TURN credentials are visible to every browser that loads the page, so they
// must never be committed to a public repository. Rotate them from your TURN
// provider dashboard if they are ever exposed.
//
// If this override is absent, the WebRTC manager falls back to the built-in
// public STUN defaults (standard mode only — no relay/IP protection).
window.SECUREBIT_ICE_SERVERS = [
{ urls: 'stun:stun.cloudflare.com:3478' },
{
urls: [
'turn:YOUR_TURN_HOST:3478?transport=udp',
'turn:YOUR_TURN_HOST:3478?transport=tcp'
],
username: 'YOUR_TURN_USERNAME',
credential: 'YOUR_TURN_CREDENTIAL'
}
];
-15
View File
@@ -1,15 +0,0 @@
// SecureBit.chat operator ICE server override.
// Loaded before the WebRTC manager is created. Credentials are visible to browsers;
// rotate them from the ExpressTURN dashboard if this file is published publicly.
window.SECUREBIT_ICE_SERVERS = [
{ urls: 'stun:stun.cloudflare.com:3478' },
{ urls: 'stun:stun.expressturn.com:3478' },
{
urls: [
'turn:free.expressturn.com:3478?transport=udp',
'turn:free.expressturn.com:3478?transport=tcp'
],
username: '000000002094555952',
credential: 't1oK9Zftes9j7E7hJmsLad9jq1M='
}
];
+491 -177
View File
@@ -5,7 +5,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf; var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty; var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() { var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; try {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
} catch (e) {
throw mod = 0, e;
}
}; };
var __copyProps = (to, from, except, desc) => { var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") { if (from && typeof from === "object" || typeof from === "function") {
@@ -954,7 +958,7 @@ function isRegex(value) {
return false; return false;
} }
} }
var html$1 = freeze(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "selectedcontent", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]); var html$1 = freeze(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]);
var svg$1 = freeze(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "enterkeyhint", "exportparts", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "inputmode", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "part", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]); var svg$1 = freeze(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "enterkeyhint", "exportparts", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "inputmode", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "part", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]);
var svgFilters = freeze(["feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence"]); var svgFilters = freeze(["feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence"]);
var svgDisallowed = freeze(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]); var svgDisallowed = freeze(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]);
@@ -981,13 +985,26 @@ var ATTR_WHITESPACE = seal(
); );
var DOCTYPE_NAME = seal(/^html$/i); var DOCTYPE_NAME = seal(/^html$/i);
var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i); var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
var ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
var COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
var FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
var SELF_CLOSING_TAG = seal(/\/>/i);
var NODE_TYPE = { var NODE_TYPE = {
element: 1, element: 1,
attribute: 2,
text: 3, text: 3,
cdataSection: 4,
entityReference: 5,
// Deprecated // Deprecated
progressingInstruction: 7, entityNode: 6,
// Deprecated
processingInstruction: 7,
comment: 8, comment: 8,
document: 9 document: 9,
documentType: 10,
documentFragment: 11,
notation: 12
// Deprecated
}; };
var getGlobal = function getGlobal2() { var getGlobal = function getGlobal2() {
return typeof window === "undefined" ? null : window; return typeof window === "undefined" ? null : window;
@@ -1029,10 +1046,13 @@ var _createHooksMap = function _createHooksMap2() {
uponSanitizeShadowNode: [] uponSanitizeShadowNode: []
}; };
}; };
var _resolveSetOption = function _resolveSetOption2(cfg, key, fallback, options) {
return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
};
function createDOMPurify() { function createDOMPurify() {
let window2 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal(); let window2 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
const DOMPurify = (root) => createDOMPurify(root); const DOMPurify = (root) => createDOMPurify(root);
DOMPurify.version = "3.4.4"; DOMPurify.version = "3.4.10";
DOMPurify.removed = []; DOMPurify.removed = [];
if (!window2 || !window2.document || window2.document.nodeType !== NODE_TYPE.document || !window2.Element) { if (!window2 || !window2.document || window2.document.nodeType !== NODE_TYPE.document || !window2.Element) {
DOMPurify.isSupported = false; DOMPurify.isSupported = false;
@@ -1041,14 +1061,21 @@ function createDOMPurify() {
let document2 = window2.document; let document2 = window2.document;
const originalDocument = document2; const originalDocument = document2;
const currentScript = originalDocument.currentScript; const currentScript = originalDocument.currentScript;
const DocumentFragment = window2.DocumentFragment, HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap, NamedNodeMap = _window$NamedNodeMap === void 0 ? window2.NamedNodeMap || window2.MozNamedAttrMap : _window$NamedNodeMap, HTMLFormElement = window2.HTMLFormElement, DOMParser = window2.DOMParser, trustedTypes = window2.trustedTypes; window2.DocumentFragment;
const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
_window$NamedNodeMap === void 0 ? window2.NamedNodeMap || window2.MozNamedAttrMap : _window$NamedNodeMap;
window2.HTMLFormElement;
const DOMParser = window2.DOMParser, trustedTypes = window2.trustedTypes;
const ElementPrototype = Element.prototype; const ElementPrototype = Element.prototype;
const cloneNode = lookupGetter(ElementPrototype, "cloneNode"); const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
const remove = lookupGetter(ElementPrototype, "remove"); const remove = lookupGetter(ElementPrototype, "remove");
const getNextSibling = lookupGetter(ElementPrototype, "nextSibling"); const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
const getChildNodes = lookupGetter(ElementPrototype, "childNodes"); const getChildNodes = lookupGetter(ElementPrototype, "childNodes");
const getParentNode = lookupGetter(ElementPrototype, "parentNode"); const getParentNode = lookupGetter(ElementPrototype, "parentNode");
const getShadowRoot = lookupGetter(ElementPrototype, "shadowRoot");
const getAttributes = lookupGetter(ElementPrototype, "attributes");
const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeType") : null; const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeType") : null;
const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, "nodeName") : null;
if (typeof HTMLTemplateElement === "function") { if (typeof HTMLTemplateElement === "function") {
const template = document2.createElement("template"); const template = document2.createElement("template");
if (template.content && template.content.ownerDocument) { if (template.content && template.content.ownerDocument) {
@@ -1057,6 +1084,39 @@ function createDOMPurify() {
} }
let trustedTypesPolicy; let trustedTypesPolicy;
let emptyHTML = ""; let emptyHTML = "";
let defaultTrustedTypesPolicy;
let defaultTrustedTypesPolicyResolved = false;
let IN_TRUSTED_TYPES_POLICY = 0;
const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy2() {
if (IN_TRUSTED_TYPES_POLICY > 0) {
throw typeErrorCreate('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.');
}
};
const _createTrustedHTML = function _createTrustedHTML2(html2) {
_assertNotInTrustedTypesPolicy();
IN_TRUSTED_TYPES_POLICY++;
try {
return trustedTypesPolicy.createHTML(html2);
} finally {
IN_TRUSTED_TYPES_POLICY--;
}
};
const _createTrustedScriptURL = function _createTrustedScriptURL2(scriptUrl) {
_assertNotInTrustedTypesPolicy();
IN_TRUSTED_TYPES_POLICY++;
try {
return trustedTypesPolicy.createScriptURL(scriptUrl);
} finally {
IN_TRUSTED_TYPES_POLICY--;
}
};
const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy2() {
if (!defaultTrustedTypesPolicyResolved) {
defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
defaultTrustedTypesPolicyResolved = true;
}
return defaultTrustedTypesPolicy;
};
const _document = document2, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName; const _document = document2, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName;
const importNode = originalDocument.importNode; const importNode = originalDocument.importNode;
let hooks = _createHooksMap(); let hooks = _createHooksMap();
@@ -1122,7 +1182,43 @@ function createDOMPurify() {
let IN_PLACE = false; let IN_PLACE = false;
let USE_PROFILES = {}; let USE_PROFILES = {};
let FORBID_CONTENTS = null; let FORBID_CONTENTS = null;
const DEFAULT_FORBID_CONTENTS = addToSet({}, ["annotation-xml", "audio", "colgroup", "desc", "foreignobject", "head", "iframe", "math", "mi", "mn", "mo", "ms", "mtext", "noembed", "noframes", "noscript", "plaintext", "script", "style", "svg", "template", "thead", "title", "video", "xmp"]); const DEFAULT_FORBID_CONTENTS = addToSet({}, [
"annotation-xml",
"audio",
"colgroup",
"desc",
"foreignobject",
"head",
"iframe",
"math",
"mi",
"mn",
"mo",
"ms",
"mtext",
"noembed",
"noframes",
"noscript",
"plaintext",
"script",
// <selectedcontent> mirrors the selected <option>'s subtree, cloned by
// the UA (customizable <select>) — including any on* handlers — and the
// engine re-mirrors synchronously whenever a removal changes which
// option/selectedcontent is current, even inside DOMPurify's inert
// DOMParser document. Hoisting its children on removal re-inserts a fresh
// mirror target ahead of the walk, which the engine refills, looping
// forever (DoS) and amplifying output. Dropping its content on removal
// (rather than hoisting) breaks that cascade; the content is a duplicate
// of the option, which is sanitized on its own. See campaign-3 F1/F6.
"selectedcontent",
"style",
"svg",
"template",
"thead",
"title",
"video",
"xmp"
]);
let DATA_URI_TAGS = null; let DATA_URI_TAGS = null;
const DEFAULT_DATA_URI_TAGS = addToSet({}, ["audio", "video", "img", "source", "image", "track"]); const DEFAULT_DATA_URI_TAGS = addToSet({}, ["audio", "video", "img", "source", "image", "track"]);
let URI_SAFE_ATTRIBUTES = null; let URI_SAFE_ATTRIBUTES = null;
@@ -1134,8 +1230,10 @@ function createDOMPurify() {
let IS_EMPTY_INPUT = false; let IS_EMPTY_INPUT = false;
let ALLOWED_NAMESPACES = null; let ALLOWED_NAMESPACES = null;
const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString); const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ["mi", "mo", "mn", "ms", "mtext"]); const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze(["mi", "mo", "mn", "ms", "mtext"]);
let HTML_INTEGRATION_POINTS = addToSet({}, ["annotation-xml"]); let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
const DEFAULT_HTML_INTEGRATION_POINTS = freeze(["annotation-xml"]);
let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ["title", "style", "font", "a", "script"]); const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ["title", "style", "font", "a", "script"]);
let PARSER_MEDIA_TYPE = null; let PARSER_MEDIA_TYPE = null;
const SUPPORTED_PARSER_MEDIA_TYPES = ["application/xhtml+xml", "text/html"]; const SUPPORTED_PARSER_MEDIA_TYPES = ["application/xhtml+xml", "text/html"];
@@ -1158,14 +1256,32 @@ function createDOMPurify() {
PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE; SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
transformCaseFunc = PARSER_MEDIA_TYPE === "application/xhtml+xml" ? stringToString : stringToLowerCase; transformCaseFunc = PARSER_MEDIA_TYPE === "application/xhtml+xml" ? stringToString : stringToLowerCase;
ALLOWED_TAGS = objectHasOwnProperty(cfg, "ALLOWED_TAGS") && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS; ALLOWED_TAGS = _resolveSetOption(cfg, "ALLOWED_TAGS", DEFAULT_ALLOWED_TAGS, {
ALLOWED_ATTR = objectHasOwnProperty(cfg, "ALLOWED_ATTR") && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR; transform: transformCaseFunc
ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, "ALLOWED_NAMESPACES") && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES; });
URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, "ADD_URI_SAFE_ATTR") && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES; ALLOWED_ATTR = _resolveSetOption(cfg, "ALLOWED_ATTR", DEFAULT_ALLOWED_ATTR, {
DATA_URI_TAGS = objectHasOwnProperty(cfg, "ADD_DATA_URI_TAGS") && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS; transform: transformCaseFunc
FORBID_CONTENTS = objectHasOwnProperty(cfg, "FORBID_CONTENTS") && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS; });
FORBID_TAGS = objectHasOwnProperty(cfg, "FORBID_TAGS") && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({}); ALLOWED_NAMESPACES = _resolveSetOption(cfg, "ALLOWED_NAMESPACES", DEFAULT_ALLOWED_NAMESPACES, {
FORBID_ATTR = objectHasOwnProperty(cfg, "FORBID_ATTR") && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({}); transform: stringToString
});
URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, "ADD_URI_SAFE_ATTR", DEFAULT_URI_SAFE_ATTRIBUTES, {
transform: transformCaseFunc,
base: DEFAULT_URI_SAFE_ATTRIBUTES
});
DATA_URI_TAGS = _resolveSetOption(cfg, "ADD_DATA_URI_TAGS", DEFAULT_DATA_URI_TAGS, {
transform: transformCaseFunc,
base: DEFAULT_DATA_URI_TAGS
});
FORBID_CONTENTS = _resolveSetOption(cfg, "FORBID_CONTENTS", DEFAULT_FORBID_CONTENTS, {
transform: transformCaseFunc
});
FORBID_TAGS = _resolveSetOption(cfg, "FORBID_TAGS", clone({}), {
transform: transformCaseFunc
});
FORBID_ATTR = _resolveSetOption(cfg, "FORBID_ATTR", clone({}), {
transform: transformCaseFunc
});
USE_PROFILES = objectHasOwnProperty(cfg, "USE_PROFILES") ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === "object" ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false; USE_PROFILES = objectHasOwnProperty(cfg, "USE_PROFILES") ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === "object" ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;
@@ -1184,8 +1300,8 @@ function createDOMPurify() {
IN_PLACE = cfg.IN_PLACE || false; IN_PLACE = cfg.IN_PLACE || false;
IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI;
NAMESPACE = typeof cfg.NAMESPACE === "string" ? cfg.NAMESPACE : HTML_NAMESPACE; NAMESPACE = typeof cfg.NAMESPACE === "string" ? cfg.NAMESPACE : HTML_NAMESPACE;
MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "MATHML_TEXT_INTEGRATION_POINTS") && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === "object" ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ["mi", "mo", "mn", "ms", "mtext"]); MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "MATHML_TEXT_INTEGRATION_POINTS") && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === "object" ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "HTML_INTEGRATION_POINTS") && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === "object" ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ["annotation-xml"]); HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "HTML_INTEGRATION_POINTS") && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === "object" ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
const customElementHandling = objectHasOwnProperty(cfg, "CUSTOM_ELEMENT_HANDLING") && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === "object" ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null); const customElementHandling = objectHasOwnProperty(cfg, "CUSTOM_ELEMENT_HANDLING") && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === "object" ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
CUSTOM_ELEMENT_HANDLING = create(null); CUSTOM_ELEMENT_HANDLING = create(null);
if (objectHasOwnProperty(customElementHandling, "tagNameCheck") && isRegexOrFunction(customElementHandling.tagNameCheck)) { if (objectHasOwnProperty(customElementHandling, "tagNameCheck") && isRegexOrFunction(customElementHandling.tagNameCheck)) {
@@ -1197,6 +1313,7 @@ function createDOMPurify() {
if (objectHasOwnProperty(customElementHandling, "allowCustomizedBuiltInElements") && typeof customElementHandling.allowCustomizedBuiltInElements === "boolean") { if (objectHasOwnProperty(customElementHandling, "allowCustomizedBuiltInElements") && typeof customElementHandling.allowCustomizedBuiltInElements === "boolean") {
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements;
} }
seal(CUSTOM_ELEMENT_HANDLING);
if (SAFE_FOR_TEMPLATES) { if (SAFE_FOR_TEMPLATES) {
ALLOW_DATA_ATTR = false; ALLOW_DATA_ATTR = false;
} }
@@ -1280,16 +1397,31 @@ function createDOMPurify() {
if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== "function") { if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== "function") {
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.'); throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
} }
const previousTrustedTypesPolicy = trustedTypesPolicy;
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY; trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
emptyHTML = trustedTypesPolicy.createHTML(""); try {
emptyHTML = _createTrustedHTML("");
} catch (error) {
trustedTypesPolicy = previousTrustedTypesPolicy;
throw error;
}
} else if (cfg.TRUSTED_TYPES_POLICY === null) {
trustedTypesPolicy = void 0;
emptyHTML = "";
} else { } else {
if (trustedTypesPolicy === void 0) { if (trustedTypesPolicy === void 0) {
trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript); trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
} }
if (trustedTypesPolicy !== null && typeof emptyHTML === "string") { if (trustedTypesPolicy && typeof emptyHTML === "string") {
emptyHTML = trustedTypesPolicy.createHTML(""); emptyHTML = _createTrustedHTML("");
} }
} }
if ((hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) && ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
ALLOWED_TAGS = clone(ALLOWED_TAGS);
}
if (hooks.uponSanitizeAttribute.length > 0 && ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
ALLOWED_ATTR = clone(ALLOWED_ATTR);
}
if (freeze) { if (freeze) {
freeze(cfg); freeze(cfg);
} }
@@ -1297,6 +1429,33 @@ function createDOMPurify() {
}; };
const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]); const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]); const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
const _checkSvgNamespace = function _checkSvgNamespace2(tagName, parent, parentTagName) {
if (parent.namespaceURI === HTML_NAMESPACE) {
return tagName === "svg";
}
if (parent.namespaceURI === MATHML_NAMESPACE) {
return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
}
return Boolean(ALL_SVG_TAGS[tagName]);
};
const _checkMathMlNamespace = function _checkMathMlNamespace2(tagName, parent, parentTagName) {
if (parent.namespaceURI === HTML_NAMESPACE) {
return tagName === "math";
}
if (parent.namespaceURI === SVG_NAMESPACE) {
return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName];
}
return Boolean(ALL_MATHML_TAGS[tagName]);
};
const _checkHtmlNamespace = function _checkHtmlNamespace2(tagName, parent, parentTagName) {
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
return false;
}
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
return false;
}
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
};
const _checkValidNamespace = function _checkValidNamespace2(element) { const _checkValidNamespace = function _checkValidNamespace2(element) {
let parent = getParentNode(element); let parent = getParentNode(element);
if (!parent || !parent.tagName) { if (!parent || !parent.tagName) {
@@ -1311,31 +1470,13 @@ function createDOMPurify() {
return false; return false;
} }
if (element.namespaceURI === SVG_NAMESPACE) { if (element.namespaceURI === SVG_NAMESPACE) {
if (parent.namespaceURI === HTML_NAMESPACE) { return _checkSvgNamespace(tagName, parent, parentTagName);
return tagName === "svg";
}
if (parent.namespaceURI === MATHML_NAMESPACE) {
return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
}
return Boolean(ALL_SVG_TAGS[tagName]);
} }
if (element.namespaceURI === MATHML_NAMESPACE) { if (element.namespaceURI === MATHML_NAMESPACE) {
if (parent.namespaceURI === HTML_NAMESPACE) { return _checkMathMlNamespace(tagName, parent, parentTagName);
return tagName === "math";
}
if (parent.namespaceURI === SVG_NAMESPACE) {
return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName];
}
return Boolean(ALL_MATHML_TAGS[tagName]);
} }
if (element.namespaceURI === HTML_NAMESPACE) { if (element.namespaceURI === HTML_NAMESPACE) {
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) { return _checkHtmlNamespace(tagName, parent, parentTagName);
return false;
}
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
return false;
}
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
} }
if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[element.namespaceURI]) { if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[element.namespaceURI]) {
return true; return true;
@@ -1350,6 +1491,37 @@ function createDOMPurify() {
getParentNode(node).removeChild(node); getParentNode(node).removeChild(node);
} catch (_) { } catch (_) {
remove(node); remove(node);
if (!getParentNode(node)) {
throw typeErrorCreate("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place");
}
}
};
const _neutralizeRoot = function _neutralizeRoot2(root) {
const childNodes = getChildNodes(root);
if (childNodes) {
const snapshot = [];
arrayForEach(childNodes, (child) => {
arrayPush(snapshot, child);
});
arrayForEach(snapshot, (child) => {
try {
remove(child);
} catch (_) {
}
});
}
const attributes = getAttributes(root);
if (attributes) {
for (let i = attributes.length - 1; i >= 0; --i) {
const attribute = attributes[i];
const name = attribute && attribute.name;
if (typeof name === "string") {
try {
root.removeAttribute(name);
} catch (_) {
}
}
}
} }
}; };
const _removeAttribute = function _removeAttribute2(name, element) { const _removeAttribute = function _removeAttribute2(name, element) {
@@ -1379,6 +1551,39 @@ function createDOMPurify() {
} }
} }
}; };
const _stripDisallowedAttributes = function _stripDisallowedAttributes2(element) {
const attributes = getAttributes(element);
if (!attributes) {
return;
}
for (let i = attributes.length - 1; i >= 0; --i) {
const attribute = attributes[i];
const name = attribute && attribute.name;
if (typeof name !== "string" || ALLOWED_ATTR[transformCaseFunc(name)]) {
continue;
}
try {
element.removeAttribute(name);
} catch (_) {
}
}
};
const _neutralizeSubtree = function _neutralizeSubtree2(root) {
const stack = [root];
while (stack.length > 0) {
const node = stack.pop();
const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
if (nodeType === NODE_TYPE.element) {
_stripDisallowedAttributes(node);
}
const childNodes = getChildNodes(node);
if (childNodes) {
for (let i = childNodes.length - 1; i >= 0; --i) {
stack.push(childNodes[i]);
}
}
}
};
const _initDocument = function _initDocument2(dirty) { const _initDocument = function _initDocument2(dirty) {
let doc = null; let doc = null;
let leadingWhitespace = null; let leadingWhitespace = null;
@@ -1391,7 +1596,7 @@ function createDOMPurify() {
if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && NAMESPACE === HTML_NAMESPACE) { if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && NAMESPACE === HTML_NAMESPACE) {
dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + "</body></html>"; dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + "</body></html>";
} }
const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; const dirtyPayload = trustedTypesPolicy ? _createTrustedHTML(dirty) : dirty;
if (NAMESPACE === HTML_NAMESPACE) { if (NAMESPACE === HTML_NAMESPACE) {
try { try {
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE); doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
@@ -1423,7 +1628,14 @@ function createDOMPurify() {
null null
); );
}; };
const _scrubTemplateExpressions = function _scrubTemplateExpressions2(node) { const _stripTemplateExpressions = function _stripTemplateExpressions2(value) {
value = stringReplace(value, MUSTACHE_EXPR$1, " ");
value = stringReplace(value, ERB_EXPR$1, " ");
value = stringReplace(value, TMPLIT_EXPR$1, " ");
return value;
};
const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
var _node$querySelectorAl;
node.normalize(); node.normalize();
const walker = createNodeIterator.call( const walker = createNodeIterator.call(
node.ownerDocument || node, node.ownerDocument || node,
@@ -1434,16 +1646,60 @@ function createDOMPurify() {
); );
let currentNode = walker.nextNode(); let currentNode = walker.nextNode();
while (currentNode) { while (currentNode) {
let data = currentNode.data; currentNode.data = _stripTemplateExpressions(currentNode.data);
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
data = stringReplace(data, expr, " ");
});
currentNode.data = data;
currentNode = walker.nextNode(); currentNode = walker.nextNode();
} }
const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, "template");
if (templates) {
arrayForEach(templates, (tmpl) => {
if (_isDocumentFragment(tmpl.content)) {
_scrubTemplateExpressions2(tmpl.content);
}
});
}
}; };
const _isClobbered = function _isClobbered2(element) { const _isClobbered = function _isClobbered2(element) {
return element instanceof HTMLFormElement && (typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function"); const realTagName = getNodeName ? getNodeName(element) : null;
if (typeof realTagName !== "string") {
return false;
}
if (transformCaseFunc(realTagName) !== "form") {
return false;
}
return typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || // Realm-safe NamedNodeMap detection: equality against the cached
// prototype getter. Clobbered .attributes (e.g. <input name="attributes">)
// makes the direct read diverge from the cached read; a clean form
// (same-realm OR foreign-realm) has both reads pointing at the same
// canonical NamedNodeMap.
element.attributes !== getAttributes(element) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function" || // NodeType clobbering probe. Cached Node.prototype.nodeType getter
// returns the integer 1 for any Element regardless of realm; direct
// read on a clobbered form (e.g. <input name="nodeType">) returns
// the named child element. Cheap addition — nodeType is read from
// an internal slot, no serialization cost — and removes a residual
// clobbering surface used by several mXSS / PI / comment branches
// in _sanitizeElements that compare currentNode.nodeType directly.
element.nodeType !== getNodeType(element) || // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
// "childNodes" shadows the prototype getter. Direct reads of
// form.childNodes from a clobbered form return the named child
// instead of the real NodeList, so any walk that reads it directly
// skips the form's real children. Compare the direct read to the
// cached Node.prototype getter — when the form's named-property
// getter intercepts the read, the two values differ and we flag
// the form. This catches every clobbering child type (input,
// select, etc.) regardless of whether the named child happens to
// carry a numeric .length, which a typeof-based probe would miss
// (e.g. HTMLSelectElement.length is a defined unsigned-long).
element.childNodes !== getChildNodes(element);
};
const _isDocumentFragment = function _isDocumentFragment2(value) {
if (!getNodeType || typeof value !== "object" || value === null) {
return false;
}
try {
return getNodeType(value) === NODE_TYPE.documentFragment;
} catch (_) {
return false;
}
}; };
const _isNode = function _isNode2(value) { const _isNode = function _isNode2(value) {
if (!getNodeType || typeof value !== "object" || value === null) { if (!getNodeType || typeof value !== "object" || value === null) {
@@ -1456,74 +1712,80 @@ function createDOMPurify() {
} }
}; };
function _executeHooks(hooks2, currentNode, data) { function _executeHooks(hooks2, currentNode, data) {
if (hooks2.length === 0) {
return;
}
arrayForEach(hooks2, (hook) => { arrayForEach(hooks2, (hook) => {
hook.call(DOMPurify, currentNode, data, CONFIG); hook.call(DOMPurify, currentNode, data, CONFIG);
}); });
} }
const _isUnsafeNode = function _isUnsafeNode2(currentNode, tagName) {
if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) {
return true;
}
if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === "style" && _isNode(currentNode.firstElementChild)) {
return true;
}
if (currentNode.nodeType === NODE_TYPE.processingInstruction) {
return true;
}
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) {
return true;
}
return false;
};
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode2(currentNode, tagName) {
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
return false;
}
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
return false;
}
}
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
const parentNode = getParentNode(currentNode);
const childNodes = getChildNodes(currentNode);
if (childNodes && parentNode) {
const childCount = childNodes.length;
for (let i = childCount - 1; i >= 0; --i) {
const hoisted = IN_PLACE ? childNodes[i] : cloneNode(childNodes[i], true);
parentNode.insertBefore(hoisted, getNextSibling(currentNode));
}
}
}
_forceRemove(currentNode);
return true;
};
const _sanitizeElements = function _sanitizeElements2(currentNode) { const _sanitizeElements = function _sanitizeElements2(currentNode) {
let content = null;
_executeHooks(hooks.beforeSanitizeElements, currentNode, null); _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
if (_isClobbered(currentNode)) { if (_isClobbered(currentNode)) {
_forceRemove(currentNode); _forceRemove(currentNode);
return true; return true;
} }
const tagName = transformCaseFunc(currentNode.nodeName); const tagName = transformCaseFunc(getNodeName ? getNodeName(currentNode) : currentNode.nodeName);
_executeHooks(hooks.uponSanitizeElement, currentNode, { _executeHooks(hooks.uponSanitizeElement, currentNode, {
tagName, tagName,
allowedTags: ALLOWED_TAGS allowedTags: ALLOWED_TAGS
}); });
if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) { if (_isUnsafeNode(currentNode, tagName)) {
_forceRemove(currentNode);
return true;
}
if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === "style" && _isNode(currentNode.firstElementChild)) {
_forceRemove(currentNode);
return true;
}
if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
_forceRemove(currentNode);
return true;
}
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
_forceRemove(currentNode); _forceRemove(currentNode);
return true; return true;
} }
if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) { if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) { return _sanitizeDisallowedNode(currentNode, tagName);
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) { }
return false; const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;
} if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
return false;
}
}
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
const parentNode = getParentNode(currentNode) || currentNode.parentNode;
const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
if (childNodes && parentNode) {
const childCount = childNodes.length;
for (let i = childCount - 1; i >= 0; --i) {
const childClone = cloneNode(childNodes[i], true);
parentNode.insertBefore(childClone, getNextSibling(currentNode));
}
}
}
_forceRemove(currentNode); _forceRemove(currentNode);
return true; return true;
} }
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) { if ((tagName === "noscript" || tagName === "noembed" || tagName === "noframes") && regExpTest(FALLBACK_TAG_CLOSE, currentNode.innerHTML)) {
_forceRemove(currentNode);
return true;
}
if ((tagName === "noscript" || tagName === "noembed" || tagName === "noframes") && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
_forceRemove(currentNode); _forceRemove(currentNode);
return true; return true;
} }
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) { if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
content = currentNode.textContent; const content = _stripTemplateExpressions(currentNode.textContent);
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
content = stringReplace(content, expr, " ");
});
if (currentNode.textContent !== content) { if (currentNode.textContent !== content) {
arrayPush(DOMPurify.removed, { arrayPush(DOMPurify.removed, {
element: currentNode.cloneNode() element: currentNode.cloneNode()
@@ -1542,9 +1804,9 @@ function createDOMPurify() {
return false; return false;
} }
const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag); const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) ;
else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ;
else if (!nameIsPermitted || FORBID_ATTR[lcName]) { else if (!nameIsPermitted) {
if ( if (
// First condition does a very basic check if a) it's basically a valid custom element tagname AND // First condition does a very basic check if a) it's basically a valid custom element tagname AND
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -1569,6 +1831,35 @@ function createDOMPurify() {
const _isBasicCustomElement = function _isBasicCustomElement2(tagName) { const _isBasicCustomElement = function _isBasicCustomElement2(tagName) {
return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName); return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
}; };
const _applyTrustedTypesToAttribute = function _applyTrustedTypesToAttribute2(lcTag, lcName, namespaceURI, value) {
if (trustedTypesPolicy && typeof trustedTypes === "object" && typeof trustedTypes.getAttributeType === "function" && !namespaceURI) {
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
case "TrustedHTML": {
return _createTrustedHTML(value);
}
case "TrustedScriptURL": {
return _createTrustedScriptURL(value);
}
}
}
return value;
};
const _setAttributeValue = function _setAttributeValue2(currentNode, name, namespaceURI, value) {
try {
if (namespaceURI) {
currentNode.setAttributeNS(namespaceURI, name, value);
} else {
currentNode.setAttribute(name, value);
}
if (_isClobbered(currentNode)) {
_forceRemove(currentNode);
} else {
arrayPop(DOMPurify.removed);
}
} catch (_) {
_removeAttribute(name, currentNode);
}
};
const _sanitizeAttributes = function _sanitizeAttributes2(currentNode) { const _sanitizeAttributes = function _sanitizeAttributes2(currentNode) {
_executeHooks(hooks.beforeSanitizeAttributes, currentNode, null); _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
const attributes = currentNode.attributes; const attributes = currentNode.attributes;
@@ -1583,6 +1874,7 @@ function createDOMPurify() {
forceKeepAttr: void 0 forceKeepAttr: void 0
}; };
let l = attributes.length; let l = attributes.length;
const lcTag = transformCaseFunc(currentNode.nodeName);
while (l--) { while (l--) {
const attr = attributes[l]; const attr = attributes[l];
const name = attr.name, namespaceURI = attr.namespaceURI, attrValue = attr.value; const name = attr.name, namespaceURI = attr.namespaceURI, attrValue = attr.value;
@@ -1614,50 +1906,20 @@ function createDOMPurify() {
_removeAttribute(name, currentNode); _removeAttribute(name, currentNode);
continue; continue;
} }
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) { if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
_removeAttribute(name, currentNode); _removeAttribute(name, currentNode);
continue; continue;
} }
if (SAFE_FOR_TEMPLATES) { if (SAFE_FOR_TEMPLATES) {
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => { value = _stripTemplateExpressions(value);
value = stringReplace(value, expr, " ");
});
} }
const lcTag = transformCaseFunc(currentNode.nodeName);
if (!_isValidAttribute(lcTag, lcName, value)) { if (!_isValidAttribute(lcTag, lcName, value)) {
_removeAttribute(name, currentNode); _removeAttribute(name, currentNode);
continue; continue;
} }
if (trustedTypesPolicy && typeof trustedTypes === "object" && typeof trustedTypes.getAttributeType === "function") { value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
if (namespaceURI) ;
else {
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
case "TrustedHTML": {
value = trustedTypesPolicy.createHTML(value);
break;
}
case "TrustedScriptURL": {
value = trustedTypesPolicy.createScriptURL(value);
break;
}
}
}
}
if (value !== initValue) { if (value !== initValue) {
try { _setAttributeValue(currentNode, name, namespaceURI, value);
if (namespaceURI) {
currentNode.setAttributeNS(namespaceURI, name, value);
} else {
currentNode.setAttribute(name, value);
}
if (_isClobbered(currentNode)) {
_forceRemove(currentNode);
} else {
arrayPop(DOMPurify.removed);
}
} catch (_) {
_removeAttribute(name, currentNode);
}
} }
} }
_executeHooks(hooks.afterSanitizeAttributes, currentNode, null); _executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
@@ -1670,28 +1932,67 @@ function createDOMPurify() {
_executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null); _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
_sanitizeElements(shadowNode); _sanitizeElements(shadowNode);
_sanitizeAttributes(shadowNode); _sanitizeAttributes(shadowNode);
if (shadowNode.content instanceof DocumentFragment) { if (_isDocumentFragment(shadowNode.content)) {
_sanitizeShadowDOM2(shadowNode.content); _sanitizeShadowDOM2(shadowNode.content);
} }
const shadowNodeType = getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType;
if (shadowNodeType === NODE_TYPE.element) {
const innerSr = getShadowRoot(shadowNode);
if (_isDocumentFragment(innerSr)) {
_sanitizeAttachedShadowRoots(innerSr);
_sanitizeShadowDOM2(innerSr);
}
}
} }
_executeHooks(hooks.afterSanitizeShadowDOM, fragment, null); _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
}; };
const _sanitizeAttachedShadowRoots2 = function _sanitizeAttachedShadowRoots(root) { const _sanitizeAttachedShadowRoots = function _sanitizeAttachedShadowRoots2(root) {
if (root.nodeType === NODE_TYPE.element && root.shadowRoot instanceof DocumentFragment) { const stack = [{
const sr = root.shadowRoot; node: root,
_sanitizeAttachedShadowRoots2(sr); shadow: null
_sanitizeShadowDOM2(sr); }];
} while (stack.length > 0) {
const childNodes = root.childNodes; const item = stack.pop();
if (!childNodes) { if (item.shadow) {
return; _sanitizeShadowDOM2(item.shadow);
} continue;
const snapshot = []; }
arrayForEach(childNodes, (child) => { const node = item.node;
arrayPush(snapshot, child); const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
}); const isElement = nodeType === NODE_TYPE.element;
for (const child of snapshot) { const childNodes = getChildNodes(node);
_sanitizeAttachedShadowRoots2(child); if (childNodes) {
for (let i = childNodes.length - 1; i >= 0; --i) {
stack.push({
node: childNodes[i],
shadow: null
});
}
}
if (isElement) {
const rootName = getNodeName ? getNodeName(node) : null;
if (typeof rootName === "string" && transformCaseFunc(rootName) === "template") {
const content = node.content;
if (_isDocumentFragment(content)) {
stack.push({
node: content,
shadow: null
});
}
}
}
if (isElement) {
const sr = getShadowRoot(node);
if (_isDocumentFragment(sr)) {
stack.push({
node: null,
shadow: sr
}, {
node: sr,
shadow: null
});
}
}
} }
}; };
DOMPurify.sanitize = function(dirty) { DOMPurify.sanitize = function(dirty) {
@@ -1717,18 +2018,24 @@ function createDOMPurify() {
_parseConfig(cfg); _parseConfig(cfg);
} }
DOMPurify.removed = []; DOMPurify.removed = [];
if (typeof dirty === "string") { const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
IN_PLACE = false; if (inPlace) {
} const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
if (IN_PLACE) {
const nn = dirty.nodeName;
if (typeof nn === "string") { if (typeof nn === "string") {
const tagName = transformCaseFunc(nn); const tagName = transformCaseFunc(nn);
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place"); throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
} }
} }
_sanitizeAttachedShadowRoots2(dirty); if (_isClobbered(dirty)) {
throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
}
try {
_sanitizeAttachedShadowRoots(dirty);
} catch (error) {
_neutralizeRoot(dirty);
throw error;
}
} else if (_isNode(dirty)) { } else if (_isNode(dirty)) {
body = _initDocument("<!---->"); body = _initDocument("<!---->");
importedNode = body.ownerDocument.importNode(dirty, true); importedNode = body.ownerDocument.importNode(dirty, true);
@@ -1739,11 +2046,11 @@ function createDOMPurify() {
} else { } else {
body.appendChild(importedNode); body.appendChild(importedNode);
} }
_sanitizeAttachedShadowRoots2(importedNode); _sanitizeAttachedShadowRoots(importedNode);
} else { } else {
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
dirty.indexOf("<") === -1) { dirty.indexOf("<") === -1) {
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty; return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
} }
body = _initDocument(dirty); body = _initDocument(dirty);
if (!body) { if (!body) {
@@ -1753,23 +2060,35 @@ function createDOMPurify() {
if (body && FORCE_BODY) { if (body && FORCE_BODY) {
_forceRemove(body.firstChild); _forceRemove(body.firstChild);
} }
const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body); const nodeIterator = _createNodeIterator(inPlace ? dirty : body);
while (currentNode = nodeIterator.nextNode()) { try {
_sanitizeElements(currentNode); while (currentNode = nodeIterator.nextNode()) {
_sanitizeAttributes(currentNode); _sanitizeElements(currentNode);
if (currentNode.content instanceof DocumentFragment) { _sanitizeAttributes(currentNode);
_sanitizeShadowDOM2(currentNode.content); if (_isDocumentFragment(currentNode.content)) {
_sanitizeShadowDOM2(currentNode.content);
}
} }
} catch (error) {
if (inPlace) {
_neutralizeRoot(dirty);
}
throw error;
} }
if (IN_PLACE) { if (inPlace) {
arrayForEach(DOMPurify.removed, (entry) => {
if (entry.element) {
_neutralizeSubtree(entry.element);
}
});
if (SAFE_FOR_TEMPLATES) { if (SAFE_FOR_TEMPLATES) {
_scrubTemplateExpressions(dirty); _scrubTemplateExpressions2(dirty);
} }
return dirty; return dirty;
} }
if (RETURN_DOM) { if (RETURN_DOM) {
if (SAFE_FOR_TEMPLATES) { if (SAFE_FOR_TEMPLATES) {
_scrubTemplateExpressions(body); _scrubTemplateExpressions2(body);
} }
if (RETURN_DOM_FRAGMENT) { if (RETURN_DOM_FRAGMENT) {
returnNode = createDocumentFragment.call(body.ownerDocument); returnNode = createDocumentFragment.call(body.ownerDocument);
@@ -1789,11 +2108,9 @@ function createDOMPurify() {
serializedHTML = "<!DOCTYPE " + body.ownerDocument.doctype.name + ">\n" + serializedHTML; serializedHTML = "<!DOCTYPE " + body.ownerDocument.doctype.name + ">\n" + serializedHTML;
} }
if (SAFE_FOR_TEMPLATES) { if (SAFE_FOR_TEMPLATES) {
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => { serializedHTML = _stripTemplateExpressions(serializedHTML);
serializedHTML = stringReplace(serializedHTML, expr, " ");
});
} }
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML; return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
}; };
DOMPurify.setConfig = function() { DOMPurify.setConfig = function() {
let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
@@ -1803,6 +2120,8 @@ function createDOMPurify() {
DOMPurify.clearConfig = function() { DOMPurify.clearConfig = function() {
CONFIG = null; CONFIG = null;
SET_CONFIG = false; SET_CONFIG = false;
trustedTypesPolicy = defaultTrustedTypesPolicy;
emptyHTML = "";
}; };
DOMPurify.isValidAttribute = function(tag, attr, value) { DOMPurify.isValidAttribute = function(tag, attr, value) {
if (!CONFIG) { if (!CONFIG) {
@@ -2592,13 +2911,8 @@ var EnhancedSecureCryptoUtils = class _EnhancedSecureCryptoUtils {
if (this.isProductionMode) { if (this.isProductionMode) {
if (level === "error") { if (level === "error") {
console.error(`\u274C [SecureChat] ${message} [ERROR_CODE: ${this._generateErrorCode(message)}]`); console.error(`\u274C [SecureChat] ${message} [ERROR_CODE: ${this._generateErrorCode(message)}]`);
if (context && Object.keys(context).length > 0) {
console.error("Error details:", context);
}
} else if (level === "warn") { } else if (level === "warn") {
console.warn(`\u26A0\uFE0F [SecureChat] ${message}`); console.warn(`\u26A0\uFE0F [SecureChat] ${message}`);
} else if (level === "info" || level === "debug") {
console.log(`[SecureChat] ${message}`, context);
} else { } else {
return; return;
} }
@@ -3883,7 +4197,7 @@ var EnhancedSecureCryptoUtils = class _EnhancedSecureCryptoUtils {
// src/transfer/EnhancedSecureFileTransfer.js // src/transfer/EnhancedSecureFileTransfer.js
var SecureFileTransferContext = class _SecureFileTransferContext { var SecureFileTransferContext = class _SecureFileTransferContext {
static #instance = null; static #instance = null;
static #contextKey = Symbol("SecureFileTransferContext"); static #contextKey = /* @__PURE__ */ Symbol("SecureFileTransferContext");
static getInstance() { static getInstance() {
if (!this.#instance) { if (!this.#instance) {
this.#instance = new _SecureFileTransferContext(); this.#instance = new _SecureFileTransferContext();
@@ -17167,7 +17481,7 @@ Right-click or Ctrl+click to disconnect`,
React.createElement("p", { React.createElement("p", {
key: "subtitle", key: "subtitle",
className: "text-xs sm:text-sm text-muted hidden sm:block" className: "text-xs sm:text-sm text-muted hidden sm:block"
}, "End-to-end freedom v4.8.5") }, "End-to-end freedom v4.8.9")
]) ])
]), ]),
// Status and Controls - Responsive // Status and Controls - Responsive
@@ -18793,6 +19107,6 @@ if (document.readyState === "loading") {
/*! Bundled license information: /*! Bundled license information:
dompurify/dist/purify.es.mjs: dompurify/dist/purify.es.mjs:
(*! @license DOMPurify 3.4.4 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.4/LICENSE *) (*! @license DOMPurify 3.4.10 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.10/LICENSE *)
*/ */
//# sourceMappingURL=app-boot.js.map //# sourceMappingURL=app-boot.js.map
+3 -3
View File
File diff suppressed because one or more lines are too long
Vendored
+1 -1
View File
@@ -1820,7 +1820,7 @@ var EnhancedSecureP2PChat = () => {
} catch (error) { } catch (error) {
} }
} }
handleMessage(" SecureBit.chat Enhanced Security Edition v4.8.7 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.", "system"); handleMessage(" SecureBit.chat Enhanced Security Edition v4.8.9 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.", "system");
const handleBeforeUnload = (event) => { const handleBeforeUnload = (event) => {
if (event.type === "beforeunload" && !isTabSwitching) { if (event.type === "beforeunload" && !isTabSwitching) {
if (webrtcManagerRef.current && webrtcManagerRef.current.isConnected()) { if (webrtcManagerRef.current && webrtcManagerRef.current.isConnected()) {
+1 -1
View File
File diff suppressed because one or more lines are too long
+5 -1
View File
@@ -5,7 +5,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf; var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty; var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() { var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; try {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
} catch (e) {
throw mod = 0, e;
}
}; };
var __copyProps = (to, from, except, desc) => { var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") { if (from && typeof from === "object" || typeof from === "function") {
+1 -1
View File
File diff suppressed because one or more lines are too long
+5 -5
View File
@@ -113,7 +113,7 @@
<!-- GitHub Pages SEO --> <!-- GitHub Pages SEO -->
<meta name="description" content="SecureBit.chat v4.8.7 — P2P messenger with ECDH + DTLS + SAS security and 18-layer military-grade cryptography"> <meta name="description" content="SecureBit.chat v4.8.9 — P2P messenger with ECDH + DTLS + SAS security and 18-layer military-grade cryptography">
<meta name="keywords" content="P2P messenger, ECDH, DTLS, SAS, encryption, WebRTC, privacy, ASN.1 validation, military-grade security, 18-layer defense, MITM protection, PFS"> <meta name="keywords" content="P2P messenger, ECDH, DTLS, SAS, encryption, WebRTC, privacy, ASN.1 validation, military-grade security, 18-layer defense, MITM protection, PFS">
<meta name="author" content="Volodymyr"> <meta name="author" content="Volodymyr">
<link rel="canonical" href="https://github.com/SecureBitChat/securebit-chat/"> <link rel="canonical" href="https://github.com/SecureBitChat/securebit-chat/">
@@ -148,13 +148,13 @@
<!-- Update Manager - система принудительного обновления --> <!-- Update Manager - система принудительного обновления -->
<script src="src/utils/updateManager.js"></script> <script src="src/utils/updateManager.js"></script>
<script type="module" src="src/components/UpdateChecker.jsx"></script> <script type="module" src="src/components/UpdateChecker.jsx"></script>
<script type="module" src="dist/qr-local.js?v=1779848383991"></script> <script type="module" src="dist/qr-local.js?v=1781550335673"></script>
<script type="module" src="src/components/QRScanner.js?v=1779848383991"></script> <script type="module" src="src/components/QRScanner.js?v=1781550335673"></script>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="dist/app-boot.js?v=1779848383991"></script> <script type="module" src="dist/app-boot.js?v=1781550335673"></script>
<script type="module" src="dist/app.js?v=1779848383991"></script> <script type="module" src="dist/app.js?v=1781550335673"></script>
<script src="src/scripts/pwa-register.js"></script> <script src="src/scripts/pwa-register.js"></script>
<script src="./src/pwa/install-prompt.js" type="module"></script> <script src="./src/pwa/install-prompt.js" type="module"></script>
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"name": "SecureBit.chat v4.8.7 - ECDH + DTLS + SAS", "name": "SecureBit.chat v4.8.9 - ECDH + DTLS + SAS",
"short_name": "SecureBit", "short_name": "SecureBit",
"description": "P2P messenger with ECDH + DTLS + SAS security, military-grade cryptography and Lightning Network payments", "description": "P2P messenger with ECDH + DTLS + SAS security, military-grade cryptography and Lightning Network payments",
"start_url": "./", "start_url": "./",
+7 -7
View File
@@ -1,10 +1,10 @@
{ {
"version": "1779848383991", "version": "1781550335673",
"buildVersion": "1779848383991", "buildVersion": "1781550335673",
"appVersion": "4.8.8", "appVersion": "4.8.9",
"buildTime": "2026-05-27T02:19:44.033Z", "buildTime": "2026-06-15T19:05:35.714Z",
"buildId": "1779848383991-2468cb4", "buildId": "1781550335673-d11f250",
"gitHash": "2468cb4", "gitHash": "d11f250",
"generated": true, "generated": true,
"generatedAt": "2026-05-27T02:19:44.035Z" "generatedAt": "2026-06-15T19:05:35.717Z"
} }
+113 -113
View File
@@ -1,12 +1,12 @@
{ {
"name": "securebit-chat", "name": "securebit-chat",
"version": "4.8.7", "version": "4.8.9",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "securebit-chat", "name": "securebit-chat",
"version": "4.8.7", "version": "4.8.9",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"base64-js": "1.5.1", "base64-js": "1.5.1",
@@ -18,7 +18,7 @@
"qrcode": "1.5.4" "qrcode": "1.5.4"
}, },
"devDependencies": { "devDependencies": {
"esbuild": "0.25.9", "esbuild": "^0.28.1",
"jsdom": "^28.1.0", "jsdom": "^28.1.0",
"tailwindcss": "3.4.17" "tailwindcss": "3.4.17"
} }
@@ -255,9 +255,9 @@
} }
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
"integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -272,9 +272,9 @@
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
"integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -289,9 +289,9 @@
} }
}, },
"node_modules/@esbuild/android-arm64": { "node_modules/@esbuild/android-arm64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
"integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -306,9 +306,9 @@
} }
}, },
"node_modules/@esbuild/android-x64": { "node_modules/@esbuild/android-x64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
"integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -323,9 +323,9 @@
} }
}, },
"node_modules/@esbuild/darwin-arm64": { "node_modules/@esbuild/darwin-arm64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
"integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -340,9 +340,9 @@
} }
}, },
"node_modules/@esbuild/darwin-x64": { "node_modules/@esbuild/darwin-x64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
"integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -357,9 +357,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-arm64": { "node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
"integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -374,9 +374,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-x64": { "node_modules/@esbuild/freebsd-x64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
"integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -391,9 +391,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm": { "node_modules/@esbuild/linux-arm": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
"integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -408,9 +408,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm64": { "node_modules/@esbuild/linux-arm64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
"integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -425,9 +425,9 @@
} }
}, },
"node_modules/@esbuild/linux-ia32": { "node_modules/@esbuild/linux-ia32": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
"integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -442,9 +442,9 @@
} }
}, },
"node_modules/@esbuild/linux-loong64": { "node_modules/@esbuild/linux-loong64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
"integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@@ -459,9 +459,9 @@
} }
}, },
"node_modules/@esbuild/linux-mips64el": { "node_modules/@esbuild/linux-mips64el": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
"integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
@@ -476,9 +476,9 @@
} }
}, },
"node_modules/@esbuild/linux-ppc64": { "node_modules/@esbuild/linux-ppc64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
"integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -493,9 +493,9 @@
} }
}, },
"node_modules/@esbuild/linux-riscv64": { "node_modules/@esbuild/linux-riscv64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
"integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -510,9 +510,9 @@
} }
}, },
"node_modules/@esbuild/linux-s390x": { "node_modules/@esbuild/linux-s390x": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
"integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -527,9 +527,9 @@
} }
}, },
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
"integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -544,9 +544,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-arm64": { "node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
"integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -561,9 +561,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-x64": { "node_modules/@esbuild/netbsd-x64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
"integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -578,9 +578,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-arm64": { "node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
"integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -595,9 +595,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-x64": { "node_modules/@esbuild/openbsd-x64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
"integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -612,9 +612,9 @@
} }
}, },
"node_modules/@esbuild/openharmony-arm64": { "node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
"integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -629,9 +629,9 @@
} }
}, },
"node_modules/@esbuild/sunos-x64": { "node_modules/@esbuild/sunos-x64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
"integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -646,9 +646,9 @@
} }
}, },
"node_modules/@esbuild/win32-arm64": { "node_modules/@esbuild/win32-arm64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
"integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -663,9 +663,9 @@
} }
}, },
"node_modules/@esbuild/win32-ia32": { "node_modules/@esbuild/win32-ia32": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
"integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -680,9 +680,9 @@
} }
}, },
"node_modules/@esbuild/win32-x64": { "node_modules/@esbuild/win32-x64": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
"integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1292,9 +1292,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/dompurify": { "node_modules/dompurify": {
"version": "3.4.4", "version": "3.4.10",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.4.tgz", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.10.tgz",
"integrity": "sha512-r8K7KGKEcztXfA/nfabSYB2hg9tDphORJTdf8xprN/luSLGmNhOBN8dm1/SYjqLLet6YUFEXOcrdTuwryp/Bew==", "integrity": "sha512-0xzNv0e7oYC6yyuOGZIABPM4qtg3QxLFniDNPP4ZP90wR8Yq3zgwpRbrNiT4N3IKqDbbYFEJLV+JWEs19aZ//w==",
"license": "(MPL-2.0 OR Apache-2.0)", "license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": { "optionalDependencies": {
"@types/trusted-types": "^2.0.7" "@types/trusted-types": "^2.0.7"
@@ -1327,9 +1327,9 @@
} }
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.25.9", "version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
@@ -1340,32 +1340,32 @@
"node": ">=18" "node": ">=18"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.9", "@esbuild/aix-ppc64": "0.28.1",
"@esbuild/android-arm": "0.25.9", "@esbuild/android-arm": "0.28.1",
"@esbuild/android-arm64": "0.25.9", "@esbuild/android-arm64": "0.28.1",
"@esbuild/android-x64": "0.25.9", "@esbuild/android-x64": "0.28.1",
"@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-arm64": "0.28.1",
"@esbuild/darwin-x64": "0.25.9", "@esbuild/darwin-x64": "0.28.1",
"@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-arm64": "0.28.1",
"@esbuild/freebsd-x64": "0.25.9", "@esbuild/freebsd-x64": "0.28.1",
"@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm": "0.28.1",
"@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-arm64": "0.28.1",
"@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-ia32": "0.28.1",
"@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-loong64": "0.28.1",
"@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-mips64el": "0.28.1",
"@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-ppc64": "0.28.1",
"@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-riscv64": "0.28.1",
"@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-s390x": "0.28.1",
"@esbuild/linux-x64": "0.25.9", "@esbuild/linux-x64": "0.28.1",
"@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-arm64": "0.28.1",
"@esbuild/netbsd-x64": "0.25.9", "@esbuild/netbsd-x64": "0.28.1",
"@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-arm64": "0.28.1",
"@esbuild/openbsd-x64": "0.25.9", "@esbuild/openbsd-x64": "0.28.1",
"@esbuild/openharmony-arm64": "0.25.9", "@esbuild/openharmony-arm64": "0.28.1",
"@esbuild/sunos-x64": "0.25.9", "@esbuild/sunos-x64": "0.28.1",
"@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-arm64": "0.28.1",
"@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-ia32": "0.28.1",
"@esbuild/win32-x64": "0.25.9" "@esbuild/win32-x64": "0.28.1"
} }
}, },
"node_modules/fast-glob": { "node_modules/fast-glob": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "securebit-chat", "name": "securebit-chat",
"version": "4.8.8", "version": "4.8.9",
"description": "Secure P2P Communication Application with End-to-End Encryption", "description": "Secure P2P Communication Application with End-to-End Encryption",
"main": "index.html", "main": "index.html",
"scripts": { "scripts": {
@@ -24,7 +24,7 @@
"author": "SecureBit Team", "author": "SecureBit Team",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"esbuild": "0.25.9", "esbuild": "^0.28.1",
"jsdom": "^28.1.0", "jsdom": "^28.1.0",
"tailwindcss": "3.4.17" "tailwindcss": "3.4.17"
}, },
+1 -1
View File
@@ -2018,7 +2018,7 @@ import { installDebugWindowHooks } from './utils/debugWindowHooks.js';
} }
} }
handleMessage(' SecureBit.chat Enhanced Security Edition v4.8.7 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system'); handleMessage(' SecureBit.chat Enhanced Security Edition v4.8.9 - ECDH + DTLS + SAS initialized. Ready to establish a secure connection with ECDH key exchange, DTLS fingerprint verification, and SAS authentication to prevent MITM attacks.', 'system');
const handleBeforeUnload = (event) => { const handleBeforeUnload = (event) => {
if (event.type === 'beforeunload' && !isTabSwitching) { if (event.type === 'beforeunload' && !isTabSwitching) {
+1 -1
View File
@@ -539,7 +539,7 @@ const EnhancedMinimalHeader = ({
React.createElement('p', { React.createElement('p', {
key: 'subtitle', key: 'subtitle',
className: 'text-xs sm:text-sm text-muted hidden sm:block' className: 'text-xs sm:text-sm text-muted hidden sm:block'
}, 'End-to-end freedom v4.8.5') }, 'End-to-end freedom v4.8.9')
]) ])
]), ]),
+3 -10
View File
@@ -973,20 +973,13 @@ class EnhancedSecureCryptoUtils {
// Production-safe console output // Production-safe console output
if (this.isProductionMode) { if (this.isProductionMode) {
if (level === 'error') { if (level === 'error') {
// В production показываем только код ошибки без деталей // In production expose only an opaque error code, never the context.
console.error(`❌ [SecureChat] ${message} [ERROR_CODE: ${this._generateErrorCode(message)}]`); console.error(`❌ [SecureChat] ${message} [ERROR_CODE: ${this._generateErrorCode(message)}]`);
// Временно показываем детали для отладки
if (context && Object.keys(context).length > 0) {
console.error('Error details:', context);
}
} else if (level === 'warn') { } else if (level === 'warn') {
// В production показываем только предупреждение без контекста // Warning text only, no context payload.
console.warn(`⚠️ [SecureChat] ${message}`); console.warn(`⚠️ [SecureChat] ${message}`);
} else if (level === 'info' || level === 'debug') {
// Временно показываем info/debug логи для отладки
console.log(`[SecureChat] ${message}`, context);
} else { } else {
// В production не показываем другие логи // info/debug and any other level: suppressed entirely in production.
return; return;
} }
} else { } else {