CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tiptap--extension-link

Link extension for tiptap rich text editor providing automatic link detection, paste handling, click behavior, and XSS protection.

88

1.23x
Overview
Eval results
Files

url-validation.mddocs/

URL Validation and Security

XSS protection utilities and URI validation for secure link handling. The validation system prevents malicious URLs while allowing legitimate protocols and custom validation logic.

Capabilities

URI Validation Function

Core URL validation function that checks if a URI is allowed based on protocol whitelist and validation rules.

/**
 * Validates if a URI is allowed based on protocol whitelist
 * @param uri - URI to validate (can be undefined)
 * @param protocols - Optional array of custom protocols to allow
 * @returns True if URI is allowed, false otherwise
 */
function isAllowedUri(
  uri: string | undefined,
  protocols?: LinkOptions['protocols']
): boolean;

Usage Examples:

import { isAllowedUri } from "@tiptap/extension-link";

// Basic validation
const isValid = isAllowedUri('https://example.com'); // true
const isValid2 = isAllowedUri('javascript:alert("xss")'); // false

// Custom protocols
const customProtocols = ['ftp', 'ssh', { scheme: 'git', optionalSlashes: true }];
const isValid3 = isAllowedUri('ftp://files.example.com', customProtocols); // true
const isValid4 = isAllowedUri('git:github.com/user/repo', customProtocols); // true

// Undefined/null handling
const isValid5 = isAllowedUri(undefined); // true (allows empty/null URIs)
const isValid6 = isAllowedUri(''); // true

Default Allowed Protocols

The validation system includes a comprehensive list of safe protocols by default.

/**
 * Default allowed protocols for URI validation
 */
const allowedProtocols: string[] = [
  'http',
  'https',
  'ftp',
  'ftps',
  'mailto',
  'tel',
  'callto',
  'sms',
  'cid',
  'xmpp'
];

Paste Detection Regex

Regular expression for detecting URLs in pasted content.

/**
 * Regular expression for detecting URLs in pasted content
 * Matches http/https URLs with proper domain and path structure
 */
const pasteRegex: RegExp;

Usage Examples:

import { pasteRegex } from "@tiptap/extension-link";

// Test if pasted content is a URL
const pastedText = 'https://tiptap.dev/docs';
const isUrl = pasteRegex.test(pastedText); // true

// Extract URLs from text
const text = 'Visit https://tiptap.dev and https://example.com for more info';
const urls = text.match(pasteRegex);
// ['https://tiptap.dev', 'https://example.com']

Validation Context Interface

Context object provided to custom validation functions with helper utilities and configuration.

interface ValidationContext {
  /**
   * The default validation function
   * @param url - URL to validate
   * @returns True if URL passes default validation
   */
  defaultValidate: (url: string) => boolean;

  /**
   * Array of allowed protocols for the URL (e.g., "http", "https")
   * As defined in the protocols option
   */
  protocols: Array<LinkProtocolOptions | string>;

  /**
   * Default protocol (e.g., 'http')
   * As defined in the defaultProtocol option
   */
  defaultProtocol: string;
}

Usage Examples:

import { Editor } from "@tiptap/core";
import { Link } from "@tiptap/extension-link";

const editor = new Editor({
  extensions: [
    Link.configure({
      isAllowedUri: (url, { defaultValidate, protocols, defaultProtocol }) => {
        // Allow relative URLs
        if (url.startsWith('./') || url.startsWith('../')) {
          return true;
        }
        
        // Allow localhost in development
        if (process.env.NODE_ENV === 'development' && url.includes('localhost')) {
          return true;
        }
        
        // Use default validation for everything else
        return defaultValidate(url);
      },
    }),
  ],
});

Custom Validation Functions

Advanced validation configurations for specific security requirements.

Security-First Validation:

import { Editor } from "@tiptap/core";
import { Link } from "@tiptap/extension-link";

