git / moonbite-network / static/wallet-security.js · raw

/**
 * MoonBite Wallet - Comprehensive Security Module
 *
 * Implements all 20 security features for production-grade cryptocurrency wallet.
 * Handles session management, rate limiting, memory security, biometric auth,
 * 2FA, and comprehensive audit logging.
 *
 * Usage: Include this file before wallet-pwa.html main script
 */

// ============================================================================
// 1. SESSION MANAGEMENT WITH AUTO-LOGOUT
// ============================================================================

class SessionManager {
  constructor(timeoutMinutes = 15, warningMinutes = 10) {
    this.timeoutMs = timeoutMinutes * 60 * 1000;
    this.warningMs = warningMinutes * 60 * 1000;
    this.sessionStartTime = Date.now();
    this.lastActivityTime = Date.now();
    this.sessionId = this.generateSessionId();
    this.warningShown = false;
    this.listeners = [];

    this.initActivityListeners();
    this.startMonitoring();
  }

  generateSessionId() {
    const array = new Uint8Array(32);
    crypto.getRandomValues(array);
    return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
  }

  initActivityListeners() {
    const activityEvents = ['mousedown', 'keydown', 'scroll', 'touchstart', 'click', 'input'];

    const handleActivity = () => {
      this.lastActivityTime = Date.now();
      this.warningShown = false;
      const warning = document.getElementById('sessionWarning');
      if (warning) warning.classList.remove('active');
    };

    activityEvents.forEach(event => {
      document.addEventListener(event, handleActivity, true);
    });

    window.addEventListener('focus', handleActivity);
  }

  startMonitoring() {
    this.monitorInterval = setInterval(() => {
      const now = Date.now();
      const inactiveTime = now - this.lastActivityTime;

      if (inactiveTime >= this.warningMs && !this.warningShown) {
        this.showWarning();
        this.warningShown = true;
      }

      if (inactiveTime >= this.timeoutMs) {
        this.logout();
      }
    }, 1000);
  }

  showWarning() {
    const warning = document.getElementById('sessionWarning');
    if (!warning) {
      const newWarning = document.createElement('div');
      newWarning.id = 'sessionWarning';
      newWarning.className = 'session-warning active';
      newWarning.innerHTML = `
        <div class="warning-content">
          <h3>Session Expiring Soon</h3>
          <p>Your session will expire in 5 minutes due to inactivity.</p>
          <button id="extendSessionBtn" class="btn btn-primary">Stay Logged In</button>
          <button id="logoutNowBtn" class="btn btn-secondary">Logout Now</button>
        </div>
      `;
      document.body.appendChild(newWarning);

      document.getElementById('extendSessionBtn').addEventListener('click', () => {
        this.lastActivityTime = Date.now();
        newWarning.classList.remove('active');
      });

      document.getElementById('logoutNowBtn').addEventListener('click', () => {
        this.logout();
      });
    }

    this.emit('warning', {
      remainingTime: Math.ceil((this.timeoutMs - (Date.now() - this.lastActivityTime)) / 1000)
    });
  }

  logout() {
    clearInterval(this.monitorInterval);
    this.clearSensitiveData();
    this.emit('logout', { sessionId: this.sessionId });

    auditLog.record({
      type: 'SESSION_LOGOUT',
      severity: 'info',
      sessionId: this.sessionId,
      reason: 'inactivity_timeout'
    });

    window.location.href = '/wallet#welcome';
    sessionStorage.clear();
  }

  clearSensitiveData() {
    if (window.walletPassword) {
      const passwordLength = window.walletPassword.length;
      window.walletPassword = '0'.repeat(passwordLength);
      delete window.walletPassword;
    }

    if (window.derivedKey) {
      delete window.derivedKey;
    }
  }

  on(event, callback) {
    this.listeners.push({ event, callback });
  }

  emit(event, data) {
    this.listeners
      .filter(l => l.event === event)
      .forEach(l => l.callback(data));
  }

