github-code-scanning[bot] commented on code in PR #7740: URL: https://github.com/apache/trafficcontrol/pull/7740#discussion_r1302168804
########## traffic_ops/traffic_ops_golang/staticdnsentry/staticdnsentry.go: ########## @@ -260,3 +265,380 @@ WHERE id=:id` return query } + +// Validate validates a statisDNSEntry entity and makes sure that all the supplied fields are valid. +func Validate(staticDNSEntry tc.StaticDNSEntryV5, tx *sql.Tx) (error, error) { + typeStr, err := tc.ValidateTypeID(tx, staticDNSEntry.TypeID, "staticdnsentry") + if err != nil { + return err, nil + } + + var addressErr, ttlErr error + switch typeStr { + case "A_RECORD": + addressErr = validation.Validate(staticDNSEntry.Address, validation.Required, is.IPv4) + case "AAAA_RECORD": + addressErr = validation.Validate(staticDNSEntry.Address, validation.Required, is.IPv6) + case "CNAME_RECORD": + addressErr = validation.Validate(staticDNSEntry.Address, validation.Required, is.DNSName) + address := *staticDNSEntry.Address + if addressErr == nil { + lastChar := address[len(address)-1:] + if lastChar != "." { + addressErr = fmt.Errorf("for type: CNAME_RECORD must have a trailing period") + } + } + default: + addressErr = validation.Validate(staticDNSEntry.Address, validation.Required) + } + + if staticDNSEntry.TTL != nil { + if *staticDNSEntry.TTL == 0 { + ttlErr = validation.Validate(staticDNSEntry.TTL, is.Digit) + } + } else { + ttlErr = validation.Validate(staticDNSEntry.TTL, validation.Required) + } + + errs := validation.Errors{ + "host": validation.Validate(staticDNSEntry.Host, validation.Required, is.DNSName), + "address": addressErr, + "deliveryserviceId": validation.Validate(staticDNSEntry.DeliveryServiceID, validation.Required), + "ttl": ttlErr, + "typeId": validation.Validate(staticDNSEntry.TypeID, validation.Required), + } + return util.JoinErrs(tovalidate.ToErrors(errs)), nil +} + +// Update will modify an existing StaticDNSEntry entity in the database, for api v5.0. +func Update(w http.ResponseWriter, r *http.Request) { + inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil) + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) + return + } + defer inf.Close() + + tx := inf.Tx.Tx + + defer r.Body.Close() + var staticDNSEntry tc.StaticDNSEntryV5 + + if err := json.NewDecoder(r.Body).Decode(&staticDNSEntry); err != nil { + api.HandleErr(w, r, nil, http.StatusInternalServerError, nil, err) + return + } + + userErr, sysErr = Validate(staticDNSEntry, tx) + if userErr != nil || sysErr != nil { + code := http.StatusBadRequest + if sysErr != nil { + code = http.StatusInternalServerError + } + api.HandleErr(w, r, tx, code, userErr, sysErr) + return + } + + var cdnName tc.CDNName + if id, ok := inf.Params["id"]; !ok { + api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("missing key: id"), nil) + return + } else { + idNum, err := strconv.Atoi(id) + if err != nil { + api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("couldn't convert ID into a numeric value: "+err.Error()), nil) + return + } + staticDNSEntry.ID = &idNum + if staticDNSEntry.DeliveryServiceID != nil { + _, cdnName, _, err = dbhelpers.GetDSNameAndCDNFromID(tx, *staticDNSEntry.DeliveryServiceID) + if err != nil { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) + return + } + userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(tx, string(cdnName), inf.User.UserName) + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) + return + } + } + + existingLastUpdated, found, err := api.GetLastUpdated(inf.Tx, idNum, "staticdnsentry") + if err == nil && found == false { + api.HandleErr(w, r, tx, http.StatusNotFound, errors.New("no staticDNSEntry found with this id"), nil) + return + } + if err != nil { + api.HandleErr(w, r, tx, http.StatusNotFound, nil, err) + return + } + if !api.IsUnmodified(r.Header, *existingLastUpdated) { + api.HandleErr(w, r, tx, http.StatusPreconditionFailed, api.ResourceModifiedError, nil) + return + } + + rows, err := inf.Tx.NamedQuery(updateQuery(), staticDNSEntry) + if err != nil { + userErr, sysErr, errCode = api.ParseDBError(err) + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + defer rows.Close() + + if !rows.Next() { + api.HandleErr(w, r, tx, http.StatusNotFound, errors.New("no staticDNSEntry found with this id"), nil) + return + } + lastUpdated := time.Time{} + if err := rows.Scan(&lastUpdated); err != nil { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("scanning lastUpdated from staticDNSEntry insert: "+err.Error())) + return + } + staticDNSEntry.LastUpdated = &lastUpdated + if rows.Next() { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("staticDNSEntry update affected too many rows: >1")) + return + } + + alerts := tc.CreateAlerts(tc.SuccessLevel, "staticDNSEntry was updated.") + api.WriteAlertsObj(w, r, http.StatusOK, alerts, staticDNSEntry) + changeLogMsg := fmt.Sprintf("STATICDNSENTRY: %s, ID: %d, ACTION: Updated staticDNSEntry", *staticDNSEntry.Host, *staticDNSEntry.ID) + api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx) + return + } +} + +// Create will add a new StaticDNSEntry entity into the database, for api v5.0. +func Create(w http.ResponseWriter, r *http.Request) { + inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil) + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) + return + } + defer inf.Close() + + tx := inf.Tx.Tx + + defer r.Body.Close() + var staticDNSEntry tc.StaticDNSEntryV5 + + if err := json.NewDecoder(r.Body).Decode(&staticDNSEntry); err != nil { + api.HandleErr(w, r, nil, http.StatusInternalServerError, nil, err) + return + } + + userErr, sysErr = Validate(staticDNSEntry, tx) + if userErr != nil || sysErr != nil { + code := http.StatusBadRequest + if sysErr != nil { + code = http.StatusInternalServerError + } + api.HandleErr(w, r, tx, code, userErr, sysErr) + return + } + + var cdnName tc.CDNName + var err error + if staticDNSEntry.DeliveryServiceID != nil { + _, cdnName, _, err = dbhelpers.GetDSNameAndCDNFromID(tx, *staticDNSEntry.DeliveryServiceID) + if err != nil { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) + return + } + userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(tx, string(cdnName), inf.User.UserName) + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) + return + } + } + + resultRows, err := inf.Tx.NamedQuery(insertQuery(), staticDNSEntry) + if err != nil { + userErr, sysErr, errCode = api.ParseDBError(err) + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + defer resultRows.Close() + + var id int + lastUpdated := time.Time{} + rowsAffected := 0 + for resultRows.Next() { + rowsAffected++ + if err := resultRows.Scan(&id, &lastUpdated); err != nil { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("staticDNSEntry create scanning: "+err.Error())) + return + } + } + + if rowsAffected == 0 { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("staticDNSEntry create: no staticDNSEntry was inserted, no id was returned")) + return + } else if rowsAffected > 1 { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("too many ids returned from staticDNSEntry insert")) + return + } + staticDNSEntry.ID = &id + staticDNSEntry.LastUpdated = &lastUpdated + + alerts := tc.CreateAlerts(tc.SuccessLevel, "staticDNSEntry was created.") + api.WriteAlertsObj(w, r, http.StatusOK, alerts, staticDNSEntry) + changeLogMsg := fmt.Sprintf("STATICDNSENTRY: %s, ID: %d, ACTION: Created staticDNSEntry", *staticDNSEntry.Host, *staticDNSEntry.ID) + api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx) + return +} + +// Delete removes a staticDNSEntry from the database, for api v5.0. +func Delete(w http.ResponseWriter, r *http.Request) { + inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil) + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) + return + } + defer inf.Close() + + tx := inf.Tx.Tx + if id, ok := inf.Params["id"]; !ok { + api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("missing key: id"), nil) + return + } else { + idNum, err := strconv.Atoi(id) + if err != nil { + api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("couldn't convert ID into a numeric value: "+err.Error()), nil) + return + } + staticDNSEntry := tc.StaticDNSEntryV5{ + ID: &idNum, + } + var cdnName tc.CDNName + var dsID int + if staticDNSEntry.DeliveryServiceID != nil { + dsID = *staticDNSEntry.DeliveryServiceID + } else if staticDNSEntry.ID != nil { + dsID, err = dbhelpers.GetDSIDFromStaticDNSEntry(tx, *staticDNSEntry.ID) + if err != nil { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("couldn't get DS ID from static dns entry ID: "+err.Error())) + return + } + } + _, cdnName, _, err = dbhelpers.GetDSNameAndCDNFromID(tx, dsID) + if err != nil { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) + return + } + userErr, sysErr, errCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(tx, string(cdnName), inf.User.UserName) + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + result, err := inf.Tx.NamedExec(deleteQuery(), staticDNSEntry) + if err != nil { + userErr, sysErr, errCode = api.ParseDBError(err) + api.HandleErr(w, r, tx, errCode, userErr, sysErr) + return + } + + if rowsAffected, err := result.RowsAffected(); err != nil { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, errors.New("deleting staticDNSEntry: getting rows affected: "+err.Error())) + return + } else if rowsAffected < 1 { + api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("no staticDNSEntry with that key found"), nil) + return + } else if rowsAffected > 1 { + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("staticDNSEntry delete affected too many rows: %d", rowsAffected)) + return + } + log.Debugf("changelog for delete on staticDNSEntry") + + alerts := tc.CreateAlerts(tc.SuccessLevel, "staticDNSEntry was deleted.") + api.WriteAlerts(w, r, http.StatusOK, alerts) + changeLogMsg := fmt.Sprintf("STATICDNSENTRY: %d, ID: %d, ACTION: Deleted staticDNSEntry", *staticDNSEntry.ID, *staticDNSEntry.ID) + api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx) + return + } +} + +// Get : function to read the staticDNSEntries, for api version 5.0. +func Get(w http.ResponseWriter, r *http.Request) { + inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil) + if userErr != nil || sysErr != nil { + api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) + return + } + defer inf.Close() + + api.DefaultSort(inf, "host") + useIMS := false + config, e := api.GetConfig(r.Context()) + if e == nil && config != nil { + useIMS = config.UseIMS + } else { + log.Warnf("Couldn't get config %v", e) + } + + var maxTime time.Time + var runSecond bool + + scList := make([]tc.StaticDNSEntryV5, 0) + + tx := inf.Tx + + where, orderBy, pagination, queryValues, errs := dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, map[string]dbhelpers.WhereColumnInfo{ + "address": dbhelpers.WhereColumnInfo{Column: "sde.address"}, + "cachegroup": dbhelpers.WhereColumnInfo{Column: "cg.name"}, + "cachegroupId": dbhelpers.WhereColumnInfo{Column: "cg.id"}, + "deliveryservice": dbhelpers.WhereColumnInfo{Column: "ds.xml_id"}, + "deliveryserviceId": dbhelpers.WhereColumnInfo{Column: "sde.deliveryservice"}, + "host": dbhelpers.WhereColumnInfo{Column: "sde.host"}, + "id": dbhelpers.WhereColumnInfo{Column: "sde.id"}, + "ttl": dbhelpers.WhereColumnInfo{Column: "sde.ttl"}, + "type": dbhelpers.WhereColumnInfo{Column: "tp.name"}, + "typeId": dbhelpers.WhereColumnInfo{Column: "tp.id"}, + }) + if len(errs) > 0 { + api.HandleErr(w, r, tx.Tx, http.StatusBadRequest, nil, util.JoinErrs(errs)) + return + } + if useIMS { + runSecond, maxTime = ims.TryIfModifiedSinceQuery(inf.Tx, r.Header, queryValues, selectMaxLastUpdatedQuery(where)) + if !runSecond { + log.Debugln("IMS HIT") + api.AddLastModifiedHdr(w, maxTime) + w.WriteHeader(http.StatusNotModified) + return + } + log.Debugln("IMS MISS") + } else { + log.Debugln("Non IMS request") + } + + // Case where we need to run the second query + query := selectQuery() + where + orderBy + pagination + rows, err := tx.NamedQuery(query, queryValues) Review Comment: ## Database query built from user-controlled sources This query depends on a [user-provided value](1). [Show more details](https://github.com/apache/trafficcontrol/security/code-scanning/304) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: issues-unsubscr...@trafficcontrol.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org