This page provides examples of common policy scenarios you might want to implement in Formal. Each example includes the policy code and an explanation of how it works.Documentation Index
Fetch the complete documentation index at: https://docs.formal.ai/llms.txt
Use this file to discover all available pages before exploring further.
Basic Policy Examples
Block All Connections by Default
A foundational security pattern: deny all access by default.Allow Only Admin Group
Combine default deny with explicit allow for specific groups.Allow Machine User with End-User in Admin Group
Control access based on both the machine user and the actual person using it.Block Writes to Production
Prevent destructive operations in production environments.Block an HTTP Request by Hostname and Path
Useinput.http.hostname to narrow a policy to a single upstream. Without it, a path-based rule would match that path on every HTTP resource.
Advanced Scenarios
Redact emails
The two policies below redact email columns coming back from a database. The first masks them, and the second blocks queries that could try to bypass the masking.Policy 1 – Mask emails
This policy masks every column whosedata_label is email_address, based on each column lineage: returned columns derived from an email source column (wildcards, joins, unions) are also masked.
Policy 2 – Block queries that try to bypass masking
Attackers can leverage advanced engine features to escape masking. This policy blocks queries containing such constructs.request instead of response to block the query before execution.
Alternatively,
CREATE queries can be blocked via input.sql_query.statement_type in another policy.Block Access to Specific Native Users
You can control which users can access specific native users when they attempt to connect using the@<native_user> syntax. This is useful for enforcing least-privilege access and preventing users from accessing highly privileged accounts.
The following policy blocks the user john@joinformal.com from using the native user devops:
- Block multiple users from specific native users
- Allow only certain users to access privileged native users
- Require additional authentication for certain native users
Block Access to Objects in AWS S3
This policy prevents access to S3 objects that were last modified more than 10 minutes ago. This is useful for ensuring data freshness and preventing access to outdated information.is_stalefunction: Compares the object’slast_modifiedtimestamp with the current time minus 10 minutes- Time calculation: Uses nanoseconds for precision (10 minutes = 600,000,000,000 nanoseconds)
- Blocking logic: Blocks
GetObjectrequests to objects inlocal-bucketthat are older than 10 minutes - Response stage: Uses
responseto evaluate after the request is processed but before returning results
- Prevent access to outdated configuration files
- Ensure users only see recent data exports
- Block access to temporary files that should have been cleaned up
Rate Limiting Access
Formal automatically tracks access patterns across different time windows and makes this data available to policies. You can use access count data to implement rate limiting policies that block users who exceed defined thresholds.Basic Rate Limiting by Minute
This policy blocks access tosensitive-bucket if the user has accessed it more than five times in the last minute:
Rate Limiting by Hour
You can also limit access over longer time windows. This policy blocks access if the user has made more than 100 requests in the last hour:- Automatic tracking: Formal automatically tracks access counts per user, per resource, per path (database/bucket name)
- Time windows: Access counts are available for minute (
data.access_count_minute) and hour (data.access_count_hour) windows - Cluster-wide: Access counts are synchronized across all Connector instances using distributed state
- Policy evaluation: Access count data is fetched and made available during policy evaluation at the
request,response, andsessionstages
data.access_count_minute[<bucket_name>]- Number of accesses in the last minutedata.access_count_hour[<bucket_name>]- Number of accesses in the last hour
- Prevent denial-of-service attacks by limiting request rates
- Enforce fair usage policies across users
- Protect against runaway scripts or applications
- Different rate limits for different buckets or databases
Rate limiting also works for databases. Use
data.access_count_minute[<database_name>] and data.access_count_hour[<database_name>] to limit database access patterns.Rewrite HTTP Request Headers
Therewrite action adds, replaces, or removes HTTP headers before the request reaches the upstream service.
The following policy adds a custom header and removes a debug header on all HTTP requests:
object.unionmerges the new headers into the existinginput.http.headers. Keys in the second object overwrite keys in the first.- Setting a header to an empty array (
[]) removes it from the request. - Header names are normalized to canonical HTTP casing before forwarding (e.g.,
x-custom-rewritebecomesX-Custom-Rewrite). - Formal logs both received and sent headers, visible in the Logs page.
Best Practices
As you can see with this example, policies can quickly get quite extensive. When creating policies, keep these best practices in mind:- Use the correct package name (
formal.v2for data policies). - Import required keywords (
future.keywords.if,future.keywords.in). - Use
session,request, andresponsestages accordingly. - Test policies before deploying to production by using the Dry-run status.
We recommend that you write and version your policies in git, and deploy them
with Terraform using our provider.