PluginProbe
The Innovative Form Builder – IvyForms / 0.5
The Innovative Form Builder – IvyForms v0.5
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / documentation / SAFE_HTML_COMPONENT.md

SAFE_HTML_COMPONENT.md in The Innovative Form Builder – IvyForms 0.5, at frontend/documentation/SAFE_HTML_COMPONENT.md

333 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 # SafeHTML Component - Double-Layer Sanitization
2
3 ## Overview
4
5 The `SafeHTML` component provides **double-layer security** for rendering HTML from the server:
6 1. **Backend Sanitization** (PHP) - Using WordPress's `esc_attr()` and `wp_kses_post()`
7 2. **Frontend Sanitization** (Vue + DOMPurify) - Client-side validation and sanitization
8
9 ## Architecture
10
11 ### Security Layers
12
13 ```
14 ┌─────────────────────────────────────────────────────────────┐
15 │ Layer 1: Backend (PHP) │
16 │ │
17 │ SignatureEntryHooks.php: │
18 │ └─ formatSignatureValue() │
19 │ └─ Returns: <img src="<?= esc_attr($value) ?>" /> │
20 │ │
21 │ Result: Server-sanitized HTML │
22 └─────────────────────────────────────────────────────────────┘
23 │
24 ▼
25 ┌─────────────────────────────────────────────────────────────┐
26 │ Layer 2: Frontend (Vue + DOMPurify) │
27 │ │
28 │ SafeHTML.vue: │
29 │ └─ DOMPurify.sanitize(html, config) │
30 │ ├─ Field-specific allowed tags │
31 │ ├─ Field-specific allowed attributes │
32 │ └─ URI validation (data URLs, https, etc.) │
33 │ │
34 │ Result: Double-sanitized, field-specific HTML │
35 └─────────────────────────────────────────────────────────────┘
36 ```
37
38 ## Usage
39
40 ### In Entry Display
41
42 ```vue
43 <template>
44 <tbody>
45 <tr v-for="field in filteredFields" :key="field.id">
46 <td>{{ getFieldLabel(field) }}</td>
47
48 <!-- HTML fields with SafeHTML -->
49 <td v-if="shouldRenderAsHtml(field)">
50 <SafeHTML
51 :html="getFieldDisplayValue(field)"
52 :field-type="getFormField(field)?.type"
53 />
54 </td>
55
56 <!-- Regular text fields -->
57 <td v-else>{{ getFieldDisplayValue(field) }}</td>
58 </tr>
59 </tbody>
60 </template>
61
62 <script setup>
63 import SafeHTML from '@/views/_components/SafeHTML.vue'
64 </script>
65 ```
66
67 ## Component API
68
69 ### Props
70
71 ```typescript
72 interface Props {
73 // The HTML string to sanitize and render (required)
74 html: string
75
76 // Field type for field-specific sanitization rules (optional)
77 fieldType?: string // 'signature', 'rich-text', 'chart', etc.
78
79 // Custom allowed HTML tags (optional, overrides field-type rules)
80 allowedTags?: string[]
81
82 // Custom allowed attributes (optional, overrides field-type rules)
83 allowedAttributes?: Record<string, string[]>
84 }
85 ```
86
87 ### Field-Specific Configurations
88
89 #### Signature Field
90
91 ```typescript
92 fieldType: 'signature'
93
94 DOMPurify Config:
95 - ALLOWED_TAGS: ['img']
96 - ALLOWED_ATTR: ['src', 'alt', 'style', 'class', 'width', 'height']
97 - ALLOWED_URI_REGEXP: /^data:image\/(png|jpeg|jpg|gif|svg\+xml);base64,/i
98
99 Additional Validation:
100 - Ensures src contains 'data:image'
101 - Blocks non-image data URLs
102 ```
103
104 #### Rich Text Field
105
106 ```typescript
107 fieldType: 'rich-text'
108
109 DOMPurify Config:
110 - ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'i', 'u', 'a', 'ul', 'ol', 'li',
111 'h1'-'h6', 'blockquote', 'code', 'pre', 'span', 'div']
112 - ALLOWED_ATTR: ['href', 'target', 'rel', 'class', 'style']
113 - Links validated for safe protocols (http, https, mailto)
114 ```
115
116 #### Chart/Visualization
117
118 ```typescript
119 fieldType: 'chart' | 'visualization'
120
121 DOMPurify Config:
122 - ALLOWED_TAGS: ['div', 'svg', 'canvas', 'img']
123 - ALLOWED_ATTR: ['class', 'style', 'width', 'height', 'viewBox', 'src', 'alt', 'data-*']
124 ```
125
126 #### Default (Unknown Types)
127
128 ```typescript
129 fieldType: undefined or unknown
130
131 DOMPurify Config:
132 - ALLOWED_TAGS: ['span', 'div'] // Very restrictive
133 - ALLOWED_ATTR: ['class']
134 ```
135
136 ## Custom Configuration
137
138 ### Example: Custom Allowed Tags
139
140 ```vue
141 <SafeHTML
142 :html="customHTML"
143 field-type="custom"
144 :allowed-tags="['div', 'p', 'img', 'a']"
145 :allowed-attributes="{
146 img: ['src', 'alt'],
147 a: ['href', 'target']
148 }"
149 />
150 ```
151
152 ## Security Features
153
154 ### 1. **Field-Type Based Rules**
155 Each field type has its own whitelist of allowed tags and attributes.
156
157 ### 2. **URI Validation**
158 - Data URLs: Only `data:image/*` for signature fields
159 - External URLs: Only `http`, `https`, `mailto`, `tel`
160 - Blocks: `javascript:`, `data:text/html`, `vbscript:`, etc.
161
162 ### 3. **Attribute Filtering**
163 Only explicitly allowed attributes pass through. Dangerous attributes like `onclick`, `onerror`, `onload` are automatically blocked.
164
165 ### 4. **XSS Protection**
166 DOMPurify removes:
167 - `<script>` tags
168 - Event handlers (`onclick`, `onerror`, etc.)
169 - `javascript:` URIs
170 - Form elements (`<form>`, `<input>`) unless explicitly allowed
171 - Iframes unless explicitly allowed
172
173 ### 5. **Error Handling**
174 If sanitization fails, returns empty string and logs error to console.
175
176 ## Performance
177
178 ### Optimization Strategies
179
180 1. **Lazy Rendering**: SafeHTML only renders when the container is mounted
181 2. **Watch Optimization**: Only re-sanitizes when `html` prop changes
182 3. **Config Caching**: DOMPurify config is determined once per render
183
184 ### Bundle Size
185
186 - **DOMPurify**: ~30KB minified + gzipped
187 - **SafeHTML Component**: ~2KB
188 - **Total Overhead**: ~32KB for XSS protection
189
190 ## Example Outputs
191
192 ### Signature Field
193
194 **Input:**
195 ```html
196 <img src="data:image/png;base64,iVBORw0KGgo..." alt="Signature" style="max-width: 100%;" />
197 ```
198
199 **After SafeHTML:**
200 ```html
201 <img src="data:image/png;base64,iVBORw0KGgo..." alt="Signature" style="max-width: 100%;">
202 ```
203
204 ### Malicious Input (Signature Field)
205
206 **Input:**
207 ```html
208 <img src="data:image/png;base64,..." onerror="alert('xss')" />
209 <script>alert('xss')</script>
210 ```
211
212 **After SafeHTML:**
213 ```html
214 <img src="data:image/png;base64,..." alt="">
215 <!-- script tag completely removed -->
216 ```
217
218 ### Rich Text Field
219
220 **Input:**
221 ```html
222 <p>Hello <strong>world</strong>!</p>
223 <a href="javascript:alert('xss')">Click me</a>
224 ```
225
226 **After SafeHTML:**
227 ```html
228 <p>Hello <strong>world</strong>!</p>
229 <a>Click me</a> <!-- href removed because javascript: protocol -->
230 ```
231
232 ## Testing
233
234 ### Security Test Cases
235
236 ```typescript
237 // Test 1: Script injection
238 const malicious1 = '<img src=x onerror="alert(1)">'
239 // Expected: <img src="x"> (onerror removed)
240
241 // Test 2: JavaScript protocol
242 const malicious2 = '<a href="javascript:alert(1)">Click</a>'
243 // Expected: <a>Click</a> (href removed)
244
245 // Test 3: Data URL for non-signature
246 const malicious3 = '<img src="data:text/html,<script>alert(1)</script>">'
247 // Expected: Empty or <img> without src (depends on field type)
248
249 // Test 4: Valid signature
250 const valid = '<img src="data:image/png;base64,iVBORw..." alt="Signature">'
251 // Expected: Same output (valid)
252 ```
253
254 ## Integration with Pro Plans
255
256 ### Adding New HTML Field Types
257
258 1. **Backend**: Create entry hooks in `{Plan}/Entries/{FieldType}EntryHooks.php`
259 2. **Frontend Hook**: Register in `plans/{plan}/hooks/entryHtmlFieldsHooks.ts`
260 3. **SafeHTML Config**: Add field-type case in `getDOMPurifyConfig()`
261
262 Example for a new "video" field:
263
264 ```typescript
265 // SafeHTML.vue
266 case 'video':
267 return {
268 ...baseConfig,
269 ALLOWED_TAGS: ['video', 'source'],
270 ALLOWED_ATTR: ['src', 'type', 'controls', 'width', 'height', 'poster'],
271 ALLOWED_URI_REGEXP: /^https:\/\//i, // Only HTTPS videos
272 }
273 ```
274
275 ## Benefits Over v-html
276
277 | Feature | `v-html` | `SafeHTML` |
278 |---------|----------|------------|
279 | Backend Sanitization | �
280 | �
281 |
282 | Frontend Sanitization | ❌ | �
283 |
284 | Field-Specific Rules | ❌ | �
285 |
286 | XSS Protection | ⚠️ (Backend only) | �
287 �
288 (Double layer) |
289 | Custom Configurations | ❌ | �
290 |
291 | Error Handling | ❌ | �
292 |
293 | Bundle Size | 0KB | +32KB |
294
295 ## Migration from v-html
296
297 ```vue
298 <!-- Before: Simple v-html -->
299 <td v-html="getFieldDisplayValue(field)"></td>
300
301 <!-- After: SafeHTML component -->
302 <td>
303 <SafeHTML
304 :html="getFieldDisplayValue(field)"
305 :field-type="getFormField(field)?.type"
306 />
307 </td>
308 ```
309
310 ## Dependencies
311
312 ```json
313 {
314 "dependencies": {
315 "dompurify": "^3.0.0"
316 },
317 "devDependencies": {
318 "@types/dompurify": "^3.0.0"
319 }
320 }
321 ```
322
323 ## Files
324
325 - **Component**: `ivyforms/frontend/src/views/_components/SafeHTML.vue`
326 - **Usage**: `ivyforms/frontend/src/views/admin/entries/entry-page/section/EntryPageSectionContent.vue`
327 - **Backend**: `ivyforms-pro/backend/src/{Plan}/Entries/*EntryHooks.php`
328 - **Frontend Hooks**: `ivyforms-pro/frontend/src/plans/{plan}/hooks/entryHtmlFieldsHooks.ts`
329
330 ## Conclusion
331
332 The `SafeHTML` component provides enterprise-grade security for rendering dynamic HTML in WordPress forms, with double-layer sanitization, field-specific rules, and comprehensive XSS protection.
333