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.

$ Instalar

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 passwordsEnvironment variables
API keys/tokens.env.example with placeholders
Private keysRailway secrets management
Real user data in testsGeneric test data

If you commit a secret:

  1. Rotate immediately (regenerate in provider dashboard)
  2. Update Railway: railway variables set KEY=new-value
  3. 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:

  1. 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';
    
  2. Exclude test patterns in .gitguardian.yaml (if format is strict):

    # .gitguardian.yaml
    secret:
      ignored-paths:
        - '**/*.test.ts'
        - '**/*.spec.ts'
        - '**/test/**'
        - '**/__mocks__/**'
    
  3. Inline ggignore comment (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