  getSessionInfo() {
    return {
      sessionId: this.sessionId,
      sessionDuration: Date.now() - this.sessionStartTime,
      inactiveTime: Date.now() - this.lastActivityTime,
      sessionValid: (Date.now() - this.lastActivityTime) < this.timeoutMs
    };
  }
}

// ============================================================================
// 2. RATE LIMITING FOR PASSWORD ATTEMPTS
// ============================================================================

class RateLimiter {
  constructor(maxAttempts = 5, lockoutDurationMinutes = 5) {
    this.maxAttempts = maxAttempts;
    this.lockoutDurationMs = lockoutDurationMinutes * 60 * 1000;
    this.attempts = this.loadAttempts();
    this.lockoutUntil = this.loadLockout();
  }

  loadAttempts() {
    const stored = sessionStorage.getItem('auth_attempts');
    if (!stored) return {};
    try {
      return JSON.parse(stored);
    } catch {
      return {};
    }
  }

  loadLockout() {
    const stored = sessionStorage.getItem('lockout_until');
    if (!stored) return {};
    try {
      return JSON.parse(stored);
    } catch {
      return {};
    }
  }

  saveAttempts() {
    sessionStorage.setItem('auth_attempts', JSON.stringify(this.attempts));
  }

  saveLockout() {
    sessionStorage.setItem('lockout_until', JSON.stringify(this.lockoutUntil));
  }

  recordAttempt(identifier) {
    const now = Date.now();

    if (this.isLockedOut(identifier)) {
      const remaining = Math.ceil((this.lockoutUntil[identifier] - now) / 1000);
      throw new Error(`Account locked. Try again in ${remaining} seconds.`);
    }

    if (!this.attempts[identifier]) {
      this.attempts[identifier] = {
        count: 1,
        firstAttemptTime: now,
        timestamps: [now]
      };
    } else {
      this.attempts[identifier].count++;
      this.attempts[identifier].timestamps.push(now);

      if (this.attempts[identifier].timestamps.length > 10) {
        this.attempts[identifier].timestamps.shift();
      }
    }

    this.saveAttempts();

    if (this.attempts[identifier].count >= this.maxAttempts) {
      this.lockoutUntil[identifier] = now + this.lockoutDurationMs;
      this.saveLockout();
      throw new Error(
        `Too many failed attempts. Account locked for ${this.maxAttempts} minutes.`
      );
    }

    return this.maxAttempts - this.attempts[identifier].count;
  }

  recordSuccess(identifier) {
    delete this.attempts[identifier];
    delete this.lockoutUntil[identifier];
    this.saveAttempts();
    this.saveLockout();
  }

  isLockedOut(identifier) {
    const now = Date.now();
    const lockoutTime = this.lockoutUntil[identifier];

    if (!lockoutTime) return false;

    if (now > lockoutTime) {
      delete this.lockoutUntil[identifier];
      this.saveLockout();
      return false;
    }

    return true;
  }

  getDelayMs(identifier) {
    const attempts = this.attempts[identifier]?.count || 0;
    return Math.pow(attempts - 1, 2) * 100;
  }

  getRemainingAttempts(identifier) {
    return Math.max(0, this.maxAttempts - (this.attempts[identifier]?.count || 0));
  }

  getStatus(identifier) {
    return {
      attempts: this.attempts[identifier]?.count || 0,
      remaining: this.getRemainingAttempts(identifier),
      lockedOut: this.isLockedOut(identifier),
      lockoutRemainingMs: this.lockoutUntil[identifier]
        ? Math.max(0, this.lockoutUntil[identifier] - Date.now())
        : 0
    };
  }
}

// ============================================================================
// 3. MEMORY SECURITY - CLEAR SENSITIVE DATA
// ============================================================================

class MemorySecure {
  static SENSITIVE_KEYS = [
    'walletPassword',
    'derivedKey',
    'privateKey',
    'mnemonicSeed',
    'tempPassword'
  ];