const editor = new Editor({
  extensions: [
    Link.configure({
      isAllowedUri: (url, { defaultValidate, protocols, defaultProtocol }) => {
        // Strict security: only allow specific domains
        const allowedDomains = ['example.com', 'trusted-site.org'];
        
        try {
          const urlObj = new URL(url);
          return allowedDomains.includes(urlObj.hostname);
        } catch {
          return false; // Invalid URLs are rejected
        }
      },
      shouldAutoLink: (url) => {
        // Don't auto-link internal URLs
        return !url.includes('internal.company.com');
      },
    }),
  ],
});

Development-Friendly Validation:

const editor = new Editor({
  extensions: [
    Link.configure({
      isAllowedUri: (url, { defaultValidate, protocols, defaultProtocol }) => {
        // Development: allow localhost and file protocols
        if (process.env.NODE_ENV === 'development') {
          if (url.startsWith('file://') || url.includes('localhost') || url.includes('127.0.0.1')) {
            return true;
          }
        }
        
        // Production: use strict validation
        return defaultValidate(url);
      },
    }),
  ],
});

Protocol Registration

The validation system works with linkifyjs protocol registration for custom schemes.

Usage Examples:

import { Editor } from "@tiptap/core";
import { Link } from "@tiptap/extension-link";

const editor = new Editor({
  extensions: [
    Link.configure({
      protocols: [
        'ftp',
        'ssh',
        { scheme: 'git', optionalSlashes: true },
        { scheme: 'vscode', optionalSlashes: false },
      ],
      isAllowedUri: (url, { defaultValidate, protocols, defaultProtocol }) => {
        // Custom validation for registered protocols
        if (url.startsWith('vscode://')) {
          // Only allow specific VSCode extension URLs
          return url.includes('vscode://extension/');
        }
        
        return defaultValidate(url);
      },
    }),
  ],
});

Whitespace Handling

Unicode whitespace utilities for robust URL processing and validation.

/** Unicode whitespace pattern from DOMPurify */
const UNICODE_WHITESPACE_PATTERN: string;

/** RegExp for matching Unicode whitespace */
const UNICODE_WHITESPACE_REGEX: RegExp;

/** RegExp for matching whitespace at end of string */
const UNICODE_WHITESPACE_REGEX_END: RegExp;

/** Global RegExp for replacing Unicode whitespace */
const UNICODE_WHITESPACE_REGEX_GLOBAL: RegExp;

Usage Examples:

import { 
  UNICODE_WHITESPACE_REGEX_GLOBAL 
} from "@tiptap/extension-link";

// Clean URL before validation
function cleanUrl(url: string): string {
  return url.replace(UNICODE_WHITESPACE_REGEX_GLOBAL, '');
}

const cleanedUrl = cleanUrl('https://example.com\u00A0\u2000'); // 'https://example.com'

XSS Prevention

The validation system specifically prevents common XSS attack vectors.

Blocked Patterns:

// These URLs are automatically blocked by default validation:
// - javascript: protocol
// - data: protocol with executable content
// - vbscript: protocol
// - about: protocol (except about:blank)
// - file: protocol (in most configurations)

const maliciousUrls = [
  'javascript:alert("xss")',
  'data:text/html,<script>alert("xss")</script>',
  'vbscript:alert("xss")',
];

maliciousUrls.forEach(url => {
  const isAllowed = isAllowedUri(url); // false for all
});

Types

/** Link protocol options for custom scheme registration */
interface LinkProtocolOptions {
  scheme: string;
  optionalSlashes?: boolean;
}

/** Validation context provided to custom validation functions */
interface ValidationContext {
  defaultValidate: (url: string) => boolean;
  protocols: Array<LinkProtocolOptions | string>;
  defaultProtocol: string;
}

Install with Tessl CLI

npx tessl i tessl/npm-tiptap--extension-link

docs

autolink.md

click-handling.md

index.md

link-commands.md

link-configuration.md

paste-processing.md

url-validation.md

tile.json