Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kubelogin for openSUSE:Factory checked in at 2026-09-24 22:59:29 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kubelogin (Old) and /work/SRC/openSUSE:Factory/.kubelogin.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kubelogin" Thu Sep 24 22:59:29 2026 rev:30 rq:1380127 version:0.2.20 Changes: -------- --- /work/SRC/openSUSE:Factory/kubelogin/kubelogin.changes 2026-06-25 10:57:55.546865397 +0200 +++ /work/SRC/openSUSE:Factory/.kubelogin.new.383539/kubelogin.changes 2026-09-24 23:01:37.159665078 +0200 @@ -1,0 +2,24 @@ +Thu Sep 24 04:55:13 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 0.2.20: + * Bug Fixes + - Fix release asset publishing by replacing + skx/github-action-publish-binaries with GitHub CLI in #815 + * Maintenance + - Pin GitHub Actions to full-length commit SHAsin #799 + - Add test coverage for NeedAuthenticate across all credential + typesin #794 + - Add unit tests for client certificate PEM/PKCS helpersin #793 + - Bump golang.org/x/sys from 0.45.0 to 0.48.0in #808 + - Bump golang.org/x/crypto to v0.56.0in #806 + - Bump gopkg.in/dnaeon/go-vcr.v4 from 4.0.2 to 4.0.7in #804 + - Bump docker/setup-buildx-action from 4.1.0 to 4.3.0in #800 + - Bump actions/checkout from 7.0.0 to 7.0.1in #798 + - Bump docker/login-action from 4.2.0 to 4.6.0in #797 + - Bump docker/build-push-action from 7.2.0 to 7.3.0in #790 + - Bump golangci/golangci-lint-action from 9.2.1 to 9.3.0in #789 + - Bump actions/setup-go from 6.4.0 to 6.5.0in #788 + * Doc Update + - Explain how to switch device code login to interactivein #795 + +------------------------------------------------------------------- Old: ---- kubelogin-0.2.19.obscpio New: ---- kubelogin-0.2.20.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kubelogin.spec ++++++ --- /var/tmp/diff_new_pack.M77kHC/_old 2026-09-24 23:01:39.311755064 +0200 +++ /var/tmp/diff_new_pack.M77kHC/_new 2026-09-24 23:01:39.312755106 +0200 @@ -17,7 +17,7 @@ Name: kubelogin -Version: 0.2.19 +Version: 0.2.20 Release: 0 Summary: Kubernetes client credential plugin implementing Azure authentication License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.M77kHC/_old 2026-09-24 23:01:39.341756319 +0200 +++ /var/tmp/diff_new_pack.M77kHC/_new 2026-09-24 23:01:39.343756403 +0200 @@ -2,7 +2,7 @@ <service name="obs_scm" mode="manual"> <param name="url">https://github.com/Azure/kubelogin.git</param> <param name="scm">git</param> - <param name="revision">v0.2.19</param> + <param name="revision">v0.2.20</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.M77kHC/_old 2026-09-24 23:01:39.362757197 +0200 +++ /var/tmp/diff_new_pack.M77kHC/_new 2026-09-24 23:01:39.364757281 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/Azure/kubelogin.git</param> - <param name="changesrevision">a9b10fbf8422f0c5b687eb58f26d7995f2fe206d</param></service></servicedata> + <param name="changesrevision">51be4471f0367039d4f79029f2ead5408199076d</param></service></servicedata> (No newline at EOF) ++++++ kubelogin-0.2.19.obscpio -> kubelogin-0.2.20.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/.github/dependabot.yml new/kubelogin-0.2.20/.github/dependabot.yml --- old/kubelogin-0.2.19/.github/dependabot.yml 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/.github/dependabot.yml 2026-09-23 19:41:35.000000000 +0200 @@ -14,3 +14,5 @@ directory: / schedule: interval: daily + cooldown: + default-days: 7 \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/.github/workflows/build.yml new/kubelogin-0.2.20/.github/workflows/build.yml --- old/kubelogin-0.2.19/.github/workflows/build.yml 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/.github/workflows/build.yml 2026-09-23 19:41:35.000000000 +0200 @@ -27,10 +27,10 @@ GO111MODULE: on steps: - name: Check out code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: "go.mod" cache: false @@ -47,10 +47,10 @@ needs: test steps: - name: Check out code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: "go.mod" cache: false @@ -97,10 +97,10 @@ needs: test steps: - name: Check out code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: "go.mod" cache: false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/.github/workflows/dependency-review.yml new/kubelogin-0.2.20/.github/workflows/dependency-review.yml --- old/kubelogin-0.2.19/.github/workflows/dependency-review.yml 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/.github/workflows/dependency-review.yml 2026-09-23 19:41:35.000000000 +0200 @@ -17,6 +17,6 @@ runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: 'Dependency Review' uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/.github/workflows/docker-publish.yml new/kubelogin-0.2.20/.github/workflows/docker-publish.yml --- old/kubelogin-0.2.19/.github/workflows/docker-publish.yml 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/.github/workflows/docker-publish.yml 2026-09-23 19:41:35.000000000 +0200 @@ -26,19 +26,19 @@ run: echo "IMAGE_NAME=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - name: Check out code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: "go.mod" cache: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 - name: Log in to GitHub Container Registry - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -70,7 +70,7 @@ file bin/linux_arm64/kubelogin - name: Build and push Docker image - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . platforms: linux/amd64,linux/arm64 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/.github/workflows/golangci-lint.yml new/kubelogin-0.2.20/.github/workflows/golangci-lint.yml --- old/kubelogin-0.2.19/.github/workflows/golangci-lint.yml 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/.github/workflows/golangci-lint.yml 2026-09-23 19:41:35.000000000 +0200 @@ -26,12 +26,12 @@ deployments: read packages: none steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: "go.mod" cache: false - name: golangci-lint - uses: golangci/golangci-lint-action@82606bf257cbaff209d206a39f5134f0cfbfd2ee # v9.2.1 + uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 with: version: v2.12.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/.github/workflows/release.yml new/kubelogin-0.2.20/.github/workflows/release.yml --- old/kubelogin-0.2.19/.github/workflows/release.yml 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/.github/workflows/release.yml 2026-09-23 19:41:35.000000000 +0200 @@ -19,7 +19,7 @@ tag_version: "v${{ steps.changelog_reader.outputs.version }}" steps: - name: Check out code into the Go module directory - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # Read changelog and read versions etc. - name: Check version is mentioned in Changelog.md @@ -58,13 +58,13 @@ if: ${{ needs.create-release.outputs.release_id != '' }} steps: - name: Check out code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Get tags run: git fetch --tags - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: "go.mod" cache: false @@ -119,13 +119,13 @@ tar -czf windows-kubelogin.tar.gz bin/windows_* - name: Upload Linux artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: linux-binaries path: linux-kubelogin.tar.gz - name: Upload Windows artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: windows-binaries path: windows-kubelogin.tar.gz @@ -137,13 +137,13 @@ if: ${{ needs.create-release.outputs.release_id != '' }} steps: - name: Check out code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Get tags run: git fetch --tags - name: Set up Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: "go.mod" cache: false @@ -169,7 +169,7 @@ tar -czf macos-kubelogin.tar.gz bin/darwin_* - name: Upload macOS artifacts - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: macos-binaries path: macos-kubelogin.tar.gz @@ -183,7 +183,7 @@ if: ${{ needs.create-release.outputs.release_id != '' }} steps: - name: Download all artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: bin @@ -255,9 +255,27 @@ run: echo -n "${{ needs.create-release.outputs.tag_version }}" > kubelogin-version.txt - name: Publish - uses: skx/github-action-publish-binaries@44887b225ceca96efd8a912d39c09ad70312af31 # master env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - args: kubelogin.zip kubelogin-win-amd64.zip kubelogin-win-arm64.zip kubelogin-darwin-amd64.zip kubelogin-darwin-arm64.zip kubelogin-linux-amd64.zip kubelogin-linux-arm64.zip kubelogin-linux-armv7.zip kubelogin.zip.sha256 kubelogin-win-amd64.zip.sha256 kubelogin-win-arm64.zip.sha256 kubelogin-darwin-amd64.zip.sha256 kubelogin-darwin-arm64.zip.sha256 kubelogin-linux-amd64.zip.sha256 kubelogin-linux-arm64.zip.sha256 kubelogin-linux-armv7.zip.sha256 kubelogin-version.txt - releaseId: ${{ needs.create-release.outputs.release_id }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + RELEASE_TAG: ${{ needs.create-release.outputs.tag_version }} + run: | + gh release upload "$RELEASE_TAG" \ + kubelogin.zip \ + kubelogin.zip.sha256 \ + kubelogin-win-amd64.zip \ + kubelogin-win-amd64.zip.sha256 \ + kubelogin-win-arm64.zip \ + kubelogin-win-arm64.zip.sha256 \ + kubelogin-darwin-amd64.zip \ + kubelogin-darwin-amd64.zip.sha256 \ + kubelogin-darwin-arm64.zip \ + kubelogin-darwin-arm64.zip.sha256 \ + kubelogin-linux-amd64.zip \ + kubelogin-linux-amd64.zip.sha256 \ + kubelogin-linux-arm64.zip \ + kubelogin-linux-arm64.zip.sha256 \ + kubelogin-linux-armv7.zip \ + kubelogin-linux-armv7.zip.sha256 \ + kubelogin-version.txt \ + --clobber diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/.github/workflows/update-changelog.yml new/kubelogin-0.2.20/.github/workflows/update-changelog.yml --- old/kubelogin-0.2.19/.github/workflows/update-changelog.yml 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/.github/workflows/update-changelog.yml 2026-09-23 19:41:35.000000000 +0200 @@ -25,13 +25,13 @@ steps: - name: Checkout repository # v4.1.1 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # Fetch all history - name: Set up Go # v4.1.0 - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version-file: "go.mod" cache: true @@ -60,7 +60,7 @@ rm changelog-entry.md - name: Create Pull Request - uses: peter-evans/create-pull-request@v8 + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: >- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/.github/workflows/website.yaml new/kubelogin-0.2.20/.github/workflows/website.yaml --- old/kubelogin-0.2.19/.github/workflows/website.yaml 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/.github/workflows/website.yaml 2026-09-23 19:41:35.000000000 +0200 @@ -16,7 +16,7 @@ runs-on: ubuntu-latest steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: true fetch-depth: 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/CHANGELOG.md new/kubelogin-0.2.20/CHANGELOG.md --- old/kubelogin-0.2.19/CHANGELOG.md 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/CHANGELOG.md 2026-09-23 19:41:35.000000000 +0200 @@ -1,5 +1,39 @@ # Change Log +## [0.2.20] + +### Bug Fixes + +* Fix release asset publishing by replacing skx/github-action-publish-binaries with GitHub CLI by @Copilot in https://github.com/Azure/kubelogin/pull/815 + +### Maintenance + +* Pin GitHub Actions to full-length commit SHAs by @danfiedler-msft in https://github.com/Azure/kubelogin/pull/799 +* Add test coverage for NeedAuthenticate across all credential types by @NaeemH in https://github.com/Azure/kubelogin/pull/794 +* Add unit tests for client certificate PEM/PKCS helpers by @NaeemH in https://github.com/Azure/kubelogin/pull/793 +* Bump golang.org/x/sys from 0.45.0 to 0.48.0 by @dependabot[bot] in https://github.com/Azure/kubelogin/pull/808 +* Bump golang.org/x/crypto to v0.56.0 by @harsh-im in https://github.com/Azure/kubelogin/pull/806 +* Bump gopkg.in/dnaeon/go-vcr.v4 from 4.0.2 to 4.0.7 by @dependabot[bot] in https://github.com/Azure/kubelogin/pull/804 +* Bump docker/setup-buildx-action from 4.1.0 to 4.3.0 by @dependabot[bot] in https://github.com/Azure/kubelogin/pull/800 +* Bump actions/checkout from 7.0.0 to 7.0.1 by @dependabot[bot] in https://github.com/Azure/kubelogin/pull/798 +* Bump docker/login-action from 4.2.0 to 4.6.0 by @dependabot[bot] in https://github.com/Azure/kubelogin/pull/797 +* Bump docker/build-push-action from 7.2.0 to 7.3.0 by @dependabot[bot] in https://github.com/Azure/kubelogin/pull/790 +* Bump golangci/golangci-lint-action from 9.2.1 to 9.3.0 by @dependabot[bot] in https://github.com/Azure/kubelogin/pull/789 +* Bump actions/setup-go from 6.4.0 to 6.5.0 by @dependabot[bot] in https://github.com/Azure/kubelogin/pull/788 + +### Doc Update + +* Explain how to switch device code login to interactive by @1fanwang in https://github.com/Azure/kubelogin/pull/795 + +### New Contributors + +* @1fanwang made their first contribution in https://github.com/Azure/kubelogin/pull/795 +* @NaeemH made their first contribution in https://github.com/Azure/kubelogin/pull/794 +* @danfiedler-msft made their first contribution in https://github.com/Azure/kubelogin/pull/799 +* @harsh-im made their first contribution in https://github.com/Azure/kubelogin/pull/806 + +**Full Changelog**: https://github.com/Azure/kubelogin/compare/v0.2.19...v0.2.20 + ## [0.2.19] ### What's Changed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/docs/book/src/concepts/login-modes/devicecode.md new/kubelogin-0.2.20/docs/book/src/concepts/login-modes/devicecode.md --- old/kubelogin-0.2.19/docs/book/src/concepts/login-modes/devicecode.md 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/docs/book/src/concepts/login-modes/devicecode.md 2026-09-23 19:41:35.000000000 +0200 @@ -21,6 +21,25 @@ ``` +## Using Interactive Mode Instead + +Device code login asks the user to open a URL and type a code by hand. Entra ID does not return the +optional `verification_uri_complete` field from [RFC 8628](https://datatracker.ietf.org/doc/html/rfc8628#section-3.3.1), +so the URL and the code cannot be combined into a single link. When the machine running `kubectl` +has a browser, [web browser interactive mode](./interactive.md) avoids the copying altogether. + +If the kubeconfig was provisioned for you and already pins `--login=devicecode` in its exec args, +you don't have to edit it. Environment variables are applied after the flags are parsed, so +`AAD_LOGIN_METHOD` takes precedence over the login mode stored in the kubeconfig: + +```sh +export AAD_LOGIN_METHOD=interactive + +kubectl get nodes +``` + +Passing `--disable-environment-override` turns this off and keeps the login mode from the kubeconfig. + ## Restrictions - Device code login mode doesn't work when Conditional Access policy is configured on AAD tenant. Use [web browser interactive mode](./interactive.md) instead. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/go.mod new/kubelogin-0.2.20/go.mod --- old/kubelogin-0.2.19/go.mod 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/go.mod 2026-09-23 19:41:35.000000000 +0200 @@ -18,9 +18,9 @@ github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.11.1 go.uber.org/mock v0.5.0 - golang.org/x/crypto v0.52.0 - golang.org/x/sys v0.45.0 - gopkg.in/dnaeon/go-vcr.v4 v4.0.2 + golang.org/x/crypto v0.56.0 + golang.org/x/sys v0.48.0 + gopkg.in/dnaeon/go-vcr.v4 v4.0.7 k8s.io/apimachinery v0.29.3 k8s.io/cli-runtime v0.29.3 k8s.io/client-go v0.29.3 @@ -69,11 +69,12 @@ github.com/pmezard/go-difflib v1.0.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/net v0.55.0 // indirect + go.yaml.in/yaml/v4 v4.0.0-rc.6 // indirect + golang.org/x/net v0.57.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/term v0.43.0 // indirect - golang.org/x/text v0.37.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/term v0.45.0 // indirect + golang.org/x/text v0.41.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/go.sum new/kubelogin-0.2.20/go.sum --- old/kubelogin-0.2.19/go.sum 2026-06-23 19:26:47.000000000 +0200 +++ new/kubelogin-0.2.20/go.sum 2026-09-23 19:41:35.000000000 +0200 @@ -184,14 +184,16 @@ go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.yaml.in/yaml/v4 v4.0.0-rc.6 h1:1h7H1ohdUh93/FyE4YaDa1Zh64K6VVbjF4K6WUxMtH4= +go.yaml.in/yaml/v4 v4.0.0-rc.6/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= -golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= +golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -211,8 +213,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= @@ -222,8 +224,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -237,21 +239,21 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= -golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -263,8 +265,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -290,8 +292,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/dnaeon/go-vcr.v4 v4.0.2 h1:7T5VYf2ifyK01ETHbJPl5A6XTpUljD4Trw3GEDcdedk= -gopkg.in/dnaeon/go-vcr.v4 v4.0.2/go.mod h1:65yxh9goQVrudqofKtHA4JNFWd6XZRkWfKN4YpMx7KI= +gopkg.in/dnaeon/go-vcr.v4 v4.0.7 h1:Mq/RF+mq3QwtEunJSsoTbYPt3elSAmdJhAxrEaqr88I= +gopkg.in/dnaeon/go-vcr.v4 v4.0.7/go.mod h1:cRwV/njsN/D8qNJu4NAXWswz6b4OUh3rMIu4SObbLBg= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/pkg/internal/token/clientcertcredential_helpers_test.go new/kubelogin-0.2.20/pkg/internal/token/clientcertcredential_helpers_test.go --- old/kubelogin-0.2.19/pkg/internal/token/clientcertcredential_helpers_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kubelogin-0.2.20/pkg/internal/token/clientcertcredential_helpers_test.go 2026-09-23 19:41:35.000000000 +0200 @@ -0,0 +1,204 @@ +package token + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// certTestKeyPair generates an RSA key and a matching self-signed certificate +// (returned as DER) for exercising the PEM/PKCS certificate helpers. +func certTestKeyPair(t *testing.T) (*rsa.PrivateKey, []byte) { + t.Helper() + key, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + + tmpl := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "kubelogin-test"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour), + } + der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key) + require.NoError(t, err) + return key, der +} + +func certTestCertPEM(der []byte) []byte { + return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) +} + +func certTestPKCS8PEM(t *testing.T, key *rsa.PrivateKey) []byte { + t.Helper() + der, err := x509.MarshalPKCS8PrivateKey(key) + require.NoError(t, err) + return pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: der}) +} + +func TestIsPublicKeyEqual(t *testing.T) { + key1, _ := certTestKeyPair(t) + key2, _ := certTestKeyPair(t) + + assert.True(t, isPublicKeyEqual(&key1.PublicKey, &key1.PublicKey), "identical keys should be equal") + assert.False(t, isPublicKeyEqual(&key1.PublicKey, &key2.PublicKey), "different keys should not be equal") + assert.False(t, isPublicKeyEqual(&rsa.PublicKey{}, &key1.PublicKey), "nil modulus (left) should not be equal") + assert.False(t, isPublicKeyEqual(&key1.PublicKey, &rsa.PublicKey{}), "nil modulus (right) should not be equal") +} + +func TestParseRsaPrivateKey(t *testing.T) { + key, _ := certTestKeyPair(t) + + pkcs1PEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) + + pkcs8DER, err := x509.MarshalPKCS8PrivateKey(key) + require.NoError(t, err) + pkcs8PEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: pkcs8DER}) + + ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + ecDER, err := x509.MarshalPKCS8PrivateKey(ecKey) + require.NoError(t, err) + ecPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: ecDER}) + + testCases := []struct { + name string + input []byte + expectErr bool + expectErrMsg string + }{ + {name: "pkcs1 rsa key", input: pkcs1PEM}, + {name: "pkcs8 rsa key", input: pkcs8PEM}, + {name: "pkcs8 non-rsa key", input: ecPEM, expectErr: true, expectErrMsg: "non-RSA"}, + {name: "not a pem block", input: []byte("not a pem block"), expectErr: true, expectErrMsg: "failed to decode"}, + {name: "empty input", input: nil, expectErr: true, expectErrMsg: "failed to decode"}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got, err := parseRsaPrivateKey(tc.input) + if tc.expectErr { + require.Error(t, err) + assert.Contains(t, err.Error(), tc.expectErrMsg) + assert.Nil(t, got) + return + } + require.NoError(t, err) + require.NotNil(t, got) + assert.Equal(t, 0, key.PublicKey.N.Cmp(got.PublicKey.N), "parsed key should match the original") + }) + } +} + +func TestSplitPEMBlock(t *testing.T) { + key, der := certTestKeyPair(t) + certPEM := certTestCertPEM(der) + keyPEM := certTestPKCS8PEM(t, key) + unknownPEM := pem.EncodeToMemory(&pem.Block{Type: "SOMETHING ELSE", Bytes: []byte("ignored")}) + + t.Run("separates cert and key and ignores unknown blocks", func(t *testing.T) { + combined := bytesJoin(certPEM, keyPEM, unknownPEM) + gotCert, gotKey := splitPEMBlock(combined) + + assert.NotEmpty(t, gotCert, "expected a certificate block") + assert.NotEmpty(t, gotKey, "expected a private key block") + assert.NotContains(t, string(gotCert), "SOMETHING ELSE") + assert.NotContains(t, string(gotKey), "SOMETHING ELSE") + + parsedKey, err := parseRsaPrivateKey(gotKey) + require.NoError(t, err) + assert.Equal(t, 0, key.PublicKey.N.Cmp(parsedKey.PublicKey.N)) + }) + + t.Run("returns nil for input without pem blocks", func(t *testing.T) { + gotCert, gotKey := splitPEMBlock([]byte("no pem here")) + assert.Nil(t, gotCert) + assert.Nil(t, gotKey) + }) +} + +func TestParseKeyPairFromPEMBlock(t *testing.T) { + key, der := certTestKeyPair(t) + certPEM := certTestCertPEM(der) + keyPEM := certTestPKCS8PEM(t, key) + + _, otherDER := certTestKeyPair(t) + otherCertPEM := certTestCertPEM(otherDER) + + t.Run("matching cert and key", func(t *testing.T) { + cert, priv, err := parseKeyPairFromPEMBlock(bytesJoin(certPEM, keyPEM)) + require.NoError(t, err) + require.NotNil(t, cert) + require.NotNil(t, priv) + assert.Equal(t, 0, key.PublicKey.N.Cmp(priv.PublicKey.N)) + }) + + t.Run("cert does not match key", func(t *testing.T) { + _, _, err := parseKeyPairFromPEMBlock(bytesJoin(otherCertPEM, keyPEM)) + require.Error(t, err) + assert.Contains(t, err.Error(), "unable to find a matching public certificate") + }) + + t.Run("missing private key", func(t *testing.T) { + _, _, err := parseKeyPairFromPEMBlock(certPEM) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to decode") + }) +} + +func TestDecodePkcs12(t *testing.T) { + t.Run("invalid pkcs12 data returns error", func(t *testing.T) { + cert, key, err := decodePkcs12([]byte("not a valid pkcs12 blob"), "") + require.Error(t, err) + assert.Nil(t, cert) + assert.Nil(t, key) + }) +} + +func TestReadCertificate(t *testing.T) { + key, der := certTestKeyPair(t) + combined := bytesJoin(certTestCertPEM(der), certTestPKCS8PEM(t, key)) + dir := t.TempDir() + + t.Run("valid pem file", func(t *testing.T) { + p := filepath.Join(dir, "cert.pem") + require.NoError(t, os.WriteFile(p, combined, 0o600)) + + cert, priv, err := readCertificate(p, "") + require.NoError(t, err) + require.NotNil(t, cert) + assert.Equal(t, 0, key.PublicKey.N.Cmp(priv.PublicKey.N)) + }) + + t.Run("missing pem file", func(t *testing.T) { + _, _, err := readCertificate(filepath.Join(dir, "does-not-exist.pem"), "") + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to read the certificate file") + }) + + t.Run("missing pfx file", func(t *testing.T) { + _, _, err := readCertificate(filepath.Join(dir, "does-not-exist.pfx"), "") + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to read the certificate file") + }) +} + +// bytesJoin concatenates byte slices without a separator. +func bytesJoin(parts ...[]byte) []byte { + var out []byte + for _, p := range parts { + out = append(out, p...) + } + return out +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/pkg/internal/token/clientcertcredential_pfx_test.go new/kubelogin-0.2.20/pkg/internal/token/clientcertcredential_pfx_test.go --- old/kubelogin-0.2.19/pkg/internal/token/clientcertcredential_pfx_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kubelogin-0.2.20/pkg/internal/token/clientcertcredential_pfx_test.go 2026-09-23 19:41:35.000000000 +0200 @@ -0,0 +1,78 @@ +package token + +import ( + "crypto/rsa" + "encoding/base64" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// testPFXBase64 is a throwaway, self-signed RSA certificate packaged as a +// password-protected PKCS#12 (PFX) file, base64-encoded. The password is +// testPFXPassword. It intentionally uses legacy SHA1/3DES PBE so it can be +// decoded by golang.org/x/crypto/pkcs12, which does not support the AES-based +// algorithms OpenSSL 3 emits by default. +// +// Regenerate with: +// +// openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \ +// -days 3650 -nodes -subj "/CN=kubelogin-test" +// openssl pkcs12 -export -inkey key.pem -in cert.pem -out test.pfx \ +// -passout pass:kubelogin-test -legacy \ +// -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1 +// base64 -w0 test.pfx +const testPFXBase64 = "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" + +const testPFXPassword = "kubelogin-test" + +func testPFXBytes(t *testing.T) []byte { + t.Helper() + data, err := base64.StdEncoding.DecodeString(testPFXBase64) + require.NoError(t, err) + return data +} + +func TestDecodePkcs12WithPassword(t *testing.T) { + pfx := testPFXBytes(t) + + t.Run("correct password decodes cert and key", func(t *testing.T) { + cert, key, err := decodePkcs12(pfx, testPFXPassword) + require.NoError(t, err) + require.NotNil(t, cert) + require.NotNil(t, key) + assert.Equal(t, "kubelogin-test", cert.Subject.CommonName) + assert.IsType(t, &rsa.PrivateKey{}, key) + require.NoError(t, key.Validate()) + }) + + t.Run("wrong password returns error", func(t *testing.T) { + cert, key, err := decodePkcs12(pfx, "not-the-password") + require.Error(t, err) + assert.Nil(t, cert) + assert.Nil(t, key) + }) +} + +func TestReadCertificatePFX(t *testing.T) { + pfx := testPFXBytes(t) + dir := t.TempDir() + pfxPath := filepath.Join(dir, "client.pfx") + require.NoError(t, os.WriteFile(pfxPath, pfx, 0o600)) + + t.Run("valid pfx with correct password", func(t *testing.T) { + cert, key, err := readCertificate(pfxPath, testPFXPassword) + require.NoError(t, err) + require.NotNil(t, cert) + require.NotNil(t, key) + assert.Equal(t, "kubelogin-test", cert.Subject.CommonName) + }) + + t.Run("valid pfx with wrong password", func(t *testing.T) { + _, _, err := readCertificate(pfxPath, "not-the-password") + require.Error(t, err) + }) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kubelogin-0.2.19/pkg/internal/token/needauthenticate_test.go new/kubelogin-0.2.20/pkg/internal/token/needauthenticate_test.go --- old/kubelogin-0.2.19/pkg/internal/token/needauthenticate_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/kubelogin-0.2.20/pkg/internal/token/needauthenticate_test.go 2026-09-23 19:41:35.000000000 +0200 @@ -0,0 +1,47 @@ +package token + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestNeedAuthenticate locks in which credential types require the MSAL +// Authenticate() step before GetToken. Only the interactive / public-client +// flows (device code, interactive browser, username-password) report true; +// every other credential authenticates as part of GetToken and reports false. +// +// NeedAuthenticate is a constant predicate on each type, so a zero-value +// instance is sufficient to exercise it. +func TestNeedAuthenticate(t *testing.T) { + testCases := []struct { + name string + cred interface{ NeedAuthenticate() bool } + want bool + }{ + {"ADALClientCertCredential", &ADALClientCertCredential{}, false}, + {"ADALClientSecretCredential", &ADALClientSecretCredential{}, false}, + {"ADALDeviceCodeCredential", &ADALDeviceCodeCredential{}, false}, + {"AzureCLICredential", &AzureCLICredential{}, false}, + {"AzureDeveloperCLICredential", &AzureDeveloperCLICredential{}, false}, + {"AzurePipelinesCredential", &AzurePipelinesCredential{}, false}, + {"ClientCertificateCredential", &ClientCertificateCredential{}, false}, + {"ClientCertificateCredentialWithPoP", &ClientCertificateCredentialWithPoP{}, false}, + {"ClientSecretCredential", &ClientSecretCredential{}, false}, + {"ClientSecretCredentialWithPoP", &ClientSecretCredentialWithPoP{}, false}, + {"DeviceCodeCredential", &DeviceCodeCredential{}, true}, + {"GithubActionsCredential", &GithubActionsCredential{}, false}, + {"InteractiveBrowserCredential", &InteractiveBrowserCredential{}, true}, + {"InteractiveBrowserCredentialWithPoP", &InteractiveBrowserCredentialWithPoP{}, false}, + {"ManagedIdentityCredential", &ManagedIdentityCredential{}, false}, + {"UsernamePasswordCredential", &UsernamePasswordCredential{}, true}, + {"UsernamePasswordCredentialWithPoP", &UsernamePasswordCredentialWithPoP{}, false}, + {"WorkloadIdentityCredential", &WorkloadIdentityCredential{}, false}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.want, tc.cred.NeedAuthenticate()) + }) + } +} ++++++ kubelogin.obsinfo ++++++ --- /var/tmp/diff_new_pack.M77kHC/_old 2026-09-24 23:01:39.516763637 +0200 +++ /var/tmp/diff_new_pack.M77kHC/_new 2026-09-24 23:01:39.519763762 +0200 @@ -1,5 +1,5 @@ name: kubelogin -version: 0.2.19 -mtime: 1782235607 -commit: a9b10fbf8422f0c5b687eb58f26d7995f2fe206d +version: 0.2.20 +mtime: 1790185295 +commit: 51be4471f0367039d4f79029f2ead5408199076d ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/kubelogin/vendor.tar.gz /work/SRC/openSUSE:Factory/.kubelogin.new.383539/vendor.tar.gz differ: char 12, line 1
