← back to Cli Printing Press
fix(openapi): Swagger 2.0 detection + resource name sanitization
4934eb508b01127e0d98fcd8d53984d52152a30c · 2026-03-23 15:28:11 -0700 · Matt Van Horn
Add swagger: keyword detection to IsOpenAPI() for Swagger 2.0 specs.
Add sanitizeResourceName() to prevent path traversal from untrusted
OpenAPI specs by stripping dots, slashes, and trimming underscores.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Files touched
M internal/openapi/detect.goM internal/openapi/parser.goM internal/openapi/parser_test.go
Diff
commit 4934eb508b01127e0d98fcd8d53984d52152a30c
Author: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Date: Mon Mar 23 15:28:11 2026 -0700
fix(openapi): Swagger 2.0 detection + resource name sanitization
Add swagger: keyword detection to IsOpenAPI() for Swagger 2.0 specs.
Add sanitizeResourceName() to prevent path traversal from untrusted
OpenAPI specs by stripping dots, slashes, and trimming underscores.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
internal/openapi/detect.go | 5 ++++-
internal/openapi/parser.go | 13 ++++++++++++-
internal/openapi/parser_test.go | 33 +++++++++++++++++++++++++++++++++
3 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/internal/openapi/detect.go b/internal/openapi/detect.go
index 4045233d..aee5b450 100644
--- a/internal/openapi/detect.go
+++ b/internal/openapi/detect.go
@@ -11,5 +11,8 @@ func IsOpenAPI(data []byte) bool {
}
content := strings.ToLower(string(data))
- return strings.Contains(content, "openapi:") || strings.Contains(content, "\"openapi\"")
+ return strings.Contains(content, "openapi:") ||
+ strings.Contains(content, "\"openapi\"") ||
+ strings.Contains(content, "swagger:") ||
+ strings.Contains(content, "\"swagger\"")
}
diff --git a/internal/openapi/parser.go b/internal/openapi/parser.go
index f56d3c46..4883c550 100644
--- a/internal/openapi/parser.go
+++ b/internal/openapi/parser.go
@@ -836,7 +836,7 @@ func resourceNameFromPath(path, basePath string) string {
if isPathParamSegment(segments[0]) {
return ""
}
- return toSnakeCase(segments[0])
+ return sanitizeResourceName(toSnakeCase(segments[0]))
}
func endpointCollisionSuffix(path, resourceName, basePath string) string {
@@ -962,6 +962,17 @@ func toSnakeCase(input string) string {
return strings.Trim(b.String(), "_")
}
+func sanitizeResourceName(name string) string {
+ name = strings.ReplaceAll(name, ".", "")
+ name = strings.ReplaceAll(name, "/", "")
+ name = strings.ReplaceAll(name, "\\", "")
+ name = strings.Trim(name, "_")
+ if name == "" {
+ return ""
+ }
+ return name
+}
+
func toKebabCase(input string) string {
var b strings.Builder
lastHyphen := true
diff --git a/internal/openapi/parser_test.go b/internal/openapi/parser_test.go
index d83610fd..c5cef31b 100644
--- a/internal/openapi/parser_test.go
+++ b/internal/openapi/parser_test.go
@@ -69,6 +69,13 @@ info:
paths: {}
`)
openAPIJSON := []byte(`{"openapi":"3.0.1","info":{"title":"Demo","version":"1.0.0"},"paths":{}}`)
+ swagger20YAML := []byte(`swagger: "2.0"
+info:
+ title: Demo
+ version: 1.0.0
+paths: {}
+`)
+ swagger20JSON := []byte(`{"swagger":"2.0","info":{"title":"Demo","version":"1.0.0"},"paths":{}}`)
internalYAML := []byte(`
name: demo
base_url: https://api.example.com
@@ -82,6 +89,8 @@ resources:
assert.True(t, IsOpenAPI(openAPIYAML))
assert.True(t, IsOpenAPI(openAPIJSON))
+ assert.True(t, IsOpenAPI(swagger20YAML))
+ assert.True(t, IsOpenAPI(swagger20JSON))
assert.False(t, IsOpenAPI(internalYAML))
}
@@ -131,3 +140,27 @@ func runGo(t *testing.T, dir string, args ...string) {
output, err := cmd.CombinedOutput()
require.NoError(t, err, string(output))
}
+
+func TestSanitizeResourceName(t *testing.T) {
+ tests := []struct {
+ input string
+ want string
+ }{
+ {"users", "users"},
+ {"user-accounts", "user_accounts"},
+ {"../../../etc/passwd", "etc_passwd"},
+ {"foo/bar", "foo_bar"},
+ {"foo\\bar", "foo_bar"},
+ {"..", ""},
+ {".", ""},
+ {"___", ""},
+ {"normal_name", "normal_name"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.input, func(t *testing.T) {
+ got := sanitizeResourceName(toSnakeCase(tt.input))
+ assert.Equal(t, tt.want, got)
+ })
+ }
+}
← 6011234c feat(openapi): integration tests + oneline template fix for
·
back to Cli Printing Press
·
docs(skill): add known-specs registry and spec-format refere c547fa57 →