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 🙏

© 2025 – Pkg Stats / Ryan Hefner

sails-hook-waterline-safe-criteria

v0.1.2

Published

Sails hook that guards Waterline queries against undefined where-clause values and missing criteria.

Readme

sails-hook-waterline-safe-criteria

Guard Sails/Waterline queries from silently matching everything when unsafe criteria slip through (e.g. undefined in a where clause or calling Model.destroy() with no filters).

Socket Badge

Key points

  • Secure by default: once installed the guard is active unless you explicitly disable it.
  • Per-model overrides let you keep legacy behavior where needed.
  • Per-query bypass is available by including meta: { allowUndefinedWhere: true } alongside your criteria.

Why this hook exists

Waterline 0.15 keeps backwards compatibility by stripping undefined values from criteria. In destructive operations that turns this:

await User.destroy({ where: { status: undefined } });

into User.destroy({}) and deletes every record.

sails-hook-waterline-safe-criteria adds a guard without forking Waterline. The hook is secure by default; you can opt out globally, per model, or on individual queries when you really need the legacy behavior.

Requirements

| Dependency | Requirement | |------------|-------------| | Sails | ≥ 1.0.0 | | Waterline | 0.15.x (bundled with Sails 1.x) | | Node.js | ≥ 14 (tests run on 16+) |

The hook inspects stage-one criteria, so it works with any adapter (sails-disk, sails-mongo, sails-postgresql, custom adapters, etc.).

Local development

  1. Install dependencies: npm install.
  2. Run the unit/integration suite with npm test.
  3. Ensure Docker is available if you plan to exercise the adapter matrix (npm run test:adapters).
  4. Use npm run test:all before sending changes to cover every scenario.

Installation

npm install sails-hook-waterline-safe-criteria --save

Sails auto-loads any dependency named sails-hook-*. If you prefer to be explicit (or to customize the config key) add the hook to config/hooks.js:

module.exports.hooks = {
  'waterline-safe-criteria': require('sails-hook-waterline-safe-criteria')
};

Configuration

| Scope | Setting | Effect | |-------|---------|--------| | Global | config/models.jsrejectUndefinedWhere (default: true) | Secure-by-default. Set to false if you intentionally want legacy behavior. | | Per model | api/models/Order.jsrejectUndefinedWhere: true | Opt in for specific models only (inherits the global default otherwise). | | Hook defaults | config/waterline-safe-criteria.js{ enabled: false } | Optional: explicitly opt out of the default guard for the whole app. |

Per-query bypass

Legacy code paths can still opt in to the old behavior (or you can disable the guard globally by setting config.models.rejectUndefinedWhere = false). Add a meta object to your stage-one criteria when you really need to bypass the undefined check:

await Order.destroy({
  where: criteria,
  meta: { allowUndefinedWhere: true }
});

The guard still requires criteria to exist, but it ignores undefined values when that meta flag is present. The original meta object is reattached to Waterline’s Deferred so adapter-level features (.meta({ fetch: true }), etc.) keep working.

What the guard does

After sails-hook-orm loads, the hook wraps these helpers on every guarded model:

find, findOne, destroy, destroyOne, update, updateOne, count, sum, avg

Each call goes through the following checks:

  1. Criteria must be supplied (no bare Model.destroy() / Model.update()).
  2. Any undefined inside the where clause—including nested and/or/in structures—is rejected.
  3. Criteria objects are not mutated; the hook works with Promises or old-school Deferreds alike.

If an unsafe pattern is detected the guard throws a flaverr with code E_UNDEFINED_WHERE. Example messages:

Unsafe DESTROY on `user` would hit every record. Pass an explicit WHERE or include `meta: { allowUndefinedWhere: true }` to bypass intentionally.
Unsafe UPDATE on `order` detected undefined inside WHERE clause. Undefined values cause Waterline to remove predicates and match everything. Scrub the criteria first, or bypass with `meta: { allowUndefinedWhere: true }`.

Migration tips

  1. Enable in staging first. Turn on rejectUndefinedWhere globally and run your test suite. Any unsafe queries now fail early.
  2. Fix or bypass intentionally. Clean up the criteria or add meta: { allowUndefinedWhere: true } where the behavior is desired.
  3. Roll out per model if needed. Leave legacy models unguarded (rejectUndefinedWhere: false) while protecting everything else.

Behavior summary

  • Throws when criteria are missing (even with bypass meta).
  • Accepts primary-key shorthand (Model.find(7) or Model.destroy([1,2])).
  • Catches undefined inside nested predicates and arrays.
  • Supports meta: { allowUndefinedWhere: true } to bypass only the undefined-value check.
  • Keeps Deferred helpers (.meta, .fetch) intact.

Example

// Before (legacy behavior wipes everything)
await User.destroy({ where: { status: undefined } });

// After enabling the hook (throws E_UNDEFINED_WHERE)
await User.destroy({ where: { status: undefined } });

// If you really need the legacy behavior:
await User.destroy({ where: { status: undefined }, meta: { allowUndefinedWhere: true } });

Testing

Run the core unit + integration suite:

npm test

Adapter coverage (MySQL, PostgreSQL, MongoDB) lives in a separate job because it depends on Docker:

npm run test:adapters

For everything at once, use:

npm run test:all

The suites verify the guard on every Waterline helper, nested criteria detection, meta bypass behavior, and three integration scenarios (guarded app, baseline app, per-model configuration). The adapter matrix re-runs the critical happy/sad paths against real adapters.

Adapter test matrix

A docker-compose file is included for local adapter testing, and the helper script bootstraps/tears down everything automatically:

npm run test:adapters

Under the hood the script starts the compose stack, waits for each service to be ready, exports the expected connection strings, runs the adapter spec, and shuts everything down. Nothing to configure.

Tip: npm run test:all chains the full suite. npm run test:adapters:raw runs just the adapter spec when you already have databases running.

| Variable | Description | |----------|-------------| | TEST_MYSQL_URL | Override MySQL connection string (defaults to compose stack). | | TEST_POSTGRES_URL | Override PostgreSQL connection string (defaults to compose stack). | | TEST_MONGO_URL | Override Mongo connection string (defaults to compose stack). |

Limitations

  • Only stage-one criteria are inspected. Raw adapter calls or stage-two tweaks are unaffected.
  • The guard doesn’t attempt to rewrite unsafe queries; it simply throws so you can decide how to fix or bypass.
  • allowUndefinedWhere bypasses undefined checks but not the “criteria required” rule.

License

MIT © Luis Lobo Borobia