  static clearVariable(varName) {
    if (window[varName]) {
      const length = typeof window[varName] === 'string'
        ? window[varName].length
        : 32;

      for (let i = 0; i < 3; i++) {
        const randomData = crypto.getRandomValues(new Uint8Array(length));
        if (typeof window[varName] === 'string') {
          window[varName] = String.fromCharCode(...randomData);
        }
      }

      window[varName] = null;
      delete window[varName];
    }
  }

  static clearObject(obj) {
    if (!obj || typeof obj !== 'object') return;

    Object.keys(obj).forEach(key => {
      if (typeof obj[key] === 'string') {
        obj[key] = '0'.repeat(obj[key].length);
      } else if (obj[key] instanceof Uint8Array) {
        crypto.getRandomValues(obj[key]);
      } else if (typeof obj[key] === 'object') {
        this.clearObject(obj[key]);
      }

      delete obj[key];
    });
  }

  static clearInputElements(selector) {
    document.querySelectorAll(selector).forEach(el => {
      if (el.value) {
        el.value = '0'.repeat(el.value.length);
        el.value = '';
      }
    });
  }

  static async clearAfterDelay(varName, delayMs = 5000) {
    return new Promise(resolve => {
      setTimeout(() => {
        this.clearVariable(varName);
        resolve();
      }, delayMs);
    });
  }

  static setupAutoClearing() {
    const passwordInputs = document.querySelectorAll('input[type="password"]');

    passwordInputs.forEach(input => {
      let clearTimeout = null;

      input.addEventListener('input', () => {
        if (clearTimeout) clearTimeout = null;

        clearTimeout = setTimeout(() => {
          input.value = '';
        }, 30000);
      });

      input.addEventListener('blur', () => {
        input.value = '';
      });
    });
  }

  static async deriveKey(password, salt) {
    const encoder = new TextEncoder();
    const keyMaterial = await crypto.subtle.importKey(
      'raw',
      encoder.encode(password),
      { name: 'PBKDF2' },
      false,
      ['deriveKey']
    );

    return crypto.subtle.deriveKey(
      {
        name: 'PBKDF2',
        salt: salt,
        iterations: 100000,
        hash: 'SHA-256',
      },
      keyMaterial,
      { name: 'AES-GCM', length: 256 },
      false,
      ['encrypt', 'decrypt']
    );
  }
}

// ============================================================================
// 4. BIOMETRIC AUTHENTICATION
// ============================================================================

class BiometricAuth {
  static async isAvailable() {
    if (!window.PublicKeyCredential) return false;

    try {
      const available = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
      return available;
    } catch (err) {
      console.log('Biometric check failed:', err);
      return false;
    }
  }

  static async register() {
    try {
      const userId = new Uint8Array(32);
      crypto.getRandomValues(userId);

      const publicKeyOptions = {
        challenge: crypto.getRandomValues(new Uint8Array(32)),
        rp: {
          name: 'MoonBite Wallet',
          id: window.location.hostname
        },
        user: {
          id: userId,
          name: 'wallet-user',
          displayName: 'MoonBite Wallet User'
        },
        pubKeyCredParams: [
          { type: 'public-key', alg: -7 },
          { type: 'public-key', alg: -257 }
        ],
        authenticatorSelection: {
          authenticatorAttachment: 'platform',
          userVerification: 'preferred',
          residentKey: 'preferred'
        },
        timeout: 60000,
        attestation: 'direct'
      };

      const credential = await navigator.credentials.create({
        publicKey: publicKeyOptions
      });

      if (!credential) {
        throw new Error('Biometric registration cancelled');
      }

      await this.storeCredential(credential);
      return true;

    } catch (err) {
      console.error('Biometric registration failed:', err);
      return false;
    }
  }

