JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 8
  • Score
    100M100P100Q48546F
  • License MIT

Data Service AND Security Layer - Universal role-based data filtering and security for Node.js applications

Package Exports

  • dsandsl
  • dsandsl/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (dsandsl) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

DSANDSL - Data Service AND Security Layer

Universal role-based data filtering and security for Node.js applications

DSANDSL provides automatic, configurable, role-based filtering of data objects, arrays, database queries, and API responses. Built for applications that need granular access control without manual security implementation everywhere.

⚠️ CRITICAL: This is NOT a Regular ORM

DSANDSL is a SECURITY FRAMEWORK that happens to include database capabilities.

  • ✅ Use it if you want bulletproof security enforced at the data layer
  • ✅ Use it if you want zero possibility of developers bypassing security
  • ✅ Use it if you want consistent, centralized data access patterns
  • 🚫 DO NOT use it if you want manual control over database queries
  • 🚫 DO NOT use it if you want to write raw SQL in API endpoints
  • 🚫 DO NOT use it if you think the Service Provider pattern is "overkill"

If you want a traditional ORM with manual control, use Prisma, Sequelize, or TypeORM instead.

💡 Real-World Origin Story

DSANDSL was born from a practical business need: preventing affiliate partners from seeing data they shouldn't have access to.

Specific Business Risks:

  • Partners seeing internal costs/revenue data
  • Partners accessing other partner's confidential information
  • Data leaks during rapid development iterations due to lack of layered security

The creator's business partner was (rightfully) paranoid about these data exposure risks, and the development team found themselves constantly contemplating "what consumes what" every time they implemented a new API endpoint.

The DSANDSL Workflow:

  1. Just implement your API and ask the service layer for whatever data you want
  2. The service layer returns only what the user role is allowed to have
  3. If something's missing that should be there, whitelist it in the DSL configuration
  4. Enable debug warnings to quickly see what's being filtered and why

Result: You stop worrying about data leaks and focus on building features. The security is automatic and consistent across your entire application.

🚀 Quick Start (Service Provider Pattern - MANDATORY)

⚠️ WARNING: If you don't use the Service Provider pattern, you're using DSANDSL wrong!

const { DSLServiceProvider, BaseService, createConfig } = require('dsandsl')

// 1. Configure your security model
const config = createConfig({
  roles: {
    admin: { level: 100 },
    manager: { level: 50 },
    user: { level: 10 }
  },
  
  fields: {
    'users.email': { minRole: 'user' },
    'users.salary': { minRole: 'admin' },
    'users.password_hash': { deny: true }
  },
  
  database: {
    tables: {
      users: { minRole: 'user', operations: ['SELECT', 'INSERT', 'UPDATE'] }
    }
  }
})

// 2. Initialize the service provider (once at app startup)
await DSLServiceProvider.initialize(config, {
  type: 'postgresql',
  connection: {
    host: 'localhost',
    database: 'myapp',
    user: 'postgres',
    password: 'password'
  }
})

// 3. Create domain services
class UserService extends BaseService {
  static async getUsers(userRole, options = {}) {
    return this.select('users', userRole, options)
  }
  
  static async createUser(userData, userRole) {
    return this.insert('users', userData, userRole)
  }
}

// 4. Use in your API endpoints (security is automatic)
app.get('/api/users', async (req, res) => {
  const users = await UserService.getUsers(req.user.role, req.query)
  res.json(users) // Automatically filtered by role!
})

🎯 This is a SECURITY FRAMEWORK, not a regular ORM. Use it correctly or use something else.

🛡️ Why DSANDSL?

Problems It Solves

  • Forgotten Security: Manual field filtering is easy to forget and inconsistent
  • Data Leaks: Accidentally exposing sensitive data in API responses
  • Complex Authorization: Role-based access control scattered throughout codebase
  • SQL Injection: Field names become attack vectors in poorly written APIs
  • API Layer Vulnerabilities: Developers bypass security with careless input handling
  • Debugging Pain: No visibility into why data is missing from responses

Defense-in-Depth Security

DSANDSL provides data layer security that protects even when API code is poorly written:

// 🔥 BAD API: No input validation
app.get('/api/users', (req, res) => {
  // Developer directly passes user input - DANGEROUS!
  const fields = req.query.fields?.split(',') || ['*']
  const users = await db.select(fields).from('users')
  res.json(users)
})

// ✅ PROTECTED: DSANDSL prevents SQL injection and unauthorized access
app.get('/api/users', (req, res) => {
  // Even with dangerous field names, DSL validates and filters
  const fields = req.query.fields?.split(',') || ['*'] // Could be malicious!
  const users = await adapter.select('users', req.user.role, { fields })
  res.json(users) // Safe - malicious fields blocked, unauthorized fields filtered
})

