Security
VelinStyle is designed with security in mind. This page covers built-in protections, CSP compatibility, and best practices for safe usage.
XSS Protection in Web Components
Components that render dynamic content should use escapeHTML(), sanitizeURL(), or sanitizeSearchUrl. Icons use sanitizeSVG() (DOMPurify).
<velin-secure-field> performs client-side encoding — never ship real secrets.
High-signal components
| Component | Notes |
|---|---|
<velin-search> | Escaped highlights; sanitized URLs |
<velin-icon> | SVG sanitized |
<velin-email> | Obfuscation only |
<velin-lightbox> | sanitizeURL |
<velin-modal>, dialogs, toasts | Escaped dynamic text |
CSS Security Utilities
Built-in CSS classes for common security patterns.
.velin-user-content
A container that sandboxes user-generated content. It resets inherited styles, enables CSS containment, and hides dangerous elements like <script>, <iframe>, <object>, and elements with inline event handlers.
<div class="velin-user-content">
<!-- Untrusted HTML goes here -->
<p>User comment with <script>alert('xss')</script></p>
<!-- The script tag is hidden via display: none !important -->
</div>
| Feature | Description |
|---|---|
| Style Reset | all: initial prevents style inheritance from parent |
| CSS Containment | contain: layout style paint isolates rendering |
| Dangerous Elements | script, iframe, object, embed, applet hidden |
| Event Handlers | Elements with [onclick], [onload], [onerror] hidden |
| Images | Constrained to max-width: 100% |
.velin-secure-frame
CSS-level clickjacking protection. Content is hidden by default and only shown when stylesheets load (same-origin). Complement with HTTP X-Frame-Options: DENY or Content-Security-Policy: frame-ancestors 'none'.
<body class="velin-secure-frame"> <!-- Content only visible when CSS loads --> </body>
Autofill Protection
VelinStyle prevents browser autofill from leaking background colors that could expose data through CSS timing attacks.
External Link Indicator
Links with target="_blank" that lack rel="noopener" automatically receive a visual indicator (arrow icon) to warn users they're leaving the site.
Form Persistence Security
The <velin-persist> component includes multiple security measures:
| Feature | Description |
|---|---|
| Password exclusion | type="password" and type="file" fields are never saved |
| Key validation | Storage keys must be alphanumeric, 1-64 chars (regex validated) |
| Size limit | Maximum 64KB per entry prevents quota abuse |
| Quota handling | Graceful QuotaExceededError handling with event |
| Type validation | Restored data must be a valid object (not string/null) |
| Session option | storage="session" for data that shouldn't persist across sessions |
Best Practices
- Never pass unsanitized user input to component methods or attributes. While VelinStyle escapes internally, defense-in-depth is critical.
- Use
rel="noopener noreferrer"on alltarget="_blank"links to prevent tab-napping. - Set CSP headers on your server. At minimum:
default-src 'self'; style-src 'self' 'unsafe-inline'. - Wrap user-generated content in
.velin-user-contentto sandbox rendering. - Use
storage="session"for<velin-persist>when handling sensitive form data that should not persist across browser sessions. - Keep VelinStyle updated. Security patches are released as minor versions.
- For frameworks (React, Vue, Angular): These frameworks already escape by default. VelinStyle's escaping is an additional layer for vanilla JS usage.
PII scanner & email protection
Three layers: CLI scan, display obfuscation, optional form encryption (transport helper only — TLS required).
npx velinstyle scan --only pii npx velinstyle scan --only pii --fix
Components
<velin-email value="contact@domain.com" label="Show email"></velin-email> <velin-secure-field type="email" label="Email"></velin-secure-field>
Import secure field separately: import '@birdapi/velinstyle/secure'
Shadow DOM Encapsulation
All interactive Web Components use Shadow DOM (mode: "open") to encapsulate their internal styles and markup. This means:
- Component styles do not leak into your page
- Your page styles do not accidentally break components
- Internal DOM structure is protected from accidental manipulation
- Components with
delegatesFocus: true(Modal, Drawer, Tabs, Dropdown) properly forward focus for accessibility
mode: "open" means JavaScript can still access element.shadowRoot if needed (for testing or customization). This is intentional and matches standard Web Component library practice.