HIGH data exposurefeathersjscockroachdb

Data Exposure in Feathersjs with Cockroachdb

Data Exposure in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability

In a Feathersjs application connected to Cockroachdb, data exposure typically occurs when service queries return more fields than the client is authorized to see. Feathersjs services are often configured with a generic ModelService that maps directly to a database table. If field-level authorization is not explicitly implemented, a query such as app.service('users').find({ query: { $select: [] } }) may still return sensitive columns like password_hash or ssn because the ORM layer does not automatically strip them. Cockroachdb, like PostgreSQL, returns all columns present in the table unless the SQL explicitly limits the column set. Without a $select or runtime row-level policy, the wire protocol from Cockroachdb to Feathersjs can expose entire rows.

Another vector specific to this stack is misuse of Feathersjs hooks where the params object is mutated or merged across hooks. A common pattern is to add the current user’s roles in an authentication hook and then inadvertently pass those roles into a downstream hook that builds the Cockroachdb query without sanitizing the projection. If the query builder uses Model.find({ where: { organizationId } }) and does not explicitly define attributes, Sequelize (or TypeORM) may generate SELECT *, thereby returning sensitive fields such as api_key or internal flags to the client.

Additionally, pagination misconfiguration can lead to data exposure. Feathersjs defaults to returning all records when paginate: false is not set explicitly. With Cockroachdb, a request like /users?$limit=1000 could dump thousands of rows containing sensitive columns if the service does not enforce a reasonable max limit or projection. The combination of an open-ended query, missing $select, and Cockroachdb’s tabular schema means sensitive columns such as reset_token or internal_notes can be delivered to an unauthenticated or low-privilege client through an otherwise valid API call.

These risks are amplified when using dynamic $expand or nested relations in Feathersjs adapters that translate to Cockroachdb JOINs. If the join pulls in columns from related tables without explicit field selection, confidential data from linked rows can leak into the response. For example, a vitals service might JOIN to a patient_records table and unintentionally expose diagnosis codes unless the projection is controlled at the Feathersjs service level.

To detect this class of issue, middleBrick runs 12 security checks in parallel, including Property Authorization and Data Exposure scans. It compares the OpenAPI/Swagger spec definitions with runtime responses, highlighting mismatches where server-side field selection is missing. The scanner does not modify data; it reports findings with severity and remediation guidance, helping teams identify unintended exposure before an attacker does.

Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes

Remediation centers on explicit field selection and query constraints in Feathersjs services. Always define events and ModelService hooks that sanitize the params object before it reaches the database layer. Below are concrete, working examples for Feathersjs with Cockroachdb using Sequelize.

1. Enforce $select via hooks

Use a before hook to restrict fields based on roles. This ensures that even if the client requests all fields, the server only returns what is permitted.

// src/hooks/restrict-fields.js
module.exports = function restrictFields(allowedFields) {
  return function(context) {
    const { params, result } = context;
    if (params.user && params.user.roles.includes('admin')) {
      // Admins see all fields
      return context;
    }
    // For non-admin, explicitly limit fields at the query level
    if (!context.params.query.$select) {
      context.params.query.$select = allowedFields;
    }
    // Optionally strip sensitive fields from result if not filtered at DB level
    if (Array.isArray(result.data)) {
      result.data = result.data.map(row => {
        const filtered = {};
        allowedFields.forEach(field => { if (row[field] !== undefined) filtered[field] = row[field]; });
        return filtered;
      });
    }
    return context;
  };
};

// src/services/user/user.hooks.js
const restrictFields = require('./restrict-fields');
module.exports = {
  before: {
    all: [],
    find: [restrictFields(['id', 'email', 'name'])],
    get: [restrictFields(['id', 'email', 'name'])]
  },
  after: {},
  error: {}
};

2. Explicit attributes in Sequelize models

Define the model with explicit attributes and avoid sequelize.define defaults that map to all columns. In Feathersjs, configure the Sequelize service options to limit columns by default.

// src/services/user/user.model.js
const { Sequelize, DataTypes } = require('@feathersjs/sequelize');
const sequelize = new Sequelize('postgres://user:pass@cockroachdb:26257/dbname?sslmode=require');

const User = sequelize.define('user', {
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  email: { type: DataTypes.STRING, allowNull: false },
  name: { type: DataTypes.STRING },
  // Omit password_hash and ssn from default attributes
}, {
  timestamps: true,
  // Explicitly exclude sensitive columns from SELECT *
  defaultScope: {
    attributes: { exclude: ['password_hash', 'ssn', 'api_key'] }
  }
});

module.exports = User;

3. Pagination and max limit guardrails

Prevent mass data dumps by configuring pagination with a strict max limit and ensuring $select is applied before the Cockroachdb query is built.

// src/services/user/user.service.js
const { Service } = require('feathers-sequelize');
class SecureUserService extends Service {
  setup(app, path) {
    super.setup(app, path);
    this.hooks.push({
      before: {
        async find(context) {
          const query = context.params.query || {};
          // Enforce a reasonable max limit to avoid dumping large result sets
          const maxLimit = 100;
          if (!query.$limit || query.$limit > maxLimit) {
            query.$limit = maxLimit;
          }
          // Ensure projection is defined to limit returned columns
          if (!query.$select) {
            query.$select = ['id', 'email', 'name'];
          }
          context.params.query = query;
          return context;
        }
      }
    });
  }
}

module.exports = function () {
  const app = this;
  app.use('/users', new SecureUserService({
    Model: require('./user.model'),
    paginate: { default: 10, max: 100 }
  }));
};

4. Secure JOINs and relations

When using relations or nested services, explicitly declare which fields to pull from related tables to avoid leaking columns from joined Cockroachdb tables.

// src/services/appointments/appointments.model.js
const Appointment = sequelize.define('appointment', {
  id: { type: DataTypes.INTEGER, primaryKey: true },
  patient_id: { type: DataTypes.INTEGER },
  doctor_id: { type: DataTypes.INTEGER },
  visit_notes: { type: DataTypes.TEXT }
});

// Explicitly include only safe fields from related Patient
Appointment.associate = (models) => {
  Appointment.belongsTo(models.user, {
    foreignKey: 'patient_id',
    as: 'patient',
    attributes: ['id', 'email', 'name'] // limit to safe fields
  });
};

By combining these patterns—field projection in hooks, model scoping, pagination guards, and controlled relations—you reduce the risk of data exposure when Feathersjs interacts with Cockroachdb. middleBrick’s Data Exposure and Property Authorization checks validate these safeguards by correlating spec-defined expectations with runtime responses, providing severity-ranked guidance without claiming to remediate automatically.

middleBrick integrates into your workflow via the CLI (middlebrick scan <url>), the Web Dashboard for tracking scores over time, and the GitHub Action to fail builds if risk thresholds are exceeded. The MCP Server also allows scanning APIs directly from AI coding assistants, helping teams catch misconfigurations early.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Can Feathersjs hooks alone prevent data exposure to Cockroachdb?
Hooks significantly reduce risk by enforcing field selection and query constraints, but they must be combined with explicit model definitions (e.g., defaultScope attributes) and pagination guards. Relying solely on hooks is insufficient if models or services allow default SELECT * behavior.
Does middleBrick automatically fix data exposure findings in Feathersjs with Cockroachdb?
No. middleBrick detects and reports data exposure with severity and remediation guidance. It does not modify code, block requests, or alter runtime behavior. Teams must apply the recommended hook- and model-level fixes manually.