npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

client-trace

v2.0.1

Published

A comprehensive client-side security and telemetry library. Features device fingerprinting, bot detection, network tampering analysis, and secure transport.

Downloads

39

Readme

client-trace

A comprehensive client-side security and telemetry library for modern web applications. client-trace provides a suite of modules to detect tampering, identify devices, monitor behavior, and secure data transport.

Features

  • Integrity Verification: Detect if your client bundle has been modified.
  • Network Analysis: Detect monkey-patched fetch/XHR, proxies, and timing anomalies.
  • Device Fingerprinting: Lightweight, privacy-friendly device identification.
  • Bot Detection: Analyze mouse movements and click patterns to identify bots.
  • Security Monitoring: Detect script injections, CSP violations, and local storage tampering.
  • Secure Transport: End-to-end encryption (AES-GCM), payload signing (HMAC), and replay protection.

Use Cases

  • Client‑side bundle integrity verificationverifyBundleIntegrity ensures the running JavaScript bundle matches a known hash.
  • Session‑token generationgenerateSessionToken creates a signed token for API calls.
  • Network‑level API tampering detectiondetectNetworkAPITampering flags monkey‑patched fetch/XHR.
  • Proxy / VPN detectiondetectProxy measures latency and header anomalies.
  • Timing‑anomaly detectiondetectTimingAnomalies spots abnormal round‑trip times.
  • Device fingerprintinggetDeviceFingerprint builds a privacy‑friendly device identifier.
  • Bot / headless‑browser detectiondetectBot analyses mouse entropy, rapid clicks, and headless flags.
  • Script‑injection monitoringdetectInjections watches for unexpected <script> tags.
  • CSP violation listenerlistenForCSPViolations aggregates CSP breach events.
  • Local‑storage tampering detectioncheckStorageIntegrity validates stored data integrity.
  • Secure transport helperssignPayload, encryptTelemetry, getNonce provide signing, encryption, and replay protection.
  • Aggregated security reportcollectSecurityReport runs all checks and returns a single JSON payload.

Installation

npm install client-trace

Usage

Quick Start (Aggregated Report)

The easiest way to use client-trace is to collect a full security report.

import { collectSecurityReport } from 'client-trace';

const config = {
  bundleUrl: '/assets/main.js',
  expectedBundleHash: 'sha256-hash-of-your-bundle', // Optional
  pingUrl: '/api/ping', // For proxy/timing detection
  userUniqueId: 'user-123', // For session token
  hashedIp: 'hash-of-ip', // Provided by server
  secret: 'your-shared-secret' // For signing/encryption
};

collectSecurityReport(config).then(report => {
  console.log('Security Report:', report);
  // Send report to your server
});

Modular Usage

You can also import and use individual modules as needed.

1. Bundle Integrity

verifyBundleIntegrity(bundleUrl, expectedHash)

Verifies that your client bundle hasn't been tampered with by comparing its SHA-256 hash against a known good hash.

Parameters:

  • bundleUrl (string): URL to the JavaScript bundle file (e.g., /assets/main.js or /js/app.bundle.js)
  • expectedHash (string): Expected SHA-256 hash of the unmodified bundle

Returns: Promise resolving to an object:

{
  integrityOk: boolean,      // true if hash matches, false if tampered
  actualHash: string,         // SHA-256 hash of the current bundle
  expectedHash: string,       // The hash you provided
  error?: string              // Error message if hash couldn't be computed
}

Example:

import { verifyBundleIntegrity } from 'client-trace';

verifyBundleIntegrity('/assets/main.js', 'sha256-abc123def456').then(result => {
  if (!result.integrityOk) {
    console.error('ALERT: Bundle has been modified!');
    console.error('Expected:', result.expectedHash);
    console.error('Actual:', result.actualHash);
    // Consider blocking the app or alerting the user
  } else {
    console.log('Bundle integrity verified ✓');
  }
});

