Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SlideFence

SlideFence is a local-first, non-executing PowerPoint OOXML review tool for serious change-control workflows. It profiles .pptx, .pptm, .potx, .potm, .ppsx, and .ppsm packages; compares two revisions; and applies a small policy-as-code gate in CI.

It focuses on stored, non-visual interaction surfaces a pixel or text diff can miss: DrawingML click, hover, and mouse-over action declarations; their relationship bindings; external package relationships; VBA-project presence; and embedded OLE/package/control evidence.

It does not open PowerPoint, render slides, start a slide show, follow a link, run a macro or program, load an object, inspect macro bytes, upload a deck, or send a network request.

Why this exists

Presentation review remains a real production workflow, but Microsoft is retiring PowerPoint's Windows Compare and Merge feature from Microsoft 365 version 2502; it is also unavailable in current PowerPoint for Mac and the web. Microsoft's support guidance documents both the existing review workflow and the retirement.

At the same time, a presentation can retain click and hover behavior without a visible text change. PowerPoint's stored action vocabulary includes links to external files and presentations, program launch, and macro invocation; the Microsoft OOXML interoperability note defines those action values. A normal external hyperlink is relationship-bound too, as Microsoft's Open XML SDK example shows.

SlideFence is not a visual compare replacement. It makes these stored package boundaries explicit, repeatable, and usable in an offline CI gate.

Quick start

git clone https://github.com/SybilGambleyyu/slidefence.git
cd slidefence
python -m venv .venv
.venv/bin/python -m pip install -e '.[dev]'

# Safe, aggregate profile. It has no slide text, URLs, macro names, or shape IDs.
slidefence profile approved.pptx --format markdown

# Compare two revisions without opening either presentation in an Office client.
slidefence diff approved.pptx candidate.pptx --format json --output report.json

# Start with a conservative baseline policy, then enforce it in CI.
slidefence init slidefence.yml
slidefence check approved.pptx candidate.pptx --policy slidefence.yml --format markdown

profile and diff exit 0 on a successful static review. check exits 1 when an enabled policy rule finds a violation and 2 for malformed, unsupported, or safety-bounded input.

What SlideFence observes

Public output is intentionally aggregate-only. It can report, for example:

  • total stored a:hlinkClick, a:hlinkHover, and a:hlinkMouseOver actions;
  • count-level action classes: macro, program, external file, external presentation, OLE verb, media, internal slide, custom show, slide-show navigation, unknown reserved, other, or unspecified;
  • counts of action references with no relationship, a missing relationship, an internal relationship, an external relationship, and external relationship subtype;
  • package-wide external relationship classes: hyperlink, image, media, OLE, package, control, or other;
  • VBA project-part presence and macro-enabled main-part count; and
  • OLE/package/control relationship and conventional ppt/embeddings/ and ppt/activeX/ payload counts.

For an action that contains a private macro name, program target, file path, or URL, SlideFence records the full stored state only in an in-memory SHA-256 signature. That lets it flag an identity rewrite even when the public counts are unchanged, without making the value a new disclosure channel.

What public output never contains

SlideFence deliberately excludes slide text, notes, shape names and IDs, relationship IDs, relationship targets and URLs, tooltips, action strings, macro names, macro bytes, OLE bytes, ActiveX bytes, file paths, and source paths. It also does not emit a source-file hash.

This contract is designed for review artifacts that can be attached to a CI run without automatically disclosing a deck's operating details.

Policy

slidefence.yml uses a deliberately small, strict YAML subset. It requires version: 1, a rules: block, two-space indentation, and literal true or false values. Unknown rules, duplicate rules, tabs, anchors, arrays, and general YAML features are rejected rather than interpreted differently by a CI runner.

version: 1
rules:
  require_no_macro_payload: true
  require_no_presentation_macro_actions: true
  require_no_presentation_program_actions: true
  require_no_presentation_external_file_actions: true
  require_no_presentation_external_presentation_actions: true
  require_no_presentation_ole_verb_actions: true
  require_no_presentation_media_actions: true
  require_no_external_relationships: false
  require_no_embedded_objects: false
  no_macro_payload_changes: true
  no_presentation_interaction_changes: true
  no_external_relationship_changes: true
  no_embedded_object_changes: true

The generated starter policy only protects changes to macro payloads, interaction actions, and external relationships. That is intentional: an established deck may legitimately retain a hyperlink or an embedded object, and a baseline-change gate is often safer than silently banning known state. See the policy reference for exact behavior.

Safety boundary

SlideFence accepts only regular, non-symlink source files with supported extensions. Before parsing package content it rejects encrypted archives, symbolic-link members, duplicate or traversal member paths, overly large sources/members/expanded archives, and extreme compression ratios. XML parts have independent byte, element-count, and nesting limits; DTD and entity declarations are rejected. It parses package data only and does not invoke an Office client.

The defaults are conservative: 128 MiB source, 4,096 members, 64 MiB per member, 512 MiB expanded total, 1,000:1 compression ratio, 16 MiB XML, 200,000 XML elements, and depth 256. PackageLimits is available to Python callers who need a tighter deployment boundary.

Scope and non-goals

The tool currently supports the bounded interaction and package inventory surface above. It is not proof that an action will be enabled, resolved, accepted by a client, run, open a target, or affect a viewer. It does not evaluate animation timing, transitions, formulas, slide rendering, visual layout, hyperlinks represented outside its DrawingML action scope, VBA code, embedded payloads, signatures, permissions, or application policy.

The action scan deliberately walks XML parts under ppt/ and recognizes only DrawingML a:hlinkClick, a:hlinkHover, and a:hlinkMouseOver markup. It does not claim every presentation behavior is represented by those elements.

Microsoft notes that Office Trusted Locations can enable active content, including hyperlinks, links to data sources and media, ActiveX controls, and VBA macros; that makes static review useful but not a substitute for the organization's client policy. See Microsoft's Trusted Locations guidance.

Development

.venv/bin/python -m pytest
.venv/bin/python -m ruff check .
.venv/bin/python -m ruff format --check .
.venv/bin/python -m build
.venv/bin/python -m twine check dist/*

The tests construct their own minimal PresentationML packages. They include same-count private-action rewrites, private target rewrites, macro and embedded payload changes, DTD rejection, ZIP compression limits, symlink rejection, atomic report publication, and strict policy parsing.

License

MIT. See LICENSE.

About

Local-first PowerPoint OOXML change assurance for CI and review workflows

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages