Skip to main content
Last Updated: 2025-11-25
Status: ✅ Implemented

Overview

The Encore OS Reporting Engine allows users to create custom SQL queries for generating reports. This document outlines the security measures implemented to prevent SQL injection, data breaches, and performance abuse.

Critical Security Measures

1. Parameterized Query Execution

Problem: String replacement of parameters ({{param}}) is vulnerable to SQL injection. Solution: Use PostgreSQL prepared statements with numbered parameters ($1, $2, etc.).

2. Query Complexity Validation

Problem: Complex queries can cause performance degradation or denial of service. Solution: Implement query complexity limits:
  • Max 5 JOINs - Prevents cartesian product explosions
  • Max 3 subqueries - Limits nested query depth
  • No UNION ALL - Prevents expensive full table scans
  • No window functions - Deferred until performance impact is understood

3. Enhanced Audit Logging

Problem: Lack of visibility into executed queries makes incident response difficult. Solution: Log all report executions with:
  • Actual executed SQL (with resolved parameters)
  • User ID and timestamp
  • IP address and user agent
  • Execution time and row count
  • Success/failure status

4. Row-Level Security (RLS)

All queries execute under the authenticated user’s RLS context:

5. Database Function Security

The execute_report_query function runs with SECURITY DEFINER but:
  • Only allows SELECT statements
  • Blocks dangerous keywords (DROP, ALTER, INSERT, UPDATE, DELETE, etc.)
  • Enforces row limit (10,000 rows max)
  • Enforces query timeout (30 seconds max)

Security Best Practices

For Developers

  1. Never bypass parameterization - Always use buildParameterizedQuery()
  2. Validate all inputs - Check parameter types and ranges
  3. Test with malicious inputs - Use SQL injection test cases
  4. Monitor query patterns - Look for anomalous query structures
  5. Review audit logs regularly - Detect suspicious activity

For Report Authors

  1. Use parameters for all user inputs - Never concatenate user data into SQL
  2. Test with minimal data first - Verify query performance before production
  3. Request only needed columns - Avoid SELECT *
  4. Use indexes - Ensure WHERE clauses use indexed columns
  5. Review generated SQL - Use dry-run mode to inspect query structure

For Security Reviewers

  1. Audit pf_report_executions logs - Look for:
    • Failed queries with injection patterns
    • Queries with excessive execution time
    • High-volume queries from single users
    • Queries accessing sensitive tables
  2. Monitor resource usage - Alert on:
    • Query execution time > 10 seconds
    • Row counts > 5,000
    • Multiple failed queries from same user
  3. Review report definitions - Flag reports that:
    • Join more than 3 tables
    • Use subqueries excessively
    • Query audit log tables
    • Access PHI/PII without justification

Known Limitations

1. No Read-Only Database Connection

Current State: Reports execute with full database permissions (constrained by RLS). Recommendation: For production, create a read-only PostgreSQL role:

2. No Query Result Caching

Current State: Every report execution re-runs the full query. Recommendation: Implement Redis caching for frequently-run reports:
  • Cache key: report:${report_id}:${JSON.stringify(params)}
  • TTL: 1 hour (configurable per report)
  • Invalidation: On report definition update

3. No Rate Limiting

Current State: No limit on number of reports a user can run. Recommendation: Implement rate limiting:
  • Max 10 reports per minute per user
  • Max 100 reports per hour per organization
  • Exponential backoff for repeated failures

Incident Response

SQL Injection Detected

  1. Immediate Actions:
    • Disable affected report
    • Revoke user access if malicious
    • Review audit logs for data exfiltration
  2. Investigation:
    • Extract all queries from pf_report_executions for user
    • Check for unusual table access patterns
    • Review RLS policy effectiveness
  3. Remediation:
    • Patch vulnerability
    • Force password reset for affected users
    • Notify security team

Performance Degradation

  1. Immediate Actions:
    • Identify long-running queries in pg_stat_activity
    • Kill queries exceeding timeout threshold
    • Temporarily disable expensive reports
  2. Investigation:
    • Analyze query plans (EXPLAIN ANALYZE)
    • Check for missing indexes
    • Review query complexity violations
  3. Remediation:
    • Add indexes to frequently-queried columns
    • Reduce query complexity limits if needed
    • Educate report authors on query optimization

Testing Checklist

  • SQL injection prevention (see test cases below)
  • Query complexity limits enforced
  • Parameterized queries work correctly
  • Audit logs capture all executions
  • RLS policies prevent cross-org access
  • Timeout enforced for long queries
  • Row limit enforced

SQL Injection Test Cases

References


Maintained by: Platform Security Team
Next Review: 2025-12-25