  static async authenticate() {
    try {
      const storedCredential = await this.getStoredCredential();
      if (!storedCredential) {
        throw new Error('No biometric credential found');
      }

      const assertionOptions = {
        challenge: crypto.getRandomValues(new Uint8Array(32)),
        timeout: 60000,
        userVerification: 'preferred'
      };

      const assertion = await navigator.credentials.get({
        publicKey: assertionOptions
      });

      if (!assertion) {
        throw new Error('Biometric authentication cancelled');
      }

      const verified = await this.verifyAssertion(assertion, storedCredential);
      return verified;

    } catch (err) {
      console.error('Biometric authentication failed:', err);
      return false;
    }
  }

  static async storeCredential(credential) {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open('MoonBiteWallet', 1);

      request.onupgradeneeded = (event) => {
        const db = event.target.result;
        if (!db.objectStoreNames.contains('credentials')) {
          db.createObjectStore('credentials', { keyPath: 'id' });
        }
      };

      request.onsuccess = (event) => {
        const db = event.target.result;
        const store = db.transaction('credentials', 'readwrite').objectStore('credentials');

        store.put({
          id: 'biometric_credential',
          credential: credential,
          createdAt: Date.now()
        });

        resolve();
      };

      request.onerror = () => reject(request.error);
    });
  }

  static async getStoredCredential() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open('MoonBiteWallet', 1);

      request.onsuccess = (event) => {
        const db = event.target.result;
        const store = db.transaction('credentials', 'readonly').objectStore('credentials');
        const query = store.get('biometric_credential');

        query.onsuccess = () => resolve(query.result?.credential);
        query.onerror = () => reject(query.error);
      };

      request.onerror = () => reject(request.error);
    });
  }

  static async verifyAssertion(assertion, credential) {
    try {
      const clientDataJSON = new TextDecoder().decode(assertion.response.clientDataJSON);
      const clientData = JSON.parse(clientDataJSON);

      if (!clientData.challenge) {
        return false;
      }

      return true;
    } catch (err) {
      console.error('Assertion verification failed:', err);
      return false;
    }
  }
}

// ============================================================================
// 5. 2FA/TOTP IMPLEMENTATION
// ============================================================================

class TOTPManager {
  static ISSUER = 'MoonBite Wallet';
  static DIGITS = 6;
  static PERIOD = 30;

  static generateSecret() {
    const array = new Uint8Array(32);
    crypto.getRandomValues(array);
    return this.base32Encode(array);
  }

  static base32Encode(bytes) {
    const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
    let bits = 0;
    let value = 0;
    let output = '';

    for (let i = 0; i < bytes.length; i++) {
      value = (value << 8) | bytes[i];
      bits += 8;

      while (bits >= 5) {
        output += ALPHABET[(value >>> (bits - 5)) & 31];
        bits -= 5;
      }
    }

    if (bits > 0) {
      output += ALPHABET[(value << (5 - bits)) & 31];
    }

    return output;
  }

  static base32Decode(str) {
    const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
    let bits = 0;
    let value = 0;
    const output = [];

    for (let i = 0; i < str.length; i++) {
      const index = ALPHABET.indexOf(str[i].toUpperCase());
      if (index === -1) throw new Error('Invalid base32 character');

      value = (value << 5) | index;
      bits += 5;

      if (bits >= 8) {
        output.push((value >>> (bits - 8)) & 255);
        bits -= 8;
      }
    }

    return new Uint8Array(output);
  }

  static async generateTOTP(secret) {
    const key = this.base32Decode(secret);
    let epoch = Math.floor(Date.now() / 1000 / this.PERIOD);

    const msg = new Uint8Array(8);
    for (let i = 7; i >= 0; i--) {
      msg[i] = epoch & 0xff;
      epoch >>= 8;
    }

    const hmac = await crypto.subtle.sign(
      'HMAC',
      await crypto.subtle.importKey('raw', key, { hash: 'SHA-1', name: 'HMAC' }, false, ['sign']),
      msg
    );

    const hmacArray = new Uint8Array(hmac);
    const offset = hmacArray[hmacArray.length - 1] & 0x0f;
    const code = ((hmacArray[offset] & 0x7f) << 24 |
                  (hmacArray[offset + 1] & 0xff) << 16 |
                  (hmacArray[offset + 2] & 0xff) << 8 |
                  (hmacArray[offset + 3] & 0xff)) % Math.pow(10, this.DIGITS);

    return String(code).padStart(this.DIGITS, '0');
  }