generateSessionToken(userUniqueId, hashedIp, secret, expiryTime)

Creates a cryptographically signed token that binds a user to their IP address and user agent, preventing session token reuse across different machines/networks.

Parameters:

  • userUniqueId (string): Unique identifier for the user (e.g., user ID from your system)
  • hashedIp (string): Server-provided hash of the user's IP (for privacy). Your server should compute this
  • secret (string): Shared secret between client and server (minimum 32 characters recommended)
  • expiryTime (number, optional): Token expiration time in milliseconds from now (default: 1 hour)

Returns: Promise resolving to an object:

{
  token: string,              // Signed JWT-like token
  signature: string,          // HMAC-SHA256 signature
  issuedAt: number,           // Timestamp when token was created
  expiresAt: number,          // Timestamp when token expires
  userUniqueId: string,       // The user ID encoded in the token
  metadata: object            // Additional claims (IP, UA, etc.)
}

Example:

import { generateSessionToken } from 'client-trace';

const token = await generateSessionToken(
  'user-456',
  'hash-of-ip-from-server',
  'your-secure-shared-secret-32chars',
  3600000  // 1 hour
);

// Send this token with API requests
fetch('/api/secure-endpoint', {
  headers: {
    'X-Session-Token': token.token
  }
});

console.log('Token expires at:', new Date(token.expiresAt));

2. Network Tampering Detection

detectNetworkAPITampering()

Checks if the native fetch and XHR (XMLHttpRequest) APIs have been monkey-patched or replaced, which could indicate malicious browser extensions or script injection.

Parameters: None

Returns: An object:

{
  tampered: boolean,                    // true if any tampering detected
  tamperedFunctions: string[],          // List of tampered functions (e.g., ['fetch', 'XMLHttpRequest'])
  fetchIsNative: boolean,               // true if fetch is original
  xhrIsNative: boolean,                 // true if XMLHttpRequest is original
  details: object                       // Detailed analysis of each API
}

Example:

import { detectNetworkAPITampering } from 'client-trace';

const result = detectNetworkAPITampering();

if (result.tampered) {
  console.warn('⚠️ Network APIs have been modified!');
  console.warn('Tampered functions:', result.tamperedFunctions);
  
  if (!result.fetchIsNative) {
    console.warn('fetch has been intercepted - consider blocking requests');
  }
  if (!result.xhrIsNative) {
    console.warn('XMLHttpRequest has been intercepted - consider blocking requests');
  }
  
  // Take defensive action (e.g., stop sending sensitive data)
} else {
  console.log('Network APIs are clean ✓');
}

detectProxy(pingUrl)

Detects if the user is behind a proxy or VPN by analyzing response headers (like X-Forwarded-For, CF-Connecting-IP) and measuring latency anomalies.

Parameters:

  • pingUrl (string): URL endpoint on your server to ping for latency testing (e.g., /api/ping)

Returns: Promise resolving to an object:

{
  proxyDetected: boolean,                // true if proxy/VPN indicators found
  confidence: number,                    // 0-1 score indicating likelihood
  indicators: string[],                  // List of detected proxy signs (e.g., ['x-forwarded-for', 'unusual-latency'])
  headerAnalysis: object,                // Proxy-related headers detected
  latencyMs: number,                     // Round-trip latency to ping endpoint
  isHighLatency: boolean,                // true if latency exceeds threshold
  details: object                        // Full analysis
}

Example:

import { detectProxy } from 'client-trace';

const proxyCheck = await detectProxy('/api/ping');

console.log('Latency:', proxyCheck.latencyMs, 'ms');
console.log('High latency:', proxyCheck.isHighLatency);

if (proxyCheck.proxyDetected) {
  console.warn(`Proxy/VPN detected with ${(proxyCheck.confidence * 100).toFixed(0)}% confidence`);
  console.warn('Indicators:', proxyCheck.indicators);
  
  // Optional: apply stricter security measures
  if (proxyCheck.confidence > 0.8) {
    console.warn('High confidence proxy detected - consider additional verification');
  }
} else {
  console.log('No proxy detected ✓');
}

