v1.0.0

πŸ”’ Security Features

OnigiriJS includes enterprise-grade security features to protect your applications from CSRF, XSS, and other attacks.

CSRF Protection

Quick Start

<!-- Add CSRF token to your HTML -->
<meta name="csrf-token" content="<?= csrf_token() ?>">

<script>
// Initialize security module (auto-detects token)
Onigiri.security.init({
    autoInjectCSRF: true  // Automatically add to forms
});

// That's it! All AJAX requests and forms now include CSRF
</script>

Configuration

Onigiri.security.init({
    csrfToken: null,              // Auto-detected from meta tag
    csrfHeader: 'X-CSRF-Token',   // Header name for AJAX
    csrfParam: '_csrf',            // Form parameter name
    csrfMetaName: 'csrf-token',   // Meta tag name
    cspNonce: null,               // Auto-detected from scripts
    autoInjectCSRF: true          // Auto-add to all forms
});

Manual Token Management

// Get current token
const token = Onigiri.security.getToken();

// Set new token
Onigiri.security.setToken('your-new-token');

// Add to specific form
const form = document.querySelector('#my-form');
Onigiri.security.addCSRFToForm(form);

// Add to headers object
let headers = { 'Content-Type': 'application/json' };
headers = Onigiri.security.addCSRFToHeaders(headers);

// Add to data object
let data = { name: 'John' };
data = Onigiri.security.addCSRFToData(data);

Automatic Form Protection

<!-- Forms automatically get CSRF token when autoInjectCSRF is true -->
<form action="/submit" method="POST">
    <!-- Hidden CSRF input added automatically -->
    <input type="text" name="username">
    <button type="submit">Submit</button>
</form>

<script>
// No extra code needed!
// CSRF token is already in the form
</script>

Automatic AJAX Protection

// All AJAX requests include CSRF token automatically
await Onigiri.post('/api/save', { data: 'value' });
// Request includes X-CSRF-Token header

// Convenience methods also include CSRF
await Onigiri.get('/api/users');
await Onigiri.put('/api/users/1', userData);
await Onigiri.delete('/api/users/1');

// Disable CSRF for specific request
await Onigiri.ajax({
    url: '/public/api',
    method: 'POST',
    csrf: false  // Disable CSRF for this request
});

CSP (Content Security Policy) Support

Nonce Management

<!-- Server generates nonce -->
<script nonce="<?= csp_nonce() ?>">
    // Nonce is auto-detected
    Onigiri.security.init();
    
    // Or set manually
    Onigiri.security.setNonce('random-nonce-value');
    
    // Get current nonce
    const nonce = Onigiri.security.getNonce();
</script>

Safe Script Creation

// Create script element with CSP nonce
const script = Onigiri.security.createScript('app.js', () => {
    console.log('πŸ™ Script loaded!');
});
document.head.appendChild(script);

// Create inline script
const inlineScript = Onigiri.security.createScript(null);
inlineScript.textContent = 'console.log("Hello");';
document.body.appendChild(inlineScript);

Safe Style Creation

