feat: hardening de tools — deny-by-default, SSRF, path traversal, allowlists

Cambios de seguridad en las 4 herramientas de agentes:

- tools/file: deny-by-default (AllowedPaths vacío = todo denegado),
  resolución de symlinks con EvalSymlinks, protección contra path
  traversal (../) y confusión de prefijos (/opt vs /opt1234)
- tools/ssh: nuevo AllowedCommands allowlist (complementa ForbiddenCommands),
  validación de sintaxis shell (bloquea pipes, subshells, redirects, chains)
- tools/http: protección SSRF bloqueando IPs privadas, loopback, link-local,
  metadata (169.254.169.254). Validación de dominio case-insensitive.
- tools/matrix: nuevo parámetro AllowedRooms para restringir rooms destino
- internal/config/schema: AllowedCommands en SSHToolCfg, MatrixToolCfg nueva
- agents/runtime: pasa MatrixToolCfg al constructor de matrix_send

Parte de issue 0019 (prompt injection hardening). Feature flag OFF.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 19:17:00 +00:00
parent 71a009f890
commit 4e7aa95adb
6 changed files with 195 additions and 21 deletions
+21 -1
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/enmanuel/agents/internal/config"
"github.com/enmanuel/agents/tools"
)
@@ -15,7 +16,8 @@ type MatrixSender interface {
}
// NewMatrixSend creates a matrix_send tool that sends a message to a Matrix room.
func NewMatrixSend(sender MatrixSender) tools.Tool {
// If AllowedRooms is configured, only those room IDs can be targeted.
func NewMatrixSend(sender MatrixSender, cfg config.MatrixToolCfg) tools.Tool {
return tools.Tool{
Def: tools.Def{
Name: "matrix_send",
@@ -32,6 +34,10 @@ func NewMatrixSend(sender MatrixSender) tools.Tool {
return tools.Result{Err: fmt.Errorf("matrix_send: room_id and message are required")}
}
if err := validateRoom(roomID, cfg.AllowedRooms); err != nil {
return tools.Result{Err: err}
}
if err := sender.SendMarkdown(ctx, roomID, message); err != nil {
return tools.Result{Err: fmt.Errorf("matrix_send: %w", err)}
}
@@ -40,3 +46,17 @@ func NewMatrixSend(sender MatrixSender) tools.Tool {
},
}
}
// validateRoom checks that roomID is in the allowed list.
// If allowedRooms is empty, all rooms are allowed.
func validateRoom(roomID string, allowedRooms []string) error {
if len(allowedRooms) == 0 {
return nil
}
for _, r := range allowedRooms {
if roomID == r {
return nil
}
}
return fmt.Errorf("matrix_send: room %q not in allowed rooms list", roomID)
}