feat: Add file manager package — VueFinder-compatible NATS request-reply handler
All checks were successful
Test Asgard Runner / test (push) Successful in 2s

Implements companion-agent/internal/filemanager with full installDir jail
enforcement (Clean + EvalSymlinks + HasPrefix on every path). Handles all
VueFinder operations: list, delete, rename, copy, move, mkdir, mkfile,
search, preview, save, upload. Wires into daemon.go as a 6th NATS
subscription on corrosion.{license_id}.files.cmd.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-21 16:11:59 -05:00
parent fee0ae2420
commit e9f9b449b1
4 changed files with 1035 additions and 0 deletions

View File

@@ -10,6 +10,7 @@ import (
"github.com/nats-io/nats.go"
"github.com/vigilcyber/corrosion-companion/internal/deploy"
"github.com/vigilcyber/corrosion-companion/internal/filemanager"
"github.com/vigilcyber/corrosion-companion/internal/files"
"github.com/vigilcyber/corrosion-companion/internal/process"
"github.com/vigilcyber/corrosion-companion/internal/update"
@@ -32,6 +33,7 @@ type Daemon struct {
cfg *DaemonConfig
gameServer *process.GameServer
fileOps *files.Operations
fm *filemanager.FileManager
updater *update.Updater
deployer *deploy.Deployer
subscriptions []*nats.Subscription
@@ -73,6 +75,7 @@ func (a *gameServerAdapter) UpdatePath(path string) {
func NewDaemon(nc *nats.Conn, cfg *DaemonConfig) (*Daemon, error) {
gameServer := process.NewGameServer(cfg.GameServerPath, cfg.GameServerArgs)
fileOps := files.NewOperations()
fm := filemanager.New(cfg.InstallDir)
updater := update.NewUpdater(cfg.Version)
adapter := &gameServerAdapter{gs: gameServer, cfg: cfg}
deployer := deploy.NewDeployer(nc, cfg.LicenseID, cfg.InstallDir, adapter)
@@ -82,6 +85,7 @@ func NewDaemon(nc *nats.Conn, cfg *DaemonConfig) (*Daemon, error) {
cfg: cfg,
gameServer: gameServer,
fileOps: fileOps,
fm: fm,
updater: updater,
deployer: deployer,
}
@@ -118,6 +122,11 @@ func (d *Daemon) Run(ctx context.Context) error {
return fmt.Errorf("failed to subscribe to deploy commands: %w", err)
}
// Subscribe to file manager commands (VueFinder-compatible request-reply)
if err := d.subscribeFileManager(); err != nil {
return fmt.Errorf("failed to subscribe to file manager commands: %w", err)
}
log.Println("All subscriptions active")
// Start heartbeat ticker
@@ -338,6 +347,26 @@ func (d *Daemon) subscribeDeployCommand() error {
return nil
}
// subscribeFileManager subscribes to the VueFinder-compatible file manager
// command subject. All operations (list, delete, rename, copy, move, mkdir,
// mkfile, search, preview, save, upload) are handled by the filemanager package
// which enforces the installDir jail on every path.
func (d *Daemon) subscribeFileManager() error {
subject := fmt.Sprintf("corrosion.%s.files.cmd", d.cfg.LicenseID)
sub, err := d.nc.Subscribe(subject, func(msg *nats.Msg) {
d.fm.HandleNatsRequest(msg)
})
if err != nil {
return err
}
d.subscriptions = append(d.subscriptions, sub)
log.Printf("Subscribed to: %s", subject)
return nil
}
// handleFileOperation processes file operation requests
func (d *Daemon) handleFileOperation(msg *nats.Msg) {
// Parse common fields