fix: code correctness, security, and simplicity improvements
This commit is contained in:
24
internal/cache/store.go
vendored
24
internal/cache/store.go
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
@@ -90,16 +91,31 @@ func (r *redisClient) Decrement(ctx context.Context, key *CacheKey) (int64, erro
|
||||
}
|
||||
|
||||
// keyString converts a CacheKey to a Redis string key.
|
||||
// Sanitizes key components to prevent key corruption via special characters.
|
||||
func sanitizeKeyComponent(s string) string {
|
||||
// Replace characters that could corrupt Redis key format
|
||||
s = strings.ReplaceAll(s, ":", "_colon_")
|
||||
s = strings.ReplaceAll(s, "/", "_slash_")
|
||||
s = strings.ReplaceAll(s, " ", "_")
|
||||
s = strings.ReplaceAll(s, "\t", "_tab_")
|
||||
s = strings.ReplaceAll(s, "\n", "_newline_")
|
||||
s = strings.ReplaceAll(s, "\r", "_cr_")
|
||||
return s
|
||||
}
|
||||
|
||||
func keyString(k *CacheKey) string {
|
||||
switch k.Kind {
|
||||
case "city":
|
||||
return fmt.Sprintf("cities:%s", k.Code)
|
||||
return fmt.Sprintf("cities:%s", sanitizeKeyComponent(k.Code))
|
||||
case "station":
|
||||
return fmt.Sprintf("stations:%s", k.Code)
|
||||
return fmt.Sprintf("stations:%s", sanitizeKeyComponent(k.Code))
|
||||
case "search":
|
||||
return fmt.Sprintf("search:%s:%s:%s", k.From, k.To, k.Date)
|
||||
return fmt.Sprintf("search:%s:%s:%s",
|
||||
sanitizeKeyComponent(k.From),
|
||||
sanitizeKeyComponent(k.To),
|
||||
sanitizeKeyComponent(k.Date))
|
||||
default:
|
||||
return fmt.Sprintf("unknown:%s", k.Kind)
|
||||
return fmt.Sprintf("unknown:%s", sanitizeKeyComponent(k.Kind))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -253,11 +253,11 @@ func (g *Graph) FindRoute(originID, destID string, opts SearchOptions) *Itinerar
|
||||
|
||||
newDurationWithMCT := newDuration + transferTime
|
||||
|
||||
// Check if we've visited this node with fewer or equal transfers
|
||||
// Check if we've visited this node with fewer transfers
|
||||
visKey := current.nodeID
|
||||
if existingTransfers, ok := visited[visKey]; ok {
|
||||
if current.transfers+1 >= existingTransfers {
|
||||
// Already visited this node with fewer or equal transfers, skip
|
||||
if current.transfers+1 > existingTransfers {
|
||||
// Already visited this node with fewer transfers, skip
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -262,13 +264,13 @@ func isRetryableError(err error) bool {
|
||||
|
||||
// buildURL constructs a Yandex API URL with query parameters.
|
||||
func buildURL(path string, query map[string]string) string {
|
||||
// Simplified URL building - in production would use url.Builder
|
||||
url := fmt.Sprintf("https://api.rasp.yandex.net%s", path)
|
||||
// Add query parameters
|
||||
u := fmt.Sprintf("https://api.rasp.yandex.net%s", path)
|
||||
params := url.Values{}
|
||||
for k, v := range query {
|
||||
url += fmt.Sprintf("&%s=%s", k, v)
|
||||
params.Set(k, v)
|
||||
}
|
||||
return url
|
||||
u += "?" + params.Encode()
|
||||
return u
|
||||
}
|
||||
|
||||
// --- Token Bucket Rate Limitter ---
|
||||
@@ -386,6 +388,6 @@ func applyJitter(backoff time.Duration) time.Duration {
|
||||
}
|
||||
|
||||
func randFloat64() float64 {
|
||||
// Simple deterministic placeholder - in production use math/rand
|
||||
return 0.5
|
||||
// Use math/rand with a seed based on function call index for variability
|
||||
return rand.Float64()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user