// Create style element with CSP nonce
const style = Onigiri.security.createStyle(\`
    body {
        background: #fff5e6;
        font-family: sans-serif;
    }
    .onigiri {
        color: #ff6b6b;
    }
\`);
document.head.appendChild(style);

Execute Inline Scripts Safely

// Execute inline code with CSP nonce
Onigiri.security.executeScript(\`
    console.log('πŸ™ This runs safely with CSP!');
    
    const widget = new OnigiriWidget();
    widget.init();
\`);

XSS Prevention

HTML Sanitization

// Sanitize user input before displaying
const userInput = '<script>alert("xss")</script>Hello <b>World</b>';
const safe = Onigiri.security.sanitizeHTML(userInput);
console.log(safe); // "Hello World" (script removed, b tag removed)

// Display safely
O('#user-content').html(safe);

HTML Entity Escaping

// Escape HTML entities for safe display
const userText = '<div>User & Content</div>';
const escaped = Onigiri.security.escapeHTML(userText);
console.log(escaped);
// "&lt;div&gt;User &amp; Content&lt;/div&gt;"

// Display as text (not HTML)
O('#display').html(escaped);
// Shows: <div>User & Content</div>

Safe User Content Display

// Never trust user input
const comment = userComment; // From form or API

// Method 1: Use text() instead of html()
O('#comment').text(comment); // Safe - displays as text

// Method 2: Escape HTML
O('#comment').html(Onigiri.security.escapeHTML(comment));

// Method 3: Sanitize HTML (removes scripts but keeps formatting)
O('#comment').html(Onigiri.security.sanitizeHTML(comment));

URL Validation

Prevent Open Redirects

// Validate URLs before redirecting
const redirectUrl = getUrlParameter('redirect');

if (Onigiri.security.isValidURL(redirectUrl)) {
    window.location = redirectUrl;  // Safe
} else {
    console.error('Invalid redirect URL');
    window.location = '/dashboard'; // Safe fallback
}

Same-Origin Check

// Check if URL is from same origin
const apiUrl = '/api/data';
const externalUrl = 'https://external-site.com/api';

if (Onigiri.security.isSameOrigin(apiUrl)) {
    // Safe to load without CORS
    await Onigiri.get(apiUrl);
}

if (!Onigiri.security.isSameOrigin(externalUrl)) {
    console.warn('External URL - CORS required');
}

πŸ”’ Complete Security Setup

PHP Backend

<?php
// Generate CSRF token
function csrf_token() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

// Generate CSP nonce
function csp_nonce() {
    if (empty($_SESSION['csp_nonce'])) {
        $_SESSION['csp_nonce'] = base64_encode(random_bytes(16));
    }
    return $_SESSION['csp_nonce'];
}

// Verify CSRF token
function verify_csrf() {
    $token = $_POST['_csrf'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
    return hash_equals($_SESSION['csrf_token'] ?? '', $token);
}

// Set CSP header
$nonce = csp_nonce();
header("Content-Security-Policy: script-src 'self' 'nonce-{$nonce}'; style-src 'self' 'nonce-{$nonce}'");
?>

HTML Template

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="csrf-token" content="<?= csrf_token() ?>">
    <title>Secure App</title>
</head>
<body>
    <form id="secure-form" action="/api/submit" method="POST">
        <input type="email" name="email" placeholder="Email">
        <input type="password" name="password" placeholder="Password">
        <button type="submit">Login</button>
    </form>
    
    <script src="onigiri.core.js"></script>
    <script src="onigiri.security.js"></script>
    <script src="onigiri.ajax.js"></script>
    
    <script nonce="<?= csp_nonce() ?>">
        // Initialize security
        Onigiri.security.init({
            autoInjectCSRF: true
        });
        
        // Form now has CSRF token
        // AJAX requests now include CSRF
        // Scripts use CSP nonce
        
        O('#secure-form').on('submit', async (e) => {
            e.preventDefault();
            
            try {
                // CSRF token automatically included
                const response = await Onigiri.post('/api/submit', {
                    email: O('[name="email"]').val(),
                    password: O('[name="password"]').val()
                });
                
                console.log('πŸ™ Success:', response);
            } catch (error) {
                console.error('❌ Error:', error);
            }
        });
    </script>
</body>
</html>

API Endpoint

<?php
// API endpoint with CSRF verification
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Verify CSRF token
    if (!verify_csrf()) {
        http_response_code(403);
        echo json_encode(['error' => 'CSRF token mismatch']);
        exit;
    }
    
    // Process request safely
    $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
    $password = $_POST['password'];
    
    // Validate
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        http_response_code(400);
        echo json_encode(['error' => 'Invalid email']);
        exit;
    }
    
    // Process login
    // ...
    
    echo json_encode(['success' => true]);
}
?>
πŸ™ Security Best Practices:
  • Always initialize the security module before making requests
  • Use text() instead of html() for user content
  • Validate and sanitize all user input
  • Use HTTPS in production
  • Rotate CSRF tokens after authentication
  • Implement rate limiting on the backend

Security Checklist

  • CSRF protection enabled with Onigiri.security.init()
  • CSP headers set with nonce for inline scripts
  • User input sanitized with sanitizeHTML() or escapeHTML()
  • URLs validated before redirects with isValidURL()
  • HTTPS enabled in production
  • Backend validates CSRF tokens
  • Sensitive data stored securely (not in localStorage)
  • Rate limiting implemented on API endpoints

Common Security Patterns

Secure Form Submission

// HTML form with validation and CSRF
<form id="registration">
    <input name="email" type="email">
    <input name="password" type="password">
    <button type="submit">Register</button>
</form>

// JavaScript with security
Onigiri.security.init({ autoInjectCSRF: true });

O('#registration').on('submit', async (e) => {
    e.preventDefault();
    
    // Validate
    const result = O(e.target).validate({
        email: { required: true, email: true },
        password: { required: true, minLength: 8 }
    });
    
    if (!result.isValid) {
        // Show errors
        return;
    }
    
    // Sanitize input
    const email = O('[name="email"]').val().trim();
    const password = O('[name="password"]').val();
    
    // Submit with CSRF (automatic)
    await Onigiri.post('/api/register', { email, password });
});

Secure API Request

async function secureApiCall(endpoint, data) {
    try {
        // Validate endpoint
        if (!Onigiri.security.isSameOrigin(endpoint)) {
            throw new Error('Invalid API endpoint');
        }
        
        // Make request with CSRF
        const response = await Onigiri.post(endpoint, data);
        return response;
    } catch (error) {
        console.error('API Error:', error);
        throw error;
    }
}

βœ… Development Roadmap

Track the progress of OnigiriJS modules. Tasks are marked complete by the development team.

OnigiriJS Module Roadmap

Implementation progress of planned modules

6 / 21 completed (29%)
onigiri-state
Shared global & scoped state management
onigiri-directives
Declarative DOM bindings (o-show, o-model, etc.)
onigiri-resource
REST-style data models over AJAX
onigiri-observe
Intersection & Mutation observer helpers
onigiri-humhub-ui
Standard HumHub UI abstractions (modal, notify, confirm)
onigiri-lifecycle
Component lifecycle hooks
onigiri-guard
Debounce, throttle, single-run guards
onigiri-scroll
Scroll save/restore & helpers (PJAX-friendly)
onigiri-permission
Client-side permission awareness
onigiri-portal
DOM teleport / overlay mounting
onigiri-router
Micro router (non-SPA, PJAX-first)
onigiri-sanitize
HTML & input sanitization
onigiri-shortcut
Keyboard shortcut manager
onigiri-queue
Sequential async task runner
onigiri-gesture
Touch & swipe helpers
onigiri-devtools
Debugging & inspection helpers
onigiri-plugin
Plugin registration system
onigiri-time
Relative time & timezone utilities
onigiri-emojis
Emoji Picker and Manager
onigiri-tasks
Task Management
onigiri-polls
Polls creation and management
Note: Task completion is managed by the OnigiriJS development team.