Skip to content

fix(security): harden policy expression handling#168

Open
bhudevbhanpuriya wants to merge 1 commit into
istSOS:mainfrom
bhudevbhanpuriya:fix/security-policy-expression-hardening
Open

fix(security): harden policy expression handling#168
bhudevbhanpuriya wants to merge 1 commit into
istSOS:mainfrom
bhudevbhanpuriya:fix/security-policy-expression-hardening

Conversation

@bhudevbhanpuriya
Copy link
Copy Markdown
Contributor

@bhudevbhanpuriya bhudevbhanpuriya commented Apr 21, 2026

Summary

This PR hardens custom policy expression handling in the policy create/update flows.

Previously, policy conditions were validated with a small blacklist and then interpolated directly into CREATE POLICY / ALTER POLICY SQL. Since policy predicates are SQL syntax, not bindable values, this left a brittle surface where malformed input could try to break out of USING (...) or WITH CHECK (...).

Changes

  • Added a constrained policy expression renderer/parser
  • Replaced raw policy condition interpolation in policy update flow
  • Reused the same safe renderer in custom policy creation
  • Preserved support for simple predicates such as:
    • true
    • network = 'IDROLOGIA'
    • "unitOfMeasurement" IS NOT NULL
    • status = 'active' AND public = true
  • Added regression tests for accepted and rejected expressions

Security Impact

This reduces SQL syntax abuse risk around row-level-security policy management.

The API no longer passes arbitrary request-provided policy text directly into policy DDL. Instead, it accepts a limited expression grammar and renders trusted SQL from parsed tokens.

Rejected examples include:

true) WITH CHECK (false
network = 'x'; DROP TABLE sensorthings."User"
exists (select 1)
Testing
Verified locally:
env PYTHONPYCACHEPREFIX=/tmp/istsos_pycache python3 -m py_compile \
  api/app/utils/policy_expression.py \
  api/app/v1/endpoints/update/policy.py \
  api/app/v1/endpoints/create/policy.py \
  api/tests/test_policy_expression_safety.py

Also smoke-tested the pure policy expression renderer.

Note: full pytest execution was not run in this environment because pytest, pip, and asyncpg are not installed locally.

Compatibility

  • No API schema changes
  • Valid simple custom policy expressions remain supported
  • Arbitrary SQL expressions are intentionally rejected
  • Some previously accepted raw SQL policy conditions may now fail validation by design

Note

PostgreSQL cannot bind an entire policy predicate as $1, because the predicate is SQL syntax rather than a data value. This PR uses a safer middle ground: parse a limited expression language and render SQL from trusted tokens.

Closes #167

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Harden policy expression handling in policy create/update endpoints

1 participant