Security
- Add looks_like_secret() entropy heuristic: vendor regex (AIza, sk-,
ghp_, gho_, Slack xox, Bearer) + length/char-class fallback +
path/whitespace denylist. Defensible hybrid: zero false-positives
on known token formats, catches custom tokens without tripping on
URLs or slugs.
- Gate generic 'key'-named fields and ?key= URL params with the
entropy heuristic. Closes the n8n queryParameters Google-API-key
bypass without false-positives on benign values.
- Entropy fallback in mask_name_value_pair for custom-header value
patterns (X-App-Token etc.) whose names we cannot enumerate.
- Redact credentials[].name per node (id retained), clear
meta.instanceId so exports no longer correlate to the source n8n
instance.
- Opt-in tag clearing at publish time: wizard step 3 checkbox with
the current tag list inline, only shown when tags exist.
- Wizard step 3 now renders a collapsible Reason / Key / Note table
so publishers can verify exactly what was masked before publishing.
Mobile
- touch-action: none on .breznflow-svg to stop the
browser-vs-plugin gesture tug-of-war.
- Rewrote pointer handling as a Map-based multi-pointer state
machine with { passive: false } listeners: single-finger pan is
now smooth on iOS and Android, pinch-to-zoom anchored at the
finger midpoint, double-tap toggles 100/200 % zoom.
- Minimap ported to pointer events + setPointerCapture — tap and
drag navigation work on touch.
Docs
- Expand Sensitive Data Masking section of both READMEs to describe
the 1.0.4 passes and the opt-in tag removal.
- Version badge 1.0.3 -> 1.0.4.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
366 lines
16 KiB
Markdown
366 lines
16 KiB
Markdown
# BreznFlow
|
||
|
||

|
||

|
||

|
||

