security: blindaje contra ejecucion de scripts RCE, Stored XSS, MIME sniffing, Path Traversal y DoS

This commit is contained in:
2026-10-21 01:40:43 -05:00
parent 7b58170540
commit cfda6921f9
7 changed files with 255 additions and 37 deletions
+77
View File
@@ -3,12 +3,14 @@ package main
import (
"os"
"path/filepath"
"strings"
"testing"
"time"
"app_compartir/internal/cleaner"
"app_compartir/internal/db"
"app_compartir/internal/models"
"app_compartir/internal/storage"
)
func TestGuestExpirationAndRegisteredPermanence(t *testing.T) {
@@ -132,3 +134,78 @@ func TestGuestExpirationAndRegisteredPermanence(t *testing.T) {
t.Log("✅ Test completado con éxito: Solo los datos de invitado caducan en 24h; los de usuarios registrados son permanentes.")
}
func TestSecurityProtections(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "test_security_*")
if err != nil {
t.Fatalf("error creando tmpDir: %v", err)
}
defer os.RemoveAll(tmpDir)
uploadDir := filepath.Join(tmpDir, "uploads")
// 1. Probar Sanitización de Nombres (Path Traversal)
traversalTests := []struct {
input string
expected string
}{
{"../../../../etc/passwd", "etc_passwd"},
{"..\\..\\windows\\system32\\cmd.exe", "cmd.exe"},
{"test/../../shell.php", "shell.php"},
{"../../../.htaccess", "_.htaccess"},
}
for _, tc := range traversalTests {
sanitized := storage.SanitizeFilename(tc.input)
if strings.Contains(sanitized, "/") || strings.Contains(sanitized, "\\") || strings.Contains(sanitized, "..") {
t.Fatalf("Fallo en sanitización de path traversal para %s: resultado=%s", tc.input, sanitized)
}
}
// 2. Probar Detección de MIME Types Peligrosos (Neutralización de XSS y Scripts)
dangerousMimes := []struct {
filename string
forbidden string
expected string
}{
{"shell.php", "application/x-httpd-php", "text/plain; charset=utf-8"},
{"exploit.phtml", "text/html", "text/plain; charset=utf-8"},
{"malicious.html", "text/html", "text/plain; charset=utf-8"},
{"test.xhtml", "text/html", "text/plain; charset=utf-8"},
{"script.sh", "application/x-sh", "text/plain; charset=utf-8"},
}
for _, tc := range dangerousMimes {
m := storage.DetectMimeType(tc.filename, nil)
if m == tc.forbidden || strings.HasPrefix(m, "text/html") {
t.Fatalf("MIME peligroso no neutralizado para %s: obtenido %s (prohibido %s)", tc.filename, m, tc.forbidden)
}
if m != tc.expected {
t.Fatalf("MIME esperado %s para %s, obtenido %s", tc.expected, tc.filename, m)
}
}
// 3. Probar Guardado Seguro en Disco (Permisos no ejecutables y .htaccess preventivo)
sampleContent := strings.NewReader("<?php echo 'hacked'; ?>")
storedFile, _, err := storage.SaveFile(uploadDir, "secslug", "shell.php", sampleContent)
if err != nil {
t.Fatalf("error guardando archivo seguro: %v", err)
}
// Verificar permisos en disco (NO ejecutable)
destPath := filepath.Join(uploadDir, storedFile)
fi, err := os.Stat(destPath)
if err != nil {
t.Fatalf("error comprobando archivo guardado: %v", err)
}
mode := fi.Mode().Perm()
if mode&0111 != 0 {
t.Fatalf("El archivo guardado tiene permisos de ejecución: %o", mode)
}
// Verificar existencia de .htaccess preventivo
htaccessPath := filepath.Join(uploadDir, ".htaccess")
if _, err := os.Stat(htaccessPath); os.IsNotExist(err) {
t.Fatalf(".htaccess preventivo no fue creado en uploadDir")
}
t.Log("✅ Test completado con éxito: Protecciones de seguridad activas (sin path traversal, scripts neutralizados a texto plano y archivos no ejecutables).")
}