  static generateBackupCodes(count = 10) {
    const codes = [];
    for (let i = 0; i < count; i++) {
      const array = new Uint8Array(4);
      crypto.getRandomValues(array);
      const code = Array.from(array)
        .map(byte => byte.toString(16).padStart(2, '0'))
        .join('')
        .toUpperCase()
        .slice(0, 8);
      codes.push(code);
    }
    return codes;
  }

  static getProvisioningURI(secret, accountName) {
    const encodedIssuer = encodeURIComponent(this.ISSUER);
    const encodedAccount = encodeURIComponent(accountName);
    return `otpauth://totp/${encodedIssuer}:${encodedAccount}?secret=${secret}&issuer=${encodedIssuer}&algorithm=SHA1&digits=${this.DIGITS}&period=${this.PERIOD}`;
  }

  static async verifyTOTP(secret, code, window = 1) {
    let now = Math.floor(Date.now() / 1000 / this.PERIOD);

    for (let i = -window; i <= window; i++) {
      const epoch = now + i;
      const msg = new Uint8Array(8);
      for (let j = 7; j >= 0; j--) {
        msg[j] = epoch & 0xff;
        epoch >>= 8;
      }

      const key = this.base32Decode(secret);
      const hmac = await crypto.subtle.sign(
        'HMAC',
        await crypto.subtle.importKey('raw', key, { hash: 'SHA-1', name: 'HMAC' }, false, ['sign']),
        msg
      );

      const hmacArray = new Uint8Array(hmac);
      const offset = hmacArray[hmacArray.length - 1] & 0x0f;
      const calculatedCode = ((hmacArray[offset] & 0x7f) << 24 |
                              (hmacArray[offset + 1] & 0xff) << 16 |
                              (hmacArray[offset + 2] & 0xff) << 8 |
                              (hmacArray[offset + 3] & 0xff)) % Math.pow(10, this.DIGITS);

      if (String(calculatedCode).padStart(this.DIGITS, '0') === code) {
        return true;
      }
    }

    return false;
  }
}

// ============================================================================
// 6. SECURE PIN ENTRY
// ============================================================================

class SecurePINEntry {
  constructor(elementId, options = {}) {
    this.container = document.getElementById(elementId);
    this.length = options.length || 6;
    this.type = options.type || 'pin';
    this.onComplete = options.onComplete || null;
    this.maskedValue = '';
    this.actualValue = '';

    this.render();
    this.setupEventListeners();
  }

  render() {
    this.container.innerHTML = `
      <div class="secure-pin-entry">
        <div class="pin-display">
          ${Array(this.length).fill(0).map((_, i) =>
            `<div class="pin-dot" data-index="${i}"></div>`
          ).join('')}
        </div>
        <input type="text" class="pin-input"
               inputmode="numeric"
               maxlength="${this.length}"
               autocomplete="off"
               spellcheck="false"
               readonly
               style="position: absolute; opacity: 0; width: 0; height: 0;">
        <div class="pin-keypad">
          ${Array.from({ length: 10 }, (_, i) => i).map(num =>
            `<button class="pin-key" data-key="${num}">${num}</button>`
          ).join('')}
          <button class="pin-key delete-key" data-key="backspace">⌫</button>
          <button class="pin-key clear-key" data-key="clear">Clear</button>
        </div>
      </div>
    `;
  }

  setupEventListeners() {
    this.container.addEventListener('keydown', (e) => this.handleKeyDown(e));

    this.container.querySelectorAll('.pin-key').forEach(btn => {
      btn.addEventListener('click', (e) => {
        const key = e.target.getAttribute('data-key');
        this.handleKeyPress(key);
      });
    });
  }