detectTimingAnomalies(options)

Measures DNS lookup time, TTFB (Time To First Byte), and total request time to detect Man-in-the-Middle (MITM) attacks or unusual network conditions.

Parameters:

  • options (object, optional):
    • testUrl (string): URL to test (default: /api/ping)
    • iterations (number): Number of requests to measure (default: 5)
    • thresholdMs (number): Latency threshold in milliseconds (default: 1000)

Returns: Promise resolving to an object:

{
  anomalyDetected: boolean,              // true if timing is abnormal
  averageLatencyMs: number,              // Average latency across all requests
  minLatencyMs: number,                  // Minimum latency observed
  maxLatencyMs: number,                  // Maximum latency observed
  variance: number,                      // Variance in latencies (high = inconsistent)
  outliers: number[],                    // Individual latencies that are outliers
  isConsistent: boolean,                 // true if latencies are consistent
  mitmLikely: boolean                    // true if MITM attack indicators present
}

Example:

import { detectTimingAnomalies } from 'client-trace';

const timingReport = await detectTimingAnomalies({
  testUrl: '/api/ping',
  iterations: 10,
  thresholdMs: 1500
});

console.log('Average latency:', timingReport.averageLatencyMs, 'ms');

if (timingReport.anomalyDetected) {
  console.warn('⚠️ Timing anomalies detected!');
  console.warn('MITM likely:', timingReport.mitmLikely);
  console.warn('Variance:', timingReport.variance, '(high = inconsistent)');
  
  if (timingReport.mitmLikely) {
    // Consider enhanced security measures
  }
}

3. Device Fingerprinting

getDeviceFingerprint()

Generates a lightweight, privacy-friendly fingerprint of the user's device by hashing non-unique signals like screen resolution, OS, timezone, and browser capabilities.

Parameters: None

Returns: Promise resolving to an object:

{
  fingerprintHash: string,               // SHA-256 hash of all fingerprint components
  components: {
    screenResolution: string,            // e.g., "1920x1080"
    colorDepth: number,                  // e.g., 24
    timezone: string,                    // e.g., "UTC-5"
    language: string,                    // Browser language, e.g., "en-US"
    platform: string,                    // e.g., "Win32", "MacIntel"
    hardwareConcurrency: number,         // Number of CPU cores
    deviceMemory: number,                // RAM in GB (approximate)
    canvasFingerprint: string,           // Hash of canvas rendering capabilities
    webglRenderer: string                // GPU renderer info
  },
  stability: number                      // 0-1: likelihood fingerprint stays same over time
}

Example:

import { getDeviceFingerprint } from 'client-trace';

const fingerprint = await getDeviceFingerprint();

console.log('Device fingerprint:', fingerprint.fingerprintHash);
console.log('Screen resolution:', fingerprint.components.screenResolution);
console.log('CPU cores:', fingerprint.components.hardwareConcurrency);
console.log('Fingerprint stability:', fingerprint.stability); // Higher = more stable

// Store for session tracking (not for device tracking across days)
sessionStorage.setItem('deviceId', fingerprint.fingerprintHash);

// Can be sent to server for additional analysis
fetch('/api/telemetry', {
  method: 'POST',
  body: JSON.stringify({ fingerprint: fingerprint.fingerprintHash })
});

4. Bot Detection

startBehaviorMonitoring()

Initiates tracking of user behavior signals (mouse movements, click patterns, keyboard activity) in the background. Call this as early as possible in your page lifecycle (e.g., in a script tag in <head>).

Parameters: None

Returns: void

Example:

import { startBehaviorMonitoring } from 'client-trace';

// Call immediately on page load
startBehaviorMonitoring();
console.log('Behavior monitoring started');

