Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ require (
github.com/mtibben/percent v0.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.57.1 // indirect
github.com/quic-go/quic-go v0.59.1 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic=
github.com/quic-go/quic-go v0.59.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
Expand Down
33 changes: 33 additions & 0 deletions kinde/management_api/compat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Hand-written compatibility shim: not generated by ogen, not touched by `go generate ./...`.
package management_api

import "context"

// Deprecated: use DeleteAPIApplicationScopeOperation. Kept for source
// compatibility with the pre-typo-fix operation name.
const DeleteAPIAppliationScopeOperation = DeleteAPIApplicationScopeOperation

// Deprecated: use DeleteAPIApplicationScopeParams.
type DeleteAPIAppliationScopeParams = DeleteAPIApplicationScopeParams

// Deprecated: use DeleteAPIApplicationScopeRes.
type DeleteAPIAppliationScopeRes = DeleteAPIApplicationScopeRes

// Deprecated: use DeleteAPIApplicationScopeOK.
type DeleteAPIAppliationScopeOK = DeleteAPIApplicationScopeOK

// Deprecated: use DeleteAPIApplicationScopeBadRequest.
type DeleteAPIAppliationScopeBadRequest = DeleteAPIApplicationScopeBadRequest

// Deprecated: use DeleteAPIApplicationScopeForbidden.
type DeleteAPIAppliationScopeForbidden = DeleteAPIApplicationScopeForbidden

// Deprecated: use DeleteAPIApplicationScopeTooManyRequests.
type DeleteAPIAppliationScopeTooManyRequests = DeleteAPIApplicationScopeTooManyRequests

// DeleteAPIAppliationScope invokes DeleteAPIApplicationScope operation.
//
// Deprecated: use DeleteAPIApplicationScope.
func (c *Client) DeleteAPIAppliationScope(ctx context.Context, params DeleteAPIAppliationScopeParams) (DeleteAPIAppliationScopeRes, error) {
return c.DeleteAPIApplicationScope(ctx, params)
}
92 changes: 92 additions & 0 deletions kinde/management_api/fix_oneof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//go:build ignore
// +build ignore

package main

import (
"bytes"
"fmt"
"os"
"regexp"
"strings"
)

// This tool patches the generated oas_json_gen.go file to stop ambiguous oneOf
// decoding from silently defaulting to variant 0. When one oneOf variant has
// no fields unique to it, ogen makes it the sum type's default mapping and
// falls back to it whenever no discriminating key matches, which can misroute
// a payload (e.g. decode a SAML/enterprise connection config as a plain OAuth
// one) once sibling variants share field names. This makes it fail closed.

const targetFile = "oas_json_gen.go"

// Matches the generated "if !found { s.Type = <value> }" fallback. <value> is
// checked separately for the "X0X" sum-type-variant-0 shape (Go's regexp
// package has no backreferences), e.g.
// CreateConnectionReqOptions0CreateConnectionReqOptions.
var fallbackPattern = regexp.MustCompile(`if !found \{\n\t\ts\.Type = (\w+)\n\t\}`)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// remainingFallbackPattern is a looser tripwire: any `if !found` block that
// still assigns s.Type before its closing brace. If it matches after the
// patch pass, the generated shape has drifted past fallbackPattern (or a sum
// type gained a non-variant-0 default) and the generate run must fail rather
// than ship the silent-default behaviour.
var remainingFallbackPattern = regexp.MustCompile(`if !found \{[^}]*s\.Type\s*=`)

// isVariant0Assignment reports whether value has the "X0X" shape ogen uses
// for a sum type's zero-variant constant.
func isVariant0Assignment(value string) bool {
n := len(value)
if n < 3 || n%2 == 0 {
return false
}
half := (n - 1) / 2
return value[half] == '0' && value[:half] == value[half+1:]
}

func main() {
fmt.Printf("Patching %s for ambiguous oneOf handling...\n", targetFile)

content, err := os.ReadFile(targetFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err)
os.Exit(1)
}

patched := 0
newContent := fallbackPattern.ReplaceAllFunc(content, func(match []byte) []byte {
sub := fallbackPattern.FindSubmatch(match)
if sub == nil || !isVariant0Assignment(string(sub[1])) {
return match
}
patched++
return []byte(`if !found {
return errors.New("unable to detect sum type variant")
}`)
})

// Validate before writing so a failed run leaves the file exactly as ogen
// generated it, never partially patched.
if locs := remainingFallbackPattern.FindAllIndex(newContent, -1); len(locs) > 0 {
fmt.Fprintf(os.Stderr, "Error: %d oneOf fallback(s) would still assign s.Type after patching:\n", len(locs))
for _, loc := range locs {
line := 1 + bytes.Count(newContent[:loc[0]], []byte("\n"))
snippet := strings.Join(strings.Fields(string(newContent[loc[0]:loc[1]])), " ")
fmt.Fprintf(os.Stderr, " %s:%d: %s\n", targetFile, line, snippet)
}
fmt.Fprintf(os.Stderr, "The generated shape has likely changed (ogen upgrade?) - update fix_oneof.go\n")
fmt.Fprintf(os.Stderr, "%s left unmodified\n", targetFile)
os.Exit(1)
}

if patched == 0 {
fmt.Println("No ambiguous oneOf fallback found - already patched or no longer generated")
return
}

if err := os.WriteFile(targetFile, newContent, 0644); err != nil {
fmt.Fprintf(os.Stderr, "Error writing patched file: %v\n", err)
os.Exit(1)
}
fmt.Printf("✅ Patched %d ambiguous oneOf fallback(s) to fail closed\n", patched)
}
1 change: 1 addition & 0 deletions kinde/management_api/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ package management_api

//go:generate go run github.com/ogen-go/ogen/cmd/ogen --target . -package management_api --clean https://api-spec.kinde.com/kinde-management-api-spec.yaml
//go:generate go run fix_optstring.go
//go:generate go run fix_oneof.go
Loading
Loading