Key Protection Mechanisms:

  • 🗺️ FieldMapper validates all field names before SQL generation
  • 🛡️ Role-based filtering enforces permissions at data layer
  • 🔒 Parameterized queries prevent SQL injection in values
  • 🏗️ Table access control validates operations by role
  • Automatic conversion between camelCase and snake_case
  • Performance: Inefficient manual filtering of large datasets

DSANDSL Solution

  • Automatic Enforcement: Configure once, apply everywhere
  • Single Source of Truth: Centralized security configuration
  • Framework Agnostic: Works with Express, Next.js, or standalone
  • Performance Optimized: Chunked processing for large datasets
  • Developer Friendly: Rich metadata for debugging and development

⚡ Key Features

🔒 Universal Field Filtering

// Configure field access by role
const config = {
  fields: {
    'financial.*': { minRole: 'admin' },
    'personal.email': { minRole: 'user' },
    'audit.*': { minRole: 'manager' }
  }
}

// ❌ WRONG WAY - Don't do this!
// Use Service Provider pattern instead (see Quick Start above)

🎯 Role-Based Access Control

const roles = {
  admin: { level: 100, inherits: ['manager'] },
  manager: { level: 50, inherits: ['user'] },
  user: { level: 10 }
}

📊 Rich Debugging Metadata

// ❌ WRONG WAY - Don't use direct DSL filtering!
// Use Service Provider pattern instead
console.log(result.metadata)
// {
//   totalFields: 10,
//   allowedFields: 6,
//   filteredFields: [
//     { field: 'salary', reason: 'insufficient_role', requires: 'admin' }
//   ],
//   userRole: 'user'
// }

🏗️ Framework Integrations

// Next.js API Route
import { NextJSAdapter } from 'dsandsl/adapters/nextjs'

export default NextJSAdapter.createHandler(dsl, {
  roleExtractor: (req) => req.user?.role
})

// Express Middleware
import { ExpressAdapter } from 'dsandsl/adapters/express'

app.use('/api/users', ExpressAdapter.middleware(dsl))

🗃️ Database Query Security

// Automatically filter query results and validate access
const safeQuery = dsl.buildQuery({
  select: ['name', 'email', 'salary'], // salary auto-filtered for non-admin
  from: 'users',
  where: { active: true },
  userRole: 'user'
})
// Result: SELECT name, email FROM users WHERE active = true

📋 Configuration

Field Access Patterns

const config = {
  // Exact field matching
  'user.email': { minRole: 'user' },
  
  // Wildcard patterns
  'financial.*': { minRole: 'admin' },
  '*.password': { deny: true },
  
  // Nested object patterns
  'profile.contact.phone': { minRole: 'manager' },
  
  // Array element patterns
  'users[].salary': { minRole: 'admin' }
}

Security Levels

const securityConfig = {
  // Production: No metadata leakage
  production: {
    includeMetadata: false,
    includeFieldNames: false,
    logFiltering: false
  },
  
  // Development: Full debugging
  development: {
    includeMetadata: true,
    includeFieldNames: true,
    includeReasons: true,
    logFiltering: true
  }
}

Database Integration

const config = {
  database: {
    type: 'postgresql', // 'mysql', 'sqlite'
    connection: process.env.DATABASE_URL,
    
    // Table-level access control
    tables: {
      'sensitive_data': { minRole: 'admin' },
      'user_profiles': { minRole: 'user' }
    },
    
    // Query template access
    queries: {
      'salary_report': { minRole: 'admin', template: '...' }
    }
  }
}

🏃‍♂️ Getting Started

Installation

npm install dsandsl

Basic Setup

const { DSLEngine, createConfig } = require('dsandsl')

// 1. Define your roles
const config = createConfig({
  roles: {
    admin: { level: 100 },
    user: { level: 10 }
  },
  
  // 2. Configure field access
  fields: {
    'sensitive_data': { minRole: 'admin' },
    'public_info': { minRole: 'user' }
  }
})

// ❌ WRONG WAY - This defeats the purpose of DSANDSL!
// If you want this pattern, use Prisma, Sequelize, or another ORM
// DSANDSL forces the Service Provider pattern for security

Next.js Integration

// ❌ WRONG WAY - This bypasses the Service Provider pattern!
// NextJS adapter should be integrated with Service Provider instead
// See docs/SERVICE-PROVIDER-PATTERN.md for correct implementation
// ❌ WRONG WAY - These patterns bypass Service Provider security!
// Framework adapters should only be used WITH Service Provider pattern
// See docs/SERVICE-PROVIDER-PATTERN.md for correct integration

🎮 Interactive Demo

Experience DSANDSL in action with real-time role-based filtering:

cd node_modules/dsandsl/demo
npm start
# Visit http://localhost:3000

The demo shows:

  • Real-time role switching (guest → user → manager → admin)
  • Field filtering visualization
  • Service Provider pattern in action
  • Performance metrics and debugging

🚨 Critical: Understand How to Use DSANDSL

⚠️ IF YOUR CODE DOESN'T LOOK LIKE THE SERVICE PROVIDER PATTERN, YOU'RE DOING IT WRONG!

