Sign In

@file-viewer/pptx

Package Overview
Dependencies
Maintainers
1
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@file-viewer/pptx - npm Package Compare versions

Comparing version
2.2.8
to
2.2.9
+2
dist/sanitize.d.ts
export declare const sanitizePptxCss: (documentRef: Document, cssText: string) => string;
export declare const sanitizePptxMarkup: (documentRef: Document, html: string) => DocumentFragment;
import createDOMPurify from 'dompurify';
const PPTX_CONTENT_STYLE_SCOPE = '.flyfish-pptx-content';
const GENERATED_STYLE_RULE = /\s*(\._(?:css|svg_css|tbl_cell_css)_[A-Za-z0-9_-]+)\s*\{([^{}]*)\}/y;
const purifierByDocument = new WeakMap();
const createSafeFallbackFragment = (documentRef) => {
const fragment = documentRef.createDocumentFragment();
const fallback = documentRef.createElement('section');
fallback.className = 'slide flyfish-pptx-slide-error';
fallback.textContent = 'This slide could not be displayed safely.';
fragment.append(fallback);
return fragment;
};
const getPurifier = (documentRef) => {
const cached = purifierByDocument.get(documentRef);
if (cached) {
return cached;
}
const windowRef = documentRef.defaultView;
if (!windowRef) {
return null;
}
const purifier = createDOMPurify(windowRef);
if (!purifier.isSupported) {
return null;
}
purifierByDocument.set(documentRef, purifier);
return purifier;
};
const isAllowedCssUrl = (value) => {
const target = value
.trim()
.replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/, '$1$2')
.trim();
if (/^#[A-Za-z0-9_.:-]+$/.test(target)) {
return true;
}
return /^data:image\/[A-Za-z0-9.+-]+;base64,[A-Za-z0-9+/=\s]+$/i.test(target);
};
const isSafeEmbeddedResourceUrl = (value, allowFragment) => {
const normalized = value.trim();
if (allowFragment && /^#[A-Za-z0-9_.:-]+$/.test(normalized)) {
return true;
}
if (/^blob:/i.test(normalized)) {
return true;
}
return /^data:image\/[A-Za-z0-9.+-]+;base64,[A-Za-z0-9+/=\s]+$/i.test(normalized);
};
const isSafeCssValue = (value) => {
const normalized = value.replace(/\/\*[\s\S]*?\*\//g, '').trim();
if (/(?:expression|image(?:-set)?|paint|var)\s*\(/i.test(normalized) ||
/(?:javascript|vbscript)\s*:/i.test(normalized) ||
/(?:^|[^-])behavior\s*:/i.test(normalized) ||
/-moz-binding/i.test(normalized) ||
normalized.includes('\\')) {
return false;
}
let unsafeUrl = false;
const withoutUrls = normalized.replace(/url\(\s*([^)]*?)\s*\)/gi, (_match, target) => {
if (!isAllowedCssUrl(target)) {
unsafeUrl = true;
}
return '';
});
return !unsafeUrl && !/url\s*\(/i.test(withoutUrls);
};
const sanitizeStyleDeclaration = (documentRef, cssText) => {
const probe = documentRef.createElement('span');
probe.setAttribute('style', cssText);
const style = probe.style;
const propertyNames = Array.from({ length: style.length }, (_, index) => style.item(index));
for (const propertyName of propertyNames) {
const value = style.getPropertyValue(propertyName);
if (!propertyName ||
propertyName.startsWith('--') ||
propertyName === 'behavior' ||
propertyName === '-moz-binding' ||
(propertyName === 'position' && /^(?:fixed|sticky)$/i.test(value.trim())) ||
!isSafeCssValue(value)) {
style.removeProperty(propertyName);
}
}
return style.cssText;
};
const sanitizeStyleAttributes = (documentRef, root) => {
root.querySelectorAll('[style]').forEach((element) => {
const sanitized = sanitizeStyleDeclaration(documentRef, element.getAttribute('style') || '');
if (sanitized) {
element.setAttribute('style', sanitized);
}
else {
element.removeAttribute('style');
}
});
};
const sanitizeEmbeddedResourceUrls = (root) => {
root
.querySelectorAll('[src],[srcset],[poster],[href],[xlink\\:href]')
.forEach((element) => {
for (const attributeName of ['src', 'srcset', 'poster', 'href', 'xlink:href']) {
if (!element.hasAttribute(attributeName)) {
continue;
}
const isHtmlAnchor = element.namespaceURI === 'http://www.w3.org/1999/xhtml' && element.localName === 'a';
if (isHtmlAnchor && attributeName === 'href') {
continue;
}
const allowFragment = element.namespaceURI === 'http://www.w3.org/2000/svg' &&
(attributeName === 'href' || attributeName === 'xlink:href');
if (!isSafeEmbeddedResourceUrl(element.getAttribute(attributeName) || '', allowFragment)) {
element.removeAttribute(attributeName);
}
}
});
};
const sanitizeSvgUrlAttributes = (root) => {
root.querySelectorAll('svg, svg *').forEach((element) => {
for (const attribute of Array.from(element.attributes)) {
const value = attribute.value;
if (/url\s*\(/i.test(value) && !isSafeCssValue(value)) {
element.removeAttribute(attribute.name);
}
}
});
};
export const sanitizePptxCss = (documentRef, cssText) => {
const rules = [];
let offset = 0;
while (offset < cssText.length) {
if (!cssText.slice(offset).trim()) {
break;
}
GENERATED_STYLE_RULE.lastIndex = offset;
const match = GENERATED_STYLE_RULE.exec(cssText);
if (!match) {
return '';
}
const declarations = sanitizeStyleDeclaration(documentRef, match[2]);
if (declarations) {
rules.push(`${PPTX_CONTENT_STYLE_SCOPE} ${match[1]}{${declarations}}`);
}
offset = GENERATED_STYLE_RULE.lastIndex;
}
return rules.join('\n');
};
export const sanitizePptxMarkup = (documentRef, html) => {
const purifier = getPurifier(documentRef);
if (!purifier) {
return createSafeFallbackFragment(documentRef);
}
const fragment = purifier.sanitize(html, {
RETURN_DOM_FRAGMENT: true,
USE_PROFILES: { html: true, svg: true, svgFilters: true },
ADD_ATTR: ['target'],
FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', 'form'],
FORBID_ATTR: ['srcdoc']
});
sanitizeStyleAttributes(documentRef, fragment);
sanitizeEmbeddedResourceUrls(fragment);
sanitizeSvgUrlAttributes(fragment);
fragment.querySelectorAll('a[target]').forEach((anchor) => {
if ((anchor.getAttribute('target') || '').trim().toLowerCase() === '_blank') {
anchor.rel = 'noopener noreferrer';
}
});
return fragment;
};
+8
-7
import { renderPptxPostProcessing } from './chart.js';
import { resolvePptxEngineOptions, RECOMMENDED_ZIP_LIMITS } from './options.js';
import { ensurePptxViewerStyles, scopePptxContentStyleText } from './styles.js';
import { sanitizePptxCss, sanitizePptxMarkup } from './sanitize.js';
import { ensurePptxViewerStyles } from './styles.js';
import { createPptxWorker } from './worker.js';

@@ -48,6 +49,5 @@ const clamp = (value, min, max) => {

const appendHtml = (container, html) => {
const template = container.ownerDocument.createElement('template');
template.innerHTML = html;
const nodes = Array.from(template.content.children);
container.append(template.content);
const fragment = sanitizePptxMarkup(container.ownerDocument, html);
const nodes = Array.from(fragment.children);
container.append(fragment);
return nodes[0] || null;

@@ -261,7 +261,8 @@ };

appendGlobalCss(css) {
if (!css) {
const sanitized = sanitizePptxCss(this.target.ownerDocument, css);
if (!sanitized) {
return;
}
const style = this.target.ownerDocument.createElement('style');
style.textContent = scopePptxContentStyleText(css);
style.textContent = sanitized;
this.content.append(style);

@@ -268,0 +269,0 @@ }

{
"name": "@file-viewer/pptx",
"version": "2.2.8",
"version": "2.2.9",
"private": false,

@@ -66,2 +66,3 @@ "type": "module",

"dingbat-to-unicode": "^1.0.1",
"dompurify": "^3.4.13",
"jszip": "^3.10.1",

@@ -68,0 +69,0 @@ "tinycolor2": "^1.6.0",

Sorry, the diff of this file is too big to display