0
# URL Validation and Security
1
2
XSS protection utilities and URI validation for secure link handling. The validation system prevents malicious URLs while allowing legitimate protocols and custom validation logic.
3
4
## Capabilities
5
6
### URI Validation Function
7
8
Core URL validation function that checks if a URI is allowed based on protocol whitelist and validation rules.
9
10
```typescript { .api }
11
/**
12
* Validates if a URI is allowed based on protocol whitelist
13
* @param uri - URI to validate (can be undefined)
14
* @param protocols - Optional array of custom protocols to allow
15
* @returns True if URI is allowed, false otherwise
16
*/
17
function isAllowedUri(
18
uri: string | undefined,
19
protocols?: LinkOptions['protocols']
20
): boolean;
21
```
22
23
**Usage Examples:**
24
25
```typescript
26
import { isAllowedUri } from "@tiptap/extension-link";
27
28
// Basic validation
29
const isValid = isAllowedUri('https://example.com'); // true
30
const isValid2 = isAllowedUri('javascript:alert("xss")'); // false
31
32
// Custom protocols
33
const customProtocols = ['ftp', 'ssh', { scheme: 'git', optionalSlashes: true }];
34
const isValid3 = isAllowedUri('ftp://files.example.com', customProtocols); // true
35
const isValid4 = isAllowedUri('git:github.com/user/repo', customProtocols); // true
36
37
// Undefined/null handling
38
const isValid5 = isAllowedUri(undefined); // true (allows empty/null URIs)
39
const isValid6 = isAllowedUri(''); // true
40
```
41
42
### Default Allowed Protocols
43
44
The validation system includes a comprehensive list of safe protocols by default.
45
46
```typescript { .api }
47
/**
48
* Default allowed protocols for URI validation
49
*/
50
const allowedProtocols: string[] = [
51
'http',
52
'https',
53
'ftp',
54
'ftps',
55
'mailto',
56
'tel',
57
'callto',
58
'sms',
59
'cid',
60
'xmpp'
61
];
62
```
63
64
### Paste Detection Regex
65
66
Regular expression for detecting URLs in pasted content.
67
68
```typescript { .api }
69
/**
70
* Regular expression for detecting URLs in pasted content
71
* Matches http/https URLs with proper domain and path structure
72
*/
73
const pasteRegex: RegExp;
74
```
75
76
**Usage Examples:**
77
78
```typescript
79
import { pasteRegex } from "@tiptap/extension-link";
80
81
// Test if pasted content is a URL
82
const pastedText = 'https://tiptap.dev/docs';
83
const isUrl = pasteRegex.test(pastedText); // true
84
85
// Extract URLs from text
86
const text = 'Visit https://tiptap.dev and https://example.com for more info';
87
const urls = text.match(pasteRegex);
88
// ['https://tiptap.dev', 'https://example.com']
89
```
90
91
### Validation Context Interface
92
93
Context object provided to custom validation functions with helper utilities and configuration.
94
95
```typescript { .api }
96
interface ValidationContext {
97
/**
98
* The default validation function
99
* @param url - URL to validate
100
* @returns True if URL passes default validation
101
*/
102
defaultValidate: (url: string) => boolean;
103
104
/**
105
* Array of allowed protocols for the URL (e.g., "http", "https")
106
* As defined in the protocols option
107
*/
108
protocols: Array<LinkProtocolOptions | string>;
109
110
/**
111
* Default protocol (e.g., 'http')
112
* As defined in the defaultProtocol option
113
*/
114
defaultProtocol: string;
115
}
116
```
117
118
**Usage Examples:**
119
120
```typescript
121
import { Editor } from "@tiptap/core";
122
import { Link } from "@tiptap/extension-link";
123
124
const editor = new Editor({
125
extensions: [
126
Link.configure({
127
isAllowedUri: (url, { defaultValidate, protocols, defaultProtocol }) => {
128
// Allow relative URLs
129
if (url.startsWith('./') || url.startsWith('../')) {
130
return true;
131
}
132
133
// Allow localhost in development
134
if (process.env.NODE_ENV === 'development' && url.includes('localhost')) {
135
return true;
136
}
137
138
// Use default validation for everything else
139
return defaultValidate(url);
140
},
141
}),
142
],
143
});
144
```
145
146
### Custom Validation Functions
147
148
Advanced validation configurations for specific security requirements.
149
150
**Security-First Validation:**
151
152
```typescript
153
import { Editor } from "@tiptap/core";
154
import { Link } from "@tiptap/extension-link";
155
156
const editor = new Editor({
157
extensions: [
158
Link.configure({
159
isAllowedUri: (url, { defaultValidate, protocols, defaultProtocol }) => {
160
// Strict security: only allow specific domains
161
const allowedDomains = ['example.com', 'trusted-site.org'];
162
163
try {
164
const urlObj = new URL(url);
165
return allowedDomains.includes(urlObj.hostname);
166
} catch {
167
return false; // Invalid URLs are rejected
168
}
169
},
170
shouldAutoLink: (url) => {
171
// Don't auto-link internal URLs
172
return !url.includes('internal.company.com');
173
},
174
}),
175
],
176
});
177
```
178
179
**Development-Friendly Validation:**
180
181
```typescript
182
const editor = new Editor({
183
extensions: [
184
Link.configure({
185
isAllowedUri: (url, { defaultValidate, protocols, defaultProtocol }) => {
186
// Development: allow localhost and file protocols
187
if (process.env.NODE_ENV === 'development') {
188
if (url.startsWith('file://') || url.includes('localhost') || url.includes('127.0.0.1')) {
189
return true;
190
}
191
}
192
193
// Production: use strict validation
194
return defaultValidate(url);
195
},
196
}),
197
],
198
});
199
```
200
201
### Protocol Registration
202
203
The validation system works with linkifyjs protocol registration for custom schemes.
204
205
**Usage Examples:**
206
207
```typescript
208
import { Editor } from "@tiptap/core";
209
import { Link } from "@tiptap/extension-link";
210
211
const editor = new Editor({
212
extensions: [
213
Link.configure({
214
protocols: [
215
'ftp',
216
'ssh',
217
{ scheme: 'git', optionalSlashes: true },
218
{ scheme: 'vscode', optionalSlashes: false },
219
],
220
isAllowedUri: (url, { defaultValidate, protocols, defaultProtocol }) => {
221
// Custom validation for registered protocols
222
if (url.startsWith('vscode://')) {
223
// Only allow specific VSCode extension URLs
224
return url.includes('vscode://extension/');
225
}
226
227
return defaultValidate(url);
228
},
229
}),
230
],
231
});
232
```
233
234
### Whitespace Handling
235
236
Unicode whitespace utilities for robust URL processing and validation.
237
238
```typescript { .api }
239
/** Unicode whitespace pattern from DOMPurify */
240
const UNICODE_WHITESPACE_PATTERN: string;
241
242
/** RegExp for matching Unicode whitespace */
243
const UNICODE_WHITESPACE_REGEX: RegExp;
244
245
/** RegExp for matching whitespace at end of string */
246
const UNICODE_WHITESPACE_REGEX_END: RegExp;
247
248
/** Global RegExp for replacing Unicode whitespace */
249
const UNICODE_WHITESPACE_REGEX_GLOBAL: RegExp;
250
```
251
252
**Usage Examples:**
253
254
```typescript
255
import {
256
UNICODE_WHITESPACE_REGEX_GLOBAL
257
} from "@tiptap/extension-link";
258
259
// Clean URL before validation
260
function cleanUrl(url: string): string {
261
return url.replace(UNICODE_WHITESPACE_REGEX_GLOBAL, '');
262
}
263
264
const cleanedUrl = cleanUrl('https://example.com\u00A0\u2000'); // 'https://example.com'
265
```
266
267
### XSS Prevention
268
269
The validation system specifically prevents common XSS attack vectors.
270
271
**Blocked Patterns:**
272
273
```typescript
274
// These URLs are automatically blocked by default validation:
275
// - javascript: protocol
276
// - data: protocol with executable content
277
// - vbscript: protocol
278
// - about: protocol (except about:blank)
279
// - file: protocol (in most configurations)
280
281
const maliciousUrls = [
282
'javascript:alert("xss")',
283
'data:text/html,<script>alert("xss")</script>',
284
'vbscript:alert("xss")',
285
];
286
287
maliciousUrls.forEach(url => {
288
const isAllowed = isAllowedUri(url); // false for all
289
});
290
```
291
292
## Types
293
294
```typescript { .api }
295
/** Link protocol options for custom scheme registration */
296
interface LinkProtocolOptions {
297
scheme: string;
298
optionalSlashes?: boolean;
299
}
300
301
/** Validation context provided to custom validation functions */
302
interface ValidationContext {
303
defaultValidate: (url: string) => boolean;
304
protocols: Array<LinkProtocolOptions | string>;
305
defaultProtocol: string;
306
}
307
```