WDP Part 11: Security Considerations
Requirements and best practices for handling sensitive data, PII, and security-critical information.
Abstract#
This document specifies security considerations for the Waddling Diagnostic Protocol (WDP). It defines requirements and best practices for handling sensitive data, personally identifiable information (PII), authentication tokens, and other security-critical information within diagnostic messages and catalogs.
Key concepts covered in this document:
- PII detection and sanitization
- Sensitive field handling
- Secure catalog distribution
- Access control for diagnostic data
- Security incident diagnostics
Specification Navigation: See STRUCTURE for an overview of all WDP specification parts.
Conformance#
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
1. Introduction#
1.1 Purpose#
Security is a critical concern when transmitting diagnostic information. This specification provides guidance on:
- PII Protection: Preventing leakage of user data in error messages
- Sensitive Data: Handling credentials, tokens, and secrets
- Data Minimization: Including only necessary information
- Secure Distribution: Protecting catalog integrity
- Compliance: Meeting GDPR, CCPA, and HIPAA requirements
1.2 Scope#
This document covers security considerations for:
- Diagnostic message content
- Field interpolation values
- Catalog file distribution
- Logging and persistence
- Cross-system diagnostic transmission
1.3 Threat Model#
WDP implementations SHOULD consider the following threat vectors:
- Information Disclosure: Leaking PII or secrets in error messages
- Catalog Tampering: Malicious modification of catalog files
- Man-in-the-Middle: Interception of diagnostic data
- Log Injection: Malicious data in diagnostic fields
- Denial of Service: Resource exhaustion via diagnostics
- Privacy Violations: Correlation of user activities
1.4 Security Principles#
Defense in Depth:
┌─────────────────────────────────────────┐
│ 1. Prevention (Do not include PII) │
│ 2. Detection (Sanitize before output) │
│ 3. Mitigation (Redact in logs) │
│ 4. Monitoring (Alert on violations) │
└─────────────────────────────────────────┘2. Personally Identifiable Information (PII)#
2.1 PII Location Rules#
CRITICAL PRINCIPLE: PII MUST be placed in the pii.data field and referenced via {{pii/field}} placeholders in message templates.
Where PII is PROHIBITED:
| Location | Rule | Example Violation |
|---|---|---|
| Catalog Message Templates (hardcoded) | PROHIBITED: NEVER hardcode PII | "User john@example.com failed login" |
| Catalog Descriptions | PROHIBITED: NEVER include PII | "Contact admin@company.com for help" |
| Catalog Hints | PROHIBITED: NEVER include PII | "Call +1-555-0100 for support" |
| Diagnostic Code | PROHIBITED: NEVER include PII | E.Auth.JohnDoe.001 |
| Named Sequences | PROHIBITED: NEVER include PII | TOKEN_JOHN_DOE_EXPIRED |
| Component Names | PROHIBITED: NEVER include PII | UserJohnDoeAuth |
| Primary Names | PROHIBITED: NEVER include PII | JohnDoeToken |
| Tags | PROHIBITED: NEVER include PII | ["user:john@example.com"] |
| f field | PROHIBITED: NEVER put PII here | {"f": {"email": "john@example.com"}} |
| ctx field | PROHIBITED: NEVER put PII here | {"ctx": {"user": {"email": "..."}}} |
| Stack Traces | PROHIBITED: NEVER include PII | Remove username from /home/john/... |
| Namespace | CONDITIONAL: Organization names OK | "acme-corp" (OK), "john-doe" (BAD) |
Where PII MUST appear (if needed):
| Location | Rule | Syntax |
|---|---|---|
| pii.data field | REQUIRED: ONLY place for PII | {"pii": {"v": 1, "data": {"email": "..."}}} |
| Message template placeholders | REQUIRED: Use pii/ prefix | "Login failed for {{pii/email}}" |
Architectural Principle:
┌──────────────────────────────────────────────────────────┐
│ CATALOG (Static, Distributed, Cached) │
│ REQUIRED: NO PII EVER │
│ - Message templates with {{field}} placeholders │
│ - PII placeholders use {{pii/field}} syntax │
│ - Generic descriptions and hints │
│ - No hardcoded personal information │
└──────────────────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ RUNTIME RESPONSE (Dynamic, Ephemeral) │
│ REQUIRED: PII ONLY IN pii.data FIELD │
│ - Regular fields in `f` object │
│ - PII fields in `pii.data` object │
│ - Referenced via {{pii/field}} in templates │
│ - Access control enforced at render time │
└──────────────────────────────────────────────────────────┘Example - CORRECT (No PII in message):
// Catalog:
{
"xY9Kp": {
"code": "E.Auth.Login.001",
"message": "Invalid credentials for user {{user_id}}",
"description": "Authentication failed due to incorrect credentials",
"hints": ["Verify your password", "Check if account is locked"]
}
}
// Runtime (opaque ID only, no PII):
{
"xY9Kp": {
"f": {
"user_id": "usr_8d7f9a2b" // Correct: Opaque identifier (not PII)
}
}
}Example - CORRECT (With PII using pii/ syntax):
// Catalog (PII placeholder):
{
"Ay75d": {
"code": "E.Auth.Token.001",
"message": "Token for {{pii/email}} expired at {{timestamp}}",
"description": "Your session has expired. Please log in again."
}
}
// Runtime (PII in pii.data field):
{
"Ay75d": {
"f": {
"timestamp": "2024-01-15T10:30:00Z" // Correct: Non-PII in f
},
"pii": {
"v": 1,
"data": {
"email": "john@example.com" // Correct: PII in pii.data
}
}
}
}
// Rendered (with PII access):
// "Token for john@example.com expired at 2024-01-15T10:30:00Z"
// Rendered (without PII access):
// "Token for [REDACTED] expired at 2024-01-15T10:30:00Z"Example - WRONG:
// Incorrect: Catalog with hardcoded PII (NEVER DO THIS):
{
"xY9Kp": {
"code": "E.Auth.Login.001",
"message": "Invalid credentials for user john.doe@example.com", // Violation: Hardcoded PII
"description": "Contact support at +1-555-0100", // Violation: Phone number
"hints": ["Email admin@company.com for password reset"] // Violation: Email
}
}
// Incorrect: Runtime with PII in wrong field:
{
"xY9Kp": {
"f": {
"email": "john.doe@example.com", // Violation: PII in f field (should be in pii.data)
"password": "secret123" // CRITICAL VIOLATION: NEVER include passwords anywhere!
}
}
}
// Incorrect: Using wrong placeholder syntax:
{
"message": "Login failed for {{email}}", // Violation: Should be {{pii/email}}
"f": {"email": "john@example.com"} // Violation: And shouldn't be in f at all
}
// Incorrect: PII in ctx field:
{
"ctx": {
"v": 1,
"user": {
"email": "john@example.com" // Violation: PII in ctx (should be in pii.data)
}
}
}2.2 Comprehensive PII Leak Vector Analysis#
The following sections enumerate all possible locations where PII could leak in WDP.
2.2.1 Catalog-Level Vectors (Static Content)
Catalog-level vectors are especially dangerous because catalogs are cached and distributed:
| Vector | Risk | Example Violation | Mitigation |
|---|---|---|---|
| Message Template | CRITICAL | "User {{name}} at {{email}} failed" | Use only technical placeholders |
| Description Field | HIGH | "This affects user john@example.com" | Generic descriptions only |
| Hints Array | MEDIUM | ["Contact John Doe at ext. 5555"] | Generic help only |
| Tags Array | MEDIUM | ["user:alice", "ip:192.168.1.1"] | Technical tags only |
| Docs URL | MEDIUM | "https://wiki.internal/users/john-doe/auth" | Generic documentation URLs |
| Field Names | LOW | "john_doe_email" | Use generic names: email, user_id |
| Namespace | LOW | "john-doe-project" | OK for organization names |
| Generated Timestamp | SAFE | "2024-01-15T10:30:00Z" | Not PII |
Code Examples:
// Incorrect: PII in catalog
{
"diags": {
"xY9Kp": {
"code": "E.Auth.Login.001",
"message": "Login failed for john.doe@example.com", // Violation: Hardcoded email
"description": "User John Doe from IP 192.168.1.100 failed authentication", // Violation: Name + IP
"hints": [
"Contact John at +1-555-0100", // Violation: Name + phone
"Your session ID is abc123xyz" // Violation: Session data
],
"tags": ["user:john-doe", "email:john@example.com"], // Violation: PII in tags
"docs_url": "https://docs.example.com/users/john-doe/auth" // Violation: Username in URL
}
}
}
// Correct: No PII in catalog
{
"diags": {
"xY9Kp": {
"code": "E.Auth.Login.001",
"message": "Authentication failed for user {{user_id}}", // Correct: Placeholder
"description": "Login attempt failed due to invalid credentials", // Correct: Generic
"hints": [
"Verify your password is correct", // Correct: Generic help
"Check if your account is locked" // Correct: No personal info
],
"tags": ["auth", "login", "security"], // Correct: Technical tags
"docs_url": "https://docs.example.com/auth/troubleshooting" // Correct: Generic URL
}
}
}2.2.2 Runtime Field Vectors (Dynamic Content)
Field values are interpolated at runtime. This is where PII is most likely to appear:
| Field Type | Risk | Example | Safe Alternative |
|---|---|---|---|
| CRITICAL | "email": "john@example.com" | "user_id": "usr_abc123" | |
| Phone | CRITICAL | "phone": "+1-555-0100" | "contact_id": "con_xyz789" |
| Name | CRITICAL | "name": "John Doe" | "user_id": "usr_abc123" |
| Address | CRITICAL | "address": "123 Main St" | "location_id": "loc_def456" |
| SSN/Tax ID | CRITICAL | "ssn": "123-45-6789" | Never include |
| Credit Card | CRITICAL | "card": "4111-1111-1111-1111" | "last4": "1111" |
| Password | CRITICAL | "password": "secret123" | NEVER include |
| Token | CRITICAL | "token": "eyJhbGc..." | NEVER include |
| IP Address | MEDIUM | "ip": "192.168.1.100" | "ip_hash": "a1b2c3..." |
| User Agent | MEDIUM | "ua": "Mozilla/5.0..." | "browser": "chrome" |
| Cookie | MEDIUM | "cookie": "session=abc..." | "session_id": "sess_xyz" |
| File Path | MEDIUM | "/home/john/.ssh/key" | "config.json" (relative) |
| Username | MEDIUM | "username": "johndoe" | "user_id": "usr_abc123" |
| Device ID | MEDIUM | "device": "iPhone-John" | "device_id": "dev_abc123" |
2.2.3 Code Structure Vectors
PII MAY be inadvertently encoded in the diagnostic code itself:
// Incorrect: PII in code components
"E.Auth.JohnDoe.001" // Username in Component
"E.JohnDoeService.Token.001" // Username in Component
"E.Auth.JohnDoeToken.001" // Username in Primary
"E.Auth.Token.JOHN_DOE_001" // Username in Sequence
// Correct: Generic code components
"E.Auth.User.001" // Generic Component
"E.UserService.Token.001" // Generic Component
"E.Auth.Token.001" // Generic Primary
"E.Auth.Token.EXPIRED" // Generic Sequence name2.2.4 Stack Trace and Error Context Vectors
System-generated error information can leak PII:
| Vector | Risk | Example | Mitigation |
|---|---|---|---|
| File Paths | MEDIUM | /home/john/app/config.js | Strip username: /app/config.js |
| Usernames in Paths | MEDIUM | C:\Users\JohnDoe\Documents\ | Strip: \Documents\ |
| Process Names | LOW | node-john-doe-app | OK if not personal |
| Environment Variables | HIGH | USER_EMAIL=john@example.com | Sanitize env vars |
| Query Strings | CRITICAL | ?email=john@example.com | Strip params |
| Database Values | CRITICAL | WHERE email='john@...' | Never log queries |
Example - Sanitizing Stack Traces:
function sanitizeStackTrace(stack: string): string {
return stack
.replace(/\/home\/[^\/]+\//g, '/home/USER/') // Remove username
.replace(/C:\\Users\\[^\\]+\\/g, 'C:\\Users\\USER\\') // Windows
.replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, '[EMAIL]') // Emails
.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[SSN]') // SSN
.slice(0, 2000); // Limit length
}2.2.5 Logging and Monitoring Vectors#
Logs and monitoring systems can accumulate PII:
| Vector | Risk | Example | Mitigation |
|-----------------|------|---------|------------|
| Log Messages | HIGH | logger.info('User john@example.com logged in') | Use user_id |
| Structured Logs | HIGH | {user: {email: "..."}} | Sanitize before logging |
| Metrics Tags | MEDIUM | user:john-doe | Use hashed IDs |
| Trace IDs | SAFE | trace_abc123 | Safe if opaque |2.2.6 API Response Vectors#
Responses can leak PII in multiple locations:
{
"success": false,
"error": "Authentication failed for john.doe@example.com", // Violation: Message
"user": {
"email": "john.doe@example.com", // Violation: Application data
"phone": "+1-555-0100" // Violation: Application data
},
"wd": {
"xY9Kp": {
"f": {
"email": "john.doe@example.com", // Violation: Diagnostic field
"ip_address": "192.168.1.100", // Violation: Diagnostic field
"session_token": "abc123xyz" // Violation: Diagnostic field
}
}
}
}2.3 PII Field Structure#
The pii field is an OPTIONAL object at the same level as f, st, src, and ctx.
Wire Format:
{
"h": "Ay75d",
"f": {
"timestamp": "2024-01-15T10:30:00Z",
"count": 42
},
"pii": {
"v": 1,
"data": {
"email": "john@example.com",
"ip": "192.168.1.1"
}
}
}2.4 PII Definition#
Personally Identifiable Information (PII) is any data that can be used to identify, contact, or locate a specific individual.
| Category | Examples | Risk Level |
|---|---|---|
| Direct Identifiers | Full name, SSN, passport number | CRITICAL |
| Contact Information | Email, phone, physical address | HIGH |
| Financial Data | Credit card, bank account, tax ID | CRITICAL |
| Government IDs | Driver's license, national ID | CRITICAL |
| Biometric Data | Fingerprints, facial recognition | CRITICAL |
| Health Information | Medical records, diagnoses | CRITICAL (HIPAA) |
| Online Identifiers | IP address, device ID, cookie ID | MEDIUM |
| Demographics | Age, gender, ethnicity, location | MEDIUM |
| Authentication | Passwords, tokens, API keys | CRITICAL |
2.5 PII In WDP Context#
Diagnostics MUST NOT include PII directly. Use generic identifiers instead.
2.6 PII Sanitization Rules#
Rule 1: Use Opaque IDs Instead of PII
// Incorrect:
const diagnostic = {
compactId: "aG8eT",
fields: {
email: "alice@example.com",
name: "Alice Johnson"
}
};
// Correct:
const diagnostic = {
compactId: "aG8eT",
fields: {
user_id: "usr_9k3m2n1p",
field: "email"
}
};2.7 PII Detection Patterns#
const PII_PATTERNS = {
email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/,
phone: /\b(\+\d{1,3}[- ]?)?\(?\d{3}\)?[- ]?\d{3}[- ]?\d{4}\b/,
ssn: /\b\d{3}-\d{2}-\d{4}\b/,
credit_card: /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/,
ip_address: /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,
jwt: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/
};
function containsPII(text: string): boolean {
return Object.values(PII_PATTERNS).some(pattern => pattern.test(text));
}2.8 Catalog Metadata for PII Warnings#
Catalog entries SHOULD indicate whether a diagnostic uses PII placeholders:
{
"Ay75d": {
"code": "E.AUTH.TOKEN.001",
"message": "Token for {{pii/email}} expired at {{timestamp}}",
"description": "Your session has expired.",
"contains_pii": true,
"pii_fields": ["email"],
"requires_pii_access": true
}
}2.10 PII Audit Checklist#
Use this checklist when creating or reviewing diagnostics:
Catalog Files (Static Content)
[ ] No hardcoded PII in messages
[ ] PII uses {{pii/field}} placeholder syntax
[ ] Regular fields use {{field}} syntax
[ ] contains_pii: true metadata present when using {{pii/...}}
Diagnostic Codes (Structure)
[ ] Component name does not contain PII
[ ] Primary name does not contain PII
[ ] Sequence does not contain PII
Runtime Fields (Wire Format)
[ ] PII data is in pii.data field ONLY
[ ] Non-PII data is in f field
[ ] No PII in ctx field
[ ] No PII in st (stack trace) field2.11 PII Quick Reference Summary#
| Data Type | In Catalog? | In Runtime Fields? | Best Practice |
|---|---|---|---|
| User email | PROHIBITED | CONDITIONAL: Avoid (use user_id) | user_id: "usr_abc123" |
| User name | PROHIBITED | CONDITIONAL: Avoid (use user_id) | user_id: "usr_abc123" |
| Phone number | PROHIBITED | PROHIBITED | Use contact ID instead |
| IP address | PROHIBITED | CONDITIONAL: Hash only | ip_hash: "a1b2c3..." |
| Password | PROHIBITED | PROHIBITED | NEVER INCLUDE |
| Token/API key | PROHIBITED | PROHIBITED | NEVER INCLUDE |
| Credit card | PROHIBITED | CONDITIONAL: Last 4 only | last4: "1111" |
| SSN/Tax ID | PROHIBITED | PROHIBITED | NEVER INCLUDE |
| Session ID | PROHIBITED | CONDITIONAL: Opaque only | session_id: "sess_xyz" |
| User ID (opaque) | ALLOWED: Placeholder | ALLOWED | {{user_id}} -> usr_abc123 |
| Order ID | ALLOWED: Placeholder | ALLOWED | {{order_id}} -> ord_xyz789 |
| Timestamp | ALLOWED: Placeholder | ALLOWED | {{timestamp}} -> 2024-01-15T... |
| Error type | ALLOWED | ALLOWED | validation_failed |
| Field name | ALLOWED | ALLOWED | "field": "email" (not the value) |
| File path | PROHIBITED: Full path | CONDITIONAL: Relative only | config.json not /home/user/... |
| Stack trace | PROHIBITED | CONDITIONAL: Sanitized only | Remove usernames from paths |
3. Sensitive Data in Diagnostics#
3.1 Types of Sensitive Data#
Beyond PII, the following categories of sensitive data require protection:
| Data Type | Examples | Protection |
|---|---|---|
| Credentials | Passwords, API keys | NEVER include |
| Tokens | JWT, OAuth tokens | NEVER include |
| Secrets | Encryption keys, certificates | NEVER include |
| Session Data | Session IDs, cookies | Use opaque IDs |
| Business Logic | Pricing, algorithms | Limit exposure |
| System Info | Internal paths, versions | Sanitize for production |
3.2 Authentication Errors#
{
"E.Auth.Token.001": {
"message": "Invalid token: {{token}}",
"fields": ["token"]
}
}
// Runtime:
{
"xY9Kp": {
"f": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
}{
"E.Auth.Token.001": {
"message": "Token validation failed",
"fields": []
}
}
// Runtime:
{
"xY9Kp": {}
}{
"E.Auth.Token.002": {
"message": "Token expired at {{expired_at}}",
"fields": ["expired_at"]
}
}
// Runtime:
{
"xY9Kp": {
"f": {
"expired_at": "2024-01-15T10:30:00Z"
}
}
}3.3 Database Errors#
{
"message": "Query failed: SELECT * FROM users WHERE email='{{email}}' AND password_hash='{{hash}}'"
}{
"message": "Database query failed",
"fields": []
}{
"message": "Database query failed on table {{table}}",
"fields": ["table"]
}
// Runtime:
{
"f": {
"table": "users" // Table name OK, no data
}
}3.4 File System Errors#
{
"message": "File not found: /home/admin/.ssh/id_rsa"
}{
"message": "Configuration file not found: {{filename}}",
"fields": ["filename"]
}
// Runtime:
{
"f": {
"filename": "config.json" // Relative path only
}
}4. Field-Level Security#
4.1 Sensitive Field Declaration#
Sensitive fields SHOULD be marked in the catalog:
{
"diags": {
"xY9Kp": {
"code": "E.Payment.Process.001",
"message": "Payment failed for order {{order_id}}",
"fields": ["order_id"],
"field_security": {
"order_id": {
"sensitive": false,
"redact_in_logs": false
}
}
},
"mN3Yr": {
"code": "E.Payment.Card.002",
"message": "Card validation failed",
"fields": ["last4"],
"field_security": {
"last4": {
"sensitive": true,
"redact_in_logs": true,
"max_log_level": "warn"
}
}
}
}
}4.2 Runtime Field Sanitization#
interface FieldSecurityPolicy {
sensitive: boolean;
redact_in_logs: boolean;
redaction_strategy: 'hash' | 'mask' | 'remove';
allowed_contexts: ('api' | 'logs' | 'monitoring')[];
}
function sanitizeField(
value: any,
policy: FieldSecurityPolicy,
context: string
): any {
if (!policy.sensitive) {
return value;
}
if (!policy.allowed_contexts.includes(context)) {
return '[REDACTED]';
}
switch (policy.redaction_strategy) {
case 'hash':
return hashValue(value);
case 'mask':
return maskValue(value);
case 'remove':
return undefined;
default:
return value;
}
}4.3 Field Value Limits#
To prevent exfiltration via oversized fields, implementations SHOULD enforce limits:
const FIELD_LIMITS = {
max_string_length: 1000,
max_array_length: 100,
max_object_depth: 3,
max_total_size: 10 * 1024 // 10KB
};
function validateFieldSize(fields: Record<string, any>): void {
const json = JSON.stringify(fields);
if (json.length > FIELD_LIMITS.max_total_size) {
throw new Error('Diagnostic fields exceed size limit');
}
// Additional validation...
}4.4 Field Injection Prevention#
To prevent log injection attacks, implementations MUST sanitize field values:
function sanitizeForLogs(value: string): string {
return value
// Remove control characters, but keep \t, \n, \r for now
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '')
// Replace newlines and tabs with a space
.replace(/[\r\n\t]/g, ' ')
.trim()
.slice(0, 500); // Max length
}
// Usage:
logger.error(`Validation failed`, {
diagnostic: "E.Validation.Field.001",
field: sanitizeForLogs(userInput)
});5. Catalog Security#
5.1 Catalog Integrity#
Implementations SHOULD use Subresource Integrity (SRI) for catalog files:
<script
src="https://cdn.example.com/wdp/catalog-v1.0.0.json"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>SRI Hash Generation:
openssl dgst -sha384 -binary catalog.json | openssl base64 -A5.2 Catalog Signing#
Catalog files SHOULD be signed for authenticity:
{
"version": "1.0.0",
"generated": "2024-01-15T10:30:00Z",
"diags": { /* ... */ },
"signature": {
"algorithm": "RS256",
"public_key_url": "https://example.com/wdp/public-key.pem",
"signature": "MEUCIQDx..."
}
}Verification:
async function verifyCatalog(catalog: any): Promise<boolean> {
const { signature, ...content } = catalog;
const publicKey = await fetchPublicKey(signature.public_key_url);
return verifySignature(
JSON.stringify(content),
signature.signature,
publicKey,
signature.algorithm
);
}5.3 Catalog Access Control#
Implementations SHOULD restrict access to sensitive catalogs:
GET /api/wdp/catalog-internal.json HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Server-side validation example:
async function serveCatalog(req: Request): Promise<Response> {
const user = await authenticate(req);
if (!user.hasRole('admin')) {
// Return public catalog only
return sendFile('catalog-public.json');
}
// Return full catalog with internal diagnostics
return sendFile('catalog-internal.json');
}5.4 Catalog Content Security#
Implementations MUST prevent XSS vulnerabilities in catalog messages:
{
"diags": {
"xY9Kp": {
"code": "E.Input.Validation.001",
"message": "Invalid value: {{value}}",
"fields": ["value"]
}
}
}Client-side escaping example:
function displayDiagnostic(diagnostic: ExpandedDiagnostic): string {
// Always escape user-provided values
const escaped = escapeHtml(diagnostic.message);
return escaped;
}
function escapeHtml(text: string): string {
return text
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}6. Transport Security#
6.1 HTTPS Requirements#
Implementations MUST use HTTPS for:
- Catalog distribution
- Diagnostic API responses
- Any transmission containing diagnostics
// Enforce HTTPS in production
if (process.env.NODE_ENV === 'production') {
if (req.protocol !== 'https') {
return res.redirect(301, `https://${req.hostname}${req.url}`);
}
}6.2 TLS Configuration#
Implementations SHOULD enforce minimum TLS version requirements:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;6.3 Certificate Pinning#
For mobile and desktop applications, implementations MAY use certificate pinning:
const trustedCertificates = [
'-----BEGIN CERTIFICATE-----\nMIID...\n-----END CERTIFICATE-----'
];
async function fetchCatalogSecure(url: string): Promise<Catalog> {
const response = await fetch(url, {
// Verify certificate
agent: new https.Agent({
ca: trustedCertificates
})
});
return response.json();
}6.4 Rate Limiting#
To prevent denial-of-service attacks via diagnostic flooding, implementations SHOULD implement rate limiting:
const rateLimiter = new RateLimiter({
windowMs: 60 * 1000, // 1 minute
max: 100, // Max 100 diagnostics per minute per IP
message: {
"C.RateLimit.Exceeded.001": {
"f": {
"retry_after": "60"
}
}
}
});
app.use('/api/diagnostics', rateLimiter);7. Logging and Storage#
7.1 Log Sanitization#
Implementations MUST sanitize diagnostic data before logging:
class SecureLogger {
private sanitize(message: string, fields: Record<string, any>): void {
// Check for PII
if (containsPII(message)) {
throw new Error('PII detected in log message');
}
// Redact sensitive fields
const sanitized = { ...fields };
for (const [key, value] of Object.entries(sanitized)) {
if (SENSITIVE_FIELD_NAMES.includes(key)) {
sanitized[key] = '[REDACTED]';
}
}
return sanitized;
}
error(message: string, fields?: Record<string, any>): void {
const sanitized = this.sanitize(message, fields || {});
console.error(message, sanitized);
}
}7.2 Log Retention#
Implementations SHOULD implement retention policies:
interface LogRetentionPolicy {
severity: string;
retention_days: number;
archive_after_days: number;
purge_pii: boolean;
}
const RETENTION_POLICIES: Record<string, LogRetentionPolicy> = {
'E': { retention_days: 90, archive_after_days: 30, purge_pii: true },
'W': { retention_days: 60, archive_after_days: 30, purge_pii: true },
'C': { retention_days: 365, archive_after_days: 90, purge_pii: false },
'I': { retention_days: 30, archive_after_days: 7, purge_pii: true }
};7.3 Encrypted Storage#
Implementations SHOULD encrypt diagnostic logs at rest:
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
class EncryptedLogStorage {
private algorithm = 'aes-256-gcm';
private key: Buffer;
constructor(encryptionKey: string) {
this.key = Buffer.from(encryptionKey, 'hex');
}
encrypt(data: string): { encrypted: string; iv: string; tag: string } {
const iv = randomBytes(16);
const cipher = createCipheriv(this.algorithm, this.key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
const tag = cipher.getAuthTag();
return {
encrypted,
iv: iv.toString('hex'),
tag: tag.toString('hex')
};
}
}7.4 Audit Logging#
Implementations SHOULD log security-relevant diagnostics:
interface AuditLog {
timestamp: string;
user_id?: string;
ip_address?: string;
diagnostic_code: string;
compact_id: string;
severity: string;
action: string;
result: 'success' | 'failure';
}
function logSecurityEvent(diagnostic: Diagnostic, context: AuditContext): void {
if (SECURITY_DIAGNOSTICS.includes(diagnostic.code)) {
auditLogger.log({
timestamp: new Date().toISOString(),
user_id: context.userId,
ip_address: hashIP(context.ipAddress), // Hash IP for privacy
diagnostic_code: diagnostic.code,
compact_id: diagnostic.compactId,
severity: diagnostic.severity,
action: context.action,
result: context.result
});
}
}8. Access Control#
8.1 Diagnostic Visibility Levels#
Implementations SHOULD define visibility levels for diagnostic content:
enum VisibilityLevel {
PUBLIC = 0, // User-facing diagnostics
INTERNAL = 1, // Internal team only
CONFIDENTIAL = 2, // Security team only
RESTRICTED = 3 // Admin only
}
interface DiagnosticMetadata {
code: string;
visibility: VisibilityLevel;
requires_role?: string[];
}8.2 Role-Based Diagnostic Filtering#
function filterDiagnosticsByRole(
diagnostics: Diagnostic[],
userRole: string
): Diagnostic[] {
const roleLevel = ROLE_LEVELS[userRole] || 0;
return diagnostics.filter(diag => {
const metadata = DIAGNOSTIC_METADATA[diag.code];
return metadata.visibility <= roleLevel;
});
}
// Usage:
app.get('/api/diagnostics', authenticate, (req, res) => {
const allDiagnostics = getDiagnostics();
const filtered = filterDiagnosticsByRole(allDiagnostics, req.user.role);
res.json({ diagnostics: filtered });
});8.3 Context-Aware Diagnostics#
Implementations SHOULD provide different detail levels for different contexts:
function generateDiagnostic(
code: string,
context: 'api' | 'internal' | 'monitoring'
): Diagnostic {
const base = DIAGNOSTICS[code];
switch (context) {
case 'api':
// Minimal details for external API
return {
code: base.code,
compact_id: base.compact_id,
message: base.message_public,
severity: base.severity
};
case 'internal':
// Full details for internal use
return {
code: base.code,
compact_id: base.compact_id,
message: base.message_internal,
severity: base.severity,
description: base.description,
hints: base.hints,
stack_trace: base.stack_trace
};
case 'monitoring':
// Metrics-focused
return {
code: base.code,
compact_id: base.compact_id,
severity: base.severity,
tags: base.tags,
metrics: base.metrics
};
}
}9. Security Incident Diagnostics#
9.1 Security-Specific Severity#
Implementations SHOULD use Critical (C) severity for security incidents:
{
"C.Security.Breach.001": {
"code": "C.Security.Breach.001",
"severity": "C",
"message": "Unauthorized access attempt detected",
"description": "Multiple failed authentication attempts from {{ip_hash}}",
"fields": ["ip_hash"],
"security_level": "critical",
"alert_team": "security",
"requires_investigation": true
}
}9.2 Security Diagnostic Categories#
The following table defines common security diagnostic code patterns:
| Code Pattern | Use Case | Example |
|---|---|---|
| C.Security.Auth.* | Authentication failures | Brute force, credential stuffing |
| C.Security.Access.* | Authorization failures | Privilege escalation attempts |
| C.Security.Injection.* | Injection attacks | SQL injection, XSS attempts |
| C.Security.Integrity.* | Data tampering | Checksum failures, replay attacks |
| W.Security.Config.* | Configuration issues | Weak passwords, missing encryption |
9.3 Incident Response Integration#
interface SecurityIncident {
diagnostic_code: string;
compact_id: string;
severity: 'C' | 'E' | 'W';
timestamp: string;
source_ip_hash: string;
user_id_hash?: string;
attack_vector: string;
automated_response: boolean;
}
async function handleSecurityDiagnostic(
diagnostic: Diagnostic,
context: RequestContext
): Promise<void> {
if (diagnostic.severity === 'C') {
// Create incident
const incident: SecurityIncident = {
diagnostic_code: diagnostic.code,
compact_id: diagnostic.compact_id,
severity: diagnostic.severity,
timestamp: new Date().toISOString(),
source_ip_hash: hashIP(context.ip),
attack_vector: classifyAttack(diagnostic),
automated_response: true
};
// Alert security team
await alertSecurityTeam(incident);
// Take automated action
if (shouldBlockIP(incident)) {
await blockIP(context.ip, '1h');
}
}
}9.4 Security Diagnostic Best Practices#
Recommended practices:
- Log security diagnostics to separate, secured audit log
- Use generic messages for authentication failures
- Include metadata for investigation (hashed IPs, timestamps)
- Alert security team for critical diagnostics
- Implement automated responses (IP blocking, rate limiting)
Practices to avoid:
- Include sensitive data in security diagnostics
- Expose system internals in error messages
- Log raw IP addresses (hash them)
- Provide detailed feedback to attackers
- Mix security logs with application logs
10. Compliance Considerations#
WDP provides mechanisms for separating, redacting, and controlling access to sensitive data (see PII handling in Sections 3-6). However, WDP conformance does NOT guarantee compliance with any regulatory framework including but not limited to GDPR, CCPA, HIPAA, PCI DSS, or SOC 2.
Compliance with applicable data protection laws and industry standards is the sole responsibility of the implementer. Organizations SHOULD consult qualified legal and security professionals when implementing systems that handle regulated data.
WDP provides tools, not compliance:
- PII field separation (
pii.data): enables access control - Redaction suffixes (
:masked,:raw): enables data minimization - Structured logging patterns: enables audit trails
- Field validation examples: demonstrates security patterns
Implementers are responsible for:
- Determining which data constitutes PII in their jurisdiction
- Implementing appropriate retention and deletion policies
- Ensuring encryption in transit and at rest
- Conducting security audits and compliance assessments
- Training personnel on data handling requirements
11. Implementation Guidelines#
11.1 Secure by Default#
Framework configuration example:
// WDP Configuration
const wdpConfig = {
security: {
// PII Detection
pii_detection: {
enabled: true,
mode: 'strict', // 'strict' | 'moderate' | 'disabled'
patterns: [...DEFAULT_PII_PATTERNS, ...CUSTOM_PATTERNS],
on_violation: 'throw' // 'throw' | 'redact' | 'warn'
},
// Field Sanitization
field_sanitization: {
enabled: true,
max_field_length: 1000,
max_fields: 50,
sanitize_html: true,
sanitize_sql: true
},
// Logging
logging: {
redact_sensitive_fields: true,
sensitive_field_patterns: [/password/i, /token/i, /secret/i],
log_level: 'info',
include_stack_trace: process.env.NODE_ENV !== 'production'
},
// Transport
transport: {
require_https: process.env.NODE_ENV === 'production',
enforce_cors: true
}
}
}12. Security Checklist#
Use this final checklist before deploying WDP-based systems:
[ ] PII Separation: All PII is in pii.data or redacted
[ ] Sanitization: Logs are free of secrets and PII
[ ] Access Control: Diagnostic API enforces roles
[ ] Transport: HTTPS enforced for all traffic
[ ] Integrity: Catalogs use SRI and signing
[ ] Validation: Input fields sanitized securely
[ ] Compliance: Legal team reviewed data handling
[ ] Audit: Security events are logged to audit trail