Guardrail: mask API keys and passwords
# Guardrail: mask API keys and passwords code: 1x2ywyz | type: guardrail | language: en | licence: MIT | version: v1 description: Guardrail that masks API keys, tokens, passwords, private keys and connection strings before a text, file or export is sent to an AI. Rules, 14 test cases, code. page: https://contextetech.com/en/guardrails/mask-secrets-api-keys-1x2ywyz mcp: connect https://contextetech.com/mcp then call load_resource with code "1x2ywyz" (or ask "load 1x2ywyz") json: https://contextetech.com/api/resources/contextetech--mask-secrets-api-keys python: pip install contexte && contexte get 1x2ywyz citation (required by the licence): contextetech. (2026). Guardrail: mask API keys and passwords (Version 1) [Guardrail]. Contexte Tech. https://contextetech.com/en/guardrails/mask-secrets-api-keys-1x2ywyz
Guardrail that masks API keys, tokens, passwords, private keys and connection strings before a text, file or export is sent to an AI. Rules, 14 test cases, code.
Rules
Mask before sending to the AI any secret that gives access to an account or a service: - API keys (OpenAI, Anthropic, Mistral, Stripe, AWS…) and access tokens (GitHub, JWT); - passwords written in clear text (password=…, pwd: …); - private keys (-----BEGIN … PRIVATE KEY-----); - credentials inside a URL (postgres://user:password@host). Let through: placeholder values (YOUR_API_KEY, xxx, <key>), URLs without credentials, order or commit IDs, text about passwords that contains none. Apply it before pasting a .env file, a database export or logs into an assistant.
Test cases
| Input | Expected |
|---|---|
| AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE | Block |
| token: ghp_000000000000000000000000000000000000 | Block |
| ANTHROPIC_API_KEY=sk-ant-00000000000000000000000000000000 | Block |
| STRIPE_SECRET=sk_live_0000000000000000000000 | Block |
| DATABASE_URL=postgres://admin:S3cretPass@db.internal:5432/app | Block |
| -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA0000000000000000 -----END RSA PRIVATE KEY----- | Block |
| Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwMDAwMDAifQ.0000000000000000000000 | Block |
| password = Sunshine2026! | Block |
| Passwords must be at least 12 characters long. | Allow |
| Replace YOUR_API_KEY with the key from your account. | Allow |
| The docs are at https://docs.example.com/api. | Allow |
| Commit a1b2c3d fixes the login bug. | Allow |
| Order #2026-10-458712 has shipped. | Allow |
| Sign in with your usual account. | Allow |
Code
import re
PATTERNS = {
"private_key": r"-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----",
"aws_key": r"\bAKIA[0-9A-Z]{16}\b",
"github_token": r"\bgh[pousr]_[A-Za-z0-9]{36,}\b",
"llm_api_key": r"\bsk-(?:ant-|proj-)?[A-Za-z0-9_-]{20,}\b",
"stripe_key": r"\b[sr]k_(?:live|test)_[A-Za-z0-9]{16,}\b",
"jwt": r"\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b",
"url_credentials": r"\b[a-z][a-z0-9+.-]*://[^\s:/@]+:[^\s@/]+@",
"password_assignment": r"(?i)\b(?:password|passwd|pwd|secret|api_?key|token|passphrase)\s*[:=]\s*[\"']?[^\s\"']{6,}",
}
PLACEHOLDERS = ("xxx", "...", "<", "your", "your_", "example_value")
def mask_secrets(text: str) -> tuple[str, bool]:
"""Replaces secrets with [type masked]; returns the text and True if anything was masked."""
found = False
for name, pattern in PATTERNS.items():
def repl(m):
nonlocal found
if any(p in m.group(0).lower() for p in PLACEHOLDERS):
return m.group(0)
found = True
return f"[{name} masked]"
text = re.sub(pattern, repl, text)
return text, found- Identifier
- contextetech--mask-secrets-api-keys
- File
- mask-secrets-api-keys.guardrail.json
- Format
- JSON (application/json)
- Size
- 2,474 bytes (2.4 KB)
- Encoding
- UTF-8
- Estimated tokens
- ≈ 685
- Commercial use
- Allowed
- Personal data
- None
- Version
- 1.0
- Published on
- October 10, 2026 at 4:24 AM
- Updated on
- October 16, 2026 at 11:20 PM
- Storage
- in database
Statistics
Likes : 0 · Comments : 0
Click a counter to show or hide it, hover the chart for details. One view per visitor per day, bots excluded; counting started on 30 September 2026.
Estimated cost
- Input tokens per call
- ≈ 1,031
| Model | Per call | Per month |
|---|---|---|
| GPT-6 Luna · OpenAIbest price | €0.00009 | €0.09 |
| DeepSeek Flash · DeepSeek | €0.00027 | €0.27 |
| Mistral Large · Mistral AI | €0.00045 | €0.45 |
| DeepSeek V4 Pro · DeepSeek | €0.00120 | €1.20 |
| GPT-6 Sol · OpenAI | €0.00182 | €1.82 |
| Claude Sonnet 5.5 · Anthropic | €0.00182 | €1.82 |
| Claude Opus 5.5 · Anthropic | €0.00363 | €3.63 |
| Self-hosted open-source model (Ollama, vLLM) | €0 in API fees (server cost only) | |
Input cost only, excluding the model's answer. Providers' public standard prices (Mistral AI, DeepSeek, OpenAI, Anthropic) converted from USD to euros at the ECB rate of October 13, 2026. Estimate: 1 token ≈ 3.6 characters.
Common to all 3 regions
Created by the author, no outside source.
Commercial use allowed · credit the author · changes allowed.
Text or data only: no access to files, network or commands.
Hosted in France (Scaleway, Paris). No transfer outside the EU.
European Union · 5/5
No personal data.
No training data: no summary required.
United States · 5/5
No personal data.
No training data: no documentation to publish.
China · 5/5
No personal data.
No training data; AI-generated content published in China must be labelled (2025).
Indicative summary as of 30/09/2026, not a legal certification. Method and sources →
Content quality
The detailed criteria are for members.
Every criterion, the points removed and a tip to fix each one.
Content analysis
The detailed criteria are for members.
Every criterion, the points removed and a tip to fix each one.
Computed from the content of the resource, by rules, without AI. The score is in the Quality tab.
MCP contexte
https://contextetech.com/mcp
load 1x2ywyz
Add the URL as a connector in your AI apps' settings, then send the request above.
API
import json, urllib.request url = "https://contextetech.com/api/resources/contextetech--mask-secrets-api-keys" fiche = json.load(urllib.request.urlopen(url)) print(fiche["title"] or fiche["name"])
mask-secrets-api-keys.guardrail.json
{
"format": "contextetech/guardrail/v1",
"id": "contextetech/mask-secrets-api-keys",
"description": "Guardrail that masks API keys, tokens, passwords, private keys and connection strings before a text, file or export is sent to an AI. Rules, 14 test cases, code.",
"license": "MIT",
"language": "en",
"type": "other",
"action": "mask",
"rules": "Mask before sending to the AI any secret that gives access to an account or a service:\n- API keys (OpenAI, Anthropic, Mistral, Stripe, AWS…) and access tokens (GitHub, JWT);\n- passwords written in clear text (password=…, pwd: …);\n- private keys (-----BEGIN … PRIVATE KEY-----);\n- credentials inside a URL (postgres://user:password@host).\nLet through: placeholder values (YOUR_API_KEY, xxx, <key>), URLs without credentials, order or commit IDs, text about passwords that contains none.\nApply it before pasting a .env file, a database export or logs into an assistant.",
"cases": [
{
"input": "AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE",
"expected": "block"
},
{
"input": "token: ghp_000000000000000000000000000000000000",
"expected": "block"
},
{
"input": "ANTHROPIC_API_KEY=sk-ant-00000000000000000000000000000000",
"expected": "block"
},
{
"input": "STRIPE_SECRET=sk_live_0000000000000000000000",
"expected": "block"
},
{
"input": "DATABASE_URL=postgres://admin:S3cretPass@db.internal:5432/app",
"expected": "block"
},
{
"input": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0000000000000000\n-----END RSA PRIVATE KEY-----",
"expected": "block"
},
{
"input": "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwMDAwMDAifQ.0000000000000000000000",
"expected": "block"
},
{
"input": "password = Sunshine2026!",
"expected": "block"
},
{
"input": "Passwords must be at least 12 characters long.",
"expected": "allow"
},
{
"input": "Replace YOUR_API_KEY with the key from your account.",
"expected": "allow"
},
{
"input": "The docs are at https://docs.example.com/api.",
"expected": "allow"
},
{
"input": "Commit a1b2c3d fixes the login bug.",
"expected": "allow"
},
{
"input": "Order #2026-10-458712 has shipped.",
"expected": "allow"
},
{
"input": "Sign in with your usual account.",
"expected": "allow"
}
]
}Check a text (Python)
import re
PATTERNS = {
"private_key": r"-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----",
"aws_key": r"\bAKIA[0-9A-Z]{16}\b",
"github_token": r"\bgh[pousr]_[A-Za-z0-9]{36,}\b",
"llm_api_key": r"\bsk-(?:ant-|proj-)?[A-Za-z0-9_-]{20,}\b",
"stripe_key": r"\b[sr]k_(?:live|test)_[A-Za-z0-9]{16,}\b",
"jwt": r"\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b",
"url_credentials": r"\b[a-z][a-z0-9+.-]*://[^\s:/@]+:[^\s@/]+@",
"password_assignment": r"(?i)\b(?:password|passwd|pwd|secret|api_?key|token|passphrase)\s*[:=]\s*[\"']?[^\s\"']{6,}",
}
PLACEHOLDERS = ("xxx", "...", "<", "your", "your_", "example_value")
def mask_secrets(text: str) -> tuple[str, bool]:
"""Replaces secrets with [type masked]; returns the text and True if anything was masked."""
found = False
for name, pattern in PATTERNS.items():
def repl(m):
nonlocal found
if any(p in m.group(0).lower() for p in PLACEHOLDERS):
return m.group(0)
found = True
return f"[{name} masked]"
text = re.sub(pattern, repl, text)
return text, found
Community
No comments yet. Share your feedback, it will help the next person.