Files
amayo/README/SECURITY_SYSTEM.md

7.9 KiB

🔒 Sistema de Seguridad para Comandos Administrativos

Descripción

Sistema de permisos para restringir comandos sensibles a:

  • Guild de Testing (variable guildTest en .env)
  • Usuarios Autorizados (whitelist por ID)
  • Administradores del Servidor
  • Dueño del Bot

📦 Instalación

El módulo está en: src/core/lib/security.ts

import { 
  requireTestGuild,
  requireTestGuildAndAdmin,
  requireAuthorizedUser,
  withTestGuild,
  withTestGuildAndAdmin 
} from "@/core/lib/security";

⚙️ Configuración en .env

# Guild de testing (requerido)
guildTest=123456789012345678

# Dueño del bot (opcional)
OWNER_ID=987654321098765432

# Whitelist de usuarios autorizados (opcional, separados por comas)
AUTHORIZED_USER_IDS=111111111111111111,222222222222222222

🛡️ Funciones Disponibles

1. requireTestGuild(source)

Verifica que el comando se ejecute solo en el guild de testing.

import { requireTestGuild } from "@/core/lib/security";

export const command: CommandSlash = {
  name: "debug",
  description: "Comandos de debug",
  type: "slash",
  run: async (interaction, client) => {
    // Bloquea si no es guild de testing
    if (!await requireTestGuild(interaction)) {
      return; // Ya respondió al usuario automáticamente
    }

    // Tu código aquí (solo se ejecuta en guild de testing)
    await interaction.reply("🐛 Debug activado");
  }
};

Respuesta automática si falla:

🔒 Este comando solo está disponible en el servidor de testing.

2. requireTestGuildAndAdmin(source)

Requiere guild de testing Y permisos de administrador.

import { requireTestGuildAndAdmin } from "@/core/lib/security";

export const command: CommandSlash = {
  name: "featureflags",
  description: "Gestión de feature flags",
  type: "slash",
  run: async (interaction, client) => {
    // Bloquea si no es guild de testing O no es admin
    if (!await requireTestGuildAndAdmin(interaction)) {
      return;
    }

    // Tu código aquí (solo admins en guild de testing)
    await interaction.reply("⚙️ Configuración de flags");
  }
};

Respuestas automáticas:

  • Si no es guild de testing: 🔒 Este comando solo está disponible en el servidor de testing.
  • Si no es admin: 🔒 Este comando requiere permisos de administrador.

3. requireAuthorizedUser(source)

Requiere que el usuario esté en la whitelist de AUTHORIZED_USER_IDS.

import { requireAuthorizedUser } from "@/core/lib/security";

export const command: CommandSlash = {
  name: "shutdown",
  description: "Apagar el bot",
  type: "slash",
  run: async (interaction, client) => {
    // Solo usuarios autorizados
    if (!await requireAuthorizedUser(interaction)) {
      return;
    }

    await interaction.reply("🛑 Apagando bot...");
    process.exit(0);
  }
};

4. withTestGuild(command) - Wrapper

Envuelve todo el comando para restringirlo al guild de testing.

import { withTestGuild } from "@/core/lib/security";
import { CommandSlash } from "@/core/types/commands";

const debugCommand: CommandSlash = {
  name: "debug",
  description: "Debug tools",
  type: "slash",
  run: async (interaction, client) => {
    await interaction.reply("🐛 Debug mode");
  }
};

// Exportar con wrapper de seguridad
export const command = withTestGuild(debugCommand);

5. withTestGuildAndAdmin(command) - Wrapper

Envuelve el comando para requerir guild de testing + admin.

import { withTestGuildAndAdmin } from "@/core/lib/security";

const adminCommand: CommandSlash = {
  name: "config",
  description: "Configuración",
  type: "slash",
  run: async (interaction, client) => {
    await interaction.reply("⚙️ Configuración");
  }
};

export const command = withTestGuildAndAdmin(adminCommand);

