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+ order-[A-Z0-9]+ temp-\w{8,}"
></textarea>
</div>
<div class="control-group">
<label>Exclude Patterns (one per line)</label>
<textarea
id="exclude-patterns"
placeholder="bearer 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, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
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🔗 Related
- ← Back to Examples Overview
- API Reference - Technical documentation
- Getting Started Guide - Setup instructions
This interactive demo is automatically embedded from examples/security-demo.html.