detectBot()

Analyzes collected behavior data to detect if the current user is likely a bot or headless browser.

Parameters: None

Returns: An object:

{
  botLikely: boolean,                    // true if bot-like behavior detected
  confidence: number,                    // 0-1: how confident we are
  signals: {
    mouseEntropy: number,                // Randomness of mouse movements (low = bot-like)
    rapidClickCount: number,             // Number of unnaturally rapid clicks
    hasMouseMovement: boolean,            // true if any mouse movement detected
    hasClickActivity: boolean,            // true if any clicks detected
    hasKeyboardActivity: boolean,         // true if any keyboard input detected
    headlessBrowserIndicators: boolean,  // true if running in headless browser
    screenTouchCapable: boolean           // true if device has touch screen
  },
  botScore: number                       // 0-1: overall bot likelihood score
}

Example:

import { startBehaviorMonitoring, detectBot } from 'client-trace';

// On page load
startBehaviorMonitoring();

// Later, before a sensitive action (e.g., form submission)
document.getElementById('submitBtn').addEventListener('click', () => {
  const botCheck = detectBot();
  
  console.log('Bot likelihood:', (botCheck.botScore * 100).toFixed(0) + '%');
  
  if (botCheck.botLikely) {
    console.warn('🤖 Bot-like behavior detected!', botCheck.signals);
    
    // Options:
    // 1. Show CAPTCHA
    // 2. Block submission
    // 3. Send to server for additional verification
    if (botCheck.confidence > 0.9) {
      alert('Please complete a CAPTCHA to continue');
      return;
    }
  }
  
  // Proceed with form submission
  console.log('Behavior looks human ✓');
});

// Additional signal details
console.log('Mouse entropy:', botCheck.signals.mouseEntropy, '(0=none, 1=highly random)');
console.log('Rapid clicks:', botCheck.signals.rapidClickCount);
console.log('Headless browser:', botCheck.signals.headlessBrowserIndicators);

5. Security Monitoring

detectInjections()

Monitors the DOM for unexpected <script> tags that could indicate malicious script injection or XSS attacks.

Parameters: None

Returns: An object:

{
  injectionsDetected: boolean,           // true if unknown scripts found
  injectedScripts: Array<{
    src: string,                         // Script URL or 'inline'
    trusted: boolean,                    // false if not in whitelist
    timestamp: number                    // When detected
  }>,
  trustedScripts: string[],              // Scripts you've whitelisted
  recommendations: string[]              // Suggested actions
}

Example:

import { detectInjections } from 'client-trace';

const injectionReport = detectInjections();

if (injectionReport.injectionsDetected) {
  console.error('⚠️ Potential script injection detected!');
  injectionReport.injectedScripts.forEach(script => {
    console.error(`Untrusted script: ${script.src}`);
  });
  
  // Alert the user or server
  fetch('/api/security-alert', {
    method: 'POST',
    body: JSON.stringify({ 
      type: 'script-injection',
      scripts: injectionReport.injectedScripts
    })
  });
} else {
  console.log('No unauthorized scripts detected ✓');
}

listenForCSPViolations(onViolation)

Listens for Content Security Policy (CSP) violation events and calls a callback whenever a violation occurs.

Parameters:

  • onViolation (function): Callback function that receives violation details

Returns: An object:

{
  isListening: boolean,                  // true if listener is active
  violationCount: number,                // Total violations captured
  stopListening: function,               // Call to remove the listener
  violations: Array<{
    blockedUri: string,
    violatedDirective: string,           // e.g., 'script-src'
    originalPolicy: string,
    timestamp: number
  }>
}

Example:

import { listenForCSPViolations } from 'client-trace';

const cspListener = listenForCSPViolations((violation) => {
  console.warn('CSP Violation detected:');
  console.warn(`  Blocked URI: ${violation.blockedUri}`);
  console.warn(`  Directive: ${violation.violatedDirective}`);
  
  // Send to your server for analysis
  fetch('/api/csp-violations', {
    method: 'POST',
    body: JSON.stringify(violation)
  });
});

