tzurot-security
Security best practices for Tzurot v3 - Secret management, AI-specific security (prompt injection, PII scrubbing), Economic DoS prevention, Discord permission verification, microservices security, and supply chain integrity. Use when handling secrets, user input, or security-critical code.
$ 설치
git clone https://github.com/lbds137/tzurot /tmp/tzurot && cp -r /tmp/tzurot/.claude/skills/tzurot-security ~/.claude/skills/tzurot// tip: Run this command in your terminal to install the skill
name: tzurot-security description: Security best practices for Tzurot v3 - Secret management, AI-specific security (prompt injection, PII scrubbing), Economic DoS prevention, Discord permission verification, microservices security, and supply chain integrity. Use when handling secrets, user input, or security-critical code. lastUpdated: '2026-01-05'
Security Skill - Tzurot v3
Use this skill when: Handling secrets, user input, file uploads, AI providers, admin commands, or when security concerns arise.
Quick Reference
# Pre-commit secret check
git diff --cached | grep -iE '(password|secret|token|api.?key|postgresql://|redis://)'
# Audit dependencies
npm audit --audit-level=moderate
# View Railway secrets (values hidden)
railway variables --service <name>
🚨 Tier 1: Core Security (MUST FOLLOW)
1. Never Commit Secrets
Happened TWICE in this project. Always verify!
| ❌ NEVER Commit | ✅ Use Instead |
|---|---|
| Database URLs with passwords | Environment variables |
| API keys/tokens | .env.example with placeholders |
| Private keys | Railway secrets management |
| Real user data in tests | Generic test data |
If you commit a secret:
- Rotate immediately (regenerate in provider dashboard)
- Update Railway:
railway variables set KEY=new-value - Consider git history rewrite if not shared
GitGuardian False Positives (Test Secrets)
GitGuardian CI checks scan for secrets. Test files with fake API keys can trigger false positives.
Solution Order of Preference:
-
Use low-entropy fake keys (BEST - scanners naturally ignore these):
// ❌ WRONG - High entropy, triggers GitGuardian const apiKey = 'sk-test-byok-key-abc123xyz789'; const apiKey = 'test-byok-key-not-a-real-secret'; // ✅ CORRECT - Low entropy, obviously fake const apiKey = 'user-test-key-12345'; const apiKey = 'fake-key-00000'; const apiKey = 'PLACEHOLDER_KEY'; -
Exclude test patterns in
.gitguardian.yaml(if format is strict):# .gitguardian.yaml secret: ignored-paths: - '**/*.test.ts' - '**/*.spec.ts' - '**/test/**' - '**/__mocks__/**' -
Inline
ggignorecomment (LAST RESORT - clutters code):const apiKey = 'must-be-this-exact-format-abc123'; // ggignore
Why low-entropy is best: GitGuardian uses Shannon entropy to detect secrets. Simple patterns like 12345 or 00000 have low entropy and don't trigger scans. This keeps code clean without config files or comments.
Reference: ggshield documentation
2. Environment Variable Management
// ✅ CORRECT - Fail fast if missing
const required = ['DISCORD_TOKEN', 'DATABASE_URL', 'REDIS_URL'] as const;
for (const v of required) {
if (!process.env[v]) throw new Error(`Missing: ${v}`);
}
3. Security Logging (No PII)
// ❌ WRONG - Logs PII
logger.info({ user }, 'User authenticated');
logger.debug({ token }, 'Initializing');
// ✅ CORRECT - Log only safe identifiers
logger.info({ userId: user.id }, 'User authenticated');
logger.debug({ tokenPrefix: token.slice(0, 10) }, 'Initializing');
NEVER log: Emails, phones, IPs, usernames, message content, API keys Safe to log: User IDs, guild IDs, channel IDs, timestamps, error codes
4. Token Budgeting (Economic DoS Prevention)
AI APIs cost money. Implement per-user token limits:
// In Redis: token_budget:{userId} → { tokensUsed, windowStart }
if (budget.tokensUsed + estimated > BUDGET_PER_HOUR) {
return res.status(429).json({ error: 'Token budget exceeded' });
}
5. Discord Permission Verification
// ❌ WRONG - Trusts client-side
if (interaction.member.permissions.has('Administrator')) {
}
// ✅ CORRECT - Server-side with cache
const guild = await client.guilds.fetch(guildId);
const member = await guild.members.fetch(userId);
const hasAdmin = member.permissions.has(PermissionFlagsBits.Administrator);
🛡️ Tier 2: Important Security (SHOULD IMPLEMENT)
6. PII Scrubbing Before Embedding
Scrub emails, phones, SSNs before storing in pgvector:
const EMAIL_REGEX = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}/g;
const scrubbedText = text.replace(EMAIL_REGEX, '<EMAIL_REDACTED>');
7. Prompt Injection Detection
const JAILBREAK_PATTERNS = [
/ignore (previous|all) (instructions|rules)/i,
/dan mode/i,
/developer mode/i,
/forget (everything|all)/i,
];
if (JAILBREAK_PATTERNS.some(p => p.test(prompt))) {
return { content: '⚠️ Prompt violates policies.', flagged: true };
}
8. Signed BullMQ Jobs (If Redis Compromised)
Sign jobs with HMAC to prevent injection:
const signature = crypto.createHmac('sha256', SECRET).update(JSON.stringify(payload)).digest('hex');
// Include signature in job, verify in worker
9. Content Validation for Attachments
Validate using magic numbers, not extensions:
import fileType from 'file-type';
const detected = await fileType.fromBuffer(buffer);
if (!ALLOWED_TYPES.includes(detected?.mime)) {
/* reject */
}
10. Dependency Management
Before installing ANY package:
npm view <package> # Exists?
npm audit # Vulnerabilities?
Pin exact versions in package.json (no ^ or ~)
Dependabot handles weekly updates. See .github/dependabot.yml.
11. Discord Markdown Injection Prevention
ALWAYS escape user-provided content before displaying in Discord embeds:
import { escapeMarkdown } from 'discord.js';
// ✅ CORRECT - Escape at display time
const displayName = escapeMarkdown(character.displayName);
embed.setTitle(`Character: ${displayName}`);
// ❌ WRONG - Raw user input in embed
embed.setTitle(`Character: ${character.displayName}`); // Vulnerable!
Fields to always escape:
- Guild names, channel names, usernames
- Character names, display names, persona fields
- Personality names, config names, preset names
- Any user-provided text in embeds
Pattern: Store raw values in data structures, escape at display time only.
Admin Endpoint Security
Current: X-Owner-Id header validation
Future: HMAC signatures with INTERNAL_SERVICE_SECRET
# Rate limit admin endpoints
const adminRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, max: 10
});
Red Flags - Consult This Skill When:
- Committing changes with credentials
- Handling user input to AI
- Processing file uploads
- Installing npm packages suggested by AI
- Implementing admin/destructive commands
- Logging anything with user data
Related Skills
- tzurot-observability - Security logging without PII
- tzurot-types - Input validation with Zod schemas
- tzurot-git-workflow - Pre-commit verification checks
References
- OWASP Top 10
- Discord Bot Security
- Prompt Injection Primer
- Project post-mortems:
docs/postmortems/PROJECT_POSTMORTEMS.md
Repository
