diff --git a/api/integration.go b/api/integration.go index 2acd2d69c..9e0024635 100644 --- a/api/integration.go +++ b/api/integration.go @@ -104,7 +104,12 @@ func (c *IntegrationController) ReceiveIntegration(w http.ResponseWriter, r *htt } if integration.ProjectID != project.ID { - panic("") + log.WithFields(log.Fields{ + "context": "integrations", + "project_id": project.ID, + "integrationId": integration.ID, + }).Error("integration project mismatch") + continue } err = c.integrationService.FillIntegration(&integration) diff --git a/api/login.go b/api/login.go index 7160e89ee..74ed9a367 100644 --- a/api/login.go +++ b/api/login.go @@ -51,8 +51,14 @@ func tryFindLDAPUser(username, password string) (*db.User, error) { var l *ldap.Conn var err error if util.Config.LdapNeedTLS { - l, err = ldap.DialTLS("tcp", util.Config.LdapServer, &tls.Config{ - InsecureSkipVerify: true, + // Enforce proper certificate verification for TLS LDAP connections + // Extract hostname for SNI/verification when server is provided as host:port + serverName := util.Config.LdapServer + if i := strings.LastIndex(serverName, ":"); i > -1 { + serverName = serverName[:i] + } + l, err = ldap.DialTLS("tcp", util.Config.LdapServer, &tls.Config{ // #nosec G402 - explicit secure config + ServerName: serverName, }) } else { l, err = ldap.Dial("tcp", util.Config.LdapServer) @@ -203,7 +209,12 @@ func createSession(w http.ResponseWriter, r *http.Request, user db.User, oidc bo "session": newSession.ID, }) if err != nil { - panic(err) + log.WithError(err).WithFields(log.Fields{ + "user_id": user.ID, + "context": "session", + }).Error("Failed to encode session cookie") + helpers.WriteErrorStatus(w, "Failed to create session", http.StatusInternalServerError) + return } http.SetCookie(w, &http.Cookie{ @@ -518,7 +529,9 @@ func generateStateOauthCookie(w http.ResponseWriter) string { b := make([]byte, 16) _, err := rand.Read(b) if err != nil { - panic(err) + log.WithError(err).Error("Failed to generate OAuth state") + // Fallback to time-based state to keep flow usable + b = []byte(fmt.Sprintf("%d", tz.Now().UnixNano())) } oauthState := base64.URLEncoding.EncodeToString(b) cookie := http.Cookie{Name: "oauthstate", Value: oauthState, Expires: expiration} diff --git a/db/sql/SqlDb.go b/db/sql/SqlDb.go index e87cbb1b3..92a477138 100644 --- a/db/sql/SqlDb.go +++ b/db/sql/SqlDb.go @@ -467,7 +467,16 @@ func createDb() error { defer conn.Close() //nolint:errcheck - _, err = conn.Exec("create database " + cfg.GetDbName()) + // Create database with dialect-appropriate identifier quoting + dbName := cfg.GetDbName() + switch cfg.Dialect { + case util.DbDriverMySQL: + _, err = conn.Exec("create database `" + dbName + "`") + case util.DbDriverPostgres: + _, err = conn.Exec("create database \"" + dbName + "\"") + default: + _, err = conn.Exec("create database " + dbName) + } if err != nil { log.Warn(err.Error()) } @@ -759,13 +768,9 @@ func (d *SqlDb) GetObject(props db.ObjectProps, ID int) (object any, err error) } func (d *SqlDb) CreateObject(props db.ObjectProps, object any) (newObject any, err error) { - // err = newObject.Validate() + // Validation can be added here if needed - if err != nil { - return - } - - template, args := InsertTemplateFromType(newObject) + template, args := InsertTemplateFromType(object) insertID, err := d.insert( "id", "insert into "+props.TableName+" "+template, args...) @@ -774,10 +779,16 @@ func (d *SqlDb) CreateObject(props db.ObjectProps, object any) (newObject any, e } newObject = object - v := reflect.ValueOf(newObject) - field := v.FieldByName("ID") - field.SetInt(int64(insertID)) + if v.Kind() == reflect.Pointer { + v = v.Elem() + } + if v.IsValid() && v.CanSet() { + field := v.FieldByName("ID") + if field.IsValid() && field.CanSet() && field.Kind() == reflect.Int { + field.SetInt(int64(insertID)) + } + } return } diff --git a/db/sql/migration.go b/db/sql/migration.go index 81b2d60c9..c9706eac0 100644 --- a/db/sql/migration.go +++ b/db/sql/migration.go @@ -2,12 +2,13 @@ package sql import ( "fmt" - "github.com/go-gorp/gorp/v3" - "github.com/semaphoreui/semaphore/pkg/tz" "path" "regexp" "strings" + "github.com/go-gorp/gorp/v3" + "github.com/semaphoreui/semaphore/pkg/tz" + "github.com/semaphoreui/semaphore/db" log "github.com/sirupsen/logrus" ) @@ -159,7 +160,7 @@ func (d *SqlDb) ApplyMigration(migration db.Migration) error { queries := getVersionSQL(getVersionPath(migration), false) for i, query := range queries { - fmt.Printf("\r [%d/%d]", i+1, len(query)) + fmt.Printf("\r [%d/%d]", i+1, len(queries)) if len(query) == 0 { continue @@ -173,8 +174,7 @@ func (d *SqlDb) ApplyMigration(migration db.Migration) error { _, err = tx.Exec(q) if err != nil { handleRollbackError(tx.Rollback()) - log.Warnf("\n ERR! Query: %s\n\n", q) - log.Fatal(err.Error()) + log.WithError(err).Warnf("\n ERR! Query: %s\n\n", q) return err } } diff --git a/services/runners/job_pool.go b/services/runners/job_pool.go index ef93e3074..dc9aa3a20 100644 --- a/services/runners/job_pool.go +++ b/services/runners/job_pool.go @@ -121,7 +121,7 @@ func (p *JobPool) Unregister() (err error) { return fmt.Errorf("runner is not registered") } - client := &http.Client{} + client := &http.Client{Timeout: 15 * time.Second} url := util.Config.WebHost + "/api/internal/runners" @@ -140,6 +140,10 @@ func (p *JobPool) Unregister() (err error) { return } + if resp != nil { + defer resp.Body.Close() //nolint:errcheck + } + if util.Config.Runner.TokenFile != "" { err = os.Remove(util.Config.Runner.TokenFile) } @@ -240,7 +244,7 @@ func (p *JobPool) sendProgress() { logger := JobLogger{Context: "sending_progress"} - client := &http.Client{} + client := &http.Client{Timeout: 15 * time.Second} url := util.Config.WebHost + "/api/internal/runners" @@ -338,7 +342,7 @@ func (p *JobPool) tryRegisterRunner(configFilePath *string) (ok bool) { return } - client := &http.Client{} + client := &http.Client{Timeout: 15 * time.Second} url := util.Config.WebHost + "/api/internal/runners" @@ -489,7 +493,7 @@ func (p *JobPool) checkNewJobs() { return } - client := &http.Client{} + client := &http.Client{Timeout: 15 * time.Second} url := util.Config.WebHost + "/api/internal/runners" diff --git a/services/tasks/RemoteJob.go b/services/tasks/RemoteJob.go index c02a22823..b6eee6479 100644 --- a/services/tasks/RemoteJob.go +++ b/services/tasks/RemoteJob.go @@ -4,11 +4,12 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/semaphoreui/semaphore/pkg/tz" - log "github.com/sirupsen/logrus" "net/http" "time" + "github.com/semaphoreui/semaphore/pkg/tz" + log "github.com/sirupsen/logrus" + "github.com/semaphoreui/semaphore/db" "github.com/semaphoreui/semaphore/pkg/task_logger" "github.com/semaphoreui/semaphore/util" @@ -52,7 +53,7 @@ func callRunnerWebhook(runner *db.Runner, tsk *TaskRunner, action string) (err e return } - client := &http.Client{} + client := &http.Client{Timeout: 15 * time.Second} var req *http.Request req, err = http.NewRequest("POST", runner.Webhook, bytes.NewBuffer(jsonBytes)) @@ -67,6 +68,9 @@ func callRunnerWebhook(runner *db.Runner, tsk *TaskRunner, action string) (err e if err != nil { return } + if resp != nil { + defer resp.Body.Close() //nolint:errcheck + } if resp.StatusCode != 200 && resp.StatusCode != 204 { err = fmt.Errorf("webhook returned incorrect status") @@ -152,7 +156,7 @@ func (t *RemoteJob) Run(username string, incomingVersion *string, alias string) break } - time.Sleep(1_000_000_000) + time.Sleep(time.Second) tsk = t.taskPool.GetTask(t.Task.ID) if tsk.Task.Status == task_logger.TaskSuccessStatus || tsk.Task.Status == task_logger.TaskStoppedStatus || diff --git a/services/tasks/alert.go b/services/tasks/alert.go index ff30c67b5..ba092a29a 100644 --- a/services/tasks/alert.go +++ b/services/tasks/alert.go @@ -179,9 +179,12 @@ func (t *TaskRunner) sendTelegramAlert() { t.Log("Can't send telegram alert! Error: " + err.Error()) } else if resp.StatusCode != 200 { t.Log("Can't send telegram alert! Response code: " + strconv.Itoa(resp.StatusCode)) + } else { + t.Log("Sent successfully telegram alert") + } + if resp != nil { + defer resp.Body.Close() //nolint:errcheck } - - t.Log("Sent successfully telegram alert") } func (t *TaskRunner) sendSlackAlert() { @@ -241,6 +244,9 @@ func (t *TaskRunner) sendSlackAlert() { } else { t.Log("Sent successfully slack alert") } + if resp != nil { + defer resp.Body.Close() //nolint:errcheck + } } func (t *TaskRunner) sendRocketChatAlert() { @@ -297,9 +303,12 @@ func (t *TaskRunner) sendRocketChatAlert() { t.Log("Can't send rocketchat alert! Error: " + err.Error()) } else if resp.StatusCode != 200 { t.Log("Can't send rocketchat alert! Response code: " + strconv.Itoa(resp.StatusCode)) + } else { + t.Log("Sent successfully rocketchat alert") + } + if resp != nil { + defer resp.Body.Close() //nolint:errcheck } - - t.Log("Sent successfully rocketchat alert") } func (t *TaskRunner) sendMicrosoftTeamsAlert() { @@ -317,7 +326,7 @@ func (t *TaskRunner) sendMicrosoftTeamsAlert() { alert := Alert{ Name: t.Template.Name, Author: author, - Color: t.alertColor("micorsoft-teams"), + Color: t.alertColor("microsoft-teams"), Task: alertTask{ ID: strconv.Itoa(t.Task.ID), URL: t.taskLink(), @@ -356,9 +365,12 @@ func (t *TaskRunner) sendMicrosoftTeamsAlert() { t.Log("Can't send microsoft teams alert! Error: " + err.Error()) } else if resp.StatusCode != 200 && resp.StatusCode != 202 { t.Log("Can't send microsoft teams alert! Response code: " + strconv.Itoa(resp.StatusCode)) + } else { + t.Log("Sent successfully microsoft teams alert") + } + if resp != nil { + defer resp.Body.Close() //nolint:errcheck } - - t.Log("Sent successfully microsoft teams alert") } func (t *TaskRunner) sendDingTalkAlert() { @@ -418,6 +430,9 @@ func (t *TaskRunner) sendDingTalkAlert() { } else { t.Log("Sent successfully dingtalk alert") } + if resp != nil { + defer resp.Body.Close() //nolint:errcheck + } } func (t *TaskRunner) sendGotifyAlert() { @@ -480,6 +495,9 @@ func (t *TaskRunner) sendGotifyAlert() { } else { t.Log("Sent successfully gotify alert") } + if resp != nil { + defer resp.Body.Close() //nolint:errcheck + } } func (t *TaskRunner) alertInfos() (string, string) {