console.log('CSP violations are being monitored');

// Later, if you want to stop listening:
// cspListener.stopListening();

checkStorageIntegrity(storageType, checkInterval)

Verifies that localStorage or sessionStorage hasn't been modified externally (e.g., by browser extensions or other tabs).

Parameters:

  • storageType (string): Either 'local' or 'session' (default: 'local')
  • checkInterval (number): How often to check in milliseconds (default: 5000)

Returns: An object:

{
  isIntact: boolean,                     // true if storage hasn't been tampered with
  tamperedKeys: string[],                // Keys that have been modified
  addedKeys: string[],                   // Keys that were added externally
  removedKeys: string[],                 // Keys that were removed externally
  stopMonitoring: function,              // Call to stop integrity checks
  lastCheckTime: number                  // Timestamp of last check
}

Example:

import { checkStorageIntegrity } from 'client-trace';

// Start monitoring localStorage
const storageCheck = checkStorageIntegrity('local', 3000);

if (!storageCheck.isIntact) {
  console.error('⚠️ Local storage has been tampered with!');
  console.error('Tampered keys:', storageCheck.tamperedKeys);
  console.error('Added keys:', storageCheck.addedKeys);
  
  // Clear potentially compromised data
  localStorage.clear();
  
  // Alert server
  fetch('/api/security-alert', {
    method: 'POST',
    body: JSON.stringify({
      type: 'storage-tampering',
      tamperedKeys: storageCheck.tamperedKeys
    })
  });
}

// Stop monitoring when done
// storageCheck.stopMonitoring();

6. Secure Transport

signPayload(payload, secret)

Signs a data payload using HMAC-SHA256 to ensure authenticity and prevent tampering in transit.

Parameters:

  • payload (object or string): Data to sign
  • secret (string): Shared secret (minimum 32 characters recommended)

Returns: Promise resolving to an object:

{
  payload: any,                          // The original payload
  signature: string,                     // HMAC-SHA256 signature (hex)
  algorithm: string,                     // Always "HMAC-SHA256"
  timestamp: number                      // When signed
}

Example:

import { signPayload } from 'client-trace';

const data = {
  userId: '12345',
  action: 'login',
  timestamp: Date.now()
};

const signed = await signPayload(data, 'your-shared-secret-key');

console.log('Payload:', signed.payload);
console.log('Signature:', signed.signature);

// Send both payload and signature to server
fetch('/api/secure-action', {
  method: 'POST',
  body: JSON.stringify(signed),
  headers: { 'Content-Type': 'application/json' }
});

// Server-side: verify using the same secret
// Server should recompute: HMAC-SHA256(payload, secret)
// and compare with received signature

encryptTelemetry(payload, secret)

Encrypts sensitive telemetry data using AES-256-GCM encryption for end-to-end security.

Parameters:

  • payload (object): Data to encrypt
  • secret (string): Encryption key (minimum 32 characters)

Returns: Promise resolving to an object:

{
  encrypted: string,                     // Encrypted payload (base64)
  iv: string,                            // Initialization vector (base64)
  authTag: string,                       // Authentication tag for integrity (base64)
  algorithm: string,                     // "AES-256-GCM"
  nonce: string,                         // Replay protection nonce
  timestamp: number                      // When encrypted
}

Example:

import { encryptTelemetry } from 'client-trace';

const telemetry = {
  event: 'user-action',
  userId: 'user-123',
  ipAddress: '192.168.1.1',
  sessionId: 'sess-456'
};

const encrypted = await encryptTelemetry(telemetry, 'your-shared-secret-key');

console.log('Encrypted payload:', encrypted.encrypted);
console.log('IV:', encrypted.iv);
console.log('Auth tag:', encrypted.authTag);

