Update to fiber 3
Change-Id: I18a7b3ed55aef5cae67db6a613ec14e0f8a9281e
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fe6aea9..59fb3f0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,7 +14,7 @@
runs-on: ubuntu-latest
strategy:
matrix:
- go-version: ['1.24']
+ go-version: ['1.26']
steps:
- uses: actions/checkout@v7
diff --git a/README.md b/README.md
index 2098ecc..f792a70 100644
--- a/README.md
+++ b/README.md
@@ -71,11 +71,12 @@
# Optional: Maximum requests per minute per IP for rate limiting (default: 100)
rateLimit: 100
-# Optional: Comma-separated list of allowed CORS origins.
+# Optional: List of allowed CORS origins.
# Defaults to the server value (trailing slash stripped).
# Required when the service is called cross-origin (e.g. as a Kalamar plugin in an iframe).
# Use "*" to allow all origins (not recommended for production).
-allowOrigins: "https://korap.ids-mannheim.de"
+allowOrigins:
+ - "https://korap.ids-mannheim.de"
# Optional: Base path for file loading confinement (default: current working directory).
# All config and mapping file paths must resolve within this directory or /tmp.
@@ -124,7 +125,7 @@
- **`loglevel`**: Log level (default: `warn`)
- **`serviceURL`**: Service URL of the KoralMapper (default: `https://korap.ids-mannheim.de/plugin/koralmapper`)
- **`rateLimit`**: Maximum number of requests per minute per IP address (default: `100`). When the limit is exceeded, the server responds with HTTP 429 (Too Many Requests).
-- **`allowOrigins`**: Comma-separated list of origins allowed for CORS (default: derived from `server` with trailing slash removed, e.g. `https://korap.ids-mannheim.de`). The service is designed to be called cross-origin as a Kalamar plugin loaded in iframes. This setting controls which origins may make cross-origin API requests. Allowed methods are `GET` and `POST`. The `Content-Type` header is permitted. Use `"*"` to allow all origins (not recommended for production).
+- **`allowOrigins`**: List of origins allowed for CORS (default: derived from `server` with trailing slash removed, e.g. `["https://korap.ids-mannheim.de"]`). Must be specified as a YAML list. The service is designed to be called cross-origin as a Kalamar plugin loaded in iframes. This setting controls which origins may make cross-origin API requests. Allowed methods are `GET` and `POST`. The `Content-Type` header is permitted. Use `["*"]` to allow all origins (not recommended for production).
- **`rewrites`**: Global default for attaching `koral:rewrite` annotations (default: `false`). When `true`, all mapping lists will attach rewrite annotations unless individually overridden. See [Rewrites Resolution](#rewrites-resolution) for the full precedence chain.
- **`basePath`**: Directory tree for file loading confinement (default: current working directory). Configuration and mapping files must resolve within this path or the system temp directory. Set to `"/"` to disable confinement. This prevents path traversal attacks (CWE-22).
@@ -143,7 +144,7 @@
- `KORAL_MAPPER_LOG_LEVEL`: Overrides `loglevel`
- `KORAL_MAPPER_PORT`: Overrides `port` (integer)
- `KORAL_MAPPER_RATE_LIMIT`: Overrides `rateLimit` (integer, requests per minute per IP)
-- `KORAL_MAPPER_ALLOW_ORIGINS`: Overrides `allowOrigins` (comma-separated list of allowed CORS origins)
+- `KORAL_MAPPER_ALLOW_ORIGINS`: Overrides `allowOrigins` (comma-separated string of allowed CORS origins, e.g. `https://a.com,https://b.com`)
- `KORAL_MAPPER_REWRITES`: Overrides `rewrites` (`true` or `false`, global default for koral:rewrite annotations)
- `KORAL_MAPPER_BASE_PATH`: Overrides `basePath` (directory path for file loading confinement)
diff --git a/cmd/koralmapper/fuzz_test.go b/cmd/koralmapper/fuzz_test.go
index 39f20c3..92c21e3 100644
--- a/cmd/koralmapper/fuzz_test.go
+++ b/cmd/koralmapper/fuzz_test.go
@@ -13,7 +13,7 @@
tmconfig "github.com/KorAP/Koral-Mapper/config"
"github.com/KorAP/Koral-Mapper/mapper"
- "github.com/gofiber/fiber/v2"
+ "github.com/gofiber/fiber/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -56,8 +56,7 @@
// Create fiber app
app := fiber.New(fiber.Config{
- DisableStartupMessage: true,
- ErrorHandler: func(c *fiber.Ctx, err error) error {
+ ErrorHandler: func(c fiber.Ctx, err error) error {
// For body limit errors, return 413 status code
if err.Error() == "body size exceeds the given limit" || errors.Is(err, fiber.ErrRequestEntityTooLarge) {
return c.Status(fiber.StatusRequestEntityTooLarge).JSON(fiber.Map{
@@ -176,8 +175,7 @@
// Create fiber app
app := fiber.New(fiber.Config{
- DisableStartupMessage: true,
- ErrorHandler: func(c *fiber.Ctx, err error) error {
+ ErrorHandler: func(c fiber.Ctx, err error) error {
// For body limit errors, return 413 status code
if err.Error() == "body size exceeds the given limit" || errors.Is(err, fiber.ErrRequestEntityTooLarge) {
return c.Status(fiber.StatusRequestEntityTooLarge).JSON(fiber.Map{
@@ -289,8 +287,7 @@
// Create fiber app
app := fiber.New(fiber.Config{
- DisableStartupMessage: true,
- ErrorHandler: func(c *fiber.Ctx, err error) error {
+ ErrorHandler: func(c fiber.Ctx, err error) error {
// For body limit errors, return 413 status code
if err.Error() == "body size exceeds the given limit" || errors.Is(err, fiber.ErrRequestEntityTooLarge) {
return c.Status(fiber.StatusRequestEntityTooLarge).JSON(fiber.Map{
diff --git a/cmd/koralmapper/main.go b/cmd/koralmapper/main.go
index 66cd96b..ccea13b 100644
--- a/cmd/koralmapper/main.go
+++ b/cmd/koralmapper/main.go
@@ -19,9 +19,9 @@
"github.com/KorAP/Koral-Mapper/config"
"github.com/KorAP/Koral-Mapper/mapper"
"github.com/alecthomas/kong"
- "github.com/gofiber/fiber/v2"
- "github.com/gofiber/fiber/v2/middleware/cors"
- "github.com/gofiber/fiber/v2/middleware/limiter"
+ "github.com/gofiber/fiber/v3"
+ "github.com/gofiber/fiber/v3/middleware/cors"
+ "github.com/gofiber/fiber/v3/middleware/limiter"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
@@ -140,12 +140,12 @@
// Only enable HTTP request logging if log level is debug or info
if currentLevel > zerolog.InfoLevel {
- return func(c *fiber.Ctx) error {
+ return func(c fiber.Ctx) error {
return c.Next()
}
}
- return func(c *fiber.Ctx) error {
+ return func(c fiber.Ctx) error {
// Record start time
start := time.Now()
@@ -179,9 +179,14 @@
}
// extractRequestParams extracts and validates common request parameters
-func extractRequestParams(c *fiber.Ctx) (*requestParams, error) {
+func extractRequestParams(c fiber.Ctx) (*requestParams, error) {
+ mapID, err := url.PathUnescape(c.Params("map"))
+ if err != nil {
+ return nil, fmt.Errorf("mapID contains invalid characters")
+ }
+
params := &requestParams{
- MapID: c.Params("map"),
+ MapID: mapID,
Dir: c.Query("dir", "atob"),
FoundryA: c.Query("foundryA", ""),
FoundryB: c.Query("foundryB", ""),
@@ -208,9 +213,9 @@
}
// parseRequestBody parses JSON request body and direction
-func parseRequestBody(c *fiber.Ctx, dir string) (any, mapper.Direction, error) {
+func parseRequestBody(c fiber.Ctx, dir string) (any, mapper.Direction, error) {
var jsonData any
- if err := c.BodyParser(&jsonData); err != nil {
+ if err := c.Bind().Body(&jsonData); err != nil {
return nil, mapper.BtoA, fmt.Errorf("invalid JSON in request body")
}
@@ -280,10 +285,9 @@
// Create fiber app
app := fiber.New(fiber.Config{
- DisableStartupMessage: true,
- BodyLimit: maxInputLength,
- ReadBufferSize: 64 * 1024, // 64KB - increase header size limit
- WriteBufferSize: 64 * 1024, // 64KB - increase response buffer size
+ BodyLimit: maxInputLength,
+ ReadBufferSize: 64 * 1024, // 64KB - increase header size limit
+ WriteBufferSize: 64 * 1024, // 64KB - increase response buffer size,
})
// Add zerolog-integrated logger middleware
@@ -305,7 +309,7 @@
)
}
- if err := app.Listen(fmt.Sprintf(":%d", finalPort)); err != nil {
+ if err := app.Listen(fmt.Sprintf(":%d", finalPort), fiber.ListenConfig{DisableStartupMessage: true}); err != nil {
log.Fatal().Err(err).Msg("Server error")
}
}()
@@ -330,7 +334,7 @@
// information leaks (OWASP Secure Headers). X-Frame-Options is
// intentionally omitted because the service is designed to be embedded
// in cross-origin iframes (Kalamar plugin).
- app.Use(func(c *fiber.Ctx) error {
+ app.Use(func(c fiber.Ctx) error {
c.Set("X-Content-Type-Options", "nosniff")
c.Set("Referrer-Policy", "strict-origin-when-cross-origin")
return c.Next()
@@ -344,8 +348,8 @@
// (default: "https://korap.ids-mannheim.de").
app.Use(cors.New(cors.Config{
AllowOrigins: yamlConfig.AllowOrigins,
- AllowMethods: "GET,POST",
- AllowHeaders: "Content-Type",
+ AllowMethods: []string{"GET", "POST"},
+ AllowHeaders: []string{"Content-Type"},
}))
// Rate limiting middleware to prevent resource exhaustion from
@@ -363,7 +367,7 @@
}))
// Health check endpoint
- app.Get("/health", func(c *fiber.Ctx) error {
+ app.Get("/health", func(c fiber.Ctx) error {
return c.SendString("OK")
})
@@ -386,7 +390,7 @@
}
func handleStaticFile() fiber.Handler {
- return func(c *fiber.Ctx) error {
+ return func(c fiber.Ctx) error {
name := c.Params("*")
data, err := fs.ReadFile(staticFS, "static/"+name)
if err != nil {
@@ -468,7 +472,7 @@
listsByID[yamlConfig.Lists[i].ID] = &yamlConfig.Lists[i]
}
- return func(c *fiber.Ctx) error {
+ return func(c fiber.Ctx) error {
cfgRaw := c.Params("cfg")
if len(cfgRaw) > maxParamLength {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
@@ -477,7 +481,7 @@
}
var jsonData any
- if err := c.BodyParser(&jsonData); err != nil {
+ if err := c.Bind().Body(&jsonData); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid JSON in request body",
})
@@ -548,7 +552,7 @@
listsByID[yamlConfig.Lists[i].ID] = &yamlConfig.Lists[i]
}
- return func(c *fiber.Ctx) error {
+ return func(c fiber.Ctx) error {
cfgRaw := c.Params("cfg")
if len(cfgRaw) > maxParamLength {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
@@ -557,7 +561,7 @@
}
var jsonData any
- if err := c.BodyParser(&jsonData); err != nil {
+ if err := c.Bind().Body(&jsonData); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid JSON in request body",
})
@@ -628,7 +632,7 @@
listsByID[yamlConfig.Lists[i].ID] = &yamlConfig.Lists[i]
}
- return func(c *fiber.Ctx) error {
+ return func(c fiber.Ctx) error {
// Extract and validate parameters
params, err := extractRequestParams(c)
if err != nil {
@@ -685,7 +689,7 @@
listsByID[yamlConfig.Lists[i].ID] = &yamlConfig.Lists[i]
}
- return func(c *fiber.Ctx) error {
+ return func(c fiber.Ctx) error {
// Extract and validate parameters
params, err := extractRequestParams(c)
if err != nil {
@@ -769,8 +773,8 @@
}
func handleKalamarPlugin(yamlConfig *config.MappingConfig, configTmpl *template.Template, pluginTmpl *template.Template) fiber.Handler {
- return func(c *fiber.Ctx) error {
- mapID := c.Params("map")
+ return func(c fiber.Ctx) error {
+ mapID, _ := url.PathUnescape(c.Params("map"))
// Config page (GET /)
if mapID == "" {
diff --git a/cmd/koralmapper/main_test.go b/cmd/koralmapper/main_test.go
index d0d77f2..475d0dd 100644
--- a/cmd/koralmapper/main_test.go
+++ b/cmd/koralmapper/main_test.go
@@ -16,7 +16,7 @@
tmconfig "github.com/KorAP/Koral-Mapper/config"
"github.com/KorAP/Koral-Mapper/mapper"
- "github.com/gofiber/fiber/v2"
+ "github.com/gofiber/fiber/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -2797,7 +2797,7 @@
require.NoError(t, err)
mockConfig := &tmconfig.MappingConfig{
- AllowOrigins: "https://custom.example.com",
+ AllowOrigins: []string{"https://custom.example.com"},
Lists: []tmconfig.MappingList{mappingList},
}
tmconfig.ApplyDefaults(mockConfig)
@@ -2830,7 +2830,7 @@
require.NoError(t, err)
mockConfig := &tmconfig.MappingConfig{
- AllowOrigins: "https://allowed.example.com",
+ AllowOrigins: []string{"https://allowed.example.com"},
Lists: []tmconfig.MappingList{mappingList},
}
tmconfig.ApplyDefaults(mockConfig)
@@ -2860,7 +2860,7 @@
require.NoError(t, err)
mockConfig := &tmconfig.MappingConfig{
- AllowOrigins: "https://first.example.com,https://second.example.com",
+ AllowOrigins: []string{"https://first.example.com", "https://second.example.com"},
Lists: []tmconfig.MappingList{mappingList},
}
tmconfig.ApplyDefaults(mockConfig)
diff --git a/config/config.go b/config/config.go
index 9347994..e024c76 100644
--- a/config/config.go
+++ b/config/config.go
@@ -109,7 +109,7 @@
ServiceURL string `yaml:"serviceURL,omitempty"`
CookieName string `yaml:"cookieName,omitempty"`
BasePath string `yaml:"basePath,omitempty"` // restricts config file loading to this directory tree
- AllowOrigins string `yaml:"allowOrigins,omitempty"` // comma-separated list of allowed CORS origins
+ AllowOrigins []string `yaml:"allowOrigins,omitempty"`
Port int `yaml:"port,omitempty"`
LogLevel string `yaml:"loglevel,omitempty"`
RateLimit int `yaml:"rateLimit,omitempty"` // max requests per minute per IP (0 = use default 100)
@@ -117,6 +117,28 @@
Lists []MappingList `yaml:"lists,omitempty"`
}
+// UnmarshalYAML rejects the deprecated comma-separated string format for
+// allowOrigins and requires a YAML list instead.
+func (m *MappingConfig) UnmarshalYAML(value *yaml.Node) error {
+ if value.Kind == yaml.MappingNode {
+ for i := 0; i < len(value.Content)-1; i += 2 {
+ if value.Content[i].Value == "allowOrigins" && value.Content[i+1].Kind == yaml.ScalarNode {
+ return fmt.Errorf(
+ "allowOrigins must be a YAML list, not a comma-separated string; update your config:\n" +
+ " allowOrigins:\n" +
+ " - \"https://example.com\"")
+ }
+ }
+ }
+ type plain MappingConfig
+ var p plain
+ if err := value.Decode(&p); err != nil {
+ return err
+ }
+ *m = MappingConfig(p)
+ return nil
+}
+
// AllowedBasePath restricts file loading to a specific directory tree.
// When set, all file paths must resolve to a location at or below this
// directory (or under the system temp directory). Defaults to the CWD at
@@ -206,6 +228,8 @@
seenIDs[list.ID] = true
}
allLists = append(allLists, globalConfig.Lists...)
+ } else if strings.Contains(err.Error(), "allowOrigins must be") {
+ return nil, fmt.Errorf("failed to parse config file '%s': %w", configFile, err)
} else {
// Fall back to old format (direct list)
var lists []MappingList
@@ -309,8 +333,8 @@
// AllowOrigins defaults to the Server value. This avoids duplicating
// the server URL string and keeps CORS in sync with the deployment.
- if config.AllowOrigins == "" {
- config.AllowOrigins = config.Server
+ if len(config.AllowOrigins) == 0 {
+ config.AllowOrigins = []string{config.Server}
}
config.AllowOrigins = normalizeOrigins(config.AllowOrigins)
@@ -322,21 +346,24 @@
}
}
-// normalizeOrigins takes a comma-separated list of origin URLs and strips
-// any path components, returning only scheme + host (+ port when present).
-// The CORS middleware requires bare origins without paths; URLs like
+// normalizeOrigins strips path components from origin URLs, returning only
+// scheme + host (+ port when present). The CORS middleware requires bare
+// origins without paths; URLs like
// "https://example.com/instance/test" are pruned to "https://example.com".
-func normalizeOrigins(raw string) string {
- parts := strings.Split(raw, ",")
- for i, part := range parts {
- part = strings.TrimSpace(part)
- if u, err := url.Parse(part); err == nil && u.Host != "" {
- parts[i] = u.Scheme + "://" + u.Host
+func normalizeOrigins(origins []string) []string {
+ result := make([]string, 0, len(origins))
+ for _, origin := range origins {
+ origin = strings.TrimSpace(origin)
+ if origin == "" {
+ continue
+ }
+ if u, err := url.Parse(origin); err == nil && u.Host != "" {
+ result = append(result, u.Scheme+"://"+u.Host)
} else {
- parts[i] = strings.TrimRight(part, "/")
+ result = append(result, strings.TrimRight(origin, "/"))
}
}
- return strings.Join(parts, ",")
+ return result
}
// ApplyEnvOverrides overrides configuration fields from environment variables.
@@ -344,14 +371,13 @@
// Non-empty environment values override any previously loaded config values.
func ApplyEnvOverrides(config *MappingConfig) {
envMappings := map[string]*string{
- "KORAL_MAPPER_SERVER": &config.Server,
- "KORAL_MAPPER_SDK": &config.SDK,
- "KORAL_MAPPER_STYLESHEET": &config.Stylesheet,
- "KORAL_MAPPER_SERVICE_URL": &config.ServiceURL,
- "KORAL_MAPPER_COOKIE_NAME": &config.CookieName,
- "KORAL_MAPPER_LOG_LEVEL": &config.LogLevel,
- "KORAL_MAPPER_BASE_PATH": &config.BasePath,
- "KORAL_MAPPER_ALLOW_ORIGINS": &config.AllowOrigins,
+ "KORAL_MAPPER_SERVER": &config.Server,
+ "KORAL_MAPPER_SDK": &config.SDK,
+ "KORAL_MAPPER_STYLESHEET": &config.Stylesheet,
+ "KORAL_MAPPER_SERVICE_URL": &config.ServiceURL,
+ "KORAL_MAPPER_COOKIE_NAME": &config.CookieName,
+ "KORAL_MAPPER_LOG_LEVEL": &config.LogLevel,
+ "KORAL_MAPPER_BASE_PATH": &config.BasePath,
}
for envKey, field := range envMappings {
@@ -360,6 +386,10 @@
}
}
+ if val := os.Getenv("KORAL_MAPPER_ALLOW_ORIGINS"); val != "" {
+ config.AllowOrigins = strings.Split(val, ",")
+ }
+
if val := os.Getenv("KORAL_MAPPER_PORT"); val != "" {
if port, err := strconv.Atoi(val); err == nil {
config.Port = port
diff --git a/config/config_test.go b/config/config_test.go
index a7d121f..d8c7789 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -1191,31 +1191,31 @@
tests := []struct {
name string
- listRewrites *bool
+ listRewrites *bool
globalDefault bool
expected bool
}{
{
name: "nil per-list, global false",
- listRewrites: nil,
+ listRewrites: nil,
globalDefault: false,
expected: false,
},
{
name: "nil per-list, global true",
- listRewrites: nil,
+ listRewrites: nil,
globalDefault: true,
expected: true,
},
{
name: "per-list true, global false",
- listRewrites: &trueVal,
+ listRewrites: &trueVal,
globalDefault: false,
expected: true,
},
{
name: "per-list false, global true",
- listRewrites: &falseVal,
+ listRewrites: &falseVal,
globalDefault: true,
expected: false,
},
@@ -1425,7 +1425,7 @@
cfg := &MappingConfig{}
ApplyDefaults(cfg)
// AllowOrigins should derive from the Server default (trailing slash stripped)
- assert.Equal(t, "https://korap.ids-mannheim.de", cfg.AllowOrigins,
+ assert.Equal(t, []string{"https://korap.ids-mannheim.de"}, cfg.AllowOrigins,
"default AllowOrigins should derive from defaultServer")
}
@@ -1434,23 +1434,25 @@
Server: "https://custom.example.com/",
}
ApplyDefaults(cfg)
- assert.Equal(t, "https://custom.example.com", cfg.AllowOrigins,
+ assert.Equal(t, []string{"https://custom.example.com"}, cfg.AllowOrigins,
"AllowOrigins should derive from the configured Server (trailing slash stripped)")
}
func TestAllowOriginsExplicitNotOverriddenByServer(t *testing.T) {
cfg := &MappingConfig{
Server: "https://custom.example.com/",
- AllowOrigins: "https://explicit-origin.example.com",
+ AllowOrigins: []string{"https://explicit-origin.example.com"},
}
ApplyDefaults(cfg)
- assert.Equal(t, "https://explicit-origin.example.com", cfg.AllowOrigins,
+ assert.Equal(t, []string{"https://explicit-origin.example.com"}, cfg.AllowOrigins,
"explicit AllowOrigins should not be overridden by Server default")
}
func TestAllowOriginsFromYAML(t *testing.T) {
content := `
-allowOrigins: "https://custom.example.com,https://other.example.com"
+allowOrigins:
+ - "https://custom.example.com"
+ - "https://other.example.com"
lists:
- id: test-mapper
mappings:
@@ -1466,15 +1468,37 @@
cfg, err := LoadFromSources(tmpfile.Name(), nil)
require.NoError(t, err)
- assert.Equal(t, "https://custom.example.com,https://other.example.com",
+ assert.Equal(t, []string{"https://custom.example.com", "https://other.example.com"},
cfg.AllowOrigins)
}
+func TestAllowOriginsStringFormatRejected(t *testing.T) {
+ content := `
+allowOrigins: "https://custom.example.com,https://other.example.com"
+lists:
+ - id: test-mapper
+ mappings:
+ - "[A] <> [B]"
+`
+ tmpfile, err := os.CreateTemp("", "config-cors-reject-*.yaml")
+ require.NoError(t, err)
+ defer os.Remove(tmpfile.Name())
+
+ _, err = tmpfile.WriteString(content)
+ require.NoError(t, err)
+ require.NoError(t, tmpfile.Close())
+
+ _, err = LoadFromSources(tmpfile.Name(), nil)
+ require.Error(t, err)
+ assert.Contains(t, err.Error(), "allowOrigins must be a YAML list")
+}
+
func TestAllowOriginsEnvOverride(t *testing.T) {
t.Setenv("KORAL_MAPPER_ALLOW_ORIGINS", "https://env-origin.example.com")
content := `
-allowOrigins: "https://yaml-origin.example.com"
+allowOrigins:
+ - "https://yaml-origin.example.com"
lists:
- id: test-mapper
mappings:
@@ -1490,7 +1514,7 @@
cfg, err := LoadFromSources(tmpfile.Name(), nil)
require.NoError(t, err)
- assert.Equal(t, "https://env-origin.example.com", cfg.AllowOrigins,
+ assert.Equal(t, []string{"https://env-origin.example.com"}, cfg.AllowOrigins,
"KORAL_MAPPER_ALLOW_ORIGINS env var should override YAML value")
}
@@ -1499,16 +1523,16 @@
Server: "https://korap.ids-mannheim.de/instance/test",
}
ApplyDefaults(cfg)
- assert.Equal(t, "https://korap.ids-mannheim.de", cfg.AllowOrigins,
+ assert.Equal(t, []string{"https://korap.ids-mannheim.de"}, cfg.AllowOrigins,
"AllowOrigins should be pruned to host-level origin when Server contains a path")
}
func TestAllowOriginsExplicitWithPathsPruned(t *testing.T) {
cfg := &MappingConfig{
- AllowOrigins: "https://korap.ids-mannheim.de/instance/test,https://other.example.com/app",
+ AllowOrigins: []string{"https://korap.ids-mannheim.de/instance/test", "https://other.example.com/app"},
}
ApplyDefaults(cfg)
- assert.Equal(t, "https://korap.ids-mannheim.de,https://other.example.com", cfg.AllowOrigins,
+ assert.Equal(t, []string{"https://korap.ids-mannheim.de", "https://other.example.com"}, cfg.AllowOrigins,
"explicit AllowOrigins entries should be pruned to host-level origins")
}
@@ -1517,7 +1541,7 @@
Server: "https://korap.ids-mannheim.de:8080/instance/test",
}
ApplyDefaults(cfg)
- assert.Equal(t, "https://korap.ids-mannheim.de:8080", cfg.AllowOrigins,
+ assert.Equal(t, []string{"https://korap.ids-mannheim.de:8080"}, cfg.AllowOrigins,
"AllowOrigins should preserve port but strip path")
}
diff --git a/go.mod b/go.mod
index 0a0097b..321de65 100644
--- a/go.mod
+++ b/go.mod
@@ -1,11 +1,11 @@
module github.com/KorAP/Koral-Mapper
-go 1.25.0
+go 1.26.0
require (
github.com/alecthomas/kong v1.15.0
github.com/alecthomas/participle/v2 v2.1.4
- github.com/gofiber/fiber/v2 v2.52.14
+ github.com/gofiber/fiber/v3 v3.4.0
github.com/orisano/gosax v1.1.4
github.com/rs/zerolog v1.35.1
github.com/stretchr/testify v1.11.1
@@ -14,17 +14,20 @@
require (
github.com/andybalholm/brotli v1.2.2 // indirect
- github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/gofiber/schema v1.8.0 // indirect
+ github.com/gofiber/utils/v2 v2.1.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.19.0 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
- github.com/mattn/go-runewidth v0.0.24 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tinylib/msgp v1.6.4 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.72.0 // indirect
+ golang.org/x/crypto v0.53.0 // indirect
+ golang.org/x/net v0.56.0 // indirect
golang.org/x/sys v0.47.0 // indirect
+ golang.org/x/text v0.38.0 // indirect
)
diff --git a/go.sum b/go.sum
index 5a0ba81..65bfb0e 100644
--- a/go.sum
+++ b/go.sum
@@ -8,12 +8,16 @@
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/andybalholm/brotli v1.2.2 h1:HzTuoo2ErYQqf5qvcJInB8uvqSVxRttzkFexPWtnceM=
github.com/andybalholm/brotli v1.2.2/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
-github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
-github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/gofiber/fiber/v2 v2.52.14 h1:Of3L+9qVFaQNwPlcmEdl5IIodHz8BSE0j37R7rWu4pE=
-github.com/gofiber/fiber/v2 v2.52.14/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
+github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78=
+github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/gofiber/fiber/v3 v3.4.0 h1:F0aND4vwZF7dR7cbvSwFQQEpBU902XHKWxrLsFBkVqw=
+github.com/gofiber/fiber/v3 v3.4.0/go.mod h1:nAhJfdxUIJJph2tPWPmqWf8QDIN2iiqQiQf3lENZpdk=
+github.com/gofiber/schema v1.8.0 h1:NGsC9toPHmj8Xg4KpznuXBzNmHG6V5YV0tXKpKMcmis=
+github.com/gofiber/schema v1.8.0/go.mod h1:lmbXPQ8hvzXSLkdS2DS7pb4kpunC2Roh7Sj3HMjGfzA=
+github.com/gofiber/utils/v2 v2.1.1 h1:kGnoGjwEnFW6w0x45W+kLlmMJvqBGkuUA4oMWKn/T/I=
+github.com/gofiber/utils/v2 v2.1.1/go.mod h1:DdOgEVwQTi8cou/AKWPqhXOR4fHGRVhA/rEWL3IXG7Q=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
@@ -24,8 +28,6 @@
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
-github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
-github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/orisano/gosax v1.1.4 h1:fJZ8180lWGOqck/unlYTo9bxjT4dcemG/NErUDcVOOw=
github.com/orisano/gosax v1.1.4/go.mod h1:mw6A5jIOFDeVOqffQkggKOOjRFevYnLyXgiZP06fRjI=
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
@@ -34,6 +36,8 @@
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
+github.com/shamaton/msgpack/v3 v3.1.2 h1:d5gWAIyMU4M0WgDjz6IFSCuXJUA2dFwRHBpDclE8CLw=
+github.com/shamaton/msgpack/v3 v3.1.2/go.mod h1:DcQG8jrdrQCIxr3HlMYkiXdMhK+KfN2CitkyzsQV4uc=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
@@ -42,10 +46,18 @@
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.72.0 h1:R7kYdoWhn1ye1fVpP+cDHDJwYm3NkwLliwgzJ/Abg7M=
github.com/valyala/fasthttp v1.72.0/go.mod h1:zsbLTYqcpIktdQytlVBwIjY9La5d6bs990nBxWg8efk=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
+golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
+golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
+golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
+golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
+golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=