HIGH logging monitoring failuresfeathersjsfirestore

Logging Monitoring Failures in Feathersjs with Firestore

Logging Monitoring Failures in Feathersjs with Firestore — how this specific combination creates or exposes the vulnerability

FeathersJS is a popular framework for building REST and real-time APIs with minimal configuration. When integrating FeathersJS with Google Cloud Firestore, logging and monitoring gaps commonly arise because Firestore operations are asynchronous and event-driven, and default FeathersJS logging does not capture detailed query contexts or errors. Without explicit instrumentation, failed queries, permission denials, and unexpected data access patterns may go unrecorded, creating blind spots in security monitoring.

The combination exposes several risks:

  • Missing structured logs: FeathersJS hooks typically log at a high level (e.g., service method invoked), but omit Firestore-specific details such as query filters, document IDs, and error codes. This makes it difficult to detect BOLA/IDOR attempts or unusual query patterns that could indicate enumeration attacks.
  • Incomplete error propagation: Firestore errors (e.g., permission denied, unavailable) may be caught and rethrown as generic FeathersJS errors without preserving the original Firestore error code. This reduces the fidelity of monitoring alerts and can delay incident response.
  • Lack of query monitoring: Without capturing query parameters and latency, it is hard to identify misconfigured rules, excessive document reads, or potential DoSS via costly queries. This also limits visibility into potential data exposure when queries return more documents than intended.

For compliance mappings, these gaps affect observability requirements in frameworks such as OWASP API Top 10 (2023) API1:2023 — Broken Object Level Authorization, where insufficient logging impedes detection of authorization failures, and SOC2 controls around monitoring and audit trails. Continuous, granular logging is essential to correlate authentication context with Firestore operations and to ensure that suspicious activity can be investigated in a timely manner.

Firestore-Specific Remediation in Feathersjs — concrete code fixes

To address logging and monitoring gaps, instrument FeathersJS services that use Firestore with structured, context-rich logs and explicit error handling. Below are concrete code examples that demonstrate best practices.

1. Enhance FeathersJS hooks to log Firestore operations and errors:

const { Firestore } = require('@google-cloud/firestore');
const firestore = new Firestore();

function firestoreLogger(hook) {
  const start = Date.now();
  const serviceName = hook.service.path;
  const method = hook.method; // 'find', 'get', 'create', 'update', 'patch', 'remove'
  const params = hook.params;

  return firestore
    .collection('items')
    .where('userId', '==', params.user.id)
    .get()
    .then(snapshot => {
      const duration = Date.now() - start;
      console.info(JSON.stringify({
        level: 'info',
        timestamp: new Date().toISOString(),
        service: serviceName,
        method,
        query: 'where userId == params.user.id',
        documentCount: snapshot.size,
        durationMs: duration,
        traceId: params.headers['x-request-id'] || null
      }));
      hook.result = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
      return hook;
    })
    .catch(err => {
      const duration = Date.now() - start;
      console.error(JSON.stringify({
        level: 'error',
        timestamp: new Date().toISOString(),
        service: serviceName,
        method,
        error: err.code || 'UNKNOWN',
        message: err.message,
        durationMs: duration,
        traceId: params.headers['x-request-id'] || null
      }));
      // Preserve Firestore error code for monitoring
      throw new Error(`FIRESTORE_ERROR:${err.code}:${err.message}`);
    });
}

// Apply hook to a FeathersJS service
const app = require('feathers')();
app.use('/items', {
  async find(params) {
    const snapshot = await firestore.collection('items').where('userId', '==', params.user.id).get();
    return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
  }
}, {
  before: {
    all: [firestoreLogger]
  }
});

This snippet adds structured JSON logs that include service name, method, query intent, document count, duration, and a trace ID for correlation. Logging the Firestore error code (e.g., 'permission-denied', 'not-found') helps monitoring systems trigger appropriate alerts.

2. Normalize Firestore errors to preserve error codes for monitoring:

function normalizeFirestoreError(err) {
  const code = err.code || 'unknown';
  const mapping = {
    permission-denied: 'AUTHZ_FAILURE',
    not-found: 'RESOURCE_MISSING',
    invalid-argument: 'BAD_REQUEST',
    unavailable: 'SERVICE_UNAVAILABLE',
    resource-exhausted: 'QUOTA_EXCEEDED'
  };
  return {
    code: mapping[code] || 'FIRESTORE_ERROR',
    message: err.message,
    details: err.details || null
  };
}

// Usage in a service method
app.use('/docs', {
  async get(id, params) {
    try {
      const doc = await firestore.collection('docs').doc(id).get();
      if (!doc.exists) {
        const err = new Error('Document not found');
        err.code = 5;
        throw err;
      }
      return { id: doc.id, ...doc.data() };
    } catch (err) {
      const normalized = normalizeFirestoreError(err);
      console.warn(JSON.stringify({
        level: 'warn',
        timestamp: new Date().toISOString(),
        error: normalized,
        traceId: params.headers['x-request-id'] || null
      }));
      throw new Error(`${normalized.code}:${normalized.message}`);
    }
  }
});

By normalizing Firestore error codes, monitoring systems can reliably categorize and alert on authorization failures (permission-denied), missing resources (not-found), and quota issues (resource-exhausted). This improves detection of BOLA/IDOR patterns and supports compliance evidence for frameworks such as PCI-DSS and SOC2, which require detailed audit trails of access and error conditions.

3. Add request context to logs for traceability across services:

function contextLogger(hook) {
  const ctx = hook.params.headers;
  const requestId = ctx['x-request-id'] || 'unknown';
  const userId = ctx['x-user-id'] || 'anonymous';
  console.info(JSON.stringify({
    level: 'info',
    timestamp: new Date().toISOString(),
    requestId,
    userId,
    path: hook.path,
    method: hook.method
  }));
  return hook;
}

Including user ID and request ID in logs enables correlation between authentication events and Firestore operations, which is critical for investigating suspicious access patterns and for forensic analysis after a security incident.

Frequently Asked Questions

Why is structured logging important when using FeathersJS with Firestore?
Structured logging provides consistent, machine-readable records that include query context, error codes, and timing. This enables monitoring systems to detect anomalies such as repeated permission-denied errors or unusual query volumes, which may indicate BOLA/IDOR probing or data exposure risks.
How does normalizing Firestore errors improve monitoring in FeathersJS services?
Normalizing Firestore errors maps low-level error codes to meaningful categories (e.g., permission-denied to AUTHZ_FAILURE). This allows monitoring tools to reliably trigger alerts for authorization failures, quota issues, and service unavailability, and supports audit requirements for frameworks like SOC2 and PCI-DSS.