diff --git a/core/application/application.go b/core/application/application.go index 0f69dde88..5e1d3915f 100644 --- a/core/application/application.go +++ b/core/application/application.go @@ -623,6 +623,7 @@ func (a *Application) waitForHealthyWorker() { } } // Add 0-1s jitter to prevent thundering-herd on the node registry + // #nosec G404 -- jitter only spreads retries, it is not security sensitive. jitter := time.Duration(rand.Int64N(int64(time.Second))) select { case <-a.applicationConfig.Context.Done(): diff --git a/core/application/startup.go b/core/application/startup.go index 7f214fa38..32f527724 100644 --- a/core/application/startup.go +++ b/core/application/startup.go @@ -730,7 +730,7 @@ func initializeWatchdog(application *Application, options *config.ApplicationCon // loadOrGenerateHMACSecret loads an HMAC secret from the given file path, // or generates a random 32-byte secret and persists it if the file doesn't exist. func loadOrGenerateHMACSecret(path string) (string, error) { - data, err := os.ReadFile(path) + data, err := os.ReadFile(filepath.Clean(path)) if err == nil { secret := string(data) if len(secret) >= 32 { diff --git a/core/cli/run.go b/core/cli/run.go index 6be697b72..dee32091f 100644 --- a/core/cli/run.go +++ b/core/cli/run.go @@ -379,7 +379,7 @@ func (r *RunCMD) Run(ctx *cliContext.Context) (result error) { config.WithMLXTunnelCallback(func(tunnels []string) { hostfile := filepath.Join(os.TempDir(), "localai_mlx_hostfile.json") data, _ := json.Marshal(tunnels) - os.WriteFile(hostfile, data, 0644) + os.WriteFile(hostfile, data, 0600) os.Setenv("MLX_DISTRIBUTED_HOSTFILE", hostfile) xlog.Debug("setting MLX_DISTRIBUTED_HOSTFILE", "value", hostfile, "tunnels", tunnels) }), diff --git a/core/http/endpoints/localai/finetune.go b/core/http/endpoints/localai/finetune.go index 9d04452a9..8405ad0c4 100644 --- a/core/http/endpoints/localai/finetune.go +++ b/core/http/endpoints/localai/finetune.go @@ -253,7 +253,7 @@ func DownloadExportedModelEndpoint(ftService *finetune.FineTuneService) echo.Han return nil } - f, err := os.Open(path) + f, err := os.Open(filepath.Clean(path)) if err != nil { return err } diff --git a/core/http/routes/localai.go b/core/http/routes/localai.go index 6327bd512..3eff66b5a 100644 --- a/core/http/routes/localai.go +++ b/core/http/routes/localai.go @@ -409,6 +409,7 @@ func RegisterLocalAIRoutes(router *echo.Echo, "load_status": "/api/models/:id/load-status", "load_cancel": "/api/models/:id/load-cancel", }, + // #nosec G101 -- route paths, not credentials. "ai_functions": map[string]string{ "tts": "/tts", "tts_voices": "/v1/audio/voices", @@ -429,6 +430,7 @@ func RegisterLocalAIRoutes(router *echo.Echo, "prompts": "/v1/mcp/prompts/:model", "resources": "/v1/mcp/resources/:model", }, + // #nosec G101 -- route paths, not credentials. "p2p": map[string]string{ "nodes": "/api/p2p", "token": "/api/p2p/token", diff --git a/core/services/addons/pins/store_file.go b/core/services/addons/pins/store_file.go index 4528f91fa..d0b314e74 100644 --- a/core/services/addons/pins/store_file.go +++ b/core/services/addons/pins/store_file.go @@ -31,7 +31,7 @@ func NewFileStore(path string) (*FileStore, error) { if path == "" { return s, nil } - b, err := os.ReadFile(path) + b, err := os.ReadFile(filepath.Clean(path)) if errors.Is(err, os.ErrNotExist) { return s, nil } diff --git a/core/services/finetune/service.go b/core/services/finetune/service.go index 31383e621..3ee9d75c4 100644 --- a/core/services/finetune/service.go +++ b/core/services/finetune/service.go @@ -115,7 +115,7 @@ func (s *FineTuneService) saveJobState(job *schema.FineTuneJob) { } statePath := filepath.Join(dir, "state.json") - if err := os.WriteFile(statePath, data, 0640); err != nil { + if err := os.WriteFile(statePath, data, 0600); err != nil { xlog.Error("Failed to write job state", "job_id", job.ID, "error", err) } } @@ -137,7 +137,7 @@ func (s *FineTuneService) loadJobsFromDisk(_ context.Context) ([]*schema.FineTun continue } statePath := filepath.Join(baseDir, entry.Name(), "state.json") - data, err := os.ReadFile(statePath) + data, err := os.ReadFile(filepath.Clean(statePath)) if err != nil { continue } @@ -660,7 +660,7 @@ func (s *FineTuneService) ExportModel(ctx context.Context, userID, jobID string, s.setExportFailed(job, fmt.Sprintf("failed to marshal config: %v", err)) return } - if err := os.WriteFile(configPath, yamlData, 0644); err != nil { + if err := os.WriteFile(configPath, yamlData, 0600); err != nil { s.setExportFailed(job, fmt.Sprintf("failed to write config file: %v", err)) return } @@ -765,7 +765,7 @@ func (s *FineTuneService) UploadDataset(filename string, data []byte) (string, e return "", fmt.Errorf("invalid filename") } filePath := filepath.Join(uploadDir, uuid.New().String()[:8]+"-"+base) - if err := os.WriteFile(filePath, data, 0640); err != nil { + if err := os.WriteFile(filePath, data, 0600); err != nil { return "", fmt.Errorf("failed to write dataset: %w", err) } diff --git a/core/services/quantization/service.go b/core/services/quantization/service.go index e4d6d78c4..e482b1216 100644 --- a/core/services/quantization/service.go +++ b/core/services/quantization/service.go @@ -138,7 +138,7 @@ func (s *QuantizationService) saveJobState(job *schema.QuantizationJob) { } statePath := filepath.Join(dir, "state.json") - if err := os.WriteFile(statePath, data, 0640); err != nil { + if err := os.WriteFile(statePath, data, 0600); err != nil { xlog.Error("Failed to write quantization job state", "job_id", job.ID, "error", err) } } @@ -160,7 +160,7 @@ func (s *QuantizationService) loadJobsFromDisk(_ context.Context) ([]*schema.Qua continue } statePath := filepath.Join(baseDir, entry.Name(), "state.json") - data, err := os.ReadFile(statePath) + data, err := os.ReadFile(filepath.Clean(statePath)) if err != nil { continue } @@ -714,12 +714,12 @@ func (s *QuantizationService) ImportModel(ctx context.Context, userID, jobID str srcFile := job.OutputFile dstFile := filepath.Join(outputPath, filepath.Base(srcFile)) - srcData, err := os.ReadFile(srcFile) + srcData, err := os.ReadFile(filepath.Clean(srcFile)) if err != nil { s.setImportFailed(job, fmt.Sprintf("failed to read output file: %v", err)) return } - if err := os.WriteFile(dstFile, srcData, 0644); err != nil { + if err := os.WriteFile(dstFile, srcData, 0600); err != nil { s.setImportFailed(job, fmt.Sprintf("failed to write model file: %v", err)) return } @@ -752,7 +752,7 @@ func (s *QuantizationService) ImportModel(ctx context.Context, userID, jobID str s.setImportFailed(job, fmt.Sprintf("failed to marshal config: %v", err)) return } - if err := os.WriteFile(configPath, yamlData, 0644); err != nil { + if err := os.WriteFile(configPath, yamlData, 0600); err != nil { s.setImportFailed(job, fmt.Sprintf("failed to write config file: %v", err)) return } diff --git a/pkg/addons/contract/hostops.go b/pkg/addons/contract/hostops.go index 5ccf4a22f..93a42d3b1 100644 --- a/pkg/addons/contract/hostops.go +++ b/pkg/addons/contract/hostops.go @@ -48,6 +48,7 @@ const ( ) // HostCredentialHeader carries the host credential of the incarnation. +// #nosec G101 -- this is a header name, not a credential value. const HostCredentialHeader = "X-Addon-Host-Credential" // hostOpName is the brief's shape ^[a-z]+\.[a-z.]+$ without an empty word: