HIGH excessive data exposurefeathersjs

Excessive Data Exposure in Feathersjs

How Excessive Data Exposure Manifests in Feathersjs

Excessive Data Exposure in Feathersjs typically occurs through service methods that return entire database records without filtering sensitive fields. The framework's convention-over-configuration approach makes this particularly dangerous when developers rely on default service behaviors.

The most common pattern appears in Feathersjs's find() and get() service methods. Consider this vulnerable service:

class UsersService {
  async find(params) {
    return this.Model.findAll();
  }

  async get(id, params) {
    return this.Model.findByPk(id);
  }
}

Both methods return complete user records including fields like password, ssn, creditCard, and internalNotes. Feathersjs doesn't automatically filter these fields, assuming developers will handle security at the application layer.

Another manifestation occurs through Feathersjs's params.query object. When using REST endpoints, clients can send arbitrary query parameters that get passed directly to database queries:

// Vulnerable: allows client to specify any field
app.service('users').find({
  query: { 
    include: 'password,ssn', // client-controlled
    fields: ['email', 'password'] // dangerous field selection
  }
});

Feathersjs's real-time features create additional exposure vectors. The publish method in real-time services determines what data clients receive:

class MessagesService {
  publish(data, hook) {
    return hook.app.channel('authenticated');
  }
}

This publishes all message data to any authenticated user, potentially exposing private conversations or sensitive content across user boundaries.

Relation handling in Feathersjs can also leak data. When using populate to automatically include related records:

class OrdersService {
  async find(params) {
    return this.Model.findAll({
      include: ['customer', 'items']
    });
  }
}

This might expose customer billing addresses, payment methods, or other sensitive relationship data that shouldn't be returned to all callers.

Feathersjs-Specific Detection

Detecting Excessive Data Exposure in Feathersjs requires examining both code patterns and runtime behavior. Start by auditing service methods for direct database access without field filtering.

Code review checklist for Feathersjs services:

// Check for these patterns:
// 1. Direct model access without field selection
async find(params) {
  return this.Model.findAll(); // <-- vulnerable
}

// 2. Missing data sanitization
async get(id, params) {
  const user = await this.Model.findByPk(id);
  return user; // <-- exposes all fields
}

// 3. Unsafe populate usage
async find(params) {
  return this.Model.findAll({
    include: ['sensitiveRelation'] // <-- dangerous
  });
}

middleBrick's Feathersjs-specific scanning identifies these patterns automatically. The scanner examines service files for:

  • Service methods that return raw database objects
  • Missing attributes or select clauses in Sequelize queries
  • Unsafe populate configurations
  • Real-time publish methods that don't filter data

Runtime detection involves testing API endpoints with tools like middleBrick to observe actual responses. A scan reveals:

{
  "excessiveDataExposure": {
    "severity": "high",
    "affectedEndpoints": [
      "GET /api/users",
      "GET /api/users/:id",
      "GET /api/orders"
    ],
    "exposedFields": [
      "password",
      "ssn",
      "creditCard",
      "internalNotes"
    ]
  }
}

middleBrick's black-box scanning tests these endpoints without requiring Feathersjs source code, making it ideal for production API security assessment.

Additional detection through OpenAPI spec analysis: middleBrick cross-references your Feathersjs-generated OpenAPI spec with runtime responses to identify discrepancies between documented and actual data exposure.

Feathersjs-Specific Remediation

Feathersjs provides several native mechanisms to prevent Excessive Data Exposure. The most effective approach uses hooks to sanitize data before it leaves your service.

Field filtering hook:

const { authenticate } = require('@feathersjs/authentication').hooks;
const { iff, isProvider, disallow } = require('feathers-hooks-common');

const sanitizeUser = context => {
  const { result, method } = context;
  
  if (result && method === 'find') {
    return context.result.map(user => {
      const { password, ssn, creditCard, internalNotes, ...safeUser } = user;
      return safeUser;
    });
  }
  
  if (result && method === 'get') {
    const { password, ssn, creditCard, internalNotes, ...safeUser } = result;
    context.result = safeUser;
  }
  
  return context;
};

class UsersService {
  constructor(options) {
    this.Model = options.Model;
    this.paginate = options.paginate;
  }
  
  async find(params) {
    return this.Model.findAll();
  }
  
  async get(id, params) {
    return this.Model.findByPk(id);
  }
}

module.exports = function() {
  const app = this;
  const options = {
    Model: app.get('models').User,
    paginate: app.get('paginate')
  };
  
  app.use('/users', new UsersService(options));
  
  app.service('users').hooks({
    before: {
      all: [iff(isProvider('external'), authenticate('jwt'))]
    },
    after: {
      all: [sanitizeUser]
    }
  });
};

Role-based data exposure using disallow hook:

const { iff, isProvider, disallow } = require('feathers-hooks-common');

const restrictByRole = context => {
  const { user } = context.params;
  
  if (!user || user.role !== 'admin') {
    context.params.attributes = {
      exclude: ['password', 'ssn', 'creditCard', 'internalNotes']
    };
  }
};

app.service('users').hooks({
  before: {
    find: [restrictByRole],
    get: [restrictByRole]
  }
});

Safe populate configuration:

const safePopulate = context => {
  const { method, type } = context;
  
  if (method === 'find' && type === 'after') {
    context.result = context.result.map(item => {
      const { customer: { billingAddress, paymentMethods, ...safeCustomer }, ...safeItem } = item;
      return {
        ...safeItem,
        customer: safeCustomer
      };
    });
  }
};

Real-time data filtering:

class MessagesService {
  publish(data, hook) {
    const { user } = hook.params;
    const channels = [];
    
    if (user.role === 'admin') {
      channels.push(hook.app.channel('authenticated'));
    } else {
      // Only publish messages the user should see
      if (data.senderId === user.id || data.recipientId === user.id) {
        channels.push(hook.app.channel('authenticated'));
      }
    }
    
    return channels;
  }
}

Database-level field selection as defense-in-depth:

class UsersService {
  async find(params) {
    return this.Model.findAll({
      attributes: {
        exclude: ['password', 'ssn', 'creditCard', 'internalNotes']
      }
    });
  }
  
  async get(id, params) {
    return this.Model.findByPk(id, {
      attributes: {
        exclude: ['password', 'ssn', 'creditCard', 'internalNotes']
      }
    });
  }
}

Related CWEs: propertyAuthorization

CWE IDNameSeverity
CWE-915Mass Assignment HIGH

Frequently Asked Questions

How does middleBrick detect Excessive Data Exposure in Feathersjs APIs?

middleBrick performs black-box scanning of Feathersjs endpoints, testing each API method to identify exposed sensitive fields. The scanner examines response payloads for common PII patterns (passwords, SSNs, credit card numbers, internal notes) and flags endpoints returning complete database records. It also analyzes OpenAPI specs generated by Feathersjs to identify discrepancies between documented and actual data exposure. No Feathersjs source code or credentials required—just submit your API URL for a 5-15 second scan.

Can I integrate Excessive Data Exposure checks into my Feathersjs CI/CD pipeline?

Yes, using middleBrick's GitHub Action you can automatically scan Feathersjs APIs during your CI/CD pipeline. Add the action to your workflow to scan staging APIs before deployment, with configurable thresholds to fail builds if Excessive Data Exposure risk exceeds your tolerance. The Pro plan includes continuous monitoring that scans your APIs on a schedule and alerts you when new data exposure vulnerabilities appear, ensuring your Feathersjs services remain secure as they evolve.