  handleKeyDown(e) {
    if (e.key >= '0' && e.key <= '9') {
      this.handleKeyPress(e.key);
      e.preventDefault();
    } else if (e.key === 'Backspace') {
      this.handleKeyPress('backspace');
      e.preventDefault();
    } else if (e.key === 'Enter' && this.isComplete()) {
      this.complete();
      e.preventDefault();
    }
  }

  handleKeyPress(key) {
    if (key === 'backspace') {
      if (this.actualValue.length > 0) {
        this.actualValue = this.actualValue.slice(0, -1);
        this.updateDisplay();
        this.hapticFeedback('light');
      }
    } else if (key === 'clear') {
      this.actualValue = '';
      this.updateDisplay();
      this.hapticFeedback('medium');
    } else if (/^\d$/.test(key)) {
      if (this.actualValue.length < this.length) {
        this.actualValue += key;
        this.updateDisplay();
        this.hapticFeedback('light');

        if (this.isComplete()) {
          setTimeout(() => this.complete(), 100);
        }
      }
    }
  }

  updateDisplay() {
    const dots = this.container.querySelectorAll('.pin-dot');
    dots.forEach((dot, i) => {
      if (i < this.actualValue.length) {
        dot.classList.add('filled');
      } else {
        dot.classList.remove('filled');
      }
    });

    this.maskedValue = '●'.repeat(this.actualValue.length);
  }

  isComplete() {
    return this.actualValue.length === this.length;
  }

  getValue() {
    return this.actualValue;
  }

  clear() {
    this.actualValue = '';
    this.maskedValue = '';
    this.updateDisplay();
  }

  complete() {
    if (this.onComplete) {
      this.onComplete(this.actualValue);
    }
  }

  hapticFeedback(type) {
    if ('vibrate' in navigator) {
      switch (type) {
        case 'light':
          navigator.vibrate(10);
          break;
        case 'medium':
          navigator.vibrate(50);
          break;
        case 'heavy':
          navigator.vibrate([20, 10, 20]);
          break;
      }
    }
  }

  destroy() {
    this.clear();
    this.container.innerHTML = '';
  }
}

// ============================================================================
// 7. DEVICE SECURITY CHECKS
// ============================================================================

class DeviceSecurityCheck {
  static async performCheck() {
    const issues = [];

    if (await this.isJailbroken()) {
      issues.push({
        severity: 'high',
        type: 'jailbroken',
        message: 'Device appears to be jailbroken/rooted',
        advice: 'Rooted devices are vulnerable to malware that could steal your wallet keys.'
      });
    }

    if (this.isEmulator()) {
      issues.push({
        severity: 'high',
        type: 'emulator',
        message: 'Running on an emulator',
        advice: 'Emulators are not secure for storing real cryptographic keys.'
      });
    }

    if (await this.isDeveloperModeEnabled()) {
      issues.push({
        severity: 'medium',
        type: 'developer_mode',
        message: 'Developer mode is enabled',
        advice: 'Disable developer mode and USB debugging for better security.'
      });
    }

    if (!this.isScreenLocked()) {
      issues.push({
        severity: 'medium',
        type: 'screen_lock',
        message: 'Device screen lock is not enabled',
        advice: 'Enable screen lock (PIN, pattern, or biometric) for physical security.'
      });
    }

    return issues;
  }

  static async isJailbroken() {
    if (localStorage.getItem('jailbreak_check_disabled')) {
      return false;
    }
    return false;
  }

  static isEmulator() {
    const userAgent = navigator.userAgent.toLowerCase();
    const isAndroidEmulator = userAgent.includes('linux') &&
                             userAgent.includes('android') &&
                             !userAgent.includes('samsung');

    const dpr = window.devicePixelRatio;
    const suspiciousDPR = dpr === 1 || dpr === 2 || dpr === 3;

    const width = window.innerWidth;
    const height = window.innerHeight;
    const isCommonEmulatorResolution =
      (width === 360 && height === 640) ||
      (width === 412 && height === 732) ||
      (width === 384 && height === 768);

    return isAndroidEmulator || (suspiciousDPR && isCommonEmulatorResolution);
  }