|
||
|
||
🇩🇪 [Deutsche Version → README.de.md](README.de.md)
|
||
|
||
---
|
||
|
||
BreznFlow is a WordPress plugin that renders n8n automation workflows as interactive SVG diagrams — directly in posts and pages. Paste your workflow JSON, and the plugin turns it into a zoomable, clickable diagram with node detail panels, sensitive data masking, and brand-colored node icons.
|
||
|
||
No external dependencies. No CDN. No tracking. Vanilla JavaScript. One shortcode: `[breznflow id="X"]`.
|
||
|
||
---
|
||
|
||
## Why This Plugin Exists
|
||
|
||
n8n workflows are powerful, but sharing them visually is surprisingly hard. Screenshots are static and get outdated. Embedding the n8n editor is impractical. Copy-pasting JSON into a blog post is unreadable.
|
||
|
||
BreznFlow solves this by turning the raw JSON export into an interactive diagram — the same nodes, the same connections, but rendered as a clean SVG inside WordPress. Readers can zoom, pan, and click any node to inspect its parameters.
|
||
|
||
Built in Passau, Bavaria — for [mifupa.com](https://mifupa.com), a personal blog where n8n automations are documented regularly and needed a better way to be presented.
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
- [Why This Plugin Exists](#why-this-plugin-exists)
|
||
- [Directory Structure](#directory-structure)
|
||
- [Features](#features)
|
||
- [Data Storage](#data-storage)
|
||
- [Security](#security)
|
||
- [Shortcode](#shortcode)
|
||
- [Installation](#installation)
|
||
- [Tech Stack](#tech-stack)
|
||
- [License](#license)
|
||
|
||
---
|
||
|
||
## Directory Structure
|
||
|
||
```
|
||
breznflow/
|
||
├── breznflow.php # Plugin header, constants (BREZNFLOW_VERSION, BREZNFLOW_DIR, BREZNFLOW_URL)
|
||
├── uninstall.php # Cleanup on plugin deletion
|
||
├── readme.txt # WordPress.org plugin readme
|
||
├── assets/
|
||
│ ├── admin.css # Admin stylesheet (wizard, settings, list table)
|
||
│ ├── admin.js # Admin JavaScript (wizard steps, JSON validation)
|
||
│ ├── renderer.css # Frontend SVG renderer styles
|
||
│ ├── renderer.js # Frontend renderer (SVG engine, pan/zoom, detail panel)
|
||
│ ├── brezn.css # Legacy Brezn theme (compat)
|
||
│ └── themes/
|
||
│ ├── dark.css # Dark theme (default)
|
||
│ ├── light.css # Light theme
|
||
│ ├── minimal.css # Minimal theme
|
||
│ ├── tech.css # Tech theme
|
||
│ └── brezn.css # Brezn theme
|
||
├── includes/
|
||
│ ├── Core.php # Singleton bootstrap, loads all dependencies
|
||
│ ├── PostType.php # CPT breznflow_workflow + taxonomy breznflow_category
|
||
│ ├── Shortcode.php # [breznflow] shortcode with 13 attributes
|
||
│ ├── DownloadHandler.php # JSON download endpoint (?breznflow_download={id})
|
||
│ ├── EmbedHandler.php # Standalone embed page (?breznflow_embed={id})
|
||
│ ├── Admin/
|
||
│ │ ├── AdminMenu.php # Menu structure + dashboard render
|
||
│ │ ├── SettingsPage.php # Plugin settings (16 options, validated)
|
||
│ │ ├── ThemesPage.php # Theme management (import/delete custom themes)
|
||
│ │ ├── WizardPage.php # 3-step import wizard (paste/upload → configure → preview)
|
||
│ │ ├── WorkflowListTable.php # WP_List_Table for workflow management
|
||
│ │ └── views/ # PHP templates for all admin pages
|
||
│ ├── Features/
|
||
│ │ ├── NodeTypeRegistry.php # 86 node types with brand colors and icons
|
||
│ │ ├── NodeCategorizer.php # Categorizes nodes (trigger, action, logic, AI, etc.)
|
||
│ │ ├── InfoBoxBuilder.php # "3× HTTP Request, 2× Code" node summary
|
||
│ │ ├── ViewCounter.php # Per-workflow view counting
|
||
│ │ ├── RelatedWorkflows.php # Related workflows by shared node types
|
||
│ │ ├── ThemeRegistry.php # 5 built-in themes + custom theme support
|
||
│ │ └── ThemeImporter.php # Import/export .breznflow.json theme files
|
||
│ └── Security/
|
||
│ ├── MaskingRules.php # Secret detection patterns (URL params, headers, entropy)
|
||
│ ├── WorkflowValidator.php # JSON schema validation for n8n exports
|
||
│ └── WorkflowSanitizer.php # Two-pass sanitization: strings + secret masking
|
||
└── languages/
|
||
├── breznflow.pot # Translation template
|
||
├── breznflow-de_DE.po # German translation
|
||
└── breznflow-de_DE.mo # Compiled German translation
|
||
```
|
||
|
||
---
|
||
|
||
## Features
|
||
|
||
### Interactive SVG Renderer
|
||
|
||
The core of BreznFlow. Every n8n node becomes a clickable SVG element with brand-colored icons, connection lines, and labels. The renderer supports:
|
||
|
||
- **Pan & zoom** — mouse wheel zooms to cursor position, click-drag to pan
|
||
- **Node click** — opens the detail panel below the diagram with all node parameters
|
||
- **Auto-fit** — workflows exceeding a configurable node threshold (default: 30) automatically zoom to the trigger node on load
|
||
- **Minimap** — optional minimap overlay for navigation in large workflows
|
||
- **Fullscreen** — portal-based fullscreen mode
|
||
|
||
All rendering happens client-side in vanilla JavaScript — no canvas, no WebGL, no external libraries.
|
||
|
||
---
|
||
|
||
### 3-Step Import Wizard
|
||
|
||
1. **Paste or upload** — paste JSON directly, upload a `.json` file, or fetch from URL
|
||
2. **Configure** — set display mode, theme, zoom level, title, categories
|
||
3. **Preview** — live SVG preview with security masking summary before publishing
|
||
|
||
The wizard validates JSON against the n8n schema, sanitizes all strings, and masks detected secrets. The masking log shows exactly what was redacted and why.
|
||
|
||
**Import from URL:** Fetches workflow JSON from any public URL using `wp_remote_get()`. Requests to private/internal network addresses (localhost, LAN ranges, cloud metadata endpoints) are blocked.
|
||
|
||
---
|
||
|
||
### Node Type Registry
|
||
|
||
86 predefined node types with brand-accurate colors and 2-letter icons:
|
||
|
||
| Category | Examples |
|
||
|---|---|
|
||
| Triggers | Schedule, Webhook, Manual, Form |
|
||
| Core Logic | HTTP Request, Code, IF, Switch, Merge, Filter |
|
||
| Data Transformation | HTML, XML, Markdown, Crypto |
|
||
| Databases | MySQL, PostgreSQL, Redis, MongoDB, SQLite, Supabase |
|
||
| Communication | Slack, Telegram, Discord, Gmail, WhatsApp |
|
||
| Google | Sheets, Drive, Calendar, Docs, YouTube |
|
||
| Dev Tools | GitHub, GitLab, Jira, Confluence, Linear, Notion |
|
||
| AI | OpenAI, Claude, Gemini, Ollama, Hugging Face, Mistral, LangChain |
|
||
| Storage | FTP, SSH, Airtable, Baserow |
|
||
| CRM/Marketing | HubSpot, Salesforce, Mailchimp, Brevo |
|
||
|
||
Unknown node types get a deterministic fallback: 2-letter initials, and a color derived from a djb2 hash — so the same unknown type always looks the same.
|
||
|
||
---
|
||
|
||
### Theme System
|
||
|
||
5 built-in themes: **Dark** (default), **Light**, **Minimal**, **Tech**, **Brezn**.
|
||
|
||
Custom themes can be imported as `.breznflow.json` files containing 41 CSS color tokens. Custom themes are stored in `wp_options` and rendered as inline CSS variables.
|
||
|
||
Themes are selectable globally, per-workflow, or per-shortcode via `theme="dark"`.
|
||
|
||
---
|
||
|
||
### Action Bar
|
||
|
||
Below the diagram (non-compact mode), the action bar provides:
|
||
|
||
| Action | Control | What it does |
|
||
|---|---|---|
|
||
| **Share** | Global setting | Shows article link + anchor link for hash navigation |
|
||
| **Embed** | Global + per-post | Shows iframe embed code for standalone embedding |
|
||
| **Get JSON** | Global setting | Displays formatted JSON with size in KB |
|
||
| **Download** | Global + per-post | Downloads sanitized JSON file |
|
||
|
||
Each action can be toggled globally in settings and overridden per shortcode.
|
||
|
||
---
|
||
|
||
### Sensitive Data Masking
|
||
|
||
BreznFlow never stores raw workflow JSON. Before saving, a three-pass sanitization runs:
|
||
|
||
**Pass 1 — String sanitization:** All string values pass through `sanitize_text_field()`. Exception: `jsCode` fields are preserved as-is but displayed with `esc_html()` (never executed).
|
||
|
||
**Pass 2 — Secret detection:**
|
||
|
||
- **URL parameters:** `api_key`, `token`, `secret`, `password`, `access_token`, `auth`, `client_secret` in query strings → `[REDACTED]`
|
||
- **Generic `?key=` URL params** *(1.0.4)*: redacted only when the captured value matches `looks_like_secret()` — closes the Google API key bypass without false-positives on harmless values.
|
||
- **Header values:** Authorization, Bearer, X-API-Key and similar header names in `{name, value}` pairs → value masked
|
||
- **Value-entropy fallback** *(1.0.4)*: `{name, value}` pairs whose name does not match the allowlist are still masked when the value itself looks secret-shaped — covers custom headers (`X-App-Token`) and n8n's `queryParameters` generic-`key` pattern.
|
||
- **Known vendor tokens** *(1.0.4)*: `looks_like_secret()` matches `AIza…`, `sk-…`, `ghp_…`, `gho_…`, Slack `xox…`, `Bearer …` (JWT) — with a length+char-class entropy fallback and a path/whitespace denylist for false-positive control.
|
||
- **High-entropy conditions:** Values in IF/Switch conditions that match UUID patterns, mixed-case+digits, or long strings without spaces → masked via entropy heuristic
|
||
- **Credential display names** *(1.0.4)*: `credentials[].name` is replaced with `[REDACTED]` per node (the credential `id` is retained — it references the n8n DB and is useless without the server).
|
||
|
||
**Pass 3 — Identifying metadata** *(1.0.4)*: `meta.instanceId` is cleared so workflow exports cannot be correlated to the originating n8n instance.
|
||
|
||
**Optional — Tag removal** *(1.0.4)*: Wizard step 3 offers an opt-in checkbox to strip workflow tags. Tags are often innocuous (`production`, `v2`) but sometimes identifying — the publisher decides per workflow.
|
||
|
||
A **mask log** records every masked item with reason, key, and note. Step 3 shows it as a collapsible Reason / Key / Note table so the publisher can review exactly what will be published.
|
||
|
||
---
|
||
|
||
### Display Modes
|
||
|
||
| Mode | What's shown |
|
||
|---|---|
|
||
| `visual` | Full diagram with toolbar, detail panel, action bar |
|
||
| `info` | Node counts only (InfoBox) — no diagram |
|
||
| `compact` | Diagram without toolbar or action bar |
|
||
|
||
Configurable globally, per-workflow, or per-shortcode.
|
||
|
||
---
|
||
|
||
### Embed Handler
|
||
|
||
Serves a standalone HTML page at `?breznflow_embed={id}` for iframe embedding. The page contains only the SVG renderer — no WordPress theme, no admin bar.
|
||
|
||
**Dual-gate security:** Both the global `allow_embed` setting and the per-post `_breznflow_show_embed` meta must be enabled.
|
||
|
||
URL parameters: `?theme={id}` and `?minimap=0|1`.
|
||
|
||
HTTP headers include `X-Robots-Tag: noindex, nofollow` and remove `X-Frame-Options` to allow embedding.
|
||
|
||
---
|
||
|
||
### Additional Features
|
||
|
||
- **View Counter** — tracks how many times each workflow is displayed
|
||
- **Related Workflows** — shows similar workflows by shared node types
|
||
- **InfoBox** — compact summary like "3× HTTP Request, 2× Code, 1× OpenAI"
|
||
- **AI Detection** — automatically detects and badges workflows containing AI nodes
|
||
- **Schema.org HowTo** — optional JSON-LD structured data output
|
||
- **Anchor Navigation** — `<span id="breznflow-{id}">` for hash-based deep linking with 60px scroll offset
|
||
|
||
---
|
||
|
||
## Data Storage
|
||
|
||
### WordPress Options (wp_options)
|
||
|
||
| Option Key | Content |
|
||
|---|---|
|
||
| `breznflow_settings` | All plugin settings (16 keys, serialized array) |
|
||
| `breznflow_custom_themes` | Custom theme definitions (serialized array) |
|
||
|
||
### Post Meta (wp_postmeta)
|
||
|
||
| Meta Key | Content |
|
||
|---|---|
|
||
| `_breznflow_raw_json` | Sanitized workflow JSON (never the raw original) |
|
||
| `_breznflow_original_name` | Original workflow name from n8n |
|
||
| `_breznflow_node_count` | Total number of nodes |
|
||
| `_breznflow_node_summary` | Categorized node counts (JSON) |
|
||
| `_breznflow_has_ai_nodes` | Whether workflow contains AI nodes |
|
||
| `_breznflow_ai_node_types` | List of AI node types present (JSON) |
|
||
| `_breznflow_mask_log` | Masked values during import (JSON) |
|
||
| `_breznflow_default_zoom` | Per-workflow zoom level (10–200) |
|
||
| `_breznflow_show_title` | Show title |
|
||
| `_breznflow_show_infobox` | Show info box |
|
||
| `_breznflow_show_download` | Allow download |
|
||
| `_breznflow_show_embed` | Allow embedding |
|
||
| `_breznflow_show_minimap` | Show minimap |
|
||
| `_breznflow_default_mode` | Display mode |
|
||
| `_breznflow_default_theme` | Theme ID |
|
||
|
||
### Uninstall Cleanup
|
||
|
||
`uninstall.php` removes on plugin deletion:
|
||
- All `breznflow_workflow` posts and their meta
|
||
- Option `breznflow_settings`
|
||
- All `breznflow_category` taxonomy terms
|
||
- All transients with prefix `breznflow_`
|
||
|
||
---
|
||
|
||
## Security
|
||
|
||
### Input Validation
|
||
|
||
- Workflow JSON is validated against the n8n schema before import
|
||
- All `$_POST` / `$_GET` values processed via `wp_unslash()` + specific sanitizers
|
||
- All output escaped with `esc_html`, `esc_attr`, `esc_url`
|
||
- SQL queries exclusively via `$wpdb->prepare()`
|
||
|
||
### CSRF Protection
|
||
|
||
Every admin action validates a WordPress nonce. Capability checks require `manage_options` for settings and `edit_posts` for workflow management.
|
||
|
||
### Download & Embed Security
|
||
|
||
Both endpoints validate:
|
||
1. Post exists, is type `breznflow_workflow`, and has status `publish`
|
||
2. The corresponding per-post meta permission is enabled
|
||
3. The global setting is enabled
|
||
4. The stored JSON is valid
|
||
|
||
Response headers include `X-Content-Type-Options: nosniff`. Downloads use `Cache-Control: no-store`.
|
||
|
||
### Import from URL
|
||
|
||
The "Fetch from URL" feature in the wizard blocks requests to private/internal network addresses (localhost, `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, cloud metadata endpoints) to prevent SSRF attacks.
|
||
|
||
---
|
||
|
||
## Shortcode
|
||
|
||
```
|
||
[breznflow id="42"]
|
||
[breznflow id="42" mode="compact" theme="light" zoom="80"]
|
||
[breznflow id="42" show_share="0" show_embed="1" show_minimap="0"]
|
||
```
|
||
|
||
| Attribute | Default | Description |
|
||
|---|---|---|
|
||
| `id` | — | Workflow post ID (required) |
|
||
| `mode` | `visual` | `visual`, `info`, or `compact` |
|
||
| `theme` | `dark` | Theme ID |
|
||
| `zoom` | `100` | Initial zoom level (10–200) |
|
||
| `show_title` | `true` | Show workflow title |
|
||
| `show_infobox` | `true` | Show node summary box |
|
||
| `show_minimap` | `true` | Show minimap overlay |
|
||
| `show_download` | `false` | Show download button |
|
||
| `show_share` | `true` | Show share action |
|
||
| `show_embed` | `false` | Show embed action |
|
||
| `show_get_json` | `false` | Show "Get JSON" action |
|
||
| `max_code_lines` | `50` | Max lines in code node display |
|
||
|
||
**Resolution hierarchy:** Shortcode attribute → Post meta → Plugin settings.
|
||
|
||
---
|
||
|
||
## Installation
|
||
|
||
**Via GitHub Release (recommended):**
|
||
1. Download `breznflow.zip` from the [latest release](https://github.com/noschmarrn/breznflow/releases/latest)
|
||
2. In WordPress go to *Plugins → Add New → Upload Plugin*
|
||
|
||
**Manual (clone):**
|
||
```bash
|
||
cd /path/to/wordpress/wp-content/plugins/
|
||
git clone https://github.com/noschmarrn/breznflow.git
|
||
wp plugin activate breznflow
|
||
```
|
||
|
||
**After activation:**
|
||
1. Go to *BreznFlow → Add Workflow*
|
||
2. Paste your n8n workflow JSON (or upload a `.json` file)
|
||
3. Configure display settings and preview
|
||
4. Publish — use `[breznflow id="X"]` in any post or page
|
||
|
||
The plugin has no build step. All assets are direct JS/CSS files.
|
||
|
||
---
|
||
|
||
## Tech Stack
|
||
|
||
| Component | Technology |
|
||
|---|---|
|
||
| Backend | PHP 8.0+, WordPress Plugin API |
|
||
| Namespace | `BreznFlow\` |
|
||
| Architecture | Singleton core, feature classes with `register()` |
|
||
| Rendering | Vanilla JavaScript SVG generation (no canvas, no libraries) |
|
||
| Database | WordPress Options API + Post Meta |
|
||
| Caching | WordPress transients (related workflows) |
|
||
| Frontend | Vanilla JS, no build step, no external CDN |
|
||
| i18n | `.pot` file, text domain `breznflow` |
|
||
| Coding Standard | WordPress PHPCS |
|
||
| License | GPL-2.0-or-later |
|
||
|
||
---
|
||
|
||
## License
|
||
|
||
GPL-2.0-or-later — [https://www.gnu.org/licenses/gpl-2.0.html](https://www.gnu.org/licenses/gpl-2.0.html)
|
||
|
||
Copyright (c) 2025–2026 [mifupa.com](https://mifupa.com)
|