Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package suseconnect-ng for openSUSE:Factory checked in at 2026-06-13 18:44:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/suseconnect-ng (Old) and /work/SRC/openSUSE:Factory/.suseconnect-ng.new.1981 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "suseconnect-ng" Sat Jun 13 18:44:50 2026 rev:35 rq:1358761 version:1.22.1 Changes: -------- --- /work/SRC/openSUSE:Factory/suseconnect-ng/suseconnect-ng.changes 2026-06-10 15:46:40.556613368 +0200 +++ /work/SRC/openSUSE:Factory/.suseconnect-ng.new.1981/suseconnect-ng.changes 2026-06-13 18:45:01.359660059 +0200 @@ -1,0 +2,8 @@ +Wed Jun 10 18:43:58 UTC 2026 - Earl Sampson <[email protected]> + +- Update version to 1.22.1: + - library: Allow clients to disable the token handling mechanism (jsc#SCC-801) + - Ensure updated system certs are included when creating HTTP client + connections (bsc#1268017, jsc#SCC-804) + +------------------------------------------------------------------- Old: ---- suseconnect-ng-1.22.0.tar.xz New: ---- suseconnect-ng-1.22.1.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ suseconnect-ng.spec ++++++ --- /var/tmp/diff_new_pack.TAp6lC/_old 2026-06-13 18:45:02.799720048 +0200 +++ /var/tmp/diff_new_pack.TAp6lC/_new 2026-06-13 18:45:02.799720048 +0200 @@ -19,7 +19,7 @@ %global project github.com/SUSE/connect-ng Name: suseconnect-ng -Version: 1.22.0 +Version: 1.22.1 Release: 0 URL: https://github.com/SUSE/connect-ng License: LGPL-3.0-or-later @@ -100,6 +100,14 @@ %description -n suseconnect-ruby-bindings This package provides bindings needed to use libsuseconnect from Ruby scripts. +%package -n mcp-server-suseconnect +Summary: MCP server for suseconnect +Group: System/Management +Requires: suseconnect-ng = %version + +%description -n mcp-server-suseconnect +This package provides an MCP server for suseconnect to enable integration into the agentic SLES framework. + %prep %autosetup -p1 -a2 -n%{name}-%{version} @@ -110,6 +118,7 @@ go build -v -ldflags "-s -w" -mod=vendor -buildmode=pie -o bin/zypper-migration %{project}/cmd/zypper-migration go build -v -ldflags "-s -w" -mod=vendor -buildmode=pie -o bin/zypper-search-packages %{project}/cmd/zypper-search-packages go build -v -ldflags "-s -w" -mod=vendor -buildmode=pie -o bin/suse-uptime-tracker %{project}/cmd/suse-uptime-tracker +go build -v -ldflags "-s -w" -mod=vendor -buildmode=pie -o bin/suseconnect-mcp %{project}/cmd/suseconnect-mcp # the library mkdir -p %_builddir/go/lib @@ -118,8 +127,9 @@ %install # Install binary + symlinks install -D -m 0755 bin/suseconnect %{buildroot}/%{_bindir}/suseconnect -install -D -m 0755 bin/suse-uptime-tracker %{buildroot}/%{_bindir}/suse-uptime-tracker ln -s %{_bindir}/suseconnect %{buildroot}/%{_bindir}/SUSEConnect +install -D -m 0755 bin/suse-uptime-tracker %{buildroot}/%{_bindir}/suse-uptime-tracker +install -D -m 0755 bin/suseconnect-mcp %{buildroot}/%{_bindir}/suseconnect-mcp install -d -m 0755 %{buildroot}/%{_sbindir} ln -s %{_bindir}/suseconnect %{buildroot}/%{_sbindir}/SUSEConnect @@ -249,3 +259,6 @@ %doc third_party/yast/README.md %{_libdir}/ruby/vendor_ruby/%rb_ver/suse +%files -n mcp-server-suseconnect +%{_bindir}/suseconnect-mcp + ++++++ _service ++++++ --- /var/tmp/diff_new_pack.TAp6lC/_old 2026-06-13 18:45:02.863722714 +0200 +++ /var/tmp/diff_new_pack.TAp6lC/_new 2026-06-13 18:45:02.871723047 +0200 @@ -2,7 +2,7 @@ <service name="tar_scm" mode="manual"> <param name="scm">git</param> <param name="url">https://github.com/SUSE/connect-ng.git</param> - <param name="revision">v1.22.0</param> + <param name="revision">v1.22.1</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(\d+\.\d+\.\d+)</param> <param name="versionrewrite-replacement">\1</param> ++++++ suseconnect-ng-1.22.0.tar.xz -> suseconnect-ng-1.22.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/README.md new/suseconnect-ng-1.22.1/README.md --- old/suseconnect-ng-1.22.0/README.md 2026-06-08 20:16:18.000000000 +0200 +++ new/suseconnect-ng-1.22.1/README.md 2026-06-11 17:47:03.000000000 +0200 @@ -15,6 +15,60 @@ SUSEConnect-ng communicates with SCC over this [REST API](https://github.com/SUSE/connect/blob/master/doc/SCC-API-%28Implemented%29.md). +### Prerequisites +The following tools should be available and verified as working: + + * A Golang v1.24 or later environment + * needed to run certain go commands locally for testing and + validation. + * `git` + * `make` + * `docker` + +### Quick Start + + * Clone the repo + ``` + git clone https://github.com/SUSE/connect-ng + cd connect-ng + ``` + + * When building for the first time, execute + ``` + make vendor + ``` + + * Run build which will create `out/suseconnect` binary. + ``` + make build + ``` + + * Setup .env file to run tests + * Copy .env-example file to .env file and fill following values + ``` + REGCODE="<regcode>" + EXPIRED_REGCODE="<regcode>" + HA_REGCODE="<ha regcode>" + ``` + + These regcodes can be your personal regcodes attached to your organization. Please refer [Activating-and-Managing-Subscriptions](https://scc.suse.com/docs/userguide#UG-Activating-and-Managing-Subscriptions) + + * To run the unit tests + ``` + make test + ``` + + * To run the feature tests + Run the feature tests within a container by using the registration codes as provided by the .env file. + ``` + make feature-tests + ``` + + * To run the YAST2 registration tests + ``` + make test-yast + ``` + ### Build Requires Go >= 1.24 @@ -24,7 +78,7 @@ This will create a `out/suseconnect` binary. ### Build in container -If you don't have a go compiler installed, you can run the build in a container: +If you don't have a go compiler installed, you can run the build in a container: ``` docker run --rm -v $(pwd):/connect registry.suse.com/bci/golang:1.24-openssl sh -c "git config --global --add safe.directory /connect; cd /connect; make build" ``` @@ -47,11 +101,12 @@ For feature tests you first need to create an `.env` file in the root directory of the project with the following contents: +For that copy .env-example file to .env file and fill following values ``` sh -VALID_REGCODE="<regcode>" +REGCODE="<regcode>" EXPIRED_REGCODE="<regcode>" -NOT_ACTIVATED_REGCODE="<regcode>" +HA_REGCODE="<regcode>" ``` These values can be picked up from Glue's production environment. Once that is diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/build/packaging/suseconnect-ng.changes new/suseconnect-ng-1.22.1/build/packaging/suseconnect-ng.changes --- old/suseconnect-ng-1.22.0/build/packaging/suseconnect-ng.changes 2026-06-08 20:16:18.000000000 +0200 +++ new/suseconnect-ng-1.22.1/build/packaging/suseconnect-ng.changes 2026-06-11 17:47:03.000000000 +0200 @@ -1,4 +1,12 @@ ------------------------------------------------------------------- +Wed Jun 10 18:43:58 UTC 2026 - Earl Sampson <[email protected]> + +- Update version to 1.22.1: + - library: Allow clients to disable the token handling mechanism (jsc#SCC-801) + - Ensure updated system certs are included when creating HTTP client + connections (bsc#1268017, jsc#SCC-804) + +------------------------------------------------------------------- Mon Apr 6 15:40:58 UTC 2026 - Fergal Mc Carthy <[email protected]> - Update version to 1.22: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/build/packaging/suseconnect-ng.spec new/suseconnect-ng-1.22.1/build/packaging/suseconnect-ng.spec --- old/suseconnect-ng-1.22.0/build/packaging/suseconnect-ng.spec 2026-06-08 20:16:18.000000000 +0200 +++ new/suseconnect-ng-1.22.1/build/packaging/suseconnect-ng.spec 2026-06-11 17:47:03.000000000 +0200 @@ -19,7 +19,7 @@ %global project github.com/SUSE/connect-ng Name: suseconnect-ng -Version: 1.22.0 +Version: 1.22.1 Release: 0 URL: https://github.com/SUSE/connect-ng License: LGPL-3.0-or-later @@ -100,6 +100,14 @@ %description -n suseconnect-ruby-bindings This package provides bindings needed to use libsuseconnect from Ruby scripts. +%package -n mcp-server-suseconnect +Summary: MCP server for suseconnect +Group: System/Management +Requires: suseconnect-ng = %version + +%description -n mcp-server-suseconnect +This package provides an MCP server for suseconnect to enable integration into the agentic SLES framework. + %prep %autosetup -p1 -a2 -n%{name}-%{version} @@ -110,6 +118,7 @@ go build -v -ldflags "-s -w" -mod=vendor -buildmode=pie -o bin/zypper-migration %{project}/cmd/zypper-migration go build -v -ldflags "-s -w" -mod=vendor -buildmode=pie -o bin/zypper-search-packages %{project}/cmd/zypper-search-packages go build -v -ldflags "-s -w" -mod=vendor -buildmode=pie -o bin/suse-uptime-tracker %{project}/cmd/suse-uptime-tracker +go build -v -ldflags "-s -w" -mod=vendor -buildmode=pie -o bin/suseconnect-mcp %{project}/cmd/suseconnect-mcp # the library mkdir -p %_builddir/go/lib @@ -118,8 +127,9 @@ %install # Install binary + symlinks install -D -m 0755 bin/suseconnect %{buildroot}/%{_bindir}/suseconnect -install -D -m 0755 bin/suse-uptime-tracker %{buildroot}/%{_bindir}/suse-uptime-tracker ln -s %{_bindir}/suseconnect %{buildroot}/%{_bindir}/SUSEConnect +install -D -m 0755 bin/suse-uptime-tracker %{buildroot}/%{_bindir}/suse-uptime-tracker +install -D -m 0755 bin/suseconnect-mcp %{buildroot}/%{_bindir}/suseconnect-mcp install -d -m 0755 %{buildroot}/%{_sbindir} ln -s %{_bindir}/suseconnect %{buildroot}/%{_sbindir}/SUSEConnect @@ -249,4 +259,7 @@ %doc third_party/yast/README.md %{_libdir}/ruby/vendor_ruby/%rb_ver/suse +%files -n mcp-server-suseconnect +%{_bindir}/suseconnect-mcp + %changelog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/cmd/public-api-demo/main.go new/suseconnect-ng-1.22.1/cmd/public-api-demo/main.go --- old/suseconnect-ng-1.22.0/cmd/public-api-demo/main.go 2026-06-08 20:16:18.000000000 +0200 +++ new/suseconnect-ng-1.22.1/cmd/public-api-demo/main.go 2026-06-11 17:47:03.000000000 +0200 @@ -42,6 +42,10 @@ isProxy = true } + if disableTokens := os.Getenv("DISABLE_TOKEN_HANDLING"); disableTokens != "" { + opts.DisableTokenHandling = true + } + if credentialTracing := os.Getenv("TRACE_CREDENTIAL_UPDATES"); credentialTracing != "" { creds.ShowTraces = true } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/cmd/suseconnect-mcp/suseconnect_mcp.go new/suseconnect-ng-1.22.1/cmd/suseconnect-mcp/suseconnect_mcp.go --- old/suseconnect-ng-1.22.0/cmd/suseconnect-mcp/suseconnect_mcp.go 2026-06-08 20:16:18.000000000 +0200 +++ new/suseconnect-ng-1.22.1/cmd/suseconnect-mcp/suseconnect_mcp.go 2026-06-11 17:47:03.000000000 +0200 @@ -3,10 +3,8 @@ import ( "context" "errors" - "flag" "fmt" "log/slog" - "net/http" "os" "github.com/SUSE/connect-ng/internal/connect" @@ -171,9 +169,6 @@ } func main() { - listenAddr := flag.String("http", "", "address for http transport, defaults to stdio") - flag.Parse() - if os.Geteuid() != 0 { fmt.Fprintln(os.Stderr, "Root privileges are required to run the MCP server.") os.Exit(1) @@ -181,46 +176,68 @@ server := mcp.NewServer(&mcp.Implementation{Name: "suseconnect", Version: "v0.0.1"}, nil) + // DestructiveHint is *bool (unset defaults to true); Go won't let us take &false. + ptr := func(b bool) *bool { return &b } + mcp.AddTool(server, &mcp.Tool{ Name: "RegistrationStatus", Description: "Tool to output the registration status of the system and activated/non-activated installed products", + Annotations: &mcp.ToolAnnotations{ + Title: "Show registration status", + ReadOnlyHint: true, + }, }, RegistrationStatus) mcp.AddTool(server, &mcp.Tool{ Name: "ListExtensions", Description: "List available extension products for your SUSE system. Your system's base product must be activated first.", + Annotations: &mcp.ToolAnnotations{ + Title: "List available extensions", + ReadOnlyHint: true, + }, }, ListExtensions) mcp.AddTool(server, &mcp.Tool{ Name: "RegisterSystem", Description: "Registers and activates your SUSE system. This will enable access to online repositories and additional extensions and modules.", + Annotations: &mcp.ToolAnnotations{ + Title: "Register system", + ReadOnlyHint: false, + DestructiveHint: ptr(false), + IdempotentHint: false, + }, }, RegisterSystem) mcp.AddTool(server, &mcp.Tool{ Name: "ActivateProduct", Description: "Activates and additional extension product or module on your SUSE system. Available extensions can get queried with the ListExtensions tool.", + Annotations: &mcp.ToolAnnotations{ + Title: "Activate product", + ReadOnlyHint: false, + DestructiveHint: ptr(false), + IdempotentHint: false, + }, }, ActivateProduct) mcp.AddTool(server, &mcp.Tool{ Name: "DeregisterSystem", Description: "Deregisters your SUSE system. This will remove the system's registration and disable access to online repositories.", + Annotations: &mcp.ToolAnnotations{ + Title: "Deregister system (destructive)", + ReadOnlyHint: false, + DestructiveHint: ptr(true), + IdempotentHint: true, + }, }, DeregisterSystem) mcp.AddTool(server, &mcp.Tool{ Name: "DeactivateProduct", Description: "Deactivates an extension product or module on your SUSE system.", + Annotations: &mcp.ToolAnnotations{ + Title: "Deactivate product (destructive)", + ReadOnlyHint: false, + DestructiveHint: ptr(true), + IdempotentHint: true, + }, }, DeactivateProduct) - if *listenAddr == "" { - // Run the server on the stdio transport. - if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil { - slog.Error("Server failed", "error", err) - } - } else { - // Create a streamable HTTP handler. - handler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server { - return server - }, nil) - - // Run the server on the HTTP transport. - slog.Info("Server listening", "address", *listenAddr) - if err := http.ListenAndServe(*listenAddr, handler); err != nil { - slog.Error("Server failed", "error", err) - } + // Run the server on the stdio transport. + if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil { + slog.Error("Server failed", "error", err) } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/internal/connect/cert_pool.go new/suseconnect-ng-1.22.1/internal/connect/cert_pool.go --- old/suseconnect-ng-1.22.0/internal/connect/cert_pool.go 2026-06-08 20:16:18.000000000 +0200 +++ new/suseconnect-ng-1.22.1/internal/connect/cert_pool.go 1970-01-01 01:00:00.000000000 +0100 @@ -1,131 +0,0 @@ -// TODO: remove when https://github.com/golang/go/issues/41888 is fixed -// code copied from standard library crypto/x509/root.go to enable system certs reloading - -package connect - -import ( - "crypto/x509" - "io/fs" - "os" - "path/filepath" - "strings" -) - -func systemRootsPool() *x509.CertPool { - systemRoots, systemRootsErr := _loadSystemRoots() - if systemRootsErr != nil { - return nil - } - return systemRoots -} - -const ( - // certFileEnv is the environment variable which identifies where to locate - // the SSL certificate file. If set this overrides the system default. - _certFileEnv = "SSL_CERT_FILE" - - // certDirEnv is the environment variable which identifies which directory - // to check for SSL certificate files. If set this overrides the system default. - // It is a colon separated list of directories. - // See https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html. - _certDirEnv = "SSL_CERT_DIR" -) - -// Possible certificate files; stop after finding one. -var _certFiles = []string{ - "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. - "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6 - "/etc/ssl/ca-bundle.pem", // OpenSUSE - "/etc/pki/tls/cacert.pem", // OpenELEC - "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7 - "/etc/ssl/cert.pem", // Alpine Linux -} - -// Possible directories with certificate files; stop after successfully -// reading at least one file from a directory. -var _certDirectories = []string{ - "/etc/ssl/certs", // SLES10/SLES11, https://golang.org/issue/12139 - "/etc/pki/tls/certs", // Fedora/RHEL - "/system/etc/security/cacerts", // Android -} - -func _loadSystemRoots() (*x509.CertPool, error) { - roots := x509.NewCertPool() - rootsLen := 0 - - files := _certFiles - if f := os.Getenv(_certFileEnv); f != "" { - files = []string{f} - } - - var firstErr error - for _, file := range files { - data, err := os.ReadFile(file) - if err == nil { - roots.AppendCertsFromPEM(data) - rootsLen++ - break - } - if firstErr == nil && !os.IsNotExist(err) { - firstErr = err - } - } - - dirs := _certDirectories - if d := os.Getenv(_certDirEnv); d != "" { - // OpenSSL and BoringSSL both use ":" as the SSL_CERT_DIR separator. - // See: - // * https://golang.org/issue/35325 - // * https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html - dirs = strings.Split(d, ":") - } - - for _, directory := range dirs { - fis, err := _readUniqueDirectoryEntries(directory) - if err != nil { - if firstErr == nil && !os.IsNotExist(err) { - firstErr = err - } - continue - } - for _, fi := range fis { - data, err := os.ReadFile(directory + "/" + fi.Name()) - if err == nil { - roots.AppendCertsFromPEM(data) - rootsLen++ - } - } - } - - if rootsLen > 0 || firstErr == nil { - return roots, nil - } - - return nil, firstErr -} - -// readUniqueDirectoryEntries is like os.ReadDir but omits -// symlinks that point within the directory. -func _readUniqueDirectoryEntries(dir string) ([]fs.DirEntry, error) { - files, err := os.ReadDir(dir) - if err != nil { - return nil, err - } - uniq := files[:0] - for _, f := range files { - if !_isSameDirSymlink(f, dir) { - uniq = append(uniq, f) - } - } - return uniq, nil -} - -// isSameDirSymlink reports whether fi in dir is a symlink with a -// target not containing a slash. -func _isSameDirSymlink(f fs.DirEntry, dir string) bool { - if f.Type()&fs.ModeSymlink == 0 { - return false - } - target, err := os.Readlink(filepath.Join(dir, f.Name())) - return err == nil && !strings.Contains(target, "/") -} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/pkg/connection/cert_pool.go new/suseconnect-ng-1.22.1/pkg/connection/cert_pool.go --- old/suseconnect-ng-1.22.0/pkg/connection/cert_pool.go 1970-01-01 01:00:00.000000000 +0100 +++ new/suseconnect-ng-1.22.1/pkg/connection/cert_pool.go 2026-06-11 17:47:03.000000000 +0200 @@ -0,0 +1,134 @@ +// TODO: remove when https://github.com/golang/go/issues/41888 is fixed +// code copied from standard library crypto/x509/root.go to enable system certs reloading + +package connection + +import ( + "crypto/x509" + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/SUSE/connect-ng/internal/util" +) + +func systemRootsPool() *x509.CertPool { + systemRoots, systemRootsErr := _loadSystemRoots() + if systemRootsErr != nil { + util.Debug.Printf("Failed to load system roots: %s", systemRootsErr.Error()) + return nil + } + return systemRoots +} + +const ( + // certFileEnv is the environment variable which identifies where to locate + // the SSL certificate file. If set this overrides the system default. + _certFileEnv = "SSL_CERT_FILE" + + // certDirEnv is the environment variable which identifies which directory + // to check for SSL certificate files. If set this overrides the system default. + // It is a colon separated list of directories. + // See https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html. + _certDirEnv = "SSL_CERT_DIR" +) + +// Possible certificate files; stop after finding one. +var _certFiles = []string{ + "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. + "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6 + "/etc/ssl/ca-bundle.pem", // OpenSUSE + "/etc/pki/tls/cacert.pem", // OpenELEC + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7 + "/etc/ssl/cert.pem", // Alpine Linux +} + +// Possible directories with certificate files; stop after successfully +// reading at least one file from a directory. +var _certDirectories = []string{ + "/etc/ssl/certs", // SLES10/SLES11, https://golang.org/issue/12139 + "/etc/pki/tls/certs", // Fedora/RHEL + "/system/etc/security/cacerts", // Android +} + +func _loadSystemRoots() (*x509.CertPool, error) { + roots := x509.NewCertPool() + rootsLen := 0 + + files := _certFiles + if f := os.Getenv(_certFileEnv); f != "" { + files = []string{f} + } + + var firstErr error + for _, file := range files { + data, err := os.ReadFile(file) + if err == nil { + roots.AppendCertsFromPEM(data) + rootsLen++ + break + } + if firstErr == nil && !os.IsNotExist(err) { + firstErr = err + } + } + + dirs := _certDirectories + if d := os.Getenv(_certDirEnv); d != "" { + // OpenSSL and BoringSSL both use ":" as the SSL_CERT_DIR separator. + // See: + // * https://golang.org/issue/35325 + // * https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html + dirs = strings.Split(d, ":") + } + + for _, directory := range dirs { + fis, err := _readUniqueDirectoryEntries(directory) + if err != nil { + if firstErr == nil && !os.IsNotExist(err) { + firstErr = err + } + continue + } + for _, fi := range fis { + data, err := os.ReadFile(directory + "/" + fi.Name()) + if err == nil { + roots.AppendCertsFromPEM(data) + rootsLen++ + } + } + } + + if rootsLen > 0 || firstErr == nil { + return roots, nil + } + + return nil, firstErr +} + +// readUniqueDirectoryEntries is like os.ReadDir but omits +// symlinks that point within the directory. +func _readUniqueDirectoryEntries(dir string) ([]fs.DirEntry, error) { + files, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + uniq := files[:0] + for _, f := range files { + if !_isSameDirSymlink(f, dir) { + uniq = append(uniq, f) + } + } + return uniq, nil +} + +// isSameDirSymlink reports whether fi in dir is a symlink with a +// target not containing a slash. +func _isSameDirSymlink(f fs.DirEntry, dir string) bool { + if f.Type()&fs.ModeSymlink == 0 { + return false + } + target, err := os.Readlink(filepath.Join(dir, f.Name())) + return err == nil && !strings.Contains(target, "/") +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/pkg/connection/connection.go new/suseconnect-ng-1.22.1/pkg/connection/connection.go --- old/suseconnect-ng-1.22.0/pkg/connection/connection.go 2026-06-08 20:16:18.000000000 +0200 +++ new/suseconnect-ng-1.22.1/pkg/connection/connection.go 2026-06-11 17:47:03.000000000 +0200 @@ -9,6 +9,8 @@ "io" "net/http" "strings" + + "github.com/SUSE/connect-ng/internal/util" ) const ( @@ -86,11 +88,19 @@ } func (conn ApiConnection) Do(request *http.Request) ([]byte, error) { - token, tokenErr := conn.Credentials.Token() - if tokenErr != nil { - return []byte{}, tokenErr + + // Allow clients to disable token handling completely if they + // do not need duplicate detection. This is the case for the + // scc-operator (https://github.com/rancher/scc-operator/) + rotateToken := conn.Options.DisableTokenHandling == false + + if rotateToken == true { + token, tokenErr := conn.Credentials.Token() + if tokenErr != nil { + return []byte{}, tokenErr + } + request.Header.Set("System-Token", token) } - request.Header.Set("System-Token", token) response, doErr := conn.setupHTTPClient().Do(request) if doErr != nil { @@ -99,9 +109,11 @@ defer response.Body.Close() // Update the credentials from the new system token. - token = response.Header.Get("System-Token") - if err := conn.Credentials.UpdateToken(token); err != nil { - return nil, err + if rotateToken == true { + token := response.Header.Get("System-Token") + if err := conn.Credentials.UpdateToken(token); err != nil { + return nil, err + } } // Check if there was an error from the given API response. @@ -149,12 +161,30 @@ transport.Proxy = conn.Options.Proxy } + // retrieve current system root certs pool; this ensures any new certs + // added since x509.SystemCertPool() was first initialised are included + // TODO: rework if https://github.com/golang/go/issues/41888 is resolved + pool := systemRootsPool() + + // if we fail to retrieve the latest systemRootsPoll fall back on using + // a clone of the x509.SystemCertPool(), otherwise use a new empty pool. + if pool == nil { + systemPool, err := x509.SystemCertPool() + if err == nil { + pool = systemPool.Clone() + } else { + util.Debug.Printf("Failed to retrieve x509.SystemCertPool(): %s", err.Error()) + pool = x509.NewCertPool() + } + } + + // if a cert has been provided add it to the pool if conn.Options.Certificate != nil { - pool := x509.NewCertPool() pool.AddCert(conn.Options.Certificate) - - transport.TLSClientConfig.RootCAs = pool } + // use the pool for handling TLS certs + transport.TLSClientConfig.RootCAs = pool + return &http.Client{Transport: transport, Timeout: conn.Options.Timeout} } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/pkg/connection/connection_test.go new/suseconnect-ng-1.22.1/pkg/connection/connection_test.go --- old/suseconnect-ng-1.22.0/pkg/connection/connection_test.go 2026-06-08 20:16:18.000000000 +0200 +++ new/suseconnect-ng-1.22.1/pkg/connection/connection_test.go 2026-06-11 17:47:03.000000000 +0200 @@ -228,6 +228,34 @@ creds.AssertExpectations(t) } +func TestConnectionTokenDisabled(t *testing.T) { + assert := assert.New(t) + + handler := func(response http.ResponseWriter) { + response.WriteHeader(http.StatusOK) + } + + server := NewTestServerSetupWith(t, "GET", "/test/api", handler) + defer server.Close() + + opts := DefaultOptions("testApp", "1.0", "en_US") + opts.URL = server.URL + opts.DisableTokenHandling = true + + creds := &MockCredentials{} + conn := New(opts, creds) + + request, buildErr := conn.BuildRequest("GET", "/test/api", "") + assert.NoError(buildErr) + + _, doErr := conn.Do(request) + assert.NoError(doErr) + + creds.AssertNotCalled(t, "Token") + creds.AssertNotCalled(t, "UpdateToken") + creds.AssertExpectations(t) +} + func TestCustomCertificateSuccess(t *testing.T) { assert := assert.New(t) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/suseconnect-ng-1.22.0/pkg/connection/options.go new/suseconnect-ng-1.22.1/pkg/connection/options.go --- old/suseconnect-ng-1.22.0/pkg/connection/options.go 2026-06-08 20:16:18.000000000 +0200 +++ new/suseconnect-ng-1.22.1/pkg/connection/options.go 2026-06-11 17:47:03.000000000 +0200 @@ -49,16 +49,20 @@ // Timeout on how long to wait for an API response Timeout time.Duration + + // Disable Token handling + DisableTokenHandling bool } // Returns the Options suitable for targeting the SCC reference server. func DefaultOptions(appName, version, language string) Options { return Options{ - URL: DefaultBaseURL, - Secure: true, - AppName: appName, - Version: version, - PreferedLanguage: language, - Timeout: DefaultTimeout, + URL: DefaultBaseURL, + Secure: true, + AppName: appName, + Version: version, + PreferedLanguage: language, + Timeout: DefaultTimeout, + DisableTokenHandling: false, } } ++++++ vendor.tar.xz ++++++