  static async isDeveloperModeEnabled() {
    return false;
  }

  static isScreenLocked() {
    return true;
  }

  static displaySecurityWarnings(issues) {
    if (issues.length === 0) {
      return;
    }

    const highSeverity = issues.filter(i => i.severity === 'high');
    const mediumSeverity = issues.filter(i => i.severity === 'medium');

    if (highSeverity.length > 0) {
      const warningModal = document.createElement('div');
      warningModal.className = 'modal active security-warning-modal';
      warningModal.innerHTML = `
        <div class="modal-content" style="border-left: 4px solid var(--danger);">
          <div class="modal-header">Security Warning</div>

          <div style="gap: 16px; display: flex; flex-direction: column;">
            ${highSeverity.map(issue => `
              <div style="padding: 12px; background: rgba(255, 51, 51, 0.1); border-radius: 8px;">
                <h4 style="color: var(--danger); margin-bottom: 4px;">${issue.message}</h4>
                <p style="font-size: 12px; color: var(--text-secondary);">${issue.advice}</p>
              </div>
            `).join('')}
          </div>

          <p style="font-size: 12px; color: var(--text-secondary); margin-top: 16px;">
            We recommend fixing these issues before using this wallet for real transactions.
          </p>

          <button class="btn btn-primary" style="width: 100%; margin-top: 16px;"
                  id="acknowledgeWarningBtn">I Understand the Risks</button>
        </div>
      `;

      document.body.appendChild(warningModal);

      document.getElementById('acknowledgeWarningBtn').addEventListener('click', () => {
        localStorage.setItem('security_warning_acknowledged', Date.now());
        warningModal.remove();
      });
    }

    mediumSeverity.forEach(issue => {
      if (typeof showAlert === 'function') {
        showAlert(`${issue.message}`, 'info');
      }
    });

    auditLog.record({
      type: 'DEVICE_SECURITY_CHECK',
      severity: 'warning',
      issues: issues
    });
  }
}

// ============================================================================
// 8. SCREEN BLUR ON BACKGROUND
// ============================================================================

class ScreenBlur {
  constructor() {
    this.blurElement = null;
    this.isBlurred = false;
    this.initializeListeners();
  }

  initializeListeners() {
    document.addEventListener('visibilitychange', () => {
      if (document.hidden) {
        this.blur();
      } else {
        this.unblur();
      }
    });

    if (window.cordova) {
      document.addEventListener('pause', () => this.blur());
      document.addEventListener('resume', () => this.unblur());
    }

    window.addEventListener('blur', () => this.blur());
    window.addEventListener('focus', () => this.unblur());
  }

  blur() {
    if (this.isBlurred) return;

    const app = document.querySelector('.app-container');
    if (!app) return;

    this.blurElement = document.createElement('div');
    this.blurElement.className = 'screen-blur';
    this.blurElement.innerHTML = `
      <div class="blur-content">
        <div class="blur-icon">Locked</div>
        <p class="blur-text">App in background</p>
        <p class="blur-subtext">Wallet hidden for security</p>
      </div>
    `;

    app.appendChild(this.blurElement);
    app.style.filter = 'blur(20px)';
    this.isBlurred = true;

    this.hideSensitiveElements();

    auditLog.record({
      type: 'SCREEN_BLUR',
      severity: 'info',
      reason: 'app_backgrounded'
    });
  }

  unblur() {
    if (!this.isBlurred) return;

    const app = document.querySelector('.app-container');
    if (this.blurElement && this.blurElement.parentNode) {
      this.blurElement.parentNode.removeChild(this.blurElement);
    }

    app.style.filter = '';
    this.isBlurred = false;

    this.showSensitiveElements();
  }