// Send encrypted data to server
fetch('/api/telemetry', {
  method: 'POST',
  body: JSON.stringify({
    data: encrypted.encrypted,
    iv: encrypted.iv,
    authTag: encrypted.authTag,
    nonce: encrypted.nonce
  })
});

// Server-side decryption:
// 1. Get IV, authTag, and nonce from request
// 2. Verify nonce hasn't been used before (replay protection)
// 3. Decrypt using: decipher.update(encrypted, 'base64') + decipher.final()
// 4. Verify auth tag

decryptTelemetry(encrypted, iv, authTag, secret)

Decrypts telemetry data that was encrypted with encryptTelemetry.

Parameters:

  • encrypted (string): Encrypted payload (base64)
  • iv (string): Initialization vector from encryption (base64)
  • authTag (string): Authentication tag from encryption (base64)
  • secret (string): Same secret used during encryption

Returns: Promise resolving to an object:

{
  decrypted: object,                     // Original decrypted payload
  verified: boolean,                     // true if auth tag is valid
  algorithm: string                      // "AES-256-GCM"
}

Example:

import { decryptTelemetry } from 'client-trace';

// Assuming server sent back encrypted data
const response = await fetch('/api/telemetry/config');
const { data, iv, authTag } = await response.json();

const decrypted = await decryptTelemetry(data, iv, authTag, 'your-shared-secret-key');

if (decrypted.verified) {
  console.log('Decrypted config:', decrypted.decrypted);
  console.log('Integrity verified ✓');
  
  // Use the decrypted configuration
  applyConfig(decrypted.decrypted);
} else {
  console.error('Authentication tag verification failed!');
  console.error('Data may have been tampered with');
}

getNonce()

Generates a unique, rotating nonce for replay protection. Each call returns a new nonce that can be validated on the server to ensure requests aren't replayed.

Parameters: None

Returns: An object:

{
  nonce: string,                         // Unique nonce value (hex)
  timestamp: number,                     // When nonce was generated
  expiresAt: number,                     // When nonce becomes invalid (10 minutes)
  isValid: boolean                       // true if nonce hasn't expired
}

Example:

import { getNonce } from 'client-trace';

// Before making a sensitive API request
const nonce = getNonce();

fetch('/api/sensitive-action', {
  method: 'POST',
  headers: {
    'X-Nonce': nonce.nonce
  },
  body: JSON.stringify({
    action: 'transfer-funds',
    amount: 100
  })
});

// Server-side:
// 1. Check if nonce has been used before (in a cache/database)
// 2. Verify nonce hasn't expired
// 3. Mark nonce as "used"
// 4. Proceed with action only if nonce is valid and unused

// Client-side error handling:
if (!nonce.isValid) {
  console.error('Nonce has expired - get a new one');
}

console.log('Nonce expires in:', Math.round((nonce.expiresAt - Date.now()) / 1000), 'seconds');

Modules Overview

| Category | Module | Description | |----------|--------|-------------| | Integrity | verifyBundleIntegrity | Checks if the script file matches expected hash. | | | generateSessionToken | Creates a signed token binding user to IP/UA. | | Network | detectNetworkAPITampering | Checks if fetch or XHR are native code. | | | detectProxy | Inspects headers for proxy signatures. | | | detectTimingAnomalies | Measures DNS/TTFB to find MITM delays. | | Fingerprint | getDeviceFingerprint | Hashes non-unique signals (screen, OS, timezone). | | | detectBot | Analyzes entropy of mouse moves and clicks. | | Security | detectInjections | Monitors DOM for new <script> tags. | | | listenForCSPViolations | Captures CSP violation events. | | | checkStorageIntegrity | Verifies localStorage hasn't been changed externally. | | Transport | signPayload | Signs data with HMAC-SHA256. | | | encryptTelemetry | Encrypts data with AES-GCM. | | | getNonce | Generates rotating nonce for replay protection. |

License

ISC