Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package amazon-cloudwatch-agent for
openSUSE:Factory checked in at 2026-03-25 21:21:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/amazon-cloudwatch-agent (Old)
and /work/SRC/openSUSE:Factory/.amazon-cloudwatch-agent.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "amazon-cloudwatch-agent"
Wed Mar 25 21:21:50 2026 rev:2 rq:1342498 version:1.300064.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/amazon-cloudwatch-agent/amazon-cloudwatch-agent.changes
2026-03-02 17:41:58.499447694 +0100
+++
/work/SRC/openSUSE:Factory/.amazon-cloudwatch-agent.new.8177/amazon-cloudwatch-agent.changes
2026-03-27 06:35:49.676151096 +0100
@@ -1,0 +2,6 @@
+Wed Mar 25 08:25:36 UTC 2026 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Add CVE-2026-33186.patch to fix authorization bypass in grpc-go due to
improper
+ validation of the HTTP/2 :path pseudo-header (bsc#1260314, CVE-2026-33186)
+
+-------------------------------------------------------------------
New:
----
CVE-2026-33186.patch
----------(New B)----------
New:
- Add CVE-2026-33186.patch to fix authorization bypass in grpc-go due to
improper
validation of the HTTP/2 :path pseudo-header (bsc#1260314, CVE-2026-33186)
----------(New E)----------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ amazon-cloudwatch-agent.spec ++++++
--- /var/tmp/diff_new_pack.QVuqPc/_old 2026-03-27 06:35:50.512185554 +0100
+++ /var/tmp/diff_new_pack.QVuqPc/_new 2026-03-27 06:35:50.516185719 +0100
@@ -31,6 +31,8 @@
URL: https://github.com/aws/amazon-cloudwatch-agent
Source0: %{repo}-%{version}.tar.gz
Source1: vendor.tar.gz
+# PATCH-FIX-UPSTREAM - grpc: enforce strict path checking for incoming
requests on the server
+Patch0: CVE-2026-33186.patch
BuildRequires: go >= 1.23.6
BuildRequires: golang-packaging
ExcludeArch: %{arm} %{ix86}
@@ -45,6 +47,9 @@
%prep
%setup -q -n %{repo}-%{version}
%setup -q -D -T -a 1 -n %{repo}-%{version}
+pushd vendor/google.golang.org/grpc
+%patch -P0 -p1
+popd
%build
%goprep %{import_path}
++++++ CVE-2026-33186.patch ++++++
>From 89d1a70cc1b25ec820344760ab9aed24ef81cb26 Mon Sep 17 00:00:00 2001
From: Easwar Swaminathan <[email protected]>
Date: Tue, 17 Mar 2026 16:35:32 -0700
Subject: [PATCH] grpc: enforce strict path checking for incoming requests on
the server (#8985)
RELEASE NOTES:
* server: fix an authorization bypass where malformed :path headers
(missing the leading slash) could bypass path-based restricted "deny"
rules in interceptors like `grpc/authz`. Any request with a
non-canonical path is now immediately rejected with an `Unimplemented`
error.
---
internal/envconfig/envconfig.go | 16 +++
server.go | 57 +++++++---
test/malformed_method_test.go | 177 ++++++++++++++++++++++++++++++++
3 files changed, 234 insertions(+), 16 deletions(-)
create mode 100644 test/malformed_method_test.go
diff --git a/internal/envconfig/envconfig.go b/internal/envconfig/envconfig.go
index cc5713fd..ea7cdd97 100644
--- a/internal/envconfig/envconfig.go
+++ b/internal/envconfig/envconfig.go
@@ -69,6 +69,22 @@ var (
// to gRFC A76. It can be enabled by setting the environment variable
// "GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY" to "true".
RingHashSetRequestHashKey =
boolFromEnv("GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY", false)
+
+ // DisableStrictPathChecking indicates whether strict path checking is
+ // disabled. This feature can be disabled by setting the environment
+ // variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to "true".
+ //
+ // When strict path checking is enabled, gRPC will reject requests with
+ // paths that do not conform to the gRPC over HTTP/2 specification
found at
+ // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md.
+ //
+ // When disabled, gRPC will allow paths that do not contain a leading
slash.
+ // Enabling strict path checking is recommended for security reasons,
as it
+ // prevents potential path traversal vulnerabilities.
+ //
+ // A future release will remove this environment variable, enabling
strict
+ // path checking behavior unconditionally.
+ DisableStrictPathChecking =
boolFromEnv("GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING", false)
)
func boolFromEnv(envVar string, def bool) bool {
diff --git a/server.go b/server.go
index 976e70ae..e62d3532 100644
--- a/server.go
+++ b/server.go
@@ -42,6 +42,7 @@ import (
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/binarylog"
"google.golang.org/grpc/internal/channelz"
+ "google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/internal/grpcutil"
istats "google.golang.org/grpc/internal/stats"
@@ -148,6 +149,8 @@ type Server struct {
serverWorkerChannel chan func()
serverWorkerChannelClose func()
+
+ strictPathCheckingLogEmitted atomic.Bool
}
type serverOptions struct {
@@ -1745,6 +1748,24 @@ func (s *Server) processStreamingRPC(ctx
context.Context, stream *transport.Serv
return ss.s.WriteStatus(statusOK)
}
+func (s *Server) handleMalformedMethodName(stream *transport.ServerStream, ti
*traceInfo) {
+ if ti != nil {
+ ti.tr.LazyLog(&fmtStringer{"Malformed method name %q",
[]any{stream.Method()}}, true)
+ ti.tr.SetError()
+ }
+ errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
+ if err := stream.WriteStatus(status.New(codes.Unimplemented, errDesc));
err != nil {
+ if ti != nil {
+ ti.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true)
+ ti.tr.SetError()
+ }
+ channelz.Warningf(logger, s.channelz, "grpc:
Server.handleStream failed to write status: %v", err)
+ }
+ if ti != nil {
+ ti.tr.Finish()
+ }
+}
+
func (s *Server) handleStream(t transport.ServerTransport, stream
*transport.ServerStream) {
ctx := stream.Context()
ctx = contextWithServer(ctx, s)
@@ -1765,26 +1786,30 @@ func (s *Server) handleStream(t
transport.ServerTransport, stream *transport.Ser
}
sm := stream.Method()
- if sm != "" && sm[0] == '/' {
+ if sm == "" {
+ s.handleMalformedMethodName(stream, ti)
+ return
+ }
+ if sm[0] != '/' {
+ // TODO(easwars): Add a link to the CVE in the below log
messages once
+ // published.
+ if envconfig.DisableStrictPathChecking {
+ if old := s.strictPathCheckingLogEmitted.Swap(true);
!old {
+ channelz.Warningf(logger, s.channelz, "grpc:
Server.handleStream received malformed method name %q. Allowing it because the
environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING is set
to true, but this option will be removed in a future release.", sm)
+ }
+ } else {
+ if old := s.strictPathCheckingLogEmitted.Swap(true);
!old {
+ channelz.Warningf(logger, s.channelz, "grpc:
Server.handleStream rejected malformed method name %q. To temporarily allow
such requests, set the environment variable
GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to true. Note that this is
not recommended as it may allow requests to bypass security policies.", sm)
+ }
+ s.handleMalformedMethodName(stream, ti)
+ return
+ }
+ } else {
sm = sm[1:]
}
pos := strings.LastIndex(sm, "/")
if pos == -1 {
- if ti != nil {
- ti.tr.LazyLog(&fmtStringer{"Malformed method name %q",
[]any{sm}}, true)
- ti.tr.SetError()
- }
- errDesc := fmt.Sprintf("malformed method name: %q",
stream.Method())
- if err := stream.WriteStatus(status.New(codes.Unimplemented,
errDesc)); err != nil {
- if ti != nil {
- ti.tr.LazyLog(&fmtStringer{"%v", []any{err}},
true)
- ti.tr.SetError()
- }
- channelz.Warningf(logger, s.channelz, "grpc:
Server.handleStream failed to write status: %v", err)
- }
- if ti != nil {
- ti.tr.Finish()
- }
+ s.handleMalformedMethodName(stream, ti)
return
}
service := sm[:pos]
diff --git a/test/malformed_method_test.go b/test/malformed_method_test.go
new file mode 100644
index 00000000..00e391a2
--- /dev/null
+++ b/test/malformed_method_test.go
@@ -0,0 +1,177 @@
+/*
+ *
+ * Copyright 2026 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package test
+
+import (
+ "bytes"
+ "context"
+ "net"
+ "testing"
+
+ "golang.org/x/net/http2"
+ "golang.org/x/net/http2/hpack"
+ "google.golang.org/grpc/internal/envconfig"
+ "google.golang.org/grpc/internal/stubserver"
+ "google.golang.org/grpc/internal/testutils"
+
+ testpb "google.golang.org/grpc/interop/grpc_testing"
+)
+
+// TestMalformedMethodPath tests that the server responds with Unimplemented
+// when the method path is malformed. This verifies that the server does not
+// route requests with a malformed method path to the application handler.
+func (s) TestMalformedMethodPath(t *testing.T) {
+ tests := []struct {
+ name string
+ path string
+ envVar bool
+ wantStatus string // string representation of codes.Code
+ }{
+ {
+ name:
"missing_leading_slash_disableStrictPathChecking_false",
+ path: "grpc.testing.TestService/UnaryCall",
+ wantStatus: "12", // Unimplemented
+ },
+ {
+ name:
"empty_path_disableStrictPathChecking_false",
+ path: "",
+ wantStatus: "12", // Unimplemented
+ },
+ {
+ name:
"just_slash_disableStrictPathChecking_false",
+ path: "/",
+ wantStatus: "12", // Unimplemented
+ },
+ {
+ name:
"missing_leading_slash_disableStrictPathChecking_true",
+ path: "grpc.testing.TestService/UnaryCall",
+ envVar: true,
+ wantStatus: "0", // OK
+ },
+ {
+ name: "empty_path_disableStrictPathChecking_true",
+ path: "",
+ envVar: true,
+ wantStatus: "12", // Unimplemented
+ },
+ {
+ name: "just_slash_disableStrictPathChecking_true",
+ path: "/",
+ envVar: true,
+ wantStatus: "12", // Unimplemented
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx, cancel :=
context.WithTimeout(context.Background(), defaultTestTimeout)
+ defer cancel()
+
+ testutils.SetEnvConfig(t,
&envconfig.DisableStrictPathChecking, tc.envVar)
+
+ ss := &stubserver.StubServer{
+ UnaryCallF: func(context.Context,
*testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
+ return &testpb.SimpleResponse{Payload:
&testpb.Payload{Body: []byte("pwned")}}, nil
+ },
+ }
+ if err := ss.Start(nil); err != nil {
+ t.Fatalf("Error starting endpoint server: %v",
err)
+ }
+ defer ss.Stop()
+
+ // Open a raw TCP connection to the server and speak
HTTP/2 directly.
+ tcpConn, err := net.Dial("tcp", ss.Address)
+ if err != nil {
+ t.Fatalf("Failed to dial tcp: %v", err)
+ }
+ defer tcpConn.Close()
+
+ // Write the HTTP/2 connection preface and the initial
settings frame.
+ if _, err := tcpConn.Write([]byte("PRI *
HTTP/2.0\r\n\r\nSM\r\n\r\n")); err != nil {
+ t.Fatalf("Failed to write preface: %v", err)
+ }
+ framer := http2.NewFramer(tcpConn, tcpConn)
+ if err := framer.WriteSettings(); err != nil {
+ t.Fatalf("Failed to write settings: %v", err)
+ }
+
+ // Encode and write the HEADERS frame.
+ var headerBuf bytes.Buffer
+ enc := hpack.NewEncoder(&headerBuf)
+ writeHeader := func(name, value string) {
+ enc.WriteField(hpack.HeaderField{Name: name,
Value: value})
+ }
+ writeHeader(":method", "POST")
+ writeHeader(":scheme", "http")
+ writeHeader(":authority", ss.Address)
+ writeHeader(":path", tc.path)
+ writeHeader("content-type", "application/grpc")
+ writeHeader("te", "trailers")
+ if err := framer.WriteHeaders(http2.HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: headerBuf.Bytes(),
+ EndStream: false,
+ EndHeaders: true,
+ }); err != nil {
+ t.Fatalf("Failed to write headers: %v", err)
+ }
+
+ // Send a small gRPC-encoded data frame (0 length).
+ if err := framer.WriteData(1, true, []byte{0, 0, 0, 0,
0}); err != nil {
+ t.Fatalf("Failed to write data: %v", err)
+ }
+
+ // Read responses and look for grpc-status.
+ gotStatus := ""
+ dec := hpack.NewDecoder(4096, func(f hpack.HeaderField)
{
+ if f.Name == "grpc-status" {
+ gotStatus = f.Value
+ }
+ })
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ for {
+ frame, err := framer.ReadFrame()
+ if err != nil {
+ return
+ }
+ if headers, ok :=
frame.(*http2.HeadersFrame); ok {
+ if _, err :=
dec.Write(headers.HeaderBlockFragment()); err != nil {
+ return
+ }
+ if headers.StreamEnded() {
+ return
+ }
+ }
+ }
+ }()
+
+ select {
+ case <-done:
+ case <-ctx.Done():
+ t.Fatalf("Timed out waiting for response")
+ }
+
+ if gotStatus != tc.wantStatus {
+ t.Errorf("Got grpc-status %v, want %v",
gotStatus, tc.wantStatus)
+ }
+ })
+ }
+}
--
2.53.0