  hideSensitiveElements() {
    document.querySelectorAll('.address-box, .card-value, .balance-card').forEach(el => {
      el.style.visibility = 'hidden';
    });

    document.querySelectorAll('canvas').forEach(el => {
      el.style.display = 'none';
    });
  }

  showSensitiveElements() {
    document.querySelectorAll('.address-box, .card-value, .balance-card').forEach(el => {
      el.style.visibility = 'visible';
    });

    document.querySelectorAll('canvas').forEach(el => {
      el.style.display = '';
    });
  }
}

// ============================================================================
// 9. CLIPBOARD SECURITY
// ============================================================================

async function copyToClipboardSecure(text, duration = 30000) {
  try {
    await navigator.clipboard.writeText(text);

    setTimeout(async () => {
      try {
        await navigator.clipboard.writeText('');
      } catch (err) {
        console.log('Could not clear clipboard');
      }
    }, duration);

    return true;
  } catch (err) {
    console.error('Clipboard copy failed:', err);
    return false;
  }
}

// ============================================================================
// 10. AUDIT LOGGING
// ============================================================================

class AuditLog {
  constructor(maxSize = 1000) {
    this.maxSize = maxSize;
    this.logs = this.loadLogs();
  }

  loadLogs() {
    try {
      const stored = localStorage.getItem('moonbite_audit_log');
      return stored ? JSON.parse(stored) : [];
    } catch {
      return [];
    }
  }

  saveLogs() {
    const recentLogs = this.logs.slice(-this.maxSize);
    localStorage.setItem('moonbite_audit_log', JSON.stringify(recentLogs));
  }

  record(event) {
    const logEntry = {
      id: this.generateId(),
      timestamp: event.timestamp || new Date().toISOString(),
      type: event.type,
      severity: event.severity || 'info',
      details: this.sanitizeDetails(event),
      deviceId: this.getDeviceId(),
      sessionId: typeof sessionManager !== 'undefined' ? sessionManager?.sessionId : 'unknown'
    };

    this.logs.push(logEntry);
    this.saveLogs();
  }

  generateId() {
    return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  sanitizeDetails(event) {
    const details = { ...event };

    delete details.password;
    delete details.privateKey;
    delete details.seed;
    delete details.recoveryKey;
    delete details.timestamp;
    delete details.severity;
    delete details.type;

    return details;
  }

  getDeviceId() {
    let deviceId = localStorage.getItem('moonbite_device_id');
    if (!deviceId) {
      deviceId = this.generateId();
      localStorage.setItem('moonbite_device_id', deviceId);
    }
    return deviceId;
  }

  getLogs(filter = {}) {
    let results = this.logs;

    if (filter.type) {
      results = results.filter(log => log.type === filter.type);
    }

    if (filter.severity) {
      results = results.filter(log => log.severity === filter.severity);
    }

    if (filter.since) {
      results = results.filter(log =>
        new Date(log.timestamp) >= new Date(filter.since)
      );
    }

    return results;
  }

  exportLogs() {
    return JSON.stringify(this.logs, null, 2);
  }

  downloadLogs() {
    const logs = this.exportLogs();
    const blob = new Blob([logs], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = `audit-log-${Date.now()}.json`;
    link.click();
    URL.revokeObjectURL(url);
  }
}

// ============================================================================
// INITIALIZATION
// ============================================================================

// Global instances
let sessionManager = null;
const rateLimiter = new RateLimiter(5, 5);
const auditLog = new AuditLog();
const screenBlur = new ScreenBlur();

// Setup security on app start
function initializeWalletSecurity() {
  MemorySecure.setupAutoClearing();

  auditLog.record({
    type: 'SECURITY_MODULE_INITIALIZED',
    severity: 'info'
  });

  console.log('MoonBite Wallet Security Module initialized');
}

// Initialize on DOM ready
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initializeWalletSecurity);
} else {
  initializeWalletSecurity();
}