Skip to content

Security Demo

Advanced redaction capabilities for sensitive data protection with real-world security scenarios and custom redaction rules.

📄 Source Code

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Loggical Security & Redaction Demo</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          Oxygen, Ubuntu, sans-serif;
        line-height: 1.6;
        color: #333;
        background: #f8f9fa;
        padding: 20px;
      }

      .container {
        max-width: 1200px;
        margin: 0 auto;
        background: white;
        border-radius: 12px;
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
        overflow: hidden;
      }

      .header {
        background: linear-gradient(135deg, #dc3545 0%, #fd7e14 100%);
        color: white;
        padding: 30px;
        text-align: center;
      }

      .header h1 {
        font-size: 2.5rem;
        margin-bottom: 10px;
      }

      .header p {
        font-size: 1.1rem;
        opacity: 0.9;
      }

      .nav {
        background: #e9ecef;
        padding: 15px 30px;
        border-bottom: 1px solid #dee2e6;
      }

      .nav a {
        color: #495057;
        text-decoration: none;
        margin-right: 20px;
        font-weight: 500;
      }

      .nav a:hover {
        color: #dc3545;
      }

      .content {
        padding: 30px;
      }

      .demo-section {
        margin-bottom: 40px;
        padding: 25px;
        background: #f8f9fa;
        border-radius: 8px;
        border-left: 4px solid #dc3545;
      }

      .demo-section h2 {
        color: #2c3e50;
        margin-bottom: 15px;
        display: flex;
        align-items: center;
        gap: 10px;
      }

      .demo-section .icon {
        font-size: 1.5rem;
      }

      .security-warning {
        background: #fff3cd;
        border: 1px solid #ffeaa7;
        border-radius: 8px;
        padding: 15px;
        margin: 15px 0;
        border-left: 4px solid #ffc107;
      }

      .security-warning h4 {
        color: #856404;
        margin-bottom: 8px;
        display: flex;
        align-items: center;
        gap: 8px;
      }

      .security-warning p {
        color: #856404;
        margin: 0;
      }

      .security-good {
        background: #d4edda;
        border: 1px solid #c3e6cb;
        border-radius: 8px;
        padding: 15px;
        margin: 15px 0;
        border-left: 4px solid #28a745;
      }

      .security-good h4 {
        color: #155724;
        margin-bottom: 8px;
        display: flex;
        align-items: center;
        gap: 8px;
      }

      .security-good p {
        color: #155724;
        margin: 0;
      }

      .comparison-grid {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 20px;
        margin: 20px 0;
      }

      .comparison-panel {
        background: white;
        border: 1px solid #dee2e6;
        border-radius: 8px;
        overflow: hidden;
      }

      .comparison-header {
        padding: 12px 15px;
        font-weight: 600;
        font-size: 14px;
      }

      .comparison-header.secure {
        background: #28a745;
        color: white;
      }

      .comparison-header.insecure {
        background: #dc3545;
        color: white;
      }

      .log-output {
        background: #1e1e1e;
        color: #d4d4d4;
        padding: 15px;
        font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas,
          monospace;
        font-size: 12px;
        line-height: 1.4;
        overflow-x: auto;
        white-space: pre-wrap;
        min-height: 120px;
      }

      .controls-panel {
        background: white;
        border: 1px solid #dee2e6;
        border-radius: 8px;
        padding: 20px;
        margin-bottom: 20px;
      }

      .controls-row {
        display: flex;
        gap: 20px;
        margin-bottom: 15px;
        align-items: center;
        flex-wrap: wrap;
      }

      .control-group {
        display: flex;
        flex-direction: column;
        gap: 5px;
      }

      .control-group label {
        font-weight: 600;
        color: #495057;
        font-size: 14px;
      }

      .control-group select,
      .control-group input {
        padding: 8px 12px;
        border: 1px solid #ced4da;
        border-radius: 4px;
        font-size: 14px;
      }

      .control-group textarea {
        padding: 8px 12px;
        border: 1px solid #ced4da;
        border-radius: 4px;
        font-size: 14px;
        font-family: monospace;
        resize: vertical;
        min-height: 60px;
      }

      .checkbox-group {
        display: flex;
        align-items: center;
        gap: 8px;
      }

      .checkbox-group input[type="checkbox"] {
        width: auto;
        margin: 0;
      }

      .demo-button {
        background: linear-gradient(135deg, #dc3545 0%, #fd7e14 100%);
        color: white;
        border: none;
        padding: 10px 20px;
        border-radius: 6px;
        cursor: pointer;
        font-weight: 500;
        transition: all 0.2s ease;
        margin-right: 10px;
        margin-bottom: 10px;
      }

      .demo-button:hover {
        transform: translateY(-2px);
        box-shadow: 0 4px 12px rgba(220, 53, 69, 0.3);
      }

      .demo-button.danger {
        background: #dc3545;
        animation: pulse 2s infinite;
      }

      @keyframes pulse {
        0% {
          box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
        }
        70% {
          box-shadow: 0 0 0 10px rgba(220, 53, 69, 0);
        }
        100% {
          box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
        }
      }

      .sensitive-data {
        background: #fff3cd;
        border: 1px solid #ffeaa7;
        padding: 10px;
        border-radius: 4px;
        font-family: monospace;
        font-size: 12px;
        margin: 10px 0;
      }

      .patterns-list {
        background: #f8f9fa;
        border: 1px solid #e9ecef;
        border-radius: 4px;
        padding: 15px;
        margin: 15px 0;
      }

      .patterns-list h5 {
        color: #495057;
        margin-bottom: 10px;
      }

      .patterns-list ul {
        list-style: none;
        padding: 0;
      }

      .patterns-list li {
        padding: 5px 0;
        border-bottom: 1px solid #e9ecef;
        font-family: monospace;
        font-size: 12px;
      }

      .patterns-list li:last-child {
        border-bottom: none;
      }

      /* ANSI color classes */
      .ansi-black {
        color: #2d3748;
      }
      .ansi-red {
        color: #f56565;
      }
      .ansi-green {
        color: #48bb78;
      }
      .ansi-yellow {
        color: #ed8936;
      }
      .ansi-blue {
        color: #4299e1;
      }
      .ansi-magenta {
        color: #9f7aea;
      }
      .ansi-cyan {
        color: #38b2ac;
      }
      .ansi-white {
        color: #f7fafc;
      }
      .ansi-gray {
        color: #a0aec0;
      }
      .ansi-bright-red {
        color: #fc8181;
      }
      .ansi-bright-green {
        color: #68d391;
      }
      .ansi-bright-yellow {
        color: #f6e05e;
      }
      .ansi-bright-blue {
        color: #63b3ed;
      }
      .ansi-bright-magenta {
        color: #b794f6;
      }
      .ansi-bright-cyan {
        color: #4fd1c7;
      }
      .ansi-bright-white {
        color: #ffffff;
      }
      .ansi-dim {
        opacity: 0.6;
      }
      .ansi-bold {
        font-weight: bold;
      }

      @media (max-width: 768px) {
        .comparison-grid {
          grid-template-columns: 1fr;
        }

        .controls-row {
          flex-direction: column;
          align-items: stretch;
        }
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="header">
        <h1>🔒 Security & Redaction Demo</h1>
        <p>
          Explore automatic sensitive data protection and custom redaction
          patterns
        </p>
      </div>

      <div class="nav">
        <a href="index.html">← Back to Demos</a>
        <a href="basic-demo.html">Basic Usage</a>
        <a href="formatting-demo.html">Formatting Showcase</a>
      </div>

      <div class="content">
        <!-- Basic Redaction Demo -->
        <div class="demo-section">
          <h2><span class="icon">🛡️</span>Automatic Redaction</h2>

          <div class="security-good">
            <h4>🔒 Security First</h4>
            <p>
              Loggical automatically redacts sensitive data by default.
              Passwords, API keys, tokens, and other sensitive information are
              protected without any configuration.
            </p>
          </div>

          <div class="comparison-grid">
            <div class="comparison-panel">
              <div class="comparison-header secure">
                ✅ Redaction Enabled (Default)
              </div>
              <div class="log-output" id="secure-output"></div>
            </div>
            <div class="comparison-panel">
              <div class="comparison-header insecure">
                ⚠️ Redaction Disabled (Dangerous)
              </div>
              <div class="log-output" id="insecure-output"></div>
            </div>
          </div>

          <button class="demo-button" onclick="demonstrateBasicRedaction()">
            🔐 Test Basic Redaction
          </button>
          <button class="demo-button" onclick="demonstratePaymentData()">
            💳 Test Payment Data
          </button>
          <button class="demo-button" onclick="demonstrateAuthData()">
            🔑 Test Auth Data
          </button>

          <div class="patterns-list">
            <h5>🎯 Automatically Protected Patterns:</h5>
            <ul>
              <li>
                <strong>Object Keys:</strong> password, secret, token, key,
                auth, jwt, bearer, credential
              </li>
              <li>
                <strong>Credit Cards:</strong> 4532-1234-5678-9012,
                4532123456789012
              </li>
              <li>
                <strong>API Keys:</strong> sk_live_..., pk_test_..., AIza...
              </li>
              <li>
                <strong>JWT Tokens:</strong>
                eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
              </li>
              <li><strong>Bearer Tokens:</strong> Bearer abc123def456...</li>
              <li><strong>SSH Keys:</strong> ssh-rsa AAAAB3NzaC1yc2E...</li>
              <li><strong>Social Security:</strong> 123-45-6789, 123456789</li>
            </ul>
          </div>
        </div>

        <!-- Advanced Redaction Configuration -->
        <div class="demo-section">
          <h2><span class="icon">⚙️</span>Advanced Redaction Configuration</h2>

          <div class="security-warning">
            <h4>⚠️ Advanced Configuration</h4>
            <p>
              Fine-tune redaction behavior for your specific needs. You can add
              custom patterns, exclude certain keys, or disable redaction
              entirely for development.
            </p>
          </div>

          <div class="controls-panel">
            <div class="controls-row">
              <div class="control-group">
                <label>Redaction Mode</label>
                <select id="redaction-mode">
                  <option value="enabled">Enabled (Recommended)</option>
                  <option value="keys-only">Keys Only</option>
                  <option value="strings-only">Strings Only</option>
                  <option value="disabled">Disabled (Development Only)</option>
                </select>
              </div>
              <div class="control-group">
                <label>Replacement Text</label>
                <input
                  type="text"
                  id="replacement-text"
                  value="[REDACTED]"
                  placeholder="[REDACTED]"
                />
              </div>
            </div>
            <div class="controls-row">
              <div class="control-group">
                <label>Additional Sensitive Keys (comma-separated)</label>
                <input
                  type="text"
                  id="custom-keys"
                  placeholder="customSecret, internalId, sessionToken"
                />
              </div>
              <div class="control-group">
                <label>Exclude Keys (comma-separated)</label>
                <input
                  type="text"
                  id="exclude-keys"
                  placeholder="username, email"
                />
              </div>
            </div>
            <div class="controls-row">
              <div class="control-group">
                <label>Custom Patterns (one per line, regex)</label>
                <textarea
                  id="custom-patterns"
                  placeholder="user-\d+&#10;order-[A-Z0-9]+&#10;temp-\w{8,}"
                ></textarea>
              </div>
              <div class="control-group">
                <label>Exclude Patterns (one per line)</label>
                <textarea
                  id="exclude-patterns"
                  placeholder="bearer&#10;jwt"
                ></textarea>
              </div>
            </div>
          </div>

          <div class="comparison-panel">
            <div class="comparison-header secure">
              🔧 Custom Configuration Result
            </div>
            <div class="log-output" id="custom-redaction-output"></div>
          </div>

          <button class="demo-button" onclick="demonstrateCustomRedaction()">
            🧪 Test Custom Configuration
          </button>
          <button class="demo-button" onclick="resetRedactionConfig()">
            🔄 Reset to Defaults
          </button>
        </div>

        <!-- Real-world Scenarios -->
        <div class="demo-section">
          <h2><span class="icon">🌍</span>Real-world Security Scenarios</h2>

          <div class="security-good">
            <h4>✅ Production-Ready Security</h4>
            <p>
              See how redaction works in realistic application scenarios. These
              examples show common patterns where sensitive data might
              accidentally be logged.
            </p>
          </div>

          <button class="demo-button" onclick="demonstrateScenario('login')">
            🔐 User Login Flow
          </button>
          <button class="demo-button" onclick="demonstrateScenario('api')">
            🌐 API Request/Response
          </button>
          <button class="demo-button" onclick="demonstrateScenario('database')">
            🗄️ Database Operations
          </button>
          <button class="demo-button" onclick="demonstrateScenario('payment')">
            💳 Payment Processing
          </button>
          <button class="demo-button" onclick="demonstrateScenario('config')">
            ⚙️ Configuration Loading
          </button>

          <div class="log-output" id="scenario-output" style="margin-top: 20px">
            Click a scenario button above to see realistic security examples!
          </div>
        </div>

        <!-- Development vs Production -->
        <div class="demo-section">
          <h2><span class="icon">🔄</span>Development vs Production</h2>

          <div class="security-warning">
            <h4>⚠️ Environment-Based Configuration</h4>
            <p>
              Different environments need different security levels. Development
              might need more visibility for debugging, while production should
              hide everything sensitive.
            </p>
          </div>

          <div class="comparison-grid">
            <div class="comparison-panel">
              <div
                class="comparison-header"
                style="background: #17a2b8; color: white"
              >
                🛠️ Development Mode
              </div>
              <div class="log-output" id="dev-output"></div>
            </div>
            <div class="comparison-panel">
              <div class="comparison-header secure">🏭 Production Mode</div>
              <div class="log-output" id="prod-output"></div>
            </div>
          </div>

          <button class="demo-button" onclick="demonstrateEnvironments()">
            🔄 Compare Environments
          </button>

          <div class="sensitive-data">
            <strong>Sample Environment Configuration:</strong>
            <br /><br />
            <strong>Development:</strong> LOGGER_REDACTION=false or selective
            redaction
            <br />
            <strong>Testing:</strong> LOGGER_REDACTION=false for test data
            visibility
            <br />
            <strong>Staging:</strong> LOGGER_REDACTION=true to match production
            <br />
            <strong>Production:</strong> LOGGER_REDACTION=true (default) for
            maximum security
          </div>
        </div>

        <!-- Security Best Practices -->
        <div class="demo-section">
          <h2><span class="icon">📋</span>Security Best Practices</h2>

          <div class="security-good">
            <h4>✅ Recommended Practices</h4>
            <p>
              Follow these guidelines to ensure your application logs are secure
              by default while maintaining debugging capabilities when needed.
            </p>
          </div>

          <div
            style="
              display: grid;
              grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
              gap: 20px;
              margin: 20px 0;
            "
          >
            <div class="security-good">
              <h4>🔒 Always Enable in Production</h4>
              <p>
                Keep redaction enabled in production environments. Sensitive
                data in logs is a major security risk and compliance issue.
              </p>
            </div>
            <div class="security-warning">
              <h4>⚠️ Careful with Development</h4>
              <p>
                You can disable redaction in development for debugging, but
                never commit disabled redaction to production code.
              </p>
            </div>
            <div class="security-good">
              <h4>🎯 Custom Patterns</h4>
              <p>
                Add custom patterns for your specific sensitive data (order IDs,
                internal tokens, etc.) to ensure comprehensive protection.
              </p>
            </div>
            <div class="security-good">
              <h4>🔍 Regular Audits</h4>
              <p>
                Regularly review your logs to ensure no sensitive data is
                leaking. Test your redaction patterns with real data.
              </p>
            </div>
          </div>

          <button class="demo-button" onclick="demonstrateBestPractices()">
            📚 Show Best Practice Examples
          </button>
          <div
            class="log-output"
            id="best-practices-output"
            style="margin-top: 20px"
          ></div>
        </div>
      </div>
    </div>

    <script type="module">
      import { Logger, ColorLevel } from "loggical";

      // ANSI to HTML conversion function
      function ansiToHtml(text) {
        const ansiMap = {
          0: "reset",
          1: "ansi-bold",
          2: "ansi-dim",
          30: "ansi-black",
          31: "ansi-red",
          32: "ansi-green",
          33: "ansi-yellow",
          34: "ansi-blue",
          35: "ansi-magenta",
          36: "ansi-cyan",
          37: "ansi-white",
          90: "ansi-gray",
          91: "ansi-bright-red",
          92: "ansi-bright-green",
          93: "ansi-bright-yellow",
          94: "ansi-bright-blue",
          95: "ansi-bright-magenta",
          96: "ansi-bright-cyan",
          97: "ansi-bright-white",
          39: "reset-fg",
        };

        let html = text
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;");

        let openSpans = [];

        html = html.replace(
          /(?:\033\[|\x1b\[|\[)([0-9;]+)m/g,
          (match, codes) => {
            const codeList = codes.split(";");
            let result = "";

            codeList.forEach((code) => {
              if (code === "0" || code === "39") {
                result += "</span>".repeat(openSpans.length);
                openSpans = [];
              } else if (ansiMap[code]) {
                const className = ansiMap[code];
                if (className !== "reset" && className !== "reset-fg") {
                  result += `<span class="${className}">`;
                  openSpans.push(className);
                }
              }
            });

            return result;
          }
        );

        html += "</span>".repeat(openSpans.length);
        return html;
      }

      // Custom transport for capturing output
      class DemoTransport {
        constructor(outputElementId) {
          this.name = "demo";
          this.outputElementId = outputElementId;
          this.messages = [];
        }

        write(formattedMessage, metadata) {
          this.messages.push(formattedMessage);
          const element = document.getElementById(this.outputElementId);
          if (element) {
            const htmlOutput = ansiToHtml(formattedMessage);
            element.innerHTML += htmlOutput + "\n";
          }
        }

        clear() {
          this.messages = [];
          const element = document.getElementById(this.outputElementId);
          if (element) {
            element.innerHTML = "";
          }
        }
      }

      // Sample sensitive data for demonstrations
      const sensitiveData = {
        user: {
          id: 12345,
          username: "john_doe",
          email: "john@example.com",
          password: "super_secret_password_123",
          apiKey: "sk_live_abcdef123456789012345678",
          token: "bearer_xyz789abc123def456",
          jwt: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
          creditCard: "4532-1234-5678-9012",
          cvv: "123",
          ssn: "123-45-6789",
        },
        database: {
          host: "db.company.com",
          username: "db_admin",
          password: "database_secret_password_456",
          connectionString: "postgresql://admin:secret123@localhost:5432/mydb",
        },
        config: {
          stripeKey: "sk_live_51234567890abcdef",
          awsAccessKey: "AKIAIOSFODNN7EXAMPLE",
          awsSecretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
          jwtSecret: "my-super-secret-jwt-signing-key-123",
        },
      };

      // Global functions for demo buttons
      window.demonstrateBasicRedaction = function () {
        const secureTransport = new DemoTransport("secure-output");
        const insecureTransport = new DemoTransport("insecure-output");

        secureTransport.clear();
        insecureTransport.clear();

        const secureLogger = new Logger({
          redaction: true,
          timestamped: false,
          transports: [secureTransport],
        });

        const insecureLogger = new Logger({
          redaction: false,
          timestamped: false,
          transports: [insecureTransport],
        });

        const testData = {
          username: "john_doe",
          password: "my_secret_password",
          token: "bearer_abc123def456",
          apiKey: "sk_live_1234567890abcdef",
          email: "john@example.com",
        };

        secureLogger.info("User registration", testData);
        insecureLogger.info("User registration", testData);
      };

      window.demonstratePaymentData = function () {
        const secureTransport = new DemoTransport("secure-output");
        const insecureTransport = new DemoTransport("insecure-output");

        const secureLogger = new Logger({
          redaction: true,
          timestamped: false,
          transports: [secureTransport],
        });

        const insecureLogger = new Logger({
          redaction: false,
          timestamped: false,
          transports: [insecureTransport],
        });

        const paymentData = {
          orderId: "order-12345",
          amount: 99.99,
          currency: "USD",
          creditCard: "4532-1234-5678-9012",
          cvv: "123",
          expiryDate: "12/25",
          customerEmail: "customer@example.com",
        };

        secureLogger.info("Processing payment", paymentData);
        insecureLogger.info("Processing payment", paymentData);
      };

      window.demonstrateAuthData = function () {
        const secureTransport = new DemoTransport("secure-output");
        const insecureTransport = new DemoTransport("insecure-output");

        const secureLogger = new Logger({
          redaction: true,
          timestamped: false,
          transports: [secureTransport],
        });

        const insecureLogger = new Logger({
          redaction: false,
          timestamped: false,
          transports: [insecureTransport],
        });

        const authData = {
          userId: "user123",
          jwt: sensitiveData.user.jwt,
          refreshToken: "rt_abc123def456ghi789",
          sessionId: "sess_xyz789",
          authHeader: "Bearer " + sensitiveData.user.token,
        };

        secureLogger.info("Authentication successful", authData);
        insecureLogger.info("Authentication successful", authData);
      };

      window.demonstrateCustomRedaction = function () {
        const mode = document.getElementById("redaction-mode").value;
        const replacement =
          document.getElementById("replacement-text").value || "[REDACTED]";
        const customKeys = document
          .getElementById("custom-keys")
          .value.split(",")
          .map((k) => k.trim())
          .filter((k) => k);
        const excludeKeys = document
          .getElementById("exclude-keys")
          .value.split(",")
          .map((k) => k.trim())
          .filter((k) => k);
        const customPatterns = document
          .getElementById("custom-patterns")
          .value.split("\n")
          .map((p) => p.trim())
          .filter((p) => p);
        const excludePatterns = document
          .getElementById("exclude-patterns")
          .value.split("\n")
          .map((p) => p.trim())
          .filter((p) => p);

        const transport = new DemoTransport("custom-redaction-output");
        transport.clear();

        let redactionConfig;
        switch (mode) {
          case "enabled":
            redactionConfig = {
              keys: true,
              strings: true,
              includeKeys: customKeys.length > 0 ? customKeys : undefined,
              excludeKeys: excludeKeys.length > 0 ? excludeKeys : undefined,
              includePatterns:
                customPatterns.length > 0 ? customPatterns : undefined,
              excludePatterns:
                excludePatterns.length > 0 ? excludePatterns : undefined,
              replacement,
            };
            break;
          case "keys-only":
            redactionConfig = { keys: true, strings: false, replacement };
            break;
          case "strings-only":
            redactionConfig = { keys: false, strings: true, replacement };
            break;
          case "disabled":
            redactionConfig = false;
            break;
        }

        const logger = new Logger({
          redaction: redactionConfig,
          timestamped: false,
          transports: [transport],
        });

        // Test with various data types
        logger.info("Custom redaction test", {
          username: "john_doe",
          password: "secret123",
          customSecret: "my-custom-secret",
          internalId: "internal-456",
          sessionToken: "sess-abc123",
          publicInfo: "this-is-visible",
        });

        logger.info(
          "String patterns test: user-12345 with order-ABC123 and temp-abcd1234"
        );
        logger.info("Bearer token test with bearer sk-1234567890abcdef");
      };

      window.resetRedactionConfig = function () {
        document.getElementById("redaction-mode").value = "enabled";
        document.getElementById("replacement-text").value = "[REDACTED]";
        document.getElementById("custom-keys").value = "";
        document.getElementById("exclude-keys").value = "";
        document.getElementById("custom-patterns").value = "";
        document.getElementById("exclude-patterns").value = "";
        demonstrateCustomRedaction();
      };

      window.demonstrateScenario = function (scenario) {
        const transport = new DemoTransport("scenario-output");
        transport.clear();

        const logger = new Logger({
          redaction: true,
          timestamped: false,
          transports: [transport],
        });

        switch (scenario) {
          case "login":
            const loginLogger = logger.withPrefix("AUTH");
            loginLogger.info("Login attempt", {
              username: "john_doe",
              password: "user_password_123", // Will be redacted
              ip: "192.168.1.100",
              userAgent: "Mozilla/5.0...",
            });
            loginLogger.info("Password verification successful");
            loginLogger.info("JWT generated", {
              userId: "user123",
              token: sensitiveData.user.jwt, // Will be redacted
              expiresIn: "24h",
            });
            break;

          case "api":
            const apiLogger = logger.withPrefix("API");
            apiLogger.info("Incoming request", {
              method: "POST",
              endpoint: "/api/users",
              headers: {
                authorization: "Bearer " + sensitiveData.user.token, // Will be redacted
                "content-type": "application/json",
              },
            });
            apiLogger.info("Request body", {
              name: "John Doe",
              email: "john@example.com",
              password: "new_password_123", // Will be redacted
            });
            break;

          case "database":
            const dbLogger = logger.withPrefix("DB");
            dbLogger.info("Connecting to database", sensitiveData.database);
            dbLogger.info("Query executed", {
              query: "SELECT * FROM users WHERE id = ?",
              params: [123],
              duration: "45ms",
            });
            break;

          case "payment":
            const paymentLogger = logger.withPrefix("PAYMENT");
            paymentLogger.info("Payment processing", {
              orderId: "order-12345",
              amount: 99.99,
              creditCard: sensitiveData.user.creditCard, // Will be redacted
              cvv: sensitiveData.user.cvv, // Will be redacted
              gateway: "stripe",
              stripeKey: sensitiveData.config.stripeKey, // Will be redacted
            });
            break;

          case "config":
            const configLogger = logger.withPrefix("CONFIG");
            configLogger.info(
              "Loading application configuration",
              sensitiveData.config
            );
            configLogger.info("Database configuration", sensitiveData.database);
            break;
        }
      };

      window.demonstrateEnvironments = function () {
        const devTransport = new DemoTransport("dev-output");
        const prodTransport = new DemoTransport("prod-output");

        devTransport.clear();
        prodTransport.clear();

        // Development logger - more permissive
        const devLogger = new Logger({
          redaction: {
            includeKeys: ["password", "secret", "privateKey"], // Only most sensitive
            strings: false, // Don't redact strings in development
            replacement: "[DEV-HIDDEN]",
          },
          timestamped: false,
          transports: [devTransport],
        });

        // Production logger - strict security
        const prodLogger = new Logger({
          redaction: true, // Full redaction
          timestamped: false,
          transports: [prodTransport],
        });

        const testData = {
          username: "john_doe",
          password: "secret123",
          apiKey: "sk_live_1234567890abcdef",
          debugInfo: "helpful-for-development",
          userAgent: "Mozilla/5.0 with bearer token abc123",
        };

        devLogger.info("Environment-aware logging", testData);
        prodLogger.info("Environment-aware logging", testData);
      };

      window.demonstrateBestPractices = function () {
        const transport = new DemoTransport("best-practices-output");
        transport.clear();

        const logger = new Logger({
          redaction: true,
          timestamped: false,
          transports: [transport],
        });

        // Good practices examples
        logger.info("✅ GOOD: Log user actions without sensitive data", {
          userId: "user123",
          action: "profile_update",
          timestamp: new Date().toISOString(),
          ip: "192.168.1.100", // IP is okay to log
        });

        logger.info("✅ GOOD: Log errors with context but no secrets", {
          error: "Database connection failed",
          database: "users_db",
          retryCount: 3,
          // password: 'secret123' // ❌ Don't log passwords even in errors
        });

        logger.info("✅ GOOD: Log API responses without sensitive fields", {
          statusCode: 200,
          responseTime: "125ms",
          endpoint: "/api/users/profile",
          // Don't log: response body with sensitive data
        });

        logger.warn(
          "⚠️ CAREFUL: This would expose sensitive data without redaction",
          {
            username: "john_doe",
            password: "this_would_be_visible_without_redaction",
            apiKey: "sk_live_this_would_also_be_visible",
          }
        );
      };

      // Initialize with basic redaction demo
      document.addEventListener("DOMContentLoaded", function () {
        demonstrateBasicRedaction();
        demonstrateCustomRedaction();
      });
    </script>
  </body>
</html>

Features

  • ✅ Automatic sensitive data detection and redaction
  • ✅ Real-world security scenarios (passwords, tokens, credit cards)
  • ✅ Custom redaction pattern configuration
  • ✅ Side-by-side redacted vs unredacted comparisons
  • ✅ Environment-based redaction settings

🚀 Running This Demo

bash
# Build the package first
pnpm run build

# Start the demo server
node examples/serve.js

# Then open in your browser:
# http://localhost:3000/security-demo.html

This interactive demo is automatically embedded from examples/security-demo.html.

Released under the MIT License.