Skip to content

Commit d5a1bf1

Browse files
erraggyclaudegithub-actions[bot]
authored
chore: prepare v1.52.0 release (#339)
* fix: address code review findings before v1.52.0 release - Add package prefix to RejectSymlinkOutput error messages - Document intentional race trade-off in pattern cache eviction - Narrow MCP sanitizeError regex to filesystem paths only - Change WithMaxFileSize signature from int64 to int for API consistency - Use allowlist sanitization for discriminator JSON names Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump plugin version to 1.52.0 * chore: add benchmark results for v1.52.0 Generated by CI benchmark workflow on chore/v1.52.0-release-prep 🤖 Generated automatically --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent e937eab commit d5a1bf1

8 files changed

Lines changed: 382 additions & 11 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
{
1111
"name": "oastools",
1212
"description": "15 MCP tools and 5 guided skills for working with OpenAPI specs — validate, fix, convert, diff, join, overlay, generate, and walk operations/schemas/parameters/responses/security/paths",
13-
"version": "1.51.6",
13+
"version": "1.52.0",
1414
"author": {
1515
"name": "erraggy",
1616
"url": "https://github.com/erraggy"

benchmarks/benchmark-v1.52.0.txt

Lines changed: 364 additions & 0 deletions
Large diffs are not rendered by default.

cmd/oastools/commands/common.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ func RejectSymlinkOutput(cleanedPath string) error {
134134
return nil
135135
}
136136
if err != nil {
137-
return fmt.Errorf("checking output path: %w", err)
137+
return fmt.Errorf("commands: checking output path: %w", err)
138138
}
139139
if info.Mode()&os.ModeSymlink != 0 {
140-
return fmt.Errorf("refusing to write to symlink: %s", cleanedPath)
140+
return fmt.Errorf("commands: refusing to write to symlink: %s", cleanedPath)
141141
}
142142
return nil
143143
}

generator/template_builders.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -444,10 +444,14 @@ func (cg *oas3CodeGenerator) buildOneOfTypeDefinition(typeName, originalName str
444444
if schema.Discriminator != nil && schema.Discriminator.PropertyName != "" {
445445
oneOfData.Discriminator = schema.Discriminator.PropertyName
446446
oneOfData.DiscriminatorField = toFieldName(schema.Discriminator.PropertyName)
447-
// Sanitize the discriminator property name to prevent injection in generated code
448-
jsonName := schema.Discriminator.PropertyName
449-
jsonName = strings.ReplaceAll(jsonName, `"`, "")
450-
jsonName = strings.ReplaceAll(jsonName, "`", "")
447+
// Sanitize the discriminator property name to prevent injection in generated code.
448+
// Only allow alphanumeric, underscore, hyphen, and dot — safe for JSON struct tags.
449+
jsonName := strings.Map(func(r rune) rune {
450+
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' || r == '.' {
451+
return r
452+
}
453+
return -1 // drop unsafe characters
454+
}, schema.Discriminator.PropertyName)
451455
oneOfData.DiscriminatorJSONName = jsonName
452456
oneOfData.HasUnmarshal = true
453457

httpvalidator/schema.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,9 @@ func (v *SchemaValidator) matchPattern(pattern, s string) (bool, error) {
531531

532532
// Size cap: if cache exceeds limit, clear and start fresh.
533533
// This prevents unbounded growth from specs with many unique patterns.
534+
// NOTE: The count check and clear are not atomic — under high concurrency,
535+
// multiple goroutines may clear simultaneously. This is acceptable because
536+
// the cache is a performance optimization; worst case is extra recompilation.
534537
if v.patternCount.Add(1) > maxPatternCacheSize {
535538
v.patternCache.Range(func(key, _ any) bool {
536539
v.patternCache.Delete(key)

internal/mcpserver/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ func makeSlice[T any](n int) []T {
175175

176176
// sanitizeError strips absolute filesystem paths from error messages
177177
// to prevent leaking internal directory structure to MCP clients.
178-
var pathPattern = regexp.MustCompile(`(?:/[a-zA-Z0-9._-]+){2,}`)
178+
var pathPattern = regexp.MustCompile(`(?:/(?:home|tmp|var|Users|etc|opt|usr|private|root|mnt|srv|run|snap|nix)[a-zA-Z0-9._/-]*)`)
179179

180180
func sanitizeError(err error) string {
181181
if err == nil {

parser/parser_options.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,12 @@ func WithMaxCachedDocuments(count int) Option {
294294
// This prevents resource exhaustion from loading arbitrarily large files.
295295
// A value of 0 means use the default (10MB).
296296
// Returns an error if size is negative.
297-
func WithMaxFileSize(size int64) Option {
297+
func WithMaxFileSize(size int) Option {
298298
return func(cfg *parseConfig) error {
299299
if size < 0 {
300300
return fmt.Errorf("parser: maxFileSize cannot be negative")
301301
}
302-
cfg.maxFileSize = size
302+
cfg.maxFileSize = int64(size)
303303
return nil
304304
}
305305
}

plugin/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "oastools",
33
"description": "OpenAPI Specification tools — validate, fix, convert, diff, walk, and generate from OAS 2.0-3.2 documents",
4-
"version": "1.51.6",
4+
"version": "1.52.0",
55
"author": {
66
"name": "erraggy",
77
"url": "https://github.com/erraggy"

0 commit comments

Comments
 (0)