vendor: update buildkit to v0.32.0-rc1
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
+2
-2
@@ -4,12 +4,12 @@
|
||||
|
||||
| Total Contributors | Total Contributions |
|
||||
| --- | --- |
|
||||
| 31 | 302 |
|
||||
| 31 | 305 |
|
||||
|
||||
| Username | All Time Contribution Count | All Commits |
|
||||
| --- | --- | --- |
|
||||
| @casualjim | 169 | <https://github.com/go-openapi/validate/commits?author=casualjim> |
|
||||
| @fredbi | 65 | <https://github.com/go-openapi/validate/commits?author=fredbi> |
|
||||
| @fredbi | 68 | <https://github.com/go-openapi/validate/commits?author=fredbi> |
|
||||
| @sttts | 11 | <https://github.com/go-openapi/validate/commits?author=sttts> |
|
||||
| @youyuanwu | 9 | <https://github.com/go-openapi/validate/commits?author=youyuanwu> |
|
||||
| @keramix | 8 | <https://github.com/go-openapi/validate/commits?author=keramix> |
|
||||
|
||||
+7
-9
@@ -18,12 +18,9 @@ A validator for OpenAPI v2 specifications and JSON schema draft 4.
|
||||
|
||||
* **2025-12-19** : new community chat on discord
|
||||
* a new discord community channel is available to be notified of changes and support users
|
||||
* our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31**
|
||||
|
||||
You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url]
|
||||
|
||||
Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url]
|
||||
|
||||
## Status
|
||||
|
||||
API is stable.
|
||||
@@ -75,9 +72,9 @@ This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE).
|
||||
## Other documentation
|
||||
|
||||
* [All-time contributors](./CONTRIBUTORS.md)
|
||||
* [Contributing guidelines](.github/CONTRIBUTING.md)
|
||||
* [Maintainers documentation](docs/MAINTAINERS.md)
|
||||
* [Code style](docs/STYLE.md)
|
||||
* [Contributing guidelines][contributing-doc-site]
|
||||
* [Maintainers documentation][maintainers-doc-site]
|
||||
* [Code style][style-doc-site]
|
||||
|
||||
## Cutting a new release
|
||||
|
||||
@@ -108,9 +105,6 @@ Maintainers can cut a new release by either:
|
||||
<!-- Badges: documentation & support -->
|
||||
[godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/validate
|
||||
[godoc-url]: http://pkg.go.dev/github.com/go-openapi/validate
|
||||
[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png
|
||||
[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM
|
||||
[slack-url]: https://goswagger.slack.com/archives/C04R30YMU
|
||||
[discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue
|
||||
[discord-url]: https://discord.gg/FfnFYaC3k5
|
||||
|
||||
@@ -122,3 +116,7 @@ Maintainers can cut a new release by either:
|
||||
[goversion-url]: https://github.com/go-openapi/validate/blob/master/go.mod
|
||||
[top-badge]: https://img.shields.io/github/languages/top/go-openapi/validate
|
||||
[commits-badge]: https://img.shields.io/github/commits-since/go-openapi/validate/latest
|
||||
<!-- Organization docs -->
|
||||
[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html
|
||||
[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html
|
||||
[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html
|
||||
|
||||
+7
-2
@@ -146,7 +146,8 @@ func (s *SpecValidator) Validate(data any) (*Result, *Result) {
|
||||
errs.Merge(s.validateNonEmptyPathParamNames())
|
||||
|
||||
// errs.Merge(s.validateRefNoSibling()) // warning only
|
||||
errs.Merge(s.validateReferenced()) // warning only
|
||||
errs.Merge(s.validateReferenced()) // warning only
|
||||
errs.Merge(s.validateDubiousRefs()) // warning only
|
||||
|
||||
return errs, warnings
|
||||
}
|
||||
@@ -553,8 +554,12 @@ DEFINITIONS:
|
||||
if schema.Required != nil { // Safeguard
|
||||
for _, pn := range schema.Required {
|
||||
red := s.validateRequiredProperties(pn, d, &schema) //#nosec
|
||||
// NOTE: capture validity before merging: Merge may redeem `red` to the
|
||||
// pool (wantsRedeemOnMerge), after which reading it races with a concurrent
|
||||
// BorrowResult().cleared() in another goroutine sharing the global pool.
|
||||
isValid := red.IsValid()
|
||||
res.Merge(red)
|
||||
if !red.IsValid() && !s.Options.ContinueOnErrors {
|
||||
if !isValid && !s.Options.ContinueOnErrors {
|
||||
break DEFINITIONS // there is an error, let's stop that bleeding
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -177,6 +177,18 @@ const (
|
||||
// UnusedResponseWarning ...
|
||||
UnusedResponseWarning = "response %q is not used anywhere"
|
||||
|
||||
// DubiousAbsoluteRefWarning flags a $ref pointing to an absolute local file location that escapes the
|
||||
// spec's base path. Absolute local references are legitimate when they stay beneath the base path
|
||||
// (flattening/expansion introduces such anchors for cyclical $refs), but an absolute reference that
|
||||
// escapes the base path - or a file:// reference in a spec with no known base - may indicate an
|
||||
// unsafe or adversarial spec.
|
||||
DubiousAbsoluteRefWarning = "$ref %q points to an absolute or local file location that escapes the spec's base path: this may be unsafe with adversarial specs"
|
||||
|
||||
// DubiousMultipleHostsWarning flags a spec whose remote $refs resolve to several distinct hosts.
|
||||
// A single consistent remote host is common and legitimate; references spread across multiple hosts
|
||||
// may indicate an unsafe or adversarial spec.
|
||||
DubiousMultipleHostsWarning = "$ref values point to %d distinct remote hosts (%s): a spec referencing multiple hosts may be unsafe"
|
||||
|
||||
InvalidObject = "expected an object in %q.%s"
|
||||
)
|
||||
|
||||
@@ -404,3 +416,11 @@ func someParametersBrokenMsg(path, method, operationID string) errors.Error {
|
||||
func refShouldNotHaveSiblingsMsg(path, operationID string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, RefShouldNotHaveSiblingsWarning, operationID, path)
|
||||
}
|
||||
|
||||
func dubiousAbsoluteRefMsg(ref string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, DubiousAbsoluteRefWarning, ref)
|
||||
}
|
||||
|
||||
func dubiousMultipleHostsMsg(count int, hosts string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, DubiousMultipleHostsWarning, count, hosts)
|
||||
}
|
||||
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
// minDistinctHostsToWarn is the number of distinct remote hosts among $refs at or above which
|
||||
// Rule 2 emits a host-spread warning. A single consistent remote host is legitimate.
|
||||
const minDistinctHostsToWarn = 2
|
||||
|
||||
// validateDubiousRefs emits warnings (never errors) when $ref locations match patterns
|
||||
// that may indicate an unsafe or adversarial spec. It inspects refs as authored, on the
|
||||
// UNEXPANDED spec, so it must run before expansion flattens them away.
|
||||
//
|
||||
// Two rules are applied over s.analyzer.AllRefs():
|
||||
//
|
||||
// - Rule 1 (absolute local escape): a $ref pointing to an absolute local file location
|
||||
// (file:// scheme, a Unix absolute path, or a Windows drive path such as C:\) is dubious
|
||||
// UNLESS it stays beneath the spec's base path. Absolute refs beneath the base are
|
||||
// legitimate: flattening/expansion in go-openapi/spec and analysis introduces absolute
|
||||
// anchors to resolve cyclical $refs. Relative and fragment-only refs are always exempt.
|
||||
//
|
||||
// - Rule 2 (host spread): when remote (http/https, or protocol-relative) refs resolve to
|
||||
// two or more distinct hosts, a single aggregate warning lists them. A single consistent
|
||||
// remote host is common and legitimate, so it is not flagged.
|
||||
//
|
||||
// All findings are warnings: they do not affect validity (see Result.IsValid).
|
||||
func (s *SpecValidator) validateDubiousRefs() *Result {
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
baseDir, hasBase := s.localBaseDir()
|
||||
|
||||
remoteHosts := make(map[string]struct{})
|
||||
for _, r := range s.analyzer.AllRefs() {
|
||||
u := r.GetURL()
|
||||
if u == nil { // Safeguard: a valid spec always yields parseable refs
|
||||
continue
|
||||
}
|
||||
|
||||
// Rule 1: absolute local reference escaping the base path.
|
||||
if refPath, isLocalAbs := absoluteLocalRefPath(r, u); isLocalAbs {
|
||||
if !hasBase || !isBeneathBase(refPath, baseDir) {
|
||||
res.AddWarnings(dubiousAbsoluteRefMsg(r.String()))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Rule 2: gather remote hosts (http/https and protocol-relative //host/...).
|
||||
if host := remoteRefHost(u); host != "" {
|
||||
remoteHosts[host] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(remoteHosts) >= minDistinctHostsToWarn {
|
||||
hosts := make([]string, 0, len(remoteHosts))
|
||||
for h := range remoteHosts {
|
||||
hosts = append(hosts, h)
|
||||
}
|
||||
sort.Strings(hosts)
|
||||
res.AddWarnings(dubiousMultipleHostsMsg(len(hosts), strings.Join(hosts, ", ")))
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// absoluteLocalRefPath reports whether r is an absolute LOCAL file reference and, if so,
|
||||
// returns the cleaned path it points to (without scheme/fragment, drive letter lower-cased).
|
||||
//
|
||||
// Classification order matters (see the empirical jsonreference flag behavior):
|
||||
// - file:// scheme is local, including UNC file://host/share (inherently dubious).
|
||||
// - a non-empty Host with no file scheme means remote (http/https or protocol-relative
|
||||
// //host/path) - NOT local; handled by Rule 2. This must be checked before the Unix
|
||||
// branch, because protocol-relative refs also set HasFullFilePath.
|
||||
// - len(u.Scheme) == 1 is a Windows drive path (C:\ or C:/), whose drive+path land in
|
||||
// Scheme/Opaque/Path rather than Path. Checked before the Unix branch because C:/x also
|
||||
// sets HasFullFilePath, and reconstructed from the authored ref string to keep the drive.
|
||||
// - !r.HasFullURL && r.HasFullFilePath is a plain Unix absolute path (/abs/models.json).
|
||||
//
|
||||
// Relative (./x.json) and fragment-only (#/definitions/X) refs return false.
|
||||
func absoluteLocalRefPath(r spec.Ref, u *url.URL) (string, bool) {
|
||||
switch {
|
||||
case r.HasFileScheme:
|
||||
return fileRefPath(u), true
|
||||
case u.Host != "":
|
||||
// Remote (http/https) or protocol-relative //host/path: handled by Rule 2.
|
||||
return "", false
|
||||
case len(u.Scheme) == 1:
|
||||
// Windows drive letter: reconstruct from the authored ref string.
|
||||
return cleanRefPath(r.String()), true
|
||||
case !r.HasFullURL && r.HasFullFilePath:
|
||||
return cleanRefPath(u.Path), true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// remoteRefHost returns the host of a remote reference (http/https), or of a protocol-relative
|
||||
// reference (//host/path). It returns "" for local and fragment-only refs. file:// hosts (UNC)
|
||||
// are deliberately excluded: those are handled as local-absolute refs by Rule 1.
|
||||
func remoteRefHost(u *url.URL) string {
|
||||
switch u.Scheme {
|
||||
case "http", "https":
|
||||
return u.Host
|
||||
case "":
|
||||
// Protocol-relative //host/path: empty scheme but a host is present.
|
||||
return u.Host
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// localBaseDir returns the directory of the spec file, slash-normalized, when the spec was
|
||||
// loaded from a local path. It returns ok=false when the base is unknown (in-memory spec) or
|
||||
// remote (http/https), in which case absolute-local refs cannot be proven beneath a base and
|
||||
// are treated as dubious.
|
||||
func (s *SpecValidator) localBaseDir() (string, bool) {
|
||||
specPath := s.spec.SpecFilePath()
|
||||
if specPath == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Strip a file:// scheme if present; reject remote bases.
|
||||
if u, err := url.Parse(specPath); err == nil && u.Scheme != "" {
|
||||
switch {
|
||||
case u.Scheme == "file":
|
||||
specPath = u.Path
|
||||
case len(u.Scheme) == 1: // Windows drive letter, treat as local
|
||||
// keep specPath as-is (authored path)
|
||||
default: // http, https, ... : no local base
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
return path.Dir(cleanRefPath(specPath)), true
|
||||
}
|
||||
|
||||
// isBeneathBase reports whether the cleaned target path is located within baseDir, i.e. it does
|
||||
// not escape baseDir via "..". Comparison is purely lexical on cleaned paths, which is sufficient
|
||||
// (and cross-platform safe) for a non-fatal warning. Both sides are expected to already be
|
||||
// cleanRefPath-normalized (slashes, drive-letter case).
|
||||
func isBeneathBase(target, baseDir string) bool {
|
||||
if baseDir == "" {
|
||||
return false
|
||||
}
|
||||
if target == baseDir {
|
||||
return true
|
||||
}
|
||||
if !strings.HasSuffix(baseDir, "/") {
|
||||
baseDir += "/"
|
||||
}
|
||||
return strings.HasPrefix(target, baseDir)
|
||||
}
|
||||
|
||||
// fileRefPath extracts the local path a file:// reference points to, accounting for the way
|
||||
// Windows file URLs parse:
|
||||
// - file:///abs/x -> /abs/x (empty host)
|
||||
// - file:///C:/dir/x -> /c:/dir/x (empty host; drive sits in the path)
|
||||
// - file://D:/a/x -> d:/a/x (drive letter lands in Host, rejoin it)
|
||||
// - file://host/share -> /host/share/x (real UNC host kept visible so it cannot match a
|
||||
// local base and stays flagged as dubious)
|
||||
func fileRefPath(u *url.URL) string {
|
||||
switch {
|
||||
case u.Host == "":
|
||||
return cleanRefPath(u.Path)
|
||||
case isDriveHost(u.Host):
|
||||
// Windows path authored as file://D:/... : the drive landed in Host (e.g. "d:").
|
||||
return cleanRefPath(u.Host + u.Path)
|
||||
default:
|
||||
// Real remote/UNC host: keep it in the path so it never matches a local base.
|
||||
return cleanRefPath("//" + u.Host + u.Path)
|
||||
}
|
||||
}
|
||||
|
||||
// isDriveHost reports whether a URL host is actually a Windows drive letter (e.g. "d:"), which
|
||||
// happens when a Windows path is authored as a two-slash file URL: file://D:/path.
|
||||
func isDriveHost(host string) bool {
|
||||
h := strings.TrimSuffix(host, ":")
|
||||
if len(h) != 1 {
|
||||
return false
|
||||
}
|
||||
c := h[0]
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||
}
|
||||
|
||||
// cleanRefPath normalizes a ref or base path for lexical comparison: backslashes to forward
|
||||
// slashes, path.Clean, and a lower-cased leading Windows drive letter (matching the behavior of
|
||||
// go-openapi/spec's normalizer). Plain Unix paths are unaffected, preserving case-sensitivity.
|
||||
func cleanRefPath(p string) string {
|
||||
p = path.Clean(strings.ReplaceAll(p, `\`, `/`))
|
||||
switch {
|
||||
case len(p) >= 2 && p[1] == ':':
|
||||
// drive-letter form: C:/dir -> c:/dir
|
||||
p = strings.ToLower(p[:1]) + p[1:]
|
||||
case len(p) >= 3 && p[0] == '/' && p[2] == ':':
|
||||
// slash-prefixed drive form from canonical file:// URLs: /C:/dir -> c:/dir.
|
||||
// The leading slash is dropped so this matches the base path derived from
|
||||
// SpecFilePath (which has no leading slash), and the bare-drive form.
|
||||
p = strings.ToLower(p[1:2]) + p[2:]
|
||||
}
|
||||
return p
|
||||
}
|
||||
Reference in New Issue
Block a user