From 71be1f0ce347169027f558e210f5b244440ce18c Mon Sep 17 00:00:00 2001 From: Jahvon Dockery Date: Sun, 26 Jul 2026 16:33:29 -0400 Subject: [PATCH 1/3] chore(deps): bump vault to v0.3.0 and adapt to its breaking changes v0.3.0 is a security release: secrets no longer travel through a shell command, empty vault files are no longer silently reinitialized and overwritten, writes are serialized across processes, and closed vaults report an error rather than panicking. The API surface it breaks here is small: - demoVaultProvider implements the new Metadata() (Metadata, error). - vaultFromName handles the new error. It reports a metadata failure as a field rather than failing the whole view: for an external vault that failure means the backend CLI is missing or the session has expired, which is precisely when someone wants to inspect the vault's configuration. It also called Metadata() twice, which for an external vault meant running the backend command twice per view. Also tightened ValidateIdentifier to reject a leading dash or underscore. This is not cosmetic: flow's rule was a *superset* of the library's new ValidateVaultID, so flow would accept a name like "-myvault" or "_myvault" that the library then refuses -- failing at creation with a less specific message, and leaving any vault already created under such a name unreachable. A new test asserts the subset property directly, since the two rules live in separate repositories and will otherwise drift apart silently. Verified: 32 packages pass under -race, lint clean. The 15 `go vet` warnings about json tags on unexported fields in types/*.gen.go are pre-existing and identical on main. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01HtuDuGkSqTfAXSepf8b51p --- go.mod | 20 +++++++++++--------- go.sum | 24 ++++++++++++++++++++++++ internal/io/vault/view.go | 12 ++++++++++-- internal/vault/demo.go | 4 ++-- internal/vault/secret.go | 10 ++++++++-- internal/vault/secret_test.go | 33 ++++++++++++++++++++++++++++++++- 6 files changed, 87 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 80431631..1335aa9a 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/charmbracelet/colorprofile v0.4.3 github.com/charmbracelet/x/exp/teatest/v2 v2.0.0-20260406091427-a791e22d5143 github.com/flowexec/tuikit v0.4.1 - github.com/flowexec/vault v0.2.1 + github.com/flowexec/vault v0.3.0 github.com/gen2brain/beeep v0.11.2 github.com/google/uuid v1.6.0 github.com/jahvon/expression v0.1.4 @@ -37,7 +37,8 @@ require ( charm.land/glamour/v2 v2.0.0 // indirect charm.land/huh/v2 v2.0.3 // indirect charm.land/log/v2 v2.0.0 // indirect - filippo.io/age v1.2.1 // indirect + filippo.io/age v1.3.1 // indirect + filippo.io/hpke v0.4.0 // indirect git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect github.com/alecthomas/chroma/v2 v2.20.0 // indirect github.com/aymanbagabas/go-udiff v0.4.1 // indirect @@ -55,16 +56,17 @@ require ( github.com/clipperhouse/displaywidth v0.11.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect - github.com/danieljoos/wincred v1.2.2 // indirect + github.com/danieljoos/wincred v1.2.3 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/esiqveland/notify v0.13.3 // indirect - github.com/expr-lang/expr v1.17.7 // indirect + github.com/expr-lang/expr v1.17.8 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/godbus/dbus/v5 v5.2.2 // indirect + github.com/gofrs/flock v0.13.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/jsonschema-go v0.4.2 // indirect github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect @@ -90,12 +92,12 @@ require ( github.com/yosida95/uritemplate/v3 v3.0.2 // indirect github.com/yuin/goldmark v1.7.13 // indirect github.com/yuin/goldmark-emoji v1.0.6 // indirect - github.com/zalando/go-keyring v0.2.6 // indirect + github.com/zalando/go-keyring v0.2.8 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.53.0 // indirect + golang.org/x/crypto v0.54.0 // indirect golang.org/x/mod v0.37.0 // indirect golang.org/x/net v0.56.0 // indirect - golang.org/x/sys v0.46.0 // indirect - golang.org/x/term v0.44.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/term v0.45.0 // indirect golang.org/x/tools v0.47.0 // indirect ) diff --git a/go.sum b/go.sum index f2fc7193..a8934561 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXy al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w= +c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd h1:ZLsPO6WdZ5zatV4UfVpr7oAwLGRZ+sebTUruuM4Ra3M= charm.land/bubbles/v2 v2.1.0 h1:YSnNh5cPYlYjPxRrzs5VEn3vwhtEn3jVGRBT3M7/I0g= charm.land/bubbles/v2 v2.1.0/go.mod h1:l97h4hym2hvWBVfmJDtrEHHCtkIKeTEb3TTJ4ZOB3wY= charm.land/bubbletea/v2 v2.0.6 h1:UHN/91OyuhaOFGSrBXQ/hMZD8IO1Uc4BvHlgHXL2WJo= @@ -16,6 +17,10 @@ charm.land/log/v2 v2.0.0 h1:SY3Cey7ipx86/MBXQHwsguOT6X1exT94mmJRdzTNs+s= charm.land/log/v2 v2.0.0/go.mod h1:c3cZSRqm20qUVVAR1WmS/7ab8bgha3C6G7DjPcaVZz0= filippo.io/age v1.2.1 h1:X0TZjehAZylOIj4DubWYU1vWQxv9bJpo+Uu2/LGhi1o= filippo.io/age v1.2.1/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004= +filippo.io/age v1.3.1 h1:hbzdQOJkuaMEpRCLSN1/C5DX74RPcNCk6oqhKMXmZi0= +filippo.io/age v1.3.1/go.mod h1:EZorDTYUxt836i3zdori5IJX/v2Lj6kWFU0cfh6C0D4= +filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A= +filippo.io/hpke v0.4.0/go.mod h1:EmAN849/P3qdeK+PCMkDpDm83vRHM5cDipBJ8xbQLVY= git.sr.ht/~jackmordaunt/go-toast v1.1.2 h1:/yrfI55LRt1M7H1vkaw+NaH1+L1CDxrqDltwm5euVuE= git.sr.ht/~jackmordaunt/go-toast v1.1.2/go.mod h1:jA4OqHKTQ4AFBdwrSnwnskUIIS3HYzlJSgdzCKqfavo= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= @@ -75,6 +80,8 @@ github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= +github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= +github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -86,10 +93,14 @@ github.com/esiqveland/notify v0.13.3 h1:QCMw6o1n+6rl+oLUfg8P1IIDSFsDEb2WlXvVvIJb github.com/esiqveland/notify v0.13.3/go.mod h1:hesw/IRYTO0x99u1JPweAl4+5mwXJibQVUcP0Iu5ORE= github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8= github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/expr-lang/expr v1.17.8 h1:W1loDTT+0PQf5YteHSTpju2qfUfNoBt4yw9+wOEU9VM= +github.com/expr-lang/expr v1.17.8/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/flowexec/tuikit v0.4.1 h1:c8qJtB0e8k8VnYnerwai/f4Gg8kkJTLE3fjsMRu619U= github.com/flowexec/tuikit v0.4.1/go.mod h1:NmuWfE/77Nj2qoyiH/4x1b5Ak1JOpL6HqpPEai38UHQ= github.com/flowexec/vault v0.2.1 h1:IYII6iXhhzUc4o0arJVH8281so67L9V8HY8ary/kTps= github.com/flowexec/vault v0.2.1/go.mod h1:6JHONK+fTf8Zn7bOwejzbKTWuIh1BYHxgAwBc/XPXeY= +github.com/flowexec/vault v0.3.0 h1:wDs+fm2dXSZbT4zUirSSOc4rSiAqz1OHZgWU9HirS3Y= +github.com/flowexec/vault v0.3.0/go.mod h1:sjkvXBu/5+lJYEh2Gi+kRlVUGGrLItvITwpG6b0Q32o= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/gen2brain/beeep v0.11.2 h1:+KfiKQBbQCuhfJFPANZuJ+oxsSKAYNe88hIpJuyKWDA= @@ -114,6 +125,10 @@ github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= +github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= +github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= +github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= @@ -208,6 +223,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -228,6 +244,8 @@ github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9 github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= +github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= +github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= @@ -236,6 +254,8 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= @@ -247,8 +267,12 @@ golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= diff --git a/internal/io/vault/view.go b/internal/io/vault/view.go index f5aa8758..c1b58f94 100644 --- a/internal/io/vault/view.go +++ b/internal/io/vault/view.go @@ -223,8 +223,16 @@ func vaultFromName(vaultName string) (*vaultEntity, error) { return nil, err } data := make(map[string]interface{}) - data["created"] = vlt.Metadata().Created - data["lastModified"] = vlt.Metadata().LastModified + // A metadata failure must not make the vault unviewable. For an external + // vault it means the backend CLI is missing or the session has expired, + // which is exactly when a user wants to look at the vault's configuration. + // Report it as a field instead of failing the whole view. + if metadata, mdErr := vlt.Metadata(); mdErr != nil { + data["metadataError"] = mdErr.Error() + } else { + data["created"] = metadata.Created + data["lastModified"] = metadata.LastModified + } v := &vaultEntity{ Name: vlt.ID(), diff --git a/internal/vault/demo.go b/internal/vault/demo.go index f393e904..d4a2e35e 100644 --- a/internal/vault/demo.go +++ b/internal/vault/demo.go @@ -49,11 +49,11 @@ func (d demoVaultProvider) ID() string { return "demo" } -func (d demoVaultProvider) Metadata() vault.Metadata { +func (d demoVaultProvider) Metadata() (vault.Metadata, error) { return vault.Metadata{ Created: time.Now().Local().Add(-24 * time.Hour), LastModified: time.Now().Local(), - } + }, nil } func (d demoVaultProvider) Close() error { diff --git a/internal/vault/secret.go b/internal/vault/secret.go index ab09077a..3d2205ec 100644 --- a/internal/vault/secret.go +++ b/internal/vault/secret.go @@ -324,10 +324,16 @@ func ValidateIdentifier(reference string) error { if reference == "" { return errors.New("reference cannot be empty") } - re := regexp.MustCompile(`^[a-zA-Z0-9-_]+$`) + // Must stay a strict subset of the vault library's own ValidateVaultID, which + // requires an alphanumeric first character. Allowing a leading dash or + // underscore here would let flow accept a name the library then rejects -- + // failing at creation with a less specific message, and making any existing + // vault so named unreachable after an upgrade. + re := regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-_]*$`) if !re.MatchString(reference) { return fmt.Errorf( - "reference (%s) must only contain alphanumeric characters, dashes and/or underscores", + "reference (%s) must start with a letter or digit and contain only "+ + "alphanumeric characters, dashes and/or underscores", reference, ) } diff --git a/internal/vault/secret_test.go b/internal/vault/secret_test.go index 4e6263ae..edd67cb3 100644 --- a/internal/vault/secret_test.go +++ b/internal/vault/secret_test.go @@ -4,6 +4,8 @@ import ( "strings" "testing" + extVault "github.com/flowexec/vault" + "github.com/flowexec/flow/v2/internal/vault" ) @@ -97,10 +99,39 @@ func TestValidateIdentifier(t *testing.T) { } } - invalid := []string{"", "../etc", "a/b", "a.b", "a b", "..", "vault/../x", "name.json"} + // A leading dash or underscore is rejected so this stays a strict subset of + // the vault library's ValidateVaultID; otherwise flow would accept a name + // the library refuses. + invalid := []string{ + "", "../etc", "a/b", "a.b", "a b", "..", "vault/../x", "name.json", + "-myvault", "_myvault", "-", "_", + } for _, name := range invalid { if err := vault.ValidateIdentifier(name); err == nil { t.Errorf("ValidateIdentifier(%q) = nil, want error", name) } } } + +// Any vault name flow accepts must also be acceptable to the vault library, +// which derives filesystem paths and keyring entry names from it. If flow were +// the laxer of the two, a name would pass flow's check and then be refused +// downstream -- and any vault already created under such a name would become +// unreachable. Asserted rather than assumed, because the two rules live in +// different repositories and will drift. +func TestValidateIdentifierIsStricterThanTheVaultLibrary(t *testing.T) { + candidates := []string{ + "myvault", "my_vault", "my-vault", "abc123", "A1_b-2", "v1", + "-myvault", "_myvault", "-", "_", "a.b", "..", "a/b", "a b", "", + "name.json", "../etc", "vault/../x", + } + + for _, name := range candidates { + if vault.ValidateIdentifier(name) != nil { + continue // flow rejects it; the library's opinion does not matter + } + if err := extVault.ValidateVaultID(name); err != nil { + t.Errorf("flow accepts vault name %q but the vault library rejects it: %v", name, err) + } + } +} From ccd41bb35d3705c1d292641181d1140e58a64e07 Mon Sep 17 00:00:00 2001 From: Jahvon Dockery Date: Sun, 26 Jul 2026 16:37:56 -0400 Subject: [PATCH 2/3] Update validation comment in secret.go Refactor validation comment for clarity and accuracy. --- internal/vault/secret.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/vault/secret.go b/internal/vault/secret.go index 3d2205ec..32bac388 100644 --- a/internal/vault/secret.go +++ b/internal/vault/secret.go @@ -325,10 +325,7 @@ func ValidateIdentifier(reference string) error { return errors.New("reference cannot be empty") } // Must stay a strict subset of the vault library's own ValidateVaultID, which - // requires an alphanumeric first character. Allowing a leading dash or - // underscore here would let flow accept a name the library then rejects -- - // failing at creation with a less specific message, and making any existing - // vault so named unreachable after an upgrade. + // requires an alphanumeric first character. re := regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-_]*$`) if !re.MatchString(reference) { return fmt.Errorf( From 343e844ecea98ade00e3cabea95488663b3af06f Mon Sep 17 00:00:00 2001 From: Jahvon Dockery Date: Sun, 26 Jul 2026 16:39:17 -0400 Subject: [PATCH 3/3] Remove comments on identifier validation rules Removed comments explaining the rejection of leading dash or underscore in identifiers. --- internal/vault/secret_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/vault/secret_test.go b/internal/vault/secret_test.go index edd67cb3..8eb2b781 100644 --- a/internal/vault/secret_test.go +++ b/internal/vault/secret_test.go @@ -99,9 +99,6 @@ func TestValidateIdentifier(t *testing.T) { } } - // A leading dash or underscore is rejected so this stays a strict subset of - // the vault library's ValidateVaultID; otherwise flow would accept a name - // the library refuses. invalid := []string{ "", "../etc", "a/b", "a.b", "a b", "..", "vault/../x", "name.json", "-myvault", "_myvault", "-", "_",