🎯 Funciones Auxiliares

isTestGuild(source)

Retorna true si es el guild de testing (no responde automáticamente).

if (isTestGuild(interaction)) {
  console.log("Estamos en guild de testing");
}

isGuildAdmin(member)

Retorna true si el miembro tiene permisos de administrador.

const member = interaction.member as GuildMember;
if (isGuildAdmin(member)) {
  console.log("Usuario es admin");
}

isBotOwner(userId)

Retorna true si el userId coincide con OWNER_ID en .env.

if (isBotOwner(interaction.user.id)) {
  console.log("Es el dueño del bot");
}

isAuthorizedUser(userId)

Retorna true si está en la whitelist o es el dueño.

if (isAuthorizedUser(interaction.user.id)) {
  console.log("Usuario autorizado");
}

📊 Ejemplo Real: Comando Feature Flags

import { requireTestGuildAndAdmin } from "@/core/lib/security";
import { CommandSlash } from "@/core/types/commands";

export const command: CommandSlash = {
  name: "featureflags",
  description: "Administra feature flags del bot",
  type: "slash",
  cooldown: 5,
  options: [
    {
      name: "list",
      description: "Lista todos los flags",
      type: 1,
    },
    {
      name: "create",
      description: "Crea un nuevo flag",
      type: 1,
      options: [
        { name: "name", description: "Nombre del flag", type: 3, required: true },
        { name: "status", description: "Estado", type: 3, required: true },
      ],
    },
  ],
  run: async (interaction) => {
    // 🔒 SECURITY: Solo guild de testing + admin
    if (!await requireTestGuildAndAdmin(interaction)) {
      return;
    }

    const subcommand = interaction.options.getSubcommand();

    switch (subcommand) {
      case "list":
        await interaction.reply("📋 Listando flags...");
        break;
      case "create":
        const name = interaction.options.getString("name", true);
        await interaction.reply(`✅ Flag "${name}" creado`);
        break;
    }
  },
};

🔐 Niveles de Seguridad

Función Guild Test Admin Whitelist Owner
requireTestGuild
requireTestGuildAndAdmin Auto-admin
requireAuthorizedUser

🚨 Logs de Seguridad

Cuando un comando es bloqueado, se registra en los logs:

{
  "level": "warn",
  "msg": "[Security] Comando bloqueado - no es guild de testing",
  "guildId": "123456789",
  "userId": "987654321"
}
{
  "level": "warn",
  "msg": "[Security] Comando bloqueado - sin permisos de admin",
  "guildId": "123456789",
  "userId": "987654321"
}

Checklist de Implementación

  1. Configurar .env:

    guildTest=TU_GUILD_ID_AQUI
    OWNER_ID=TU_USER_ID_AQUI
    
  2. Importar el guard:

    import { requireTestGuildAndAdmin } from "@/core/lib/security";
    
  3. Aplicar al comando:

    run: async (interaction) => {
      if (!await requireTestGuildAndAdmin(interaction)) {
        return;
      }
      // Tu código...
    }
    
  4. Reiniciar el bot:

    pm2 restart amayo
    
  5. Probar en Discord:

    • En guild de testing con admin → Funciona
    • En otro guild → Bloqueado
    • En guild de testing sin admin → Bloqueado

🎮 Casos de Uso

Comando de Testing

export const command = withTestGuild({
  name: "test",
  run: async (interaction) => {
    await interaction.reply("🧪 Test mode");
  }
});

Comando Admin

export const command = withTestGuildAndAdmin({
  name: "config",
  run: async (interaction) => {
    await interaction.reply("⚙️ Configuración");
  }
});

Comando Ultra-Sensible

run: async (interaction) => {
  if (!await requireAuthorizedUser(interaction)) {
    return;
  }
  // Solo usuarios whitelisteados
}

Fecha: 2025-10-31
Archivo: src/core/lib/security.ts
Estado: Implementado y probado