Skip to main content

Secure by design. Safe by defaults.

ServiceQuery® compiles requests into expression trees (no string concatenation), and supports guardrails that prevent abuse: allowlists, paging caps, complexity limits, and authorization-safe patterns.

Security Scope

What we secure vs. what remains your responsibility.

What ServiceQuery® Secures

  • Query construction is injection-proof (expression trees, no SQL strings)
  • Provides guardrail APIs for limiting query scope
  • Supports field allowlists and complexity limits

Your Responsibility

  • Authorization — who can query what
  • Authentication — who is making the request
  • Rate limiting — how often they can query
  • Data classification — what fields are sensitive

ServiceQuery® doesn't do authorization for you — it gives you patterns to enforce it safely. The query layer handles "how to filter" — your app handles "what they're allowed to see."

Threat Model

Plain-language security analysis. What's handled, what needs attention.

✓ Prevented

SQL / Query Injection

ServiceQuery® does not build SQL strings. It compiles a structured request into expression trees and lets your LINQ provider translate it.

This shuts down the entire class of "user input became SQL."

⚠ Guardrails

Data Exfiltration

If clients can query fields you didn't intend to expose, they'll find them.

Mitigation: Field allowlists. Define exactly which fields are queryable.

⚠ Guardrails

Denial of Service

If a client can request huge pages, deep filters, or expensive sorts, your database will pay the bill.

Mitigation: Max page size, complexity limits, timeouts, rate limiting.

⚠ Your Code

Authorization Bypass

If your endpoint forgets to enforce tenant/user scope, the query layer will happily return data you didn't mean to return.

Mitigation: Apply server-enforced scope BEFORE user queries. See patterns →

⚠ Your Code

Logging Leaks

Logging raw user input can leak PII into your logs.

Mitigation: Log normalized query structure, not raw request.

ℹ Know It

Provider Differences

Different providers may handle nulls, case sensitivity, or collation differently.

Mitigation: Test your specific queries on your specific provider. See provider notes →

Summary: Injection is handled by architecture. Abuse and access control are handled by guardrails and auth patterns.

Production Guardrails

If you expose dynamic queries without limits, you've built an accidental "export everything" API.

Guardrail Purpose Implementation
Field allowlist Prevent field discovery/exfiltration Define queryable fields, reject others
Max page size Prevent data dumps Server-enforced cap (e.g., 100)
Complexity limits Prevent expensive queries Cap filter depth, predicate count
Timeouts Prevent runaway queries Cancellation tokens
Paging required Prevent unbounded results Reject requests without pagination

Example Configuration

var options = new ServiceQueryOptions
{
    AllowedFields = new[] { "Name", "Status", "CreatedAt" },
    MaxPageSize = 100,
    MaxFilterDepth = 5,
    RequirePaging = true
};

var result = request.Execute(queryable, options);

If you don't see the knob you need, treat that as a blocker and open an issue — security requirements aren't "nice to have."

Authorization Patterns

Copy-paste implementations for secure query endpoints.

1Mandatory Scope First

Always apply server-enforced scope before user queries.

// ALWAYS apply server scope before user query
var baseQuery = db.Orders
    .Where(o => o.TenantId == tenantId);

return request.Execute(baseQuery);

2Field-Level Permissions

Validate requested fields against user's allowed set.

var userAllowedFields = GetFieldsForRole(user.Role);
var requestedFields = request.GetReferencedFields();

if (requestedFields.Except(userAllowedFields).Any())
    return Results.Forbid();

3Own Records Only

Restrict queries to user's own data.

// Only allow querying own records
var baseQuery = db.Records
    .Where(r => r.OwnerId == userId);

return request.Execute(baseQuery);

4Multi-Tenant Isolation

Tenant scope is non-negotiable.

app.MapPost("/api/{tenantId}/items", 
    (string tenantId, ServiceQueryRequest req, AppDb db) =>
{
    var baseQuery = db.Items
        .Where(i => i.TenantId == tenantId);
    return Results.Ok(req.Execute(baseQuery));
});

Critical: Dynamic queries don't bypass authorization — sloppy endpoints do. Apply server scope first, then user filters inside that scope. Never the other way around.

Operational Hardening

Production deployment guidance.

Rate Limiting

Apply at gateway or middleware level

Caching

Cache compiled expressions for performance

Observability

Structured logs, slow query metrics

Load Testing

Test with realistic query patterns

Timeouts

Set reasonable query timeouts

Provider Testing

Verify behavior on your specific provider

Vulnerability Reporting

If you discover a security vulnerability, we want to hear about it.

To report a security vulnerability:
  1. Email: support@holomodular.com
  2. Include: description, reproduction steps, impact assessment
  3. We aim to acknowledge within 2 business days
  4. We aim to provide a fix timeline within 5 business days
Supported Versions

Current major version and previous major version receive security updates.

Disclosure Policy

We follow coordinated disclosure. We'll work with you on timing to ensure users have time to update before public disclosure.

Security Checklist

Before deploying ServiceQuery® to production.

  • Field allowlist configured (only expose intended fields)
  • Max page size enforced (prevent data dumps)
  • Complexity limits set (prevent expensive queries)
  • Authorization scope applied before user queries
  • Timeouts and cancellation configured
  • Query logging enabled (normalized, not raw)
  • Rate limiting in place
  • Provider-specific behavior tested

Ready to implement?

See real integration patterns or start building now.