# See the RIGHT WAY vs WRONG WAY comparison
node examples/service-pattern-comparison.js

# Bad API, Good DSL protection examples
node examples/bad-api-good-dsl.js

# FieldMapper security demonstration  
node examples/fieldmapper-protection.js

# SQL injection protection tests
node test-security.cjs

🎯 Key Message: If you want manual control over database queries, use Prisma, Sequelize, or another ORM. DSANDSL is a SECURITY FRAMEWORK that enforces secure patterns.

These examples demonstrate:

  • 🚫 Why direct DSL usage defeats the purpose
  • ✅ How Service Provider pattern ensures security
  • ✅ Protection against SQL injection in field names
  • ✅ Role-based filtering despite API vulnerabilities
  • ✅ Defense-in-depth security at the data layer
  • ✅ FieldMapper preventing column name attacks

🔧 Advanced Usage

Advanced Service Configuration

⚠️ Remember: Always use the Service Provider pattern!

// 1. Configure your DSL
const config = createConfig({
  roles: { admin: { level: 100 }, user: { level: 10 } },
  fields: {
    'audit_logs.*': { minRole: 'admin' },
    'users.salary': { minRole: 'admin' }
  }
})

// 2. Initialize service provider once at startup
await DSLServiceProvider.initialize(config, {
  type: 'postgresql',
  connection: { /* db config */ }
})

// 3. Create domain services
class AuditService extends BaseService {
  static async getAuditLogs(userRole, options = {}) {
    return this.select('audit_logs', userRole, options)
  }
}

// 4. Use in APIs - security is automatic
app.get('/api/audit', async (req, res) => {
  const logs = await AuditService.getAuditLogs(req.user.role)
  res.json(logs) // Automatically filtered!
})

🔌 Framework Adapters

DSANDSL provides first-class adapters for popular frameworks:

  • Express.js - Middleware and route helpers
  • Next.js - API route handlers and middleware
  • Generic - Use with any Node.js framework

See the complete adapter documentation for detailed examples and configuration options.

📚 API Reference

⚠️ These APIs are for framework integration only. Use the Service Provider pattern in your application code.

For application development, use:

  • DSLServiceProvider.initialize() - Initialize once at startup
  • BaseService - Extend for domain services
  • UserService - Example service implementation

Direct DSL usage is only for framework adapters and internal implementation.

checkAccess(fieldName, userRole)

Checks if a user role has access to a specific field.

buildQuery(queryOptions)

Builds a database query with automatic role-based field filtering.

Configuration Schema

interface DSLConfig {
  roles: {
    [roleName: string]: {
      level: number
      inherits?: string[]
    }
  }
  
  fields: {
    [fieldPattern: string]: {
      minRole: string
      category?: string
      deny?: boolean
    }
  }
  
  security?: {
    includeMetadata?: boolean
    includeFieldNames?: boolean
    logFiltering?: boolean
  }
}

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes
  4. Add tests: npm test
  5. Commit: git commit -m 'Add amazing feature'
  6. Push: git push origin feature/amazing-feature
  7. Open a Pull Request

📄 License

MIT License - see LICENSE file for details.

🙏 Acknowledgments

Built with inspiration from production affiliate tracking systems requiring granular data security and high-performance field filtering.

⛪ The Church of Murphy Engineering Philosophy

DSANDSL was built following the sacred Church of Murphy engineering principles for the troubleshooting, development, and testing phases of software engineering:

Murphy's Law: "Anything that can go wrong, will go wrong."

The Church of Murphy Tenets:

  • 🔧 Measure twice, cut once - Engineer systematically during implementation
  • 🔍 Investigate until you stop finding problems - Thorough testing and debugging
  • 🛡️ Assume developers will make mistakes - Build defensive systems and safeguards
  • 🏗️ Make the wrong way difficult - Design APIs that guide toward correct usage
  • Fail fast and fail safe - Catch problems early in development and testing

In the Church of Murphy, we don't fight Murphy's Law - we embrace it by building systems that make "going wrong" much harder.

DSANDSL's Church of Murphy Design:

  • Makes accidentally exposing sensitive data difficult ✅
  • Makes bypassing security require deliberate effort ✅
  • Makes writing vulnerable queries harder ✅
  • Makes forgetting authorization unlikely ✅
  • Reduces data leak risk during rapid development ✅

🙏 A Prayer to Murphy

Our Murphy, who art in chaos,
Hallowed be thy law.
Thy failures come,
Thy bugs be done,
On prod as they are in staging.

Give us this day our daily builds,
And forgive us our tech debt,
As we forgive those who merge without testing.
And lead us not into production,
But deliver us from data leaks.

For thine is the chaos,
The edge cases,
And the midnight pages,
Forever and ever.

Amen.


DSANDSL - Because security should be automatic, not an afterthought.

Built with the blessing of Murphy - may your data always be filtered and your queries always be safe. 🛡️