pptx-angular-viewer 1.13.0 → 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ All notable changes to this project are documented here.
4
4
  This file is generated from [Conventional Commits](https://www.conventionalcommits.org)
5
5
  by [git-cliff](https://git-cliff.org); do not edit it by hand.
6
6
 
7
+ ## [1.13.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.13.0) - 2026-07-09
8
+
9
+ ### Other
10
+
11
+ - Reconcile with origin/main before push (by @ChristopherVR) ([ef5fc85](https://github.com/ChristopherVR/pptx-viewer/commit/ef5fc85dca2e20ff3e105d622594e0f65d010fb0))
12
+ - Reconcile with origin/main before push (by @ChristopherVR) ([030b28b](https://github.com/ChristopherVR/pptx-viewer/commit/030b28bb21697ed681e4e59aa40db29f4b4a18d0))
13
+
7
14
  ## [1.12.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.12.0) - 2026-07-09
8
15
 
9
16
  ### Features
@@ -34036,20 +34036,47 @@ function sanitizeCssDeclaration(value) {
34036
34036
  * JS callers, so a runtime check keeps the value that reaches `@page` /
34037
34037
  * `<style>` interpolation confined to those two known-safe strings.
34038
34038
  */
34039
- function sanitizeOrientation(value) {
34039
+ function sanitizeOrientation$1(value) {
34040
34040
  return value === 'portrait' ? 'portrait' : 'landscape';
34041
34041
  }
34042
+ /** Element/attribute shapes that are never legitimate in a rendered slide SVG. */
34043
+ const UNSAFE_SVG_SUBSTRINGS = [
34044
+ '</section',
34045
+ '<script',
34046
+ '<foreignobject',
34047
+ '<iframe',
34048
+ '<embed',
34049
+ '<object',
34050
+ // eslint-disable-next-line no-script-url -- security deny-list entry: verifies the scheme is rejected, never executed.
34051
+ 'javascript:',
34052
+ ];
34053
+ /** Matches an `on<event>=` handler attribute, e.g. `onload=`, `onclick=`. */
34054
+ const EVENT_HANDLER_ATTR_RE$1 = /\son\w+\s*=/iu;
34042
34055
  /**
34043
34056
  * Guard against a malformed or tampered per-slide SVG string breaking out
34044
34057
  * of the `<section>` wrapper it's embedded in (unescaped) by
34045
- * {@link buildPrintDocument}. Legitimate SVG produced by this library's own
34046
- * `SvgExporter` render path is well-formed markup that never contains a
34047
- * `</section` or `<script` substring, so this only ever rejects input that
34048
- * has been corrupted or crafted to inject sibling markup.
34058
+ * {@link buildPrintDocument}.
34059
+ *
34060
+ * This is deliberately an allow-list first, deny-list second check, not
34061
+ * just a deny-list: legitimate SVG produced by this library's own
34062
+ * `SvgExporter` render path is always a single well-formed `<svg>...</svg>`
34063
+ * root element, so anything that doesn't structurally look like that is
34064
+ * rejected outright. The deny-list then additionally screens for common
34065
+ * SVG-based script-injection vectors (`<script>`, `<foreignObject>` (which
34066
+ * can embed arbitrary HTML), `on*=` event handler attributes, `javascript:`
34067
+ * URIs) so a crafted-but-still-`<svg>...</svg>`-shaped payload is also
34068
+ * rejected.
34049
34069
  */
34050
34070
  function isSafeSvgMarkup(svg) {
34051
- const lower = svg.toLowerCase();
34052
- return !lower.includes('</section') && !lower.includes('<script');
34071
+ const trimmed = svg.trim();
34072
+ if (!trimmed.toLowerCase().startsWith('<svg') || !trimmed.toLowerCase().endsWith('</svg>')) {
34073
+ return false;
34074
+ }
34075
+ const lower = trimmed.toLowerCase();
34076
+ if (UNSAFE_SVG_SUBSTRINGS.some((needle) => lower.includes(needle))) {
34077
+ return false;
34078
+ }
34079
+ return !EVENT_HANDLER_ATTR_RE$1.test(trimmed);
34053
34080
  }
34054
34081
  /* ------------------------------------------------------------------ */
34055
34082
  /* Print stylesheet / document construction */
@@ -34091,7 +34118,7 @@ function buildPrintStyleSheet(width, height, customCss) {
34091
34118
  */
34092
34119
  function buildPrintDocument(svgs, width, height, options = {}) {
34093
34120
  const { title = 'Print', orientation = 'landscape', colorFilter = '' } = options;
34094
- const safeOrientation = sanitizeOrientation(orientation);
34121
+ const safeOrientation = sanitizeOrientation$1(orientation);
34095
34122
  const safeColorFilter = sanitizeCssDeclaration(colorFilter);
34096
34123
  const slidePages = svgs
34097
34124
  .map((svg, i) => {
@@ -35558,12 +35585,51 @@ function buildHandoutsHtml(slideImages, slideIndices, slidesPerPage) {
35558
35585
  }
35559
35586
  return pages.join('');
35560
35587
  }
35588
+ /**
35589
+ * `orientation` is typed as a union at compile time, but `buildPrintHtmlDocument`
35590
+ * is a public export reachable from plain JS callers, so a runtime check keeps
35591
+ * the value that reaches `@page` / `<style>` interpolation confined to those
35592
+ * two known-safe strings.
35593
+ */
35594
+ function sanitizeOrientation(value) {
35595
+ return value === 'portrait' ? 'portrait' : 'landscape';
35596
+ }
35597
+ /** Element/attribute shapes that must never appear in assembled print-window body HTML. */
35598
+ const UNSAFE_BODY_HTML_SUBSTRINGS = [
35599
+ '<script',
35600
+ '<iframe',
35601
+ '<embed',
35602
+ '<object',
35603
+ '<foreignobject',
35604
+ // eslint-disable-next-line no-script-url -- security deny-list entry: verifies the scheme is rejected, never executed.
35605
+ 'javascript:',
35606
+ ];
35607
+ /** Matches an `on<event>=` handler attribute, e.g. `onload=`, `onclick=`. */
35608
+ const EVENT_HANDLER_ATTR_RE = /\son\w+\s*=/iu;
35609
+ /**
35610
+ * Defense-in-depth guard for the assembled print-window body HTML. Every
35611
+ * dynamic value the `build*Html` helpers above embed (titles, notes, image
35612
+ * `src`) is already escaped via {@link escapeHtml} / {@link safeDataImageSrc}
35613
+ * before it's spliced into `bodyHtml`; this additionally screens the
35614
+ * assembled fragment for script-injection shapes, so a future caller that
35615
+ * forgets to escape a new field doesn't reach the printed window unnoticed.
35616
+ */
35617
+ function isSafePrintBodyHtml(html) {
35618
+ const lower = html.toLowerCase();
35619
+ if (UNSAFE_BODY_HTML_SUBSTRINGS.some((needle) => lower.includes(needle))) {
35620
+ return false;
35621
+ }
35622
+ return !EVENT_HANDLER_ATTR_RE.test(html);
35623
+ }
35561
35624
  /**
35562
35625
  * Assemble the complete printable HTML document string (doctype + head with
35563
35626
  * print CSS + body). Pure: the caller writes it into a print window.
35564
35627
  */
35565
35628
  function buildPrintHtmlDocument(options) {
35566
- const { title, bodyHtml, orientation, colorFilter, frameSlides } = options;
35629
+ const { title, bodyHtml, frameSlides } = options;
35630
+ const orientation = sanitizeOrientation(options.orientation);
35631
+ const colorFilter = options.colorFilter;
35632
+ const safeBodyHtml = isSafePrintBodyHtml(bodyHtml) ? bodyHtml : '';
35567
35633
  const frameStyle = frameSlides
35568
35634
  ? 'img.slide-img, .notes-slide, .handout-cell img { border: 2px solid #000 !important; }'
35569
35635
  : '';
@@ -35611,7 +35677,7 @@ function buildPrintHtmlDocument(options) {
35611
35677
  ${frameStyle}
35612
35678
  </style>
35613
35679
  </head>
35614
- <body>${bodyHtml}</body>
35680
+ <body>${safeBodyHtml}</body>
35615
35681
  </html>`;
35616
35682
  }
35617
35683