tzurot-security

📁 lbds137/tzurot 📅 9 days ago
1
总安装量
1
周安装量
#44511
全站排名
安装命令
npx skills add https://github.com/lbds137/tzurot --skill tzurot-security

Agent 安装分布

opencode 1

Skill 文档

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:

  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);

6. Shell Command Safety (Command Injection Prevention)

This vulnerability has occurred multiple times. When executing shell commands with user-controlled or external data, NEVER use string interpolation.

import { execFileSync, execSync } from 'node:child_process';

// ❌ WRONG - Command injection via string interpolation
execSync(`railway variables --set "${key}=${value}"`);
// If value = 'foo"; rm -rf /; echo "' → disaster!

// ❌ WRONG - Even with "trusted" variables, avoid habits
execSync(`git commit -m "${message}"`);

// ✅ CORRECT - Use execFileSync with array arguments
execFileSync('railway', ['variables', '--set', `${key}=${value}`]);

// ✅ CORRECT - Shell never interprets the arguments
execFileSync('git', ['commit', '-m', message]);

// ✅ OK - Static commands without interpolation
execSync('railway status');
execSync('git log --oneline -5');

Why execFileSync is safe:

  • Arguments are passed directly to the process, not through a shell
  • Shell metacharacters ("; $ \ ()`) are treated as literal strings
  • No possibility of command injection regardless of input

When to use which:

Function Use When Safety
execFileSync(cmd, args) Any external data in args ✅ Safe
execSync(staticCmd) Fully static command string ✅ Safe
execSync(\…${var}`)` ❌ NEVER ⚠️ Vulnerable

Test for this: Add tests with shell metacharacters in values:

const maliciousValue = 'test"; rm -rf /; echo "pwned';
// Verify your code passes this literally, not interprets it

🛡️ Tier 2: Important Security (SHOULD IMPLEMENT)

7. 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>');

8. 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 };
}

9. 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

10. 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 */
}

11. 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.

12. 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
  • Writing shell commands with execSync/spawn (use execFileSync with arrays!)

Related Skills

  • tzurot-observability – Security logging without PII
  • tzurot-types – Input validation with Zod schemas
  • tzurot-git-workflow – Pre-commit verification checks

References