What You’ll Learn
- Creating
TransformationRule entries with regex match and replace fields
- Controlling direction with
OUTBOUND, INBOUND, or BOTH
- Using
TransformationRule.apply() locally to verify regex matching before deploying
- Using
TransformationRule.applies_to() to check which directions a rule covers
- Attaching rules to a
SecurityPolicy at sandbox creation time
Prerequisites
- Declaw instance running and
DECLAW_API_KEY / DECLAW_DOMAIN set
pip install declaw python-dotenv
Code Walkthrough
This example is available in Python. TypeScript support coming soon.
Each TransformationRule takes a regex match pattern, a replace string, and a direction.
from declaw import Sandbox, SecurityPolicy, TransformationRule, TransformDirection
rules = [
# Mask internal hostnames before requests leave the sandbox
TransformationRule(
match=r"internal\.company\.com",
replace="api.example.com",
direction="outbound",
),
# Strip bearer tokens from inbound API responses
TransformationRule(
match=r"Bearer sk-[a-zA-Z0-9]+",
replace="Bearer [MASKED]",
direction="inbound",
),
# Filter passwords in both directions
TransformationRule(
match=r"password=\w+",
replace="password=[FILTERED]",
direction="both",
),
]
2. Create a sandbox with the rules attached
sbx = Sandbox.create(
template="base",
timeout=300,
security=SecurityPolicy(
transformations=rules,
),
)
The proxy applies each rule to all matching traffic for the lifetime of the sandbox.
| Direction | Behaviour |
|---|
OUTBOUND | Applied to traffic leaving the sandbox (requests to external APIs). Use this to mask internal hostnames, tokens, or other data before it reaches third-party services. |
INBOUND | Applied to traffic entering the sandbox (responses from external APIs). Use this to strip or mask sensitive data in API responses before the sandboxed code sees it. |
BOTH | Applied in both directions. Use for patterns like passwords that should never appear in any traffic through the proxy. |
4. Test rules locally with apply() and applies_to()
Before deploying, verify your regex patterns work as expected:
# TransformationRule.apply() runs the regex substitution locally
rule = TransformationRule(
match=r"internal\.company\.com",
replace="api.example.com",
direction="outbound",
)
before = "Calling https://internal.company.com/api/v2/users to fetch data"
after = rule.apply(before)
# after -> "Calling https://api.example.com/api/v2/users to fetch data"
print(f"Before: {before}")
print(f"After: {after}")
# TransformationRule.applies_to() checks direction filtering
print(rule.applies_to("outbound")) # True
print(rule.applies_to("inbound")) # False
5. Before/after examples for all three rules
| Rule | Direction | Before | After |
|---|
| Hostname mask | outbound | Calling https://internal.company.com/api/v2/users | Calling https://api.example.com/api/v2/users |
| Bearer token | inbound | Authorization: Bearer sk-abc123XYZ789secretToken | Authorization: Bearer [MASKED] |
| Password filter | both | POST /login?password=hunter2&user=admin HTTP/1.1 | POST /login?password=[FILTERED]&user=admin HTTP/1.1 |
6. Cleanup
Expected Output
============================================================
Declaw Transformation Rules Example
============================================================
--- Creating Sandbox with Transformation Rules ---
Sandbox created: sbx_abc123
Security policy applied:
transformations: 3 rule(s)
1. match='internal\\.company\\.com' replace='api.example.com' direction=outbound
2. match='Bearer sk-[a-zA-Z0-9]+' replace='Bearer [MASKED]' direction=inbound
3. match='password=\\w+' replace='password=[FILTERED]' direction=both
------------------------------------------------------------
Local Regex Matching Demo (TransformationRule.apply)
------------------------------------------------------------
Rule: match='internal\\.company\\.com' replace='api.example.com'
Direction: outbound
Before: Calling https://internal.company.com/api/v2/users to fetch data
After: Calling https://api.example.com/api/v2/users to fetch data
Rule: match='Bearer sk-[a-zA-Z0-9]+' replace='Bearer [MASKED]'
Direction: inbound
Before: Authorization: Bearer sk-abc123XYZ789secretToken
After: Authorization: Bearer [MASKED]
Rule: match='password=\\w+' replace='password=[FILTERED]'
Direction: both
Before: POST /login?password=hunter2&user=admin HTTP/1.1
After: POST /login?password=[FILTERED]&user=admin HTTP/1.1
--- Cleaning Up ---
Sandbox killed.
============================================================
Done!
============================================================