Repository: ratify-project/ratify
Pull request #2038 (2025-01-19 06:35:07)

chore: automated PR to main 2025-01-19
Automated Pull Request to main branch


Patch:
diff --git a/.github/crd.trivyignore.yaml b/.github/crd.trivyignore.yaml
new file mode 100644
index 0000000..bfa6424
--- /dev/null
+++ b/.github/crd.trivyignore.yaml
@@ -0,0 +1,3 @@
+vulnerabilities:
+  - id: CVE-2024-45338
+    statement: kubectl is not vulnerable to this and is reason for being flagged
\ No newline at end of file
diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml
index 0dd6e71..b36a54a 100644
--- a/.github/workflows/build-pr.yml
+++ b/.github/workflows/build-pr.yml
@@ -70,7 +70,7 @@ jobs:
     environment: azure-test
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/.github/workflows/cache-cleanup.yml b/.github/workflows/cache-cleanup.yml
index af26b92..97267a2 100644
--- a/.github/workflows/cache-cleanup.yml
+++ b/.github/workflows/cache-cleanup.yml
@@ -12,7 +12,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/.github/workflows/clean-dev-package.yml b/.github/workflows/clean-dev-package.yml
index 0cbdbb5..e6367c3 100644
--- a/.github/workflows/clean-dev-package.yml
+++ b/.github/workflows/clean-dev-package.yml
@@ -13,7 +13,7 @@ jobs:
       packages: write
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 173fb4e..b152ae6 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -26,7 +26,7 @@ jobs:
 
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -37,7 +37,7 @@ jobs:
         with:
           go-version: "1.22"
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # tag=v3.28.0
+        uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # tag=v3.28.1
         with:
           languages: go
       - name: Run tidy
@@ -45,4 +45,4 @@ jobs:
       - name: Build CLI
         run: make build
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # tag=v3.28.0
+        uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # tag=v3.28.1
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index d74fea8..73c9e3a 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -24,7 +24,7 @@ jobs:
           go-version: "1.22"
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
       - name: golangci-lint
-        uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1
+        uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # v6.2.0
         with:
           version: v1.62.2
           args: --timeout=10m
diff --git a/.github/workflows/high-availability.yml b/.github/workflows/high-availability.yml
index 23a3fd2..eae0d58 100644
--- a/.github/workflows/high-availability.yml
+++ b/.github/workflows/high-availability.yml
@@ -30,7 +30,7 @@ jobs:
         DAPR_VERSION: ["1.14.4"]
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -60,7 +60,7 @@ jobs:
           kubectl logs -n gatekeeper-system -l app=ratify --tail=-1 > logs-ratify-preinstall-${{ matrix.DAPR_VERSION }}.json
           kubectl logs -n gatekeeper-system -l app.kubernetes.io/name=ratify --tail=-1 > logs-ratify-${{ matrix.DAPR_VERSION }}.json
       - name: Upload artifacts
-        uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: ${{ always() }}
         with:
           name: e2e-logs-${{ matrix.DAPR_VERSION }}
diff --git a/.github/workflows/pr-to-main.yml b/.github/workflows/pr-to-main.yml
index df966cf..13e308c 100644
--- a/.github/workflows/pr-to-main.yml
+++ b/.github/workflows/pr-to-main.yml
@@ -13,7 +13,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/.github/workflows/publish-charts.yml b/.github/workflows/publish-charts.yml
index aa2069e..58fcb68 100644
--- a/.github/workflows/publish-charts.yml
+++ b/.github/workflows/publish-charts.yml
@@ -13,7 +13,7 @@ jobs:
       contents: write
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/.github/workflows/publish-cosign-sample.yml b/.github/workflows/publish-cosign-sample.yml
index e2064db..4b7f1ca 100644
--- a/.github/workflows/publish-cosign-sample.yml
+++ b/.github/workflows/publish-cosign-sample.yml
@@ -20,7 +20,7 @@ jobs:
       id-token: write
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/.github/workflows/publish-dev-assets.yml b/.github/workflows/publish-dev-assets.yml
index 4e4d1bf..c68551c 100644
--- a/.github/workflows/publish-dev-assets.yml
+++ b/.github/workflows/publish-dev-assets.yml
@@ -17,7 +17,7 @@ jobs:
     environment: azure-publish
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
       - name: Checkout
diff --git a/.github/workflows/publish-package.yml b/.github/workflows/publish-package.yml
index e8036f2..f2db01b 100644
--- a/.github/workflows/publish-package.yml
+++ b/.github/workflows/publish-package.yml
@@ -18,7 +18,7 @@ jobs:
     environment: azure-publish
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
       - name: Checkout
diff --git a/.github/workflows/publish-sample.yml b/.github/workflows/publish-sample.yml
index 54a2157..382b87d 100644
--- a/.github/workflows/publish-sample.yml
+++ b/.github/workflows/publish-sample.yml
@@ -19,7 +19,7 @@ jobs:
       packages: write
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/.github/workflows/quick-start.yml b/.github/workflows/quick-start.yml
index 8152923..2c0525e 100644
--- a/.github/workflows/quick-start.yml
+++ b/.github/workflows/quick-start.yml
@@ -30,7 +30,7 @@ jobs:
         KUBERNETES_VERSION: ["1.30.6"]
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -59,7 +59,7 @@ jobs:
           kubectl logs -n gatekeeper-system -l app=ratify --tail=-1 > logs-ratify-preinstall-${{ matrix.KUBERNETES_VERSION }}-config-policy.json
           kubectl logs -n gatekeeper-system -l app.kubernetes.io/name=ratify --tail=-1 > logs-ratify-${{ matrix.KUBERNETES_VERSION }}-config-policy.json
       - name: Upload artifacts
-        uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: ${{ always() }}
         with:
           name: e2e-logs-${{ matrix.KUBERNETES_VERSION }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e7c1a05..0c99c9c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -16,7 +16,7 @@ jobs:
       contents: write
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -49,7 +49,7 @@ jobs:
           $RUNNER_TEMP/sbom-tool generate -b . -bc . -pn ratify -pv $GITHUB_REF_NAME -ps Microsoft -nsb https://microsoft.com -V Verbose
 
       - name: Upload a Build Artifact
-        uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # tag=v4.5.0
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # tag=v4.6.0
         with:
           name: SBOM SPDX files
           path: _manifest/spdx_2.2/**
diff --git a/.github/workflows/run-full-validation.yml b/.github/workflows/run-full-validation.yml
index 62a6127..6911e40 100644
--- a/.github/workflows/run-full-validation.yml
+++ b/.github/workflows/run-full-validation.yml
@@ -58,7 +58,7 @@ jobs:
     environment: azure-test
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/.github/workflows/scan-vulns.yaml b/.github/workflows/scan-vulns.yaml
index 1d0b852..c39bbc1 100644
--- a/.github/workflows/scan-vulns.yaml
+++ b/.github/workflows/scan-vulns.yaml
@@ -23,7 +23,7 @@ jobs:
     timeout-minutes: 15
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -38,10 +38,10 @@ jobs:
     runs-on: ubuntu-22.04
     timeout-minutes: 15
     env:
-      TRIVY_VERSION: 0.49.1
+      TRIVY_VERSION: 0.58.2
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -73,6 +73,5 @@ jobs:
           done
       - name: Run trivy on images and exit on HIGH/CRITICAL severity
         run: |
-          for img in "localbuild:test" "localbuildcrd:test"; do
-              trivy image --skip-db-update --ignore-unfixed --exit-code 1 --severity HIGH,CRITICAL --vuln-type="os,library" "${img}"
-          done
\ No newline at end of file
+          trivy image --skip-db-update --ignore-unfixed --exit-code 1 --severity HIGH,CRITICAL --vuln-type="os,library" "localbuild:test"
+          trivy image --skip-db-update --ignore-unfixed --exit-code 1 --severity HIGH,CRITICAL --vuln-type="os,library" --show-suppressed --ignorefile ./.github/crd.trivyignore.yaml "localbuildcrd:test"
\ No newline at end of file
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 2d85f87..520ae1b 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -30,7 +30,7 @@ jobs:
 
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -48,13 +48,13 @@ jobs:
           publish_results: true
 
       - name: "Upload artifact"
-        uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # tag=v4.5.0
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # tag=v4.6.0
         with:
           name: SARIF file
           path: results.sarif
           retention-days: 5
 
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # tag=v3.28.0
+        uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # tag=v3.28.1
         with:
           sarif_file: results.sarif
diff --git a/.github/workflows/sync-gh-pages.yml b/.github/workflows/sync-gh-pages.yml
index 55069d7..01d62cd 100644
--- a/.github/workflows/sync-gh-pages.yml
+++ b/.github/workflows/sync-gh-pages.yml
@@ -17,7 +17,7 @@ jobs:
       repository-projects: write
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/charts/ratify/Chart.yaml b/charts/ratify/Chart.yaml
index b516594..a6a6fde 100644
--- a/charts/ratify/Chart.yaml
+++ b/charts/ratify/Chart.yaml
@@ -1,7 +1,7 @@
 apiVersion: v2
 name: ratify
 description: A Helm chart for Ratify
-version: 1.14.1
-appVersion: v1.3.1
+version: 1.15.0-rc.1
+appVersion: v1.4.0-rc.1
 home: https://github.com/ratify-project/ratify
 icon: https://raw.githubusercontent.com/ratify-project/ratify/main/logo.svg
diff --git a/charts/ratify/values.yaml b/charts/ratify/values.yaml
index c60c219..537ccb2 100644
--- a/charts/ratify/values.yaml
+++ b/charts/ratify/values.yaml
@@ -1,7 +1,7 @@
 image:
   repository: ghcr.io/ratify-project/ratify
   crdRepository: ghcr.io/ratify-project/ratify-crds
-  tag: v1.3.1
+  tag: v1.4.0-rc.1
   pullPolicy: IfNotPresent
 
 nameOverride: ""
diff --git a/docs/discussion/Multi-Tenancy Support.md b/docs/discussion/Multi-Tenancy Support.md
index b9c339f..db972e9 100644
--- a/docs/discussion/Multi-Tenancy Support.md
+++ b/docs/discussion/Multi-Tenancy Support.md
@@ -96,7 +96,7 @@ Ratify can be deployed either per namespace or per cluster. Redis can also be de
 |  | Ratify per Namespace | Ratify per Cluster |
 |--|--|--|
 | Redis per Namespace | Data Isolated | It requires Ratify route requests to Redis instances in different namespaces |
-| Redis per Cluster | Prefix keys and restrict access via [ACL](https://redis.io/docs/management/security/acl/) | Prefix keys and restrict access via [ACL](https://redis.io/docs/management/security/acl/) |
+| Redis per Cluster | Prefix keys and restrict access via [ACL](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/) | Prefix keys and restrict access via [ACL](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/) |
 
 #### Log
 Ratify saves logs to the standard output and then handled by container runtime. Whatever path does container runtime save logs, those logs may include data from different teams. This situation mirrors the challenges observed with cache isolation.
diff --git a/docs/proposals/Release-Supply-Chain-Metadata.md b/docs/proposals/Release-Supply-Chain-Metadata.md
index e50806d..3445b1f 100644
--- a/docs/proposals/Release-Supply-Chain-Metadata.md
+++ b/docs/proposals/Release-Supply-Chain-Metadata.md
@@ -67,7 +67,7 @@ There are multiple tools for generating SBOMS such as Syft, Trivy, Microsoft SBO
 
 ### Docker Buildx Attestations
 
-Recently, Ratify added support for generating SBOM in-toto attestations via buildx. Buildx supports generating SBOM via Syft and attaching the attestation as an OCI Image part of the top level image index. Read more [here](https://ratify.dev/docs/next/troubleshoot/security#build-attestations).
+Recently, Ratify added support for generating SBOM in-toto attestations via buildx. Buildx supports generating SBOM via Syft and attaching the attestation as an OCI Image part of the top level image index. Read more [here](https://ratify.dev/docs/next/reference/security#build-attestations).
 
 Other OSS projects such as Gatekeeper publish attestations in a similar fashion. It's very simple to add such support making adoption more widespread.
 
diff --git a/go.mod b/go.mod
index 0d995e4..3260735 100644
--- a/go.mod
+++ b/go.mod
@@ -21,9 +21,9 @@ require (
 	github.com/alibabacloud-go/tea v1.2.2
 	github.com/alibabacloud-go/tea-utils/v2 v2.0.7
 	github.com/aliyun/credentials-go v1.3.11
-	github.com/aws/aws-sdk-go-v2 v1.32.7
-	github.com/aws/aws-sdk-go-v2/config v1.28.7
-	github.com/aws/aws-sdk-go-v2/credentials v1.17.48
+	github.com/aws/aws-sdk-go-v2 v1.32.8
+	github.com/aws/aws-sdk-go-v2/config v1.28.10
+	github.com/aws/aws-sdk-go-v2/credentials v1.17.51
 	github.com/aws/aws-sdk-go-v2/service/ecr v1.28.6
 	github.com/cespare/xxhash/v2 v2.3.0
 	github.com/dapr/go-sdk v1.8.0
@@ -47,7 +47,7 @@ require (
 	github.com/owenrumney/go-sarif/v2 v2.3.3
 	github.com/pkg/errors v0.9.1
 	github.com/sigstore/cosign/v2 v2.2.4
-	github.com/sigstore/sigstore v1.8.11
+	github.com/sigstore/sigstore v1.8.12
 	github.com/sirupsen/logrus v1.9.3
 	github.com/spdx/tools-golang v0.5.5
 	github.com/spf13/cobra v1.8.1
@@ -94,7 +94,7 @@ require (
 	github.com/clbanning/mxj/v2 v2.7.0 // indirect
 	github.com/cloudflare/circl v1.3.7 // indirect
 	github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
-	github.com/coreos/go-oidc/v3 v3.11.0 // indirect
+	github.com/coreos/go-oidc/v3 v3.12.0 // indirect
 	github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
 	github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
 	github.com/dimchansky/utfbom v1.1.1 // indirect
@@ -145,14 +145,14 @@ require (
 	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
 	github.com/OneOfOne/xxhash v1.2.8 // indirect
 	github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
-	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect
-	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 // indirect
 	github.com/aws/smithy-go v1.22.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/blang/semver v3.5.1+incompatible // indirect
@@ -214,14 +214,14 @@ require (
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
 	github.com/sassoftware/relic v7.2.1+incompatible // indirect
-	github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
+	github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
 	github.com/shibumi/go-pathspec v1.3.0 // indirect
 	github.com/sigstore/rekor v1.3.7
 	github.com/spf13/afero v1.11.0 // indirect
 	github.com/spf13/cast v1.7.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/spf13/viper v1.19.0 // indirect
-	github.com/stretchr/testify v1.9.0
+	github.com/stretchr/testify v1.10.0
 	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
 	github.com/theupdateframework/go-tuf v0.7.0 // indirect
@@ -240,13 +240,13 @@ require (
 	go.uber.org/atomic v1.11.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.27.0 // indirect
-	golang.org/x/crypto v0.31.0
+	golang.org/x/crypto v0.32.0
 	golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
 	golang.org/x/mod v0.22.0 // indirect
 	golang.org/x/net v0.33.0 // indirect
-	golang.org/x/oauth2 v0.24.0 // indirect
-	golang.org/x/sys v0.28.0 // indirect
-	golang.org/x/term v0.27.0 // indirect
+	golang.org/x/oauth2 v0.25.0 // indirect
+	golang.org/x/sys v0.29.0 // indirect
+	golang.org/x/term v0.28.0 // indirect
 	golang.org/x/text v0.21.0 // indirect
 	golang.org/x/time v0.8.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect
diff --git a/go.sum b/go.sum
index d410091..2c54cd3 100644
--- a/go.sum
+++ b/go.sum
@@ -170,18 +170,18 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
 github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
-github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw=
-github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
-github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE=
-github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8=
+github.com/aws/aws-sdk-go-v2 v1.32.8 h1:cZV+NUS/eGxKXMtmyhtYPJ7Z4YLoI/V8bkTdRZfYhGo=
+github.com/aws/aws-sdk-go-v2 v1.32.8/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
+github.com/aws/aws-sdk-go-v2/config v1.28.10 h1:fKODZHfqQu06pCzR69KJ3GuttraRJkhlC8g80RZ0Dfg=
+github.com/aws/aws-sdk-go-v2/config v1.28.10/go.mod h1:PvdxRYZ5Um9QMq9PQ0zHHNdtKK+he2NHtFCUFMXWXeg=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.51 h1:F/9Sm6Y6k4LqDesZDPJCLxQGXNNHd/ZtJiWd0lCZKRk=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.51/go.mod h1:TKbzCHm43AoPyA+iLGGcruXd4AFhF8tOmLex2R9jWNQ=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 h1:IBAoD/1d8A8/1aA8g4MBVtTRHhXRiNAgwdbo/xRM2DI=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23/go.mod h1:vfENuCM7dofkgKpYzuzf1VT1UKkA/YL3qanfBn7HCaA=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 h1:jSJjSBzw8VDIbWv+mmvBSP8ezsztMYJGH+eKqi9AmNs=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27/go.mod h1:/DAhLbFRgwhmvJdOfSm+WwikZrCuUJiA4WgJG0fTNSw=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 h1:l+X4K77Dui85pIj5foXDhPlnqcNRG2QUyvca300lXh8=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27/go.mod h1:KvZXSFEXm6x84yE8qffKvT3x8J5clWnVFXphpohhzJ8=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
 github.com/aws/aws-sdk-go-v2/service/ecr v1.28.6 h1:CnQNpQv+WGl5aECyAXrJ4w+Qccz2aC/uXg2OjxiPl30=
@@ -190,16 +190,16 @@ github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.23.7 h1:dsmihXaPkhFuUTiL+ygm9R
 github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.23.7/go.mod h1:g7If3uXj+mKcmIuxh08qh8I9ju6f/aOSWMyc6hEEi58=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 h1:cWno7lefSH6Pp+mSznagKCgfDGeZRin66UvYUqAkyeA=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8/go.mod h1:tPD+VjU3ABTBoEJ3nctu5Nyg4P4yjqSH5bJGGkY4+XE=
 github.com/aws/aws-sdk-go-v2/service/kms v1.37.5 h1:5dQJ6Q5QrQOqZxXjSbRXukBqU8Pgu6Ro6Qqtyd8yiz4=
 github.com/aws/aws-sdk-go-v2/service/kms v1.37.5/go.mod h1:A9vfQcNHVBCE7ZZN6H+UUJpXtbH26Vv6L7Zhk5nIJAY=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc=
+github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 h1:YqtxripbjWb2QLyzRK9pByfEDvgg95gpC2AyDq4hFE8=
+github.com/aws/aws-sdk-go-v2/service/sso v1.24.9/go.mod h1:lV8iQpg6OLOfBnqbGMBKYjilBlf633qwHnBEiMSPoHY=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 h1:6dBT1Lz8fK11m22R+AqfRsFn8320K0T5DTGxxOQBSMw=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8/go.mod h1:/kiBvRQXBc6xeJTYzhSdGvJ5vm1tjaDEjH+MSeRJnlY=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 h1:VwhTrsTuVn52an4mXx29PqRzs2Dvu921NpGk7y43tAM=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.6/go.mod h1:+8h7PZb3yY5ftmVLD7ocEoE98hdc8PoKS0H3wfx1dlc=
 github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
 github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
 github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 h1:SoFYaT9UyGkR0+nogNyD/Lj+bsixB+SNuAS4ABlEs6M=
@@ -248,8 +248,8 @@ github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL
 github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w=
 github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
 github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
-github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
-github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
+github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
+github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
 github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 h1:2Dx4IHfC1yHWI12AxQDJM1QbRCDfk6M+blLzlZCXdrc=
 github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
@@ -651,8 +651,8 @@ github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGq
 github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk=
 github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4=
 github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k=
-github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA=
-github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU=
+github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc=
+github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw=
 github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
 github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
 github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
@@ -663,8 +663,8 @@ github.com/sigstore/fulcio v1.4.5 h1:WWNnrOknD0DbruuZWCbN+86WRROpEl3Xts+WT2Ek1yc
 github.com/sigstore/fulcio v1.4.5/go.mod h1:oz3Qwlma8dWcSS/IENR/6SjbW4ipN0cxpRVfgdsjMU8=
 github.com/sigstore/rekor v1.3.7 h1:Z5UW5TmqbTZnyOFkMRfi32q/CWcxK6VuzIkx+33mbq8=
 github.com/sigstore/rekor v1.3.7/go.mod h1:TihqJscZ6L6398x68EHY82t0AOnGYfrQ0siXe3WgbR4=
-github.com/sigstore/sigstore v1.8.11 h1:tEqeQqbT+awtM87ec9KEeSUxT/AFvJNawneYJyAkFrQ=
-github.com/sigstore/sigstore v1.8.11/go.mod h1:fdrFQosxCQ4wTL5H1NrZcQkqQ72AQbPjtpcL2QOGKV0=
+github.com/sigstore/sigstore v1.8.12 h1:S8xMVZbE2z9ZBuQUEG737pxdLjnbOIcFi5v9UFfkJFc=
+github.com/sigstore/sigstore v1.8.12/go.mod h1:+PYQAa8rfw0QdPpBcT+Gl3egKD9c+TUgAlF12H3Nmjo=
 github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.10 h1:e5GfVngPjGap/N3ODefayt7vKIPS1/v3hWLZ9+4MrN4=
 github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.10/go.mod h1:HOr3AdFPKdND2FNl/sUD5ZifPl1OMJvrbf9xIaaWcus=
 github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.10 h1:9tZEpfIL/ewAG9G87AHe3aVoy8Ujos2F1qLfCckX6jQ=
@@ -720,8 +720,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
@@ -833,8 +834,8 @@ golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq
 golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
-golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
-golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
 golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
@@ -882,8 +883,8 @@ golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
 golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
-golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
+golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -929,8 +930,8 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
-golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -943,8 +944,8 @@ golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
 golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
 golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
-golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
-golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
+golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
diff --git a/helmfile.yaml b/helmfile.yaml
index 88e536d..4ea5557 100644
--- a/helmfile.yaml
+++ b/helmfile.yaml
@@ -23,7 +23,7 @@ releases:
   - name: ratify
     namespace: gatekeeper-system
     chart: ratify/ratify
-    version: 1.14.1 # Make sure this matches Chart.yaml
+    version: 1.15.0-rc.1 # Make sure this matches Chart.yaml
     wait: true
     needs:
       - gatekeeper
diff --git a/high-availability.helmfile.yaml b/high-availability.helmfile.yaml
index 311cd3e..ec00199 100644
--- a/high-availability.helmfile.yaml
+++ b/high-availability.helmfile.yaml
@@ -50,7 +50,7 @@ releases:
   - name: ratify
     namespace: gatekeeper-system
     chart: ratify/ratify
-    version: 1.14.1 # Make sure this matches Chart.yaml
+    version: 1.15.0-rc.1 # Make sure this matches Chart.yaml
     wait: true
     needs:
       - dapr-system/dapr
diff --git a/httpserver/Dockerfile b/httpserver/Dockerfile
index b134a05..fa3d21f 100644
--- a/httpserver/Dockerfile
+++ b/httpserver/Dockerfile
@@ -41,7 +41,7 @@ RUN if [ "$build_licensechecker" = "true" ]; then go build -o /app/out/plugins/
 RUN if [ "$build_schemavalidator" = "true" ]; then go build -o /app/out/plugins/ /app/plugins/verifier/schemavalidator; fi
 RUN if [ "$build_vulnerabilityreport" = "true" ]; then go build -o /app/out/plugins/ /app/plugins/verifier/vulnerabilityreport; fi
 
-FROM gcr.io/distroless/static:nonroot@sha256:6cd937e9155bdfd805d1b94e037f9d6a899603306030936a3b11680af0c2ed58
+FROM gcr.io/distroless/static:nonroot@sha256:6ec5aa99dc335666e79dc64e4a6c8b89c33a543a1967f20d360922a80dd21f02
 LABEL org.opencontainers.image.source https://github.com/ratify-project/ratify
 
 ARG RATIFY_FOLDER=$HOME/.ratify/
diff --git a/pkg/keymanagementprovider/azurekeyvault/provider.go b/pkg/keymanagementprovider/azurekeyvault/provider.go
index 5a77692..6127733 100644
--- a/pkg/keymanagementprovider/azurekeyvault/provider.go
+++ b/pkg/keymanagementprovider/azurekeyvault/provider.go
@@ -28,6 +28,7 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	"sort"
 	"strconv"
 	"strings"
 	"time"
@@ -43,6 +44,7 @@ import (
 	"golang.org/x/crypto/pkcs12"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
 	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
 	"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates"
 	"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys"
@@ -50,9 +52,10 @@ import (
 )
 
 const (
-	ProviderName      string = "azurekeyvault"
-	PKCS12ContentType string = "application/x-pkcs12"
-	PEMContentType    string = "application/x-pem-file"
+	ProviderName               string = "azurekeyvault"
+	PKCS12ContentType          string = "application/x-pkcs12"
+	PEMContentType             string = "application/x-pem-file"
+	versionHistoryLimitDefault int    = 1
 )
 
 var logOpt = logger.Option{
@@ -82,12 +85,20 @@ type akvKMProvider struct {
 	certificateKVClient certificateKVClient
 }
 
+// VersionInfo holds the version and created time of an azure key vault object
+type VersionInfo struct {
+	Version string
+	Created time.Time
+}
+
 type akvKMProviderFactory struct{}
 
 // kvClient is an interface to interact with the keyvault client used for mocking purposes
 type keyKVClient interface {
 	// GetKey retrieves a key from the keyvault
 	GetKey(ctx context.Context, keyName string, keyVersion string) (azkeys.GetKeyResponse, error)
+	// NewListKeyVersionsPager retrieves a pager for listing key versions
+	NewListKeyVersionsPager(name string, options *azkeys.ListKeyVersionsOptions) *runtime.Pager[azkeys.ListKeyVersionsResponse]
 }
 type secretKVClient interface {
 	// GetSecret retrieves a secret from the keyvault
@@ -96,6 +107,8 @@ type secretKVClient interface {
 type certificateKVClient interface {
 	// GetCertificate retrieves a certificate from the keyvault
 	GetCertificate(ctx context.Context, certificateName string, certificateVersion string) (azcertificates.GetCertificateResponse, error)
+	// NewListCertificateVersionsPager creates a new instance of the ListCertificateVersionsPager
+	NewListCertificateVersionsPager(certificateName string, options *azcertificates.ListCertificateVersionsOptions) *runtime.Pager[azcertificates.ListCertificateVersionsResponse]
 }
 
 type keyKVClientImpl struct {
@@ -113,11 +126,21 @@ func (c *certificateKVClientImpl) GetCertificate(ctx context.Context, certificat
 	return c.Client.GetCertificate(ctx, certificateName, certificateVersion, nil)
 }
 
+// NewListCertificateVersionsPager retrieves a pager for listing certificate versions
+func (c *certificateKVClientImpl) NewListCertificateVersionsPager(certificateName string, options *azcertificates.ListCertificateVersionsOptions) *runtime.Pager[azcertificates.ListCertificateVersionsResponse] {
+	return c.Client.NewListCertificateVersionsPager(certificateName, options)
+}
+
 // GetKey retrieves a key from the keyvault
 func (c *keyKVClientImpl) GetKey(ctx context.Context, keyName string, keyVersion string) (azkeys.GetKeyResponse, error) {
 	return c.Client.GetKey(ctx, keyName, keyVersion, nil)
 }
 
+// NewListKeyVersionsPager retrieves a pager for listing key versions
+func (c *keyKVClientImpl) NewListKeyVersionsPager(keyName string, options *azkeys.ListKeyVersionsOptions) *runtime.Pager[azkeys.ListKeyVersionsResponse] {
+	return c.Client.NewListKeyVersionsPager(keyName, options)
+}
+
 // GetSecret retrieves a secret from the keyvault
 func (c *secretKVClientImpl) GetSecret(ctx context.Context, secretName string, secretVersion string) (azsecrets.GetSecretResponse, error) {
 	return c.Client.GetSecret(ctx, secretName, secretVersion, nil)
@@ -186,39 +209,114 @@ func (s *akvKMProvider) GetCertificates(ctx context.Context) (map[keymanagementp
 		logger.GetLogger(ctx, logOpt).Debugf("fetching secret from key vault, certName %v, certVersion %v, vaultURI: %v", keyVaultCert.Name, keyVaultCert.Version, s.vaultURI)
 
 		startTime := time.Now()
-		secretResponse, err := s.secretKVClient.GetSecret(ctx, keyVaultCert.Name, keyVaultCert.Version)
-		if err != nil {
-			if isSecretDisabledError(err) {
-				// if secret is disabled, get the version of the certificate for status
-				certResponse, err := s.certificateKVClient.GetCertificate(ctx, keyVaultCert.Name, keyVaultCert.Version)
-				if err != nil {
-					return nil, nil, fmt.Errorf("failed to get certificate objectName:%s, objectVersion:%s, error: %w", keyVaultCert.Name, keyVaultCert.Version, err)
+
+		// if versionHistoryLimit isn't used, fetch a single version without pager to avoid breaking changes caused by needing list permissions on the keyvault
+		if keyVaultCert.VersionHistoryLimit == 0 {
+			secretResponse, err := s.secretKVClient.GetSecret(ctx, keyVaultCert.Name, keyVaultCert.Version)
+			if err != nil {
+				if isSecretDisabledError(err) {
+					// if secret is disabled, get the version of the certificate for status
+					certResponse, err := s.certificateKVClient.GetCertificate(ctx, keyVaultCert.Name, keyVaultCert.Version)
+					if err != nil {
+						return nil, nil, fmt.Errorf("failed to get certificate objectName:%s, objectVersion:%s, error: %w", keyVaultCert.Name, keyVaultCert.Version, err)
+					}
+					certBundle := certResponse.CertificateBundle
+					disabledVersion := getObjectVersion(*certBundle.KID)
+					isEnabled := *certBundle.Attributes.Enabled
+					lastRefreshed := startTime.Format(time.RFC3339)
+					certProperty := getStatusProperty(keyVaultCert.Name, disabledVersion, lastRefreshed, isEnabled)
+					certsStatus = append(certsStatus, certProperty)
+					mapKey := keymanagementprovider.KMPMapKey{Name: keyVaultCert.Name, Version: disabledVersion, Enabled: isEnabled}
+					keymanagementprovider.DeleteCertificateFromMap(s.resource, mapKey)
+					continue
 				}
-				certBundle := certResponse.CertificateBundle
-				keyVaultCert.Version = getObjectVersion(*certBundle.KID)
-				isEnabled := *certBundle.Attributes.Enabled
-				lastRefreshed := startTime.Format(time.RFC3339)
-				certProperty := getStatusProperty(keyVaultCert.Name, keyVaultCert.Version, lastRefreshed, isEnabled)
-				certsStatus = append(certsStatus, certProperty)
-				mapKey := keymanagementprovider.KMPMapKey{Name: keyVaultCert.Name, Version: keyVaultCert.Version, Enabled: isEnabled}
-				keymanagementprovider.DeleteCertificateFromMap(s.resource, mapKey)
+				return nil, nil, fmt.Errorf("failed to get secret objectName:%s, objectVersion:%s, error: %w", keyVaultCert.Name, keyVaultCert.Version, err)
+			}
+
+			secretBundle := secretResponse.SecretBundle
+			if secretBundle.Attributes == nil || secretBundle.Attributes.Enabled == nil {
+				logger.GetLogger(ctx, logOpt).Warnf("certificate %s, version %s, found invalid secret bundle, attributes or attribute.enabled not be nil", keyVaultCert.Name, keyVaultCert.Version)
 				continue
 			}
-			return nil, nil, fmt.Errorf("failed to get secret objectName:%s, objectVersion:%s, error: %w", keyVaultCert.Name, keyVaultCert.Version, err)
+			isEnabled := *secretBundle.Attributes.Enabled
+
+			certResult, certProperty, err := getCertsFromSecretBundle(ctx, secretBundle, keyVaultCert.Name, isEnabled)
+			if err != nil {
+				return nil, nil, fmt.Errorf("failed to get certificates from secret bundle:%w", err)
+			}
+
+			metrics.ReportAKVCertificateDuration(ctx, time.Since(startTime).Milliseconds(), keyVaultCert.Name)
+			certsStatus = append(certsStatus, certProperty...)
+			certMapKey := keymanagementprovider.KMPMapKey{Name: keyVaultCert.Name, Version: keyVaultCert.Version, Enabled: isEnabled}
+			certsMap[certMapKey] = certResult
+			continue
 		}
 
-		secretBundle := secretResponse.SecretBundle
-		isEnabled := *secretBundle.Attributes.Enabled
+		var versionHistory []VersionInfo
+
+		certVersionPager := s.certificateKVClient.NewListCertificateVersionsPager(keyVaultCert.Name, &azcertificates.ListCertificateVersionsOptions{})
+		for certVersionPager.More() {
+			pager, err := certVersionPager.NextPage(ctx)
+			if err != nil {
+				return nil, nil, fmt.Errorf("failed to get certificate versions for objectName:%s, error: %w", keyVaultCert.Name, err)
+			}
+			for _, cert := range pager.Value {
+				if cert.ID == nil || cert.Attributes == nil || cert.Attributes.Created == nil {
+					logger.GetLogger(ctx, logOpt).Warnf("certificate %s, version %s, found invalid certificate object, id, attributes or created time must not be nil", keyVaultCert.Name, cert.ID.Version())
+					continue
+				}
+				versionInfo := VersionInfo{
+					Version: cert.ID.Version(),
+					Created: *cert.Attributes.Created,
+				}
+				versionHistory = append(versionHistory, versionInfo)
+			}
+		}
 
-		certResult, certProperty, err := getCertsFromSecretBundle(ctx, secretBundle, keyVaultCert.Name, isEnabled)
+		versions, err := processVersionHistory(versionHistory, keyVaultCert.VersionHistoryLimit, keyVaultCert.Version)
 		if err != nil {
-			return nil, nil, fmt.Errorf("failed to get certificates from secret bundle:%w", err)
+			logger.GetLogger(ctx, logOpt).Errorf("failed to process version history for certificate %s: %v", keyVaultCert.Name, err)
+			continue
+		}
+
+		if len(versions) == 0 {
+			logger.GetLogger(ctx, logOpt).Infof("no versions found for certificate %s", keyVaultCert.Name)
+			continue
 		}
 
-		metrics.ReportAKVCertificateDuration(ctx, time.Since(startTime).Milliseconds(), keyVaultCert.Name)
-		certsStatus = append(certsStatus, certProperty...)
-		certMapKey := keymanagementprovider.KMPMapKey{Name: keyVaultCert.Name, Version: keyVaultCert.Version, Enabled: isEnabled}
-		certsMap[certMapKey] = certResult
+		// get the latest version of the certificate up to the limit
+		for _, version := range versions {
+			secretReponse, err := s.secretKVClient.GetSecret(ctx, keyVaultCert.Name, version)
+			if err != nil {
+				if isSecretDisabledError(err) {
+					isEnabled := false
+					lastRefreshed := startTime.Format(time.RFC3339)
+					certProperty := getStatusProperty(keyVaultCert.Name, version, lastRefreshed, isEnabled)
+					certsStatus = append(certsStatus, certProperty)
+					mapKey := keymanagementprovider.KMPMapKey{Name: keyVaultCert.Name, Version: version, Enabled: isEnabled}
+					keymanagementprovider.DeleteCertificateFromMap(s.resource, mapKey)
+					continue
+				}
+				return nil, nil, fmt.Errorf("failed to get secret objectName:%s, objectVersion:%s, error: %w", keyVaultCert.Name, version, err)
+			}
+
+			secretBundle := secretReponse.SecretBundle
+			if secretBundle.Attributes == nil || secretBundle.Attributes.Enabled == nil {
+				logger.GetLogger(ctx, logOpt).Warnf("certificate %s, version %s, found invalid secret bundle, attributes or attribute.enabled not be nil", keyVaultCert.Name, version)
+				continue
+			}
+			isEnabled := *secretBundle.Attributes.Enabled
+
+			certResult, certProperty, err := getCertsFromSecretBundle(ctx, secretBundle, keyVaultCert.Name, isEnabled)
+			if err != nil {
+				return nil, nil, fmt.Errorf("failed to get certificates from secret bundle:%w", err)
+			}
+
+			metrics.ReportAKVCertificateDuration(ctx, time.Since(startTime).Milliseconds(), keyVaultCert.Name)
+			certsStatus = append(certsStatus, certProperty...)
+			certMapKey := keymanagementprovider.KMPMapKey{Name: keyVaultCert.Name, Version: version, Enabled: isEnabled}
+			certsMap[certMapKey] = certResult
+		}
 	}
 	return certsMap, getStatusMap(certsStatus, types.CertificatesStatus), nil
 }
@@ -233,33 +331,105 @@ func (s *akvKMProvider) GetKeys(ctx context.Context) (map[keymanagementprovider.
 
 		// fetch the key object from Key Vault
 		startTime := time.Now()
-		keyResponse, err := s.keyKVClient.GetKey(ctx, keyVaultKey.Name, keyVaultKey.Version)
-		if err != nil {
-			return nil, nil, fmt.Errorf("failed to get key objectName:%s, objectVersion:%s, error: %w", keyVaultKey.Name, keyVaultKey.Version, err)
-		}
-		keyBundle := keyResponse.KeyBundle
-		isEnabled := *keyBundle.Attributes.Enabled
-		// if version is set as "" in the config, use the version from the key bundle
-		keyVaultKey.Version = getObjectVersion(string(*keyBundle.Key.KID))
-
-		if !isEnabled {
-			startTime := time.Now()
-			lastRefreshed := startTime.Format(time.RFC3339)
-			properties := getStatusProperty(keyVaultKey.Name, keyVaultKey.Version, lastRefreshed, isEnabled)
+		// if versionHistoryLimit isn't used, fetch a single version without pager to avoid breaking changes caused by needing list permissions on the keyvault
+		if keyVaultKey.VersionHistoryLimit == 0 {
+			keyResponse, err := s.keyKVClient.GetKey(ctx, keyVaultKey.Name, keyVaultKey.Version)
+			if err != nil {
+				return nil, nil, fmt.Errorf("failed to get key objectName:%s, objectVersion:%s, error: %w", keyVaultKey.Name, keyVaultKey.Version, err)
+			}
+
+			keyBundle := keyResponse.KeyBundle
+			if keyBundle.Attributes == nil || keyBundle.Attributes.Enabled == nil {
+				logger.GetLogger(ctx, logOpt).Warnf("key %s, version %s, found invalid key bundle, attributes or attribute.enabled not be nil", keyVaultKey.Name, keyVaultKey.Version)
+				continue
+			}
+			isEnabled := *keyBundle.Attributes.Enabled
+
+			keyVaultKey.Version = getObjectVersion(string(*keyBundle.Key.KID))
+
+			if !isEnabled {
+				startTime := time.Now()
+				lastRefreshed := startTime.Format(time.RFC3339)
+				properties := getStatusProperty(keyVaultKey.Name, keyVaultKey.Version, lastRefreshed, isEnabled)
+				keysStatus = append(keysStatus, properties)
+				mapKey := keymanagementprovider.KMPMapKey{Name: keyVaultKey.Name, Version: keyVaultKey.Version, Enabled: isEnabled}
+				keymanagementprovider.DeleteKeyFromMap(s.resource, mapKey)
+				continue
+			}
+
+			publicKey, err := getKeyFromKeyBundle(keyBundle)
+			if err != nil {
+				return nil, nil, fmt.Errorf("failed to get key from key bundle:%w", err)
+			}
+			keysMap[keymanagementprovider.KMPMapKey{Name: keyVaultKey.Name, Version: keyVaultKey.Version, Enabled: isEnabled}] = publicKey
+			metrics.ReportAKVCertificateDuration(ctx, time.Since(startTime).Milliseconds(), keyVaultKey.Name)
+			properties := getStatusProperty(keyVaultKey.Name, keyVaultKey.Version, time.Now().Format(time.RFC3339), isEnabled)
 			keysStatus = append(keysStatus, properties)
-			mapKey := keymanagementprovider.KMPMapKey{Name: keyVaultKey.Name, Version: keyVaultKey.Version, Enabled: isEnabled}
-			keymanagementprovider.DeleteKeyFromMap(s.resource, mapKey)
 			continue
 		}
 
-		publicKey, err := getKeyFromKeyBundle(keyBundle)
+		versionHistory := []VersionInfo{}
+
+		keyVersionPager := s.keyKVClient.NewListKeyVersionsPager(keyVaultKey.Name, &azkeys.ListKeyVersionsOptions{})
+		for keyVersionPager.More() {
+			pager, err := keyVersionPager.NextPage(ctx)
+			if err != nil {
+				return nil, nil, fmt.Errorf("failed to get key versions for objectName:%s, error: %w", keyVaultKey.Name, err)
+			}
+			for _, key := range pager.Value {
+				if key.KID == nil || key.Attributes == nil || key.Attributes.Created == nil {
+					logger.GetLogger(ctx, logOpt).Warnf("key %s, version %s, found invalid key object, id, attributes or created time must not be nil", keyVaultKey.Name, key.KID.Version())
+					continue
+				}
+				versionInfo := VersionInfo{
+					Version: key.KID.Version(),
+					Created: *key.Attributes.Created,
+				}
+				versionHistory = append(versionHistory, versionInfo)
+			}
+		}
+
+		versions, err := processVersionHistory(versionHistory, keyVaultKey.VersionHistoryLimit, keyVaultKey.Version)
 		if err != nil {
-			return nil, nil, fmt.Errorf("failed to get key from key bundle:%w", err)
+			logger.GetLogger(ctx, logOpt).Errorf("failed to process version history for key %s: %v", keyVaultKey.Name, err)
+			continue
+		}
+		if len(versions) == 0 {
+			logger.GetLogger(ctx, logOpt).Infof("no versions found for key %s", keyVaultKey.Name)
+			continue
+		}
+
+		// get the latest version of the certificate up to the limit
+		for _, version := range versions {
+			keyResponse, err := s.keyKVClient.GetKey(ctx, keyVaultKey.Name, version)
+			if err != nil {
+				return nil, nil, fmt.Errorf("failed to get key objectName:%s, objectVersion:%s, error: %w", keyVaultKey.Name, version, err)
+			}
+			keyBundle := keyResponse.KeyBundle
+			if keyBundle.Attributes == nil || keyBundle.Attributes.Enabled == nil {
+				logger.GetLogger(ctx, logOpt).Warnf("key %s, version %s, found invalid key bundle, attributes or attribute.enabled not be nil", keyVaultKey.Name, version)
+				continue
+			}
+			isEnabled := *keyBundle.Attributes.Enabled
+
+			if !isEnabled {
+				lastRefresh := time.Now().Format(time.RFC3339)
+				keyProperties := getStatusProperty(keyVaultKey.Name, version, lastRefresh, isEnabled)
+				keysStatus = append(keysStatus, keyProperties)
+				mapKey := keymanagementprovider.KMPMapKey{Name: keyVaultKey.Name, Version: version, Enabled: isEnabled}
+				keymanagementprovider.DeleteKeyFromMap(s.resource, mapKey)
+				continue
+			}
+
+			publicKey, err := getKeyFromKeyBundle(keyBundle)
+			if err != nil {
+				return nil, nil, fmt.Errorf("failed to get key from key bundle:%w", err)
+			}
+			keysMap[keymanagementprovider.KMPMapKey{Name: keyVaultKey.Name, Version: version, Enabled: isEnabled}] = publicKey
+			metrics.ReportAKVCertificateDuration(ctx, time.Since(startTime).Milliseconds(), keyVaultKey.Name)
+			keyProperties := getStatusProperty(keyVaultKey.Name, version, time.Now().Format(time.RFC3339), isEnabled)
+			keysStatus = append(keysStatus, keyProperties)
 		}
-		keysMap[keymanagementprovider.KMPMapKey{Name: keyVaultKey.Name, Version: keyVaultKey.Version, Enabled: isEnabled}] = publicKey
-		metrics.ReportAKVCertificateDuration(ctx, time.Since(startTime).Milliseconds(), keyVaultKey.Name)
-		properties := getStatusProperty(keyVaultKey.Name, keyVaultKey.Version, time.Now().Format(time.RFC3339), isEnabled)
-		keysStatus = append(keysStatus, properties)
 	}
 
 	return keysMap, getStatusMap(keysStatus, types.KeysStatus), nil
@@ -478,6 +648,52 @@ func isSecretDisabledError(err error) bool {
 	return false
 }
 
+// processVersionHistory returns the versions of the object based on the versionHistoryLimit and version
+func processVersionHistory(versionHistory []VersionInfo, versionHistoryLimit int, version string) (versions []string, err error) {
+	sortVersionHistory(versionHistory)
+
+	if version == "" {
+		//Ensure that the versionHistoryLimit is not greater than the number of versions to avoid out of bounds error
+		if versionHistoryLimit > len(versionHistory) {
+			versionHistoryLimit = len(versionHistory)
+		}
+		return getVersions(versionHistory[len(versionHistory)-versionHistoryLimit:]), nil
+	}
+	// Find the index of the specified version
+	versionIndex := -1
+	for i, v := range versionHistory {
+		if v.Version == version {
+			versionIndex = i
+			break
+		}
+	}
+
+	// Calculate the start index to include up to `versionHistoryLimit` older versions
+	start := versionIndex - versionHistoryLimit
+	if start < 0 {
+		start = 0 // Ensure we don't go out of bounds
+	}
+
+	// Create the slice including the specified version as the tail
+	return getVersions(versionHistory[start : versionIndex+1]), nil
+}
+
+// sortVersionHistory sorts the version history by created time
+func sortVersionHistory(sortedVersionHistory []VersionInfo) {
+	sort.Slice(sortedVersionHistory, func(i, j int) bool {
+		return sortedVersionHistory[i].Created.Before(sortedVersionHistory[j].Created)
+	})
+}
+
+// GetVersions returns the sorted versions of the object
+func getVersions(versionHistory []VersionInfo) []string {
+	sortedVersions := make([]string, 0, len(versionHistory))
+	for _, version := range versionHistory {
+		sortedVersions = append(sortedVersions, version.Version)
+	}
+	return sortedVersions
+}
+
 // validate checks vaultURI, tenantID, clientID are set and all certificates/keys have a name
 func (s *akvKMProvider) validate() error {
 	if s.vaultURI == "" {
diff --git a/pkg/keymanagementprovider/azurekeyvault/types/types.go b/pkg/keymanagementprovider/azurekeyvault/types/types.go
index e51650d..3acf572 100644
--- a/pkg/keymanagementprovider/azurekeyvault/types/types.go
+++ b/pkg/keymanagementprovider/azurekeyvault/types/types.go
@@ -37,4 +37,6 @@ type KeyVaultValue struct {
 	Name string `json:"name" yaml:"name"`
 	// the version of the Azure Key Vault certificate/key
 	Version string `json:"version" yaml:"version"`
+	// the number of versions to keep in the cache
+	VersionHistoryLimit int `json:"versionHistoryLimit" yaml:"versionHistoryLimit"`
 }


Test patch:
diff --git a/.github/workflows/e2e-aks.yml b/.github/workflows/e2e-aks.yml
index 1edd4a7..6ba7251 100644
--- a/.github/workflows/e2e-aks.yml
+++ b/.github/workflows/e2e-aks.yml
@@ -28,7 +28,7 @@ jobs:
       contents: read
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -67,7 +67,7 @@ jobs:
           make e2e-aks KUBERNETES_VERSION=${{ inputs.k8s_version }} GATEKEEPER_VERSION=${{ inputs.gatekeeper_version }} TENANT_ID=${{ secrets.AZURE_TENANT_ID }} AZURE_SP_OBJECT_ID=${{ secrets.AZURE_SP_OBJECT_ID }}
 
       - name: Upload artifacts
-        uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: ${{ always() }}
         with:
           name: e2e-logs-aks-${{ inputs.k8s_version }}-${{ inputs.gatekeeper_version }}
diff --git a/.github/workflows/e2e-cli.yml b/.github/workflows/e2e-cli.yml
index cab3dd8..c250f02 100644
--- a/.github/workflows/e2e-cli.yml
+++ b/.github/workflows/e2e-cli.yml
@@ -14,7 +14,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -34,7 +34,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -63,7 +63,7 @@ jobs:
       contents: read
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -93,7 +93,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
diff --git a/.github/workflows/e2e-k8s.yml b/.github/workflows/e2e-k8s.yml
index aeb8d46..b9235e1 100644
--- a/.github/workflows/e2e-k8s.yml
+++ b/.github/workflows/e2e-k8s.yml
@@ -26,7 +26,7 @@ jobs:
       contents: read
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
+        uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
         with:
           egress-policy: audit
 
@@ -66,7 +66,7 @@ jobs:
           kubectl logs -n gatekeeper-system -l app=ratify --tail=-1 > logs-ratify-preinstall-${{ matrix.KUBERNETES_VERSION }}-${{ matrix.GATEKEEPER_VERSION }}-rego-policy.json
           kubectl logs -n gatekeeper-system -l app.kubernetes.io/name=ratify --tail=-1 > logs-ratify-${{ matrix.KUBERNETES_VERSION }}-${{ matrix.GATEKEEPER_VERSION }}-rego-policy.json
       - name: Upload artifacts
-        uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
+        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
         if: ${{ always() }}
         with:
           name: e2e-logs-${{ inputs.k8s_version }}-${{ inputs.gatekeeper_version }}
diff --git a/pkg/keymanagementprovider/azurekeyvault/provider_test.go b/pkg/keymanagementprovider/azurekeyvault/provider_test.go
index 9bb444a..90aa125 100644
--- a/pkg/keymanagementprovider/azurekeyvault/provider_test.go
+++ b/pkg/keymanagementprovider/azurekeyvault/provider_test.go
@@ -28,6 +28,7 @@ import (
 	"time"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
+	"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
 	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
 	"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates"
 	"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys"
@@ -38,6 +39,16 @@ import (
 	"github.com/stretchr/testify/mock"
 )
 
+const rawResponse = `{
+						"error": {
+							"code": "Forbidden",
+							"message": "Operation get is not allowed on a disabled secret.",
+							"innererror": {
+								"code": "SecretDisabled"
+							}
+						}
+					}`
+
 // TestCreate tests the Create function
 func TestCreate(t *testing.T) {
 	factory := &akvKMProviderFactory{}
@@ -46,6 +57,22 @@ func TestCreate(t *testing.T) {
 		config    config.KeyManagementProviderConfig
 		expectErr bool
 	}{
+		{
+			name: "valid config with versionHistory",
+			config: config.KeyManagementProviderConfig{
+				"inline":   "azurekeyvault",
+				"vaultURI": "https://testkv.vault.azure.net/",
+				"tenantID": "tid",
+				"clientID": "clientid",
+				"certificates": []map[string]interface{}{
+					{
+						"name":           "cert1",
+						"versionHistory": 2,
+					},
+				},
+			},
+			expectErr: false,
+		},
 		{
 			name: "valid config",
 			config: config.KeyManagementProviderConfig{
@@ -170,13 +197,15 @@ func TestGetCertificates_original(t *testing.T) {
 }
 
 type MockKeyKVClient struct {
-	GetKeyFunc func(ctx context.Context, keyName string, keyVersion string) (azkeys.GetKeyResponse, error)
+	GetKeyFunc                  func(ctx context.Context, keyName string, keyVersion string) (azkeys.GetKeyResponse, error)
+	NewListKeyVersionsPagerFunc func(keyName string, options *azkeys.ListKeyVersionsOptions) *runtime.Pager[azkeys.ListKeyVersionsResponse]
 }
 type MockSecretKVClient struct {
 	GetSecretFunc func(ctx context.Context, secretName string, secretVersion string) (azsecrets.GetSecretResponse, error)
 }
 type MockCertificateKVClient struct {
-	GetCertificateFunc func(ctx context.Context, certificateName string, certificateVersion string) (azcertificates.GetCertificateResponse, error)
+	GetCertificateFunc                  func(ctx context.Context, certificateName string, certificateVersion string) (azcertificates.GetCertificateResponse, error)
+	NewListCertificateVersionsPagerFunc func(certificateName string, options *azcertificates.ListCertificateVersionsOptions) *runtime.Pager[azcertificates.ListCertificateVersionsResponse]
 }
 
 func (m *MockKeyKVClient) GetKey(ctx context.Context, keyName string, keyVersion string) (azkeys.GetKeyResponse, error) {
@@ -185,12 +214,44 @@ func (m *MockKeyKVClient) GetKey(ctx context.Context, keyName string, keyVersion
 	}
 	return azkeys.GetKeyResponse{}, nil
 }
+
+func (m *MockKeyKVClient) NewListKeyVersionsPager(keyName string, options *azkeys.ListKeyVersionsOptions) *runtime.Pager[azkeys.ListKeyVersionsResponse] {
+	if m.NewListKeyVersionsPagerFunc != nil {
+		return m.NewListKeyVersionsPagerFunc(keyName, options)
+	}
+	KeyCreated := time.Now()
+	return runtime.NewPager(runtime.PagingHandler[azkeys.ListKeyVersionsResponse]{
+		More: func(_ azkeys.ListKeyVersionsResponse) bool {
+			return false
+		},
+		Fetcher: func(_ context.Context, _ *azkeys.ListKeyVersionsResponse) (azkeys.ListKeyVersionsResponse, error) {
+			var resp azkeys.ListKeyVersionsResponse
+			var keyID azkeys.ID = "https://testkv.vault.azure.net/keys/key1/c1f03df1113d460491d970737dfdc35d"
+			resp = azkeys.ListKeyVersionsResponse{
+				KeyListResult: azkeys.KeyListResult{
+					NextLink: nil,
+					Value: []*azkeys.KeyItem{
+						{
+							KID: &keyID,
+							Attributes: &azkeys.KeyAttributes{
+								Created: &KeyCreated,
+							},
+						},
+					},
+				},
+			}
+			return resp, nil
+		},
+	})
+}
+
 func (m *MockSecretKVClient) GetSecret(ctx context.Context, secretName string, secretVersion string) (azsecrets.GetSecretResponse, error) {
 	if m.GetSecretFunc != nil {
 		return m.GetSecretFunc(ctx, secretName, secretVersion)
 	}
 	return azsecrets.GetSecretResponse{}, nil
 }
+
 func (m *MockCertificateKVClient) GetCertificate(ctx context.Context, certificateName string, certificateVersion string) (azcertificates.GetCertificateResponse, error) {
 	if m.GetCertificateFunc != nil {
 		return m.GetCertificateFunc(ctx, certificateName, certificateVersion)
@@ -198,6 +259,36 @@ func (m *MockCertificateKVClient) GetCertificate(ctx context.Context, certificat
 	return azcertificates.GetCertificateResponse{}, nil
 }
 
+func (m *MockCertificateKVClient) NewListCertificateVersionsPager(certificateName string, options *azcertificates.ListCertificateVersionsOptions) *runtime.Pager[azcertificates.ListCertificateVersionsResponse] {
+	if m.NewListCertificateVersionsPagerFunc != nil {
+		return m.NewListCertificateVersionsPagerFunc(certificateName, options)
+	}
+	CertCreated := time.Now()
+	return runtime.NewPager(runtime.PagingHandler[azcertificates.ListCertificateVersionsResponse]{
+		More: func(_ azcertificates.ListCertificateVersionsResponse) bool {
+			return false
+		},
+		Fetcher: func(_ context.Context, _ *azcertificates.ListCertificateVersionsResponse) (azcertificates.ListCertificateVersionsResponse, error) {
+			var resp azcertificates.ListCertificateVersionsResponse
+			var certID azcertificates.ID = "https://testkv.vault.azure.net/certificates/cert1/c1f03df1113d460491d970737dfdc35d"
+			resp = azcertificates.ListCertificateVersionsResponse{
+				CertificateListResult: azcertificates.CertificateListResult{
+					NextLink: nil,
+					Value: []*azcertificates.CertificateItem{
+						{
+							ID: &certID,
+							Attributes: &azcertificates.CertificateAttributes{
+								Created: &CertCreated,
+							},
+						},
+					},
+				},
+			}
+			return resp, nil
+		},
+	})
+}
+
 // stringPtr returns a pointer to the given string.
 func stringPtr(s string) *string {
 	return &s
@@ -210,26 +301,64 @@ func boolPtr(b bool) *bool {
 
 // TestGetCertificates tests the GetCertificates function
 func TestGetCertificates(t *testing.T) {
-	certID := azcertificates.ID("https://testkv.vault.azure.net/certificates/cert1")
+	certID := azcertificates.ID("https://testkv.vault.azure.net/certificates/cert1/d47a1c09f5b6437da28e9c72b1f4e0fd")
+	certIDCreated := time.Now()
+	certIDmiddle := azcertificates.ID("https://testkv.vault.azure.net/certificates/cert1/a1f03df1113d460491d970737dfdc35d")
+	certIDmiddleCreated := time.Now().Add(1 * time.Minute)
+	certIDLatest := azcertificates.ID("https://testkv.vault.azure.net/certificates/cert1/8f2e5a13c4b74960d7a8e2f1c0d6b3a9")
+	certIDLatestCreated := time.Now().Add(2 * time.Minute)
 	secretID := azsecrets.ID("https://testkv.vault.azure.net/secrets/secret1")
 	testCases := []struct {
 		name                    string
+		version                 string
+		versionHistoryLimit     int
 		mockKeyKVClient         *MockKeyKVClient
 		mockSecretKVClient      *MockSecretKVClient
 		mockCertificateKVClient *MockCertificateKVClient
 		expectedErr             bool
 	}{
 		{
-			name: "GetSecret error",
+			name:                    "FetchSingleVersion: certificate secret enabled",
+			versionHistoryLimit:     0,
+			mockCertificateKVClient: &MockCertificateKVClient{},
 			mockSecretKVClient: &MockSecretKVClient{
 				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
-					return azsecrets.GetSecretResponse{}, errors.New("error")
+					return azsecrets.GetSecretResponse{
+						SecretBundle: azsecrets.SecretBundle{
+							ContentType: stringPtr("application/x-pem-file"),
+							ID:          &secretID,
+							Kid:         stringPtr("https://testkv.vault.azure.net/keys/key1"),
+							Attributes: &azsecrets.SecretAttributes{
+								Enabled: boolPtr(true),
+							},
+							Value: stringPtr("-----BEGIN CERTIFICATE-----\nMIIC8TCCAdmgAwIBAgIUaNrwbhs/I1ecqUYdzD2xuAVNdmowDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzdaFw0yNDA2MjAwMTIyMzdaMBkxFzAVBgNVBAMMDnJhdGlm\neS5kZWZhdWx0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtskG1BUt\n4Fw2lbm53KbwZb1hnLmWdwRotZyznhhk/yrUDcq3uF6klwpk/E2IKfUKIo6doHSk\nXaEZXR68UtXygvA4wdg7xZ6kKpXy0gu+RxGE6CGtDHTyDDzITu+NBjo21ZSsyGpQ\nJeIKftUCHdwdygKf0CdJx8A29GBRpHGCmJadmt7tTzOnYjmbuPVLeqJo/Ex9qXcG\nZbxoxnxr5NCocFeKx+EbLo+k/KjdFB2PKnhgzxAaMMMP6eXPr8l5AlzkC83EmPvN\ntveuaBbamdlFkD+53TZeZlxt3GIdq93Iw/UpbQ/pvhbrztMT+UVEkm15sShfX8Xn\nL2st5A4n0V+66QIDAQABoyAwHjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIH\ngDANBgkqhkiG9w0BAQsFAAOCAQEAGpOqozyfDSBjoTepsRroxxcZ4sq65gw45Bme\nm36BS6FG0WHIg3cMy6KIIBefTDSKrPkKNTtuF25AeGn9jM+26cnfDM78ZH0+Lnn7\n7hs0MA64WMPQaWs9/+89aM9NADV9vp2zdG4xMi6B7DruvKWyhJaNoRqK/qP6LdSQ\nw8M+21sAHvXgrRkQtJlVOzVhgwt36NOb1hzRlQiZB+nhv2Wbw7fbtAaADk3JAumf\nvM+YdPS1KfAFaYefm4yFd+9/C0KOkHico3LTbELO5hG0Mo/EYvtjM+Fljb42EweF\n3nAx1GSPe5Tn8p3h6RyJW5HIKozEKyfDuLS0ccB/nqT3oNjcTw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDRTCCAi2gAwIBAgIUcC33VfaMhOnsl7avNTRVQozoVtUwDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzZaFw0yMzA2MjIwMTIyMzZaMCoxDzANBgNVBAoMBlJhdGlm\neTEXMBUGA1UEAwwOUmF0aWZ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDDFhDnyPrVDZaeRu6Tbg1a/iTwus+IuX+h8aKhKS1yHz4EF/Lz\nxCy7lNSQ9srGMMVumWuNom/ydIphff6PejZM1jFKPU6OQR/0JX5epcVIjbKa562T\nDguUxJ+h5V3EIyM4RqOWQ2g/xZo86x5TzyNJXiVdHHRvmDvUNwPpMeDjr/EHVAni\n5YQObxkJRiiZ7XOa5zz3YztVm8sSZAwPWroY1HIfvtP+KHpiNDIKSymmuJkH4SEr\nJn++iqN8na18a9DFBPTTrLPe3CxATGrMfosCMZ6LP3iFLLc/FaSpwcnugWdewsUK\nYs+sUY7jFWR7x7/1nyFWyRrQviM4f4TY+K7NAgMBAAGjYzBhMB0GA1UdDgQWBBQH\nYePW7QPP2p1utr3r6gqzEkKs+DAfBgNVHSMEGDAWgBQHYePW7QPP2p1utr3r6gqz\nEkKs+DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDANBgkqhkiG9w0B\nAQsFAAOCAQEAjKp4vx3bFaKVhAbQeTsDjWJgmXLK2vLgt74MiUwSF6t0wehlfszE\nIcJagGJsvs5wKFf91bnwiqwPjmpse/thPNBAxh1uEoh81tOklv0BN790vsVpq3t+\ncnUvWPiCZdRlAiGGFtRmKk3Keq4sM6UdiUki9s+wnxypHVb4wIpVxu5R271Lnp5I\n+rb2EQ48iblt4XZPczf/5QJdTgbItjBNbuO8WVPOqUIhCiFuAQziLtNUq3p81dHO\nQ2BPgmaitCpIUYHVYighLauBGCH8xOFzj4a4KbOxKdxyJTd0La/vRCKaUtJX67Lc\nfQYVR9HXQZ0YlmwPcmIG5v7wBfcW34NUvA==\n-----END CERTIFICATE-----\n"),
+						},
+					}, nil
 				},
 			},
-			expectedErr: true,
+			expectedErr: false,
 		},
 		{
-			name: "Certificate disabled",
+			name:                    "FetchSingleVersion: certificate secret enabled nil attributes",
+			versionHistoryLimit:     0,
+			mockCertificateKVClient: &MockCertificateKVClient{},
+			mockSecretKVClient: &MockSecretKVClient{
+				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
+					return azsecrets.GetSecretResponse{
+						SecretBundle: azsecrets.SecretBundle{
+							ContentType: stringPtr("application/x-pem-file"),
+							ID:          &secretID,
+							Kid:         stringPtr("https://testkv.vault.azure.net/keys/key1"),
+							Value:       stringPtr("-----BEGIN CERTIFICATE-----\nMIIC8TCCAdmgAwIBAgIUaNrwbhs/I1ecqUYdzD2xuAVNdmowDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzdaFw0yNDA2MjAwMTIyMzdaMBkxFzAVBgNVBAMMDnJhdGlm\neS5kZWZhdWx0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtskG1BUt\n4Fw2lbm53KbwZb1hnLmWdwRotZyznhhk/yrUDcq3uF6klwpk/E2IKfUKIo6doHSk\nXaEZXR68UtXygvA4wdg7xZ6kKpXy0gu+RxGE6CGtDHTyDDzITu+NBjo21ZSsyGpQ\nJeIKftUCHdwdygKf0CdJx8A29GBRpHGCmJadmt7tTzOnYjmbuPVLeqJo/Ex9qXcG\nZbxoxnxr5NCocFeKx+EbLo+k/KjdFB2PKnhgzxAaMMMP6eXPr8l5AlzkC83EmPvN\ntveuaBbamdlFkD+53TZeZlxt3GIdq93Iw/UpbQ/pvhbrztMT+UVEkm15sShfX8Xn\nL2st5A4n0V+66QIDAQABoyAwHjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIH\ngDANBgkqhkiG9w0BAQsFAAOCAQEAGpOqozyfDSBjoTepsRroxxcZ4sq65gw45Bme\nm36BS6FG0WHIg3cMy6KIIBefTDSKrPkKNTtuF25AeGn9jM+26cnfDM78ZH0+Lnn7\n7hs0MA64WMPQaWs9/+89aM9NADV9vp2zdG4xMi6B7DruvKWyhJaNoRqK/qP6LdSQ\nw8M+21sAHvXgrRkQtJlVOzVhgwt36NOb1hzRlQiZB+nhv2Wbw7fbtAaADk3JAumf\nvM+YdPS1KfAFaYefm4yFd+9/C0KOkHico3LTbELO5hG0Mo/EYvtjM+Fljb42EweF\n3nAx1GSPe5Tn8p3h6RyJW5HIKozEKyfDuLS0ccB/nqT3oNjcTw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDRTCCAi2gAwIBAgIUcC33VfaMhOnsl7avNTRVQozoVtUwDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzZaFw0yMzA2MjIwMTIyMzZaMCoxDzANBgNVBAoMBlJhdGlm\neTEXMBUGA1UEAwwOUmF0aWZ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDDFhDnyPrVDZaeRu6Tbg1a/iTwus+IuX+h8aKhKS1yHz4EF/Lz\nxCy7lNSQ9srGMMVumWuNom/ydIphff6PejZM1jFKPU6OQR/0JX5epcVIjbKa562T\nDguUxJ+h5V3EIyM4RqOWQ2g/xZo86x5TzyNJXiVdHHRvmDvUNwPpMeDjr/EHVAni\n5YQObxkJRiiZ7XOa5zz3YztVm8sSZAwPWroY1HIfvtP+KHpiNDIKSymmuJkH4SEr\nJn++iqN8na18a9DFBPTTrLPe3CxATGrMfosCMZ6LP3iFLLc/FaSpwcnugWdewsUK\nYs+sUY7jFWR7x7/1nyFWyRrQviM4f4TY+K7NAgMBAAGjYzBhMB0GA1UdDgQWBBQH\nYePW7QPP2p1utr3r6gqzEkKs+DAfBgNVHSMEGDAWgBQHYePW7QPP2p1utr3r6gqz\nEkKs+DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDANBgkqhkiG9w0B\nAQsFAAOCAQEAjKp4vx3bFaKVhAbQeTsDjWJgmXLK2vLgt74MiUwSF6t0wehlfszE\nIcJagGJsvs5wKFf91bnwiqwPjmpse/thPNBAxh1uEoh81tOklv0BN790vsVpq3t+\ncnUvWPiCZdRlAiGGFtRmKk3Keq4sM6UdiUki9s+wnxypHVb4wIpVxu5R271Lnp5I\n+rb2EQ48iblt4XZPczf/5QJdTgbItjBNbuO8WVPOqUIhCiFuAQziLtNUq3p81dHO\nQ2BPgmaitCpIUYHVYighLauBGCH8xOFzj4a4KbOxKdxyJTd0La/vRCKaUtJX67Lc\nfQYVR9HXQZ0YlmwPcmIG5v7wBfcW34NUvA==\n-----END CERTIFICATE-----\n"),
+						},
+					}, nil
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name:                "FetchSingleVersion: certificate secret disabled",
+			versionHistoryLimit: 0,
 			mockCertificateKVClient: &MockCertificateKVClient{
 				GetCertificateFunc: func(_ context.Context, _ string, _ string) (azcertificates.GetCertificateResponse, error) {
 					return azcertificates.GetCertificateResponse{
@@ -245,16 +374,6 @@ func TestGetCertificates(t *testing.T) {
 			},
 			mockSecretKVClient: &MockSecretKVClient{
 				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
-					rawResponse := `{
-						"error": {
-							"code": "Forbidden",
-							"message": "Operation get is not allowed on a disabled secret.",
-							"innererror": {
-								"code": "SecretDisabled"
-							}
-						}
-					}`
-
 					httpErr := &azcore.ResponseError{
 						StatusCode: http.StatusForbidden,
 						RawResponse: &http.Response{
@@ -267,24 +386,15 @@ func TestGetCertificates(t *testing.T) {
 			expectedErr: false,
 		},
 		{
-			name: "Certificate disabled error",
+			name:                "FetchSingleVersion: GetCertificate error with disabled secret",
+			versionHistoryLimit: 0,
 			mockCertificateKVClient: &MockCertificateKVClient{
 				GetCertificateFunc: func(_ context.Context, _ string, _ string) (azcertificates.GetCertificateResponse, error) {
-					return azcertificates.GetCertificateResponse{}, errors.New("error")
+					return azcertificates.GetCertificateResponse{}, errors.New("Operation get is not allowed on a disabled secret")
 				},
 			},
 			mockSecretKVClient: &MockSecretKVClient{
 				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
-					rawResponse := `{
-						"error": {
-							"code": "Forbidden",
-							"message": "Operation get is not allowed on a disabled secret.",
-							"innererror": {
-								"code": "SecretDisabled"
-							}
-						}
-					}`
-
 					httpErr := &azcore.ResponseError{
 						StatusCode: http.StatusForbidden,
 						RawResponse: &http.Response{
@@ -297,20 +407,182 @@ func TestGetCertificates(t *testing.T) {
 			expectedErr: true,
 		},
 		{
-			name: "Certificate enabled",
+			name:                    "FetchSingleVersion: getCertsFromSecretBundle error",
+			versionHistoryLimit:     0,
+			mockCertificateKVClient: &MockCertificateKVClient{},
+			mockSecretKVClient: &MockSecretKVClient{
+				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
+					return azsecrets.GetSecretResponse{
+						SecretBundle: azsecrets.SecretBundle{
+							ID:  &secretID,
+							Kid: stringPtr("https://testkv.vault.azure.net/keys/key1"),
+							Attributes: &azsecrets.SecretAttributes{
+								Enabled: boolPtr(true),
+							},
+							Value: stringPtr("-----BEGIN CERTIFICATE-----\nMIIC8TCCAdmgAwIBAgIUaNrwbhs/I1ecqUYdzD2xuAVNdmowDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzdaFw0yNDA2MjAwMTIyMzdaMBkxFzAVBgNVBAMMDnJhdGlm\neS5kZWZhdWx0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtskG1BUt\n4Fw2lbm53KbwZb1hnLmWdwRotZyznhhk/yrUDcq3uF6klwpk/E2IKfUKIo6doHSk\nXaEZXR68UtXygvA4wdg7xZ6kKpXy0gu+RxGE6CGtDHTyDDzITu+NBjo21ZSsyGpQ\nJeIKftUCHdwdygKf0CdJx8A29GBRpHGCmJadmt7tTzOnYjmbuPVLeqJo/Ex9qXcG\nZbxoxnxr5NCocFeKx+EbLo+k/KjdFB2PKnhgzxAaMMMP6eXPr8l5AlzkC83EmPvN\ntveuaBbamdlFkD+53TZeZlxt3GIdq93Iw/UpbQ/pvhbrztMT+UVEkm15sShfX8Xn\nL2st5A4n0V+66QIDAQABoyAwHjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIH\ngDANBgkqhkiG9w0BAQsFAAOCAQEAGpOqozyfDSBjoTepsRroxxcZ4sq65gw45Bme\nm36BS6FG0WHIg3cMy6KIIBefTDSKrPkKNTtuF25AeGn9jM+26cnfDM78ZH0+Lnn7\n7hs0MA64WMPQaWs9/+89aM9NADV9vp2zdG4xMi6B7DruvKWyhJaNoRqK/qP6LdSQ\nw8M+21sAHvXgrRkQtJlVOzVhgwt36NOb1hzRlQiZB+nhv2Wbw7fbtAaADk3JAumf\nvM+YdPS1KfAFaYefm4yFd+9/C0KOkHico3LTbELO5hG0Mo/EYvtjM+Fljb42EweF\n3nAx1GSPe5Tn8p3h6RyJW5HIKozEKyfDuLS0ccB/nqT3oNjcTw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDRTCCAi2gAwIBAgIUcC33VfaMhOnsl7avNTRVQozoVtUwDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzZaFw0yMzA2MjIwMTIyMzZaMCoxDzANBgNVBAoMBlJhdGlm\neTEXMBUGA1UEAwwOUmF0aWZ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDDFhDnyPrVDZaeRu6Tbg1a/iTwus+IuX+h8aKhKS1yHz4EF/Lz\nxCy7lNSQ9srGMMVumWuNom/ydIphff6PejZM1jFKPU6OQR/0JX5epcVIjbKa562T\nDguUxJ+h5V3EIyM4RqOWQ2g/xZo86x5TzyNJXiVdHHRvmDvUNwPpMeDjr/EHVAni\n5YQObxkJRiiZ7XOa5zz3YztVm8sSZAwPWroY1HIfvtP+KHpiNDIKSymmuJkH4SEr\nJn++iqN8na18a9DFBPTTrLPe3CxATGrMfosCMZ6LP3iFLLc/FaSpwcnugWdewsUK\nYs+sUY7jFWR7x7/1nyFWyRrQviM4f4TY+K7NAgMBAAGjYzBhMB0GA1UdDgQWBBQH\nYePW7QPP2p1utr3r6gqzEkKs+DAfBgNVHSMEGDAWgBQHYePW7QPP2p1utr3r6gqz\nEkKs+DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDANBgkqhkiG9w0B\nAQsFAAOCAQEAjKp4vx3bFaKVhAbQeTsDjWJgmXLK2vLgt74MiUwSF6t0wehlfszE\nIcJagGJsvs5wKFf91bnwiqwPjmpse/thPNBAxh1uEoh81tOklv0BN790vsVpq3t+\ncnUvWPiCZdRlAiGGFtRmKk3Keq4sM6UdiUki9s+wnxypHVb4wIpVxu5R271Lnp5I\n+rb2EQ48iblt4XZPczf/5QJdTgbItjBNbuO8WVPOqUIhCiFuAQziLtNUq3p81dHO\nQ2BPgmaitCpIUYHVYighLauBGCH8xOFzj4a4KbOxKdxyJTd0La/vRCKaUtJX67Lc\nfQYVR9HXQZ0YlmwPcmIG5v7wBfcW34NUvA==\n-----END CERTIFICATE-----\n"),
+						},
+					}, nil
+				},
+			},
+			expectedErr: true,
+		},
+		{
+			name:                "FetchVersionHistory: Certificate enabled with multiple versions nil attributes",
+			versionHistoryLimit: 2,
 			mockCertificateKVClient: &MockCertificateKVClient{
 				GetCertificateFunc: func(_ context.Context, _ string, _ string) (azcertificates.GetCertificateResponse, error) {
 					return azcertificates.GetCertificateResponse{
 						CertificateBundle: azcertificates.CertificateBundle{
 							ID:  &certID,
 							KID: stringPtr("https://testkv.vault.azure.net/keys/key1"),
-							Attributes: &azcertificates.CertificateAttributes{
+						},
+					}, nil
+				},
+				NewListCertificateVersionsPagerFunc: func(_ string, _ *azcertificates.ListCertificateVersionsOptions) *runtime.Pager[azcertificates.ListCertificateVersionsResponse] {
+					pageCounter := 0
+					return runtime.NewPager(runtime.PagingHandler[azcertificates.ListCertificateVersionsResponse]{
+						More: func(resp azcertificates.ListCertificateVersionsResponse) bool {
+							return resp.CertificateListResult.NextLink != nil
+						},
+						Fetcher: func(_ context.Context, _ *azcertificates.ListCertificateVersionsResponse) (azcertificates.ListCertificateVersionsResponse, error) {
+							var resp azcertificates.ListCertificateVersionsResponse
+
+							if pageCounter == 0 {
+								resp = azcertificates.ListCertificateVersionsResponse{
+									CertificateListResult: azcertificates.CertificateListResult{
+										NextLink: stringPtr("https://testkv.vault.azure.net/certificates/cert1/versions?api-version=7.2"),
+										Value: []*azcertificates.CertificateItem{
+											{
+												ID: &certID,
+												Attributes: &azcertificates.CertificateAttributes{
+													Enabled: boolPtr(true),
+													Created: nil,
+												},
+											},
+										},
+									},
+								}
+							}
+
+							if pageCounter == 1 {
+								resp = azcertificates.ListCertificateVersionsResponse{
+									CertificateListResult: azcertificates.CertificateListResult{
+										NextLink: nil,
+										Value: []*azcertificates.CertificateItem{
+											{
+												ID: &certIDLatest,
+												Attributes: &azcertificates.CertificateAttributes{
+													Enabled: boolPtr(true),
+													Created: &certIDLatestCreated,
+												},
+											},
+										},
+									},
+								}
+							}
+
+							pageCounter++
+							return resp, nil
+						},
+					})
+				},
+			},
+			mockSecretKVClient: &MockSecretKVClient{
+				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
+					return azsecrets.GetSecretResponse{
+						SecretBundle: azsecrets.SecretBundle{
+							ID:          &secretID,
+							Kid:         stringPtr("https://testkv.vault.azure.net/keys/key1"),
+							ContentType: stringPtr("application/x-pem-file"),
+							Attributes: &azsecrets.SecretAttributes{
 								Enabled: boolPtr(true),
 							},
+							Value: stringPtr("-----BEGIN CERTIFICATE-----\nMIIC8TCCAdmgAwIBAgIUaNrwbhs/I1ecqUYdzD2xuAVNdmowDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzdaFw0yNDA2MjAwMTIyMzdaMBkxFzAVBgNVBAMMDnJhdGlm\neS5kZWZhdWx0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtskG1BUt\n4Fw2lbm53KbwZb1hnLmWdwRotZyznhhk/yrUDcq3uF6klwpk/E2IKfUKIo6doHSk\nXaEZXR68UtXygvA4wdg7xZ6kKpXy0gu+RxGE6CGtDHTyDDzITu+NBjo21ZSsyGpQ\nJeIKftUCHdwdygKf0CdJx8A29GBRpHGCmJadmt7tTzOnYjmbuPVLeqJo/Ex9qXcG\nZbxoxnxr5NCocFeKx+EbLo+k/KjdFB2PKnhgzxAaMMMP6eXPr8l5AlzkC83EmPvN\ntveuaBbamdlFkD+53TZeZlxt3GIdq93Iw/UpbQ/pvhbrztMT+UVEkm15sShfX8Xn\nL2st5A4n0V+66QIDAQABoyAwHjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIH\ngDANBgkqhkiG9w0BAQsFAAOCAQEAGpOqozyfDSBjoTepsRroxxcZ4sq65gw45Bme\nm36BS6FG0WHIg3cMy6KIIBefTDSKrPkKNTtuF25AeGn9jM+26cnfDM78ZH0+Lnn7\n7hs0MA64WMPQaWs9/+89aM9NADV9vp2zdG4xMi6B7DruvKWyhJaNoRqK/qP6LdSQ\nw8M+21sAHvXgrRkQtJlVOzVhgwt36NOb1hzRlQiZB+nhv2Wbw7fbtAaADk3JAumf\nvM+YdPS1KfAFaYefm4yFd+9/C0KOkHico3LTbELO5hG0Mo/EYvtjM+Fljb42EweF\n3nAx1GSPe5Tn8p3h6RyJW5HIKozEKyfDuLS0ccB/nqT3oNjcTw==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDRTCCAi2gAwIBAgIUcC33VfaMhOnsl7avNTRVQozoVtUwDQYJKoZIhvcNAQEL\nBQAwKjEPMA0GA1UECgwGUmF0aWZ5MRcwFQYDVQQDDA5SYXRpZnkgUm9vdCBDQTAe\nFw0yMzA2MjEwMTIyMzZaFw0yMzA2MjIwMTIyMzZaMCoxDzANBgNVBAoMBlJhdGlm\neTEXMBUGA1UEAwwOUmF0aWZ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB\nDwAwggEKAoIBAQDDFhDnyPrVDZaeRu6Tbg1a/iTwus+IuX+h8aKhKS1yHz4EF/Lz\nxCy7lNSQ9srGMMVumWuNom/ydIphff6PejZM1jFKPU6OQR/0JX5epcVIjbKa562T\nDguUxJ+h5V3EIyM4RqOWQ2g/xZo86x5TzyNJXiVdHHRvmDvUNwPpMeDjr/EHVAni\n5YQObxkJRiiZ7XOa5zz3YztVm8sSZAwPWroY1HIfvtP+KHpiNDIKSymmuJkH4SEr\nJn++iqN8na18a9DFBPTTrLPe3CxATGrMfosCMZ6LP3iFLLc/FaSpwcnugWdewsUK\nYs+sUY7jFWR7x7/1nyFWyRrQviM4f4TY+K7NAgMBAAGjYzBhMB0GA1UdDgQWBBQH\nYePW7QPP2p1utr3r6gqzEkKs+DAfBgNVHSMEGDAWgBQHYePW7QPP2p1utr3r6gqz\nEkKs+DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDANBgkqhkiG9w0B\nAQsFAAOCAQEAjKp4vx3bFaKVhAbQeTsDjWJgmXLK2vLgt74MiUwSF6t0wehlfszE\nIcJagGJsvs5wKFf91bnwiqwPjmpse/thPNBAxh1uEoh81tOklv0BN790vsVpq3t+\ncnUvWPiCZdRlAiGGFtRmKk3Keq4sM6UdiUki9s+wnxypHVb4wIpVxu5R271Lnp5I\n+rb2EQ48iblt4XZPczf/5QJdTgbItjBNbuO8WVPOqUIhCiFuAQziLtNUq3p81dHO\nQ2BPgmaitCpIUYHVYighLauBGCH8xOFzj4a4KbOxKdxyJTd0La/vRCKaUtJX67Lc\nfQYVR9HXQZ0YlmwPcmIG5v7wBfcW34NUvA==\n-----END CERTIFICATE-----\n"),
 						},
 					}, nil
 				},
 			},
+			expectedErr: false,
+		},
+		{
+			name:                "FetchVersionHistory: Certificate enabled with multiple versions(test version specified)",
+			version:             "a1f03df1113d460491d970737dfdc35d",
+			versionHistoryLimit: 1,
+			mockCertificateKVClient: &MockCertificateKVClient{
+				GetCertificateFunc: func(_ context.Context, _ string, _ string) (azcertificates.GetCertificateResponse, error) {
+					return azcertificates.GetCertificateResponse{
+						CertificateBundle: azcertificates.CertificateBundle{
+							ID:  &certID,
+							KID: stringPtr("https://testkv.vault.azure.net/keys/key1"),
+						},
+					}, nil
+				},
+				NewListCertificateVersionsPagerFunc: func(_ string, _ *azcertificates.ListCertificateVersionsOptions) *runtime.Pager[azcertificates.ListCertificateVersionsResponse] {
+					pageCounter := 0
+					return runtime.NewPager(runtime.PagingHandler[azcertificates.ListCertificateVersionsResponse]{
+						More: func(resp azcertificates.ListCertificateVersionsResponse) bool {
+							return resp.CertificateListResult.NextLink != nil
+						},
+						Fetcher: func(_ context.Context, _ *azcertificates.ListCertificateVersionsResponse) (azcertificates.ListCertificateVersionsResponse, error) {
+							var resp azcertificates.ListCertificateVersionsResponse
+
+							if pageCounter == 0 {
+								resp = azcertificates.ListCertificateVersionsResponse{
+									CertificateListResult: azcertificates.CertificateListResult{
+										NextLink: stringPtr("https://testkv.vault.azure.net/certificates/cert1/versions?api-version=7.2"),
+										Value: []*azcertificates.CertificateItem{
+											{
+												ID: &certID,
+												Attributes: &azcertificates.CertificateAttributes{
+													Enabled: boolPtr(true),
+													Created: &certIDCreated,
+												},
+											},
+										},
+									},
+								}
+							}
+
+							if pageCounter == 1 {
+								resp = azcertificates.ListCertificateVersionsResponse{
+									CertificateListResult: azcertificates.CertificateListResult{
+										NextLink: stringPtr("https://testkv.vault.azure.net/certificates/cert1/versions?api-version=7.2"),
+										Value: []*azcertificates.CertificateItem{
+											{
+												ID: &certIDmiddle,
+												Attributes: &azcertificates.CertificateAttributes{
+													Enabled: boolPtr(true),
+													Created: &certIDmiddleCreated,
+												},
+											},
+										},
+									},
+								}
+							}
+
+							if pageCounter == 2 {
+								resp = azcertificates.ListCertificateVersionsResponse{
+									CertificateListResult: azcertificates.CertificateListResult{
+										NextLink: nil,
+										Value: []*azcertificates.CertificateItem{
+											{
+												ID: &certIDLatest,
+												Attributes: &azcertificates.CertificateAttributes{
+													Enabled: boolPtr(true),
+													Created: &certIDLatestCreated,
+												},
+											},
+										},
+									},
+								}
+							}
+
+							pageCounter++
+							return resp, nil
+						},
+					})
+				},
+			},
 			mockSecretKVClient: &MockSecretKVClient{
 				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
 					return azsecrets.GetSecretResponse{
@@ -329,7 +601,83 @@ func TestGetCertificates(t *testing.T) {
 			expectedErr: false,
 		},
 		{
-			name: "getCertsFromSecretBundle error",
+			name:                "FetchVersionHistory: No versions returned by pager",
+			versionHistoryLimit: 1,
+			mockCertificateKVClient: &MockCertificateKVClient{
+				NewListCertificateVersionsPagerFunc: func(_ string, _ *azcertificates.ListCertificateVersionsOptions) *runtime.Pager[azcertificates.ListCertificateVersionsResponse] {
+					return runtime.NewPager(runtime.PagingHandler[azcertificates.ListCertificateVersionsResponse]{
+						More: func(_ azcertificates.ListCertificateVersionsResponse) bool {
+							return false
+						},
+						Fetcher: func(_ context.Context, _ *azcertificates.ListCertificateVersionsResponse) (azcertificates.ListCertificateVersionsResponse, error) {
+							return azcertificates.ListCertificateVersionsResponse{}, nil
+						},
+					})
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name:                "FetchVersionHistory: error returned by pager",
+			versionHistoryLimit: 1,
+			mockCertificateKVClient: &MockCertificateKVClient{
+				NewListCertificateVersionsPagerFunc: func(_ string, _ *azcertificates.ListCertificateVersionsOptions) *runtime.Pager[azcertificates.ListCertificateVersionsResponse] {
+					return runtime.NewPager(runtime.PagingHandler[azcertificates.ListCertificateVersionsResponse]{
+						More: func(_ azcertificates.ListCertificateVersionsResponse) bool {
+							return false
+						},
+						Fetcher: func(_ context.Context, _ *azcertificates.ListCertificateVersionsResponse) (azcertificates.ListCertificateVersionsResponse, error) {
+							return azcertificates.ListCertificateVersionsResponse{}, errors.New("error")
+						},
+					})
+				},
+			},
+			expectedErr: true,
+		},
+		{
+			name:                    "FetchVersionHistory: GetSecret error",
+			versionHistoryLimit:     1,
+			mockCertificateKVClient: &MockCertificateKVClient{},
+			mockSecretKVClient: &MockSecretKVClient{
+				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
+					return azsecrets.GetSecretResponse{}, errors.New("error")
+				},
+			},
+			expectedErr: true,
+		},
+		{
+			name:                "FetchVersionHistory: Certificate secret disabled",
+			versionHistoryLimit: 1,
+			mockCertificateKVClient: &MockCertificateKVClient{
+				GetCertificateFunc: func(_ context.Context, _ string, _ string) (azcertificates.GetCertificateResponse, error) {
+					return azcertificates.GetCertificateResponse{
+						CertificateBundle: azcertificates.CertificateBundle{
+							ID:  &certID,
+							KID: stringPtr("https://testkv.vault.azure.net/keys/key1"),
+							Attributes: &azcertificates.CertificateAttributes{
+								Enabled: boolPtr(false),
+							},
+						},
+					}, nil
+				},
+			},
+			mockSecretKVClient: &MockSecretKVClient{
+				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
+					httpErr := &azcore.ResponseError{
+						StatusCode: http.StatusForbidden,
+						RawResponse: &http.Response{
+							Body: io.NopCloser(strings.NewReader(rawResponse)),
+						},
+					}
+					return azsecrets.GetSecretResponse{}, httpErr
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name:                    "FetchVersionHistory: getCertsFromSecretBundle error",
+			versionHistoryLimit:     1,
+			mockCertificateKVClient: &MockCertificateKVClient{},
 			mockSecretKVClient: &MockSecretKVClient{
 				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
 					return azsecrets.GetSecretResponse{
@@ -347,6 +695,23 @@ func TestGetCertificates(t *testing.T) {
 			},
 			expectedErr: true,
 		},
+		{
+			name:                    "FetchVersionHistory: GetSecret nil attributes",
+			versionHistoryLimit:     1,
+			mockCertificateKVClient: &MockCertificateKVClient{},
+			mockSecretKVClient: &MockSecretKVClient{
+				GetSecretFunc: func(_ context.Context, _ string, _ string) (azsecrets.GetSecretResponse, error) {
+					return azsecrets.GetSecretResponse{
+						SecretBundle: azsecrets.SecretBundle{
+							ContentType: stringPtr("application/x-pem-file"),
+							ID:          &secretID,
+							Kid:         stringPtr("https://testkv.vault.azure.net/keys/key1"),
+						},
+					}, nil
+				},
+			},
+			expectedErr: false,
+		},
 	}
 
 	for _, tc := range testCases {
@@ -354,8 +719,9 @@ func TestGetCertificates(t *testing.T) {
 			provider := &akvKMProvider{
 				certificates: []types.KeyVaultValue{
 					{
-						Name:    "cert1",
-						Version: "c1f03df1113d460491d970737dfdc35d",
+						Name:                "cert1",
+						Version:             tc.version,
+						VersionHistoryLimit: tc.versionHistoryLimit,
 					},
 				},
 				keyKVClient:         tc.mockKeyKVClient,
@@ -373,15 +739,259 @@ func TestGetCertificates(t *testing.T) {
 
 // TestGetKeys tests the GetKeys function
 func TestGetKeys(t *testing.T) {
-	keyID := azkeys.ID("https://testkv.vault.azure.net/keys/key1")
+	keyID := azkeys.ID("https://testkv.vault.azure.net/keys/key1/c1f03df1113d460491d970737dfdc35d")
+	keyIDLatest := azkeys.ID("https://testkv.vault.azure.net/keys/key1/8f2e5a13c4b74960d7a8e2f1c0d6b3a9")
+	keyCreated := time.Now()
+	keyCreatedLatest := time.Now().Add(1 * time.Minute)
 	keyTY := azkeys.JSONWebKeyTypeRSA
 	testCases := []struct {
-		name            string
-		mockKeyKVClient *MockKeyKVClient
-		expectedErr     bool
+		name                string
+		versionHistoryLimit int
+		mockKeyKVClient     *MockKeyKVClient
+		expectedErr         bool
 	}{
 		{
-			name: "GetKey error",
+			name: "FetchSingleVersion: Key enabled",
+			mockKeyKVClient: &MockKeyKVClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
+					return azkeys.GetKeyResponse{
+						KeyBundle: azkeys.KeyBundle{
+							Key: &azkeys.JSONWebKey{
+								KID: &keyID,
+								Kty: &keyTY,
+								N:   []byte("n"),
+								E:   []byte("e"),
+							},
+							Attributes: &azkeys.KeyAttributes{
+								Enabled: boolPtr(true),
+							},
+						},
+					}, nil
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name: "FetchSingleVersion: keyBundle nil",
+			mockKeyKVClient: &MockKeyKVClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
+					return azkeys.GetKeyResponse{
+						KeyBundle: azkeys.KeyBundle{
+							Key: nil,
+						},
+					}, nil
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name: "FetchSingleVersion: Key disabled",
+			mockKeyKVClient: &MockKeyKVClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
+					return azkeys.GetKeyResponse{
+						KeyBundle: azkeys.KeyBundle{
+							Key: &azkeys.JSONWebKey{
+								KID: &keyID,
+								Kty: &keyTY,
+								N:   []byte("n"),
+								E:   []byte("e"),
+							},
+							Attributes: &azkeys.KeyAttributes{
+								Enabled: boolPtr(false),
+							},
+						},
+					}, nil
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name: "FetchSingleVersion: getKeyFromKeyBundle error",
+			mockKeyKVClient: &MockKeyKVClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
+					return azkeys.GetKeyResponse{
+						KeyBundle: azkeys.KeyBundle{
+							Key: &azkeys.JSONWebKey{
+								KID: &keyID,
+							},
+							Attributes: &azkeys.KeyAttributes{
+								Enabled: boolPtr(true),
+							},
+						},
+					}, nil
+				},
+			},
+			expectedErr: true,
+		},
+		{
+			name:                "FetchVersionHistory: Key enabled with multiple versions",
+			versionHistoryLimit: 3,
+			mockKeyKVClient: &MockKeyKVClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
+					return azkeys.GetKeyResponse{
+						KeyBundle: azkeys.KeyBundle{
+							Key: &azkeys.JSONWebKey{
+								KID: &keyID,
+								Kty: &keyTY,
+								N:   []byte("n"),
+								E:   []byte("e"),
+							},
+							Attributes: &azkeys.KeyAttributes{
+								Enabled: boolPtr(true),
+							},
+						},
+					}, nil
+				},
+				NewListKeyVersionsPagerFunc: func(_ string, _ *azkeys.ListKeyVersionsOptions) *runtime.Pager[azkeys.ListKeyVersionsResponse] {
+					pageCounter := 0
+					return runtime.NewPager(runtime.PagingHandler[azkeys.ListKeyVersionsResponse]{
+						More: func(resp azkeys.ListKeyVersionsResponse) bool {
+							return resp.KeyListResult.NextLink != nil
+						},
+						Fetcher: func(_ context.Context, _ *azkeys.ListKeyVersionsResponse) (azkeys.ListKeyVersionsResponse, error) {
+							var resp azkeys.ListKeyVersionsResponse
+							if pageCounter == 0 {
+								resp = azkeys.ListKeyVersionsResponse{
+									KeyListResult: azkeys.KeyListResult{
+										NextLink: stringPtr("https://testkv.vault.azure.net/keys/key1/versions?api-version=7.2"),
+										Value: []*azkeys.KeyItem{
+											{
+												KID: &keyID,
+												Attributes: &azkeys.KeyAttributes{
+													Created: &keyCreated,
+												},
+											},
+										},
+									},
+								}
+							}
+
+							if pageCounter == 1 {
+								resp = azkeys.ListKeyVersionsResponse{
+									KeyListResult: azkeys.KeyListResult{
+										Value: []*azkeys.KeyItem{
+											{
+												KID: &keyIDLatest,
+												Attributes: &azkeys.KeyAttributes{
+													Created: &keyCreatedLatest,
+												},
+											},
+										},
+									},
+								}
+							}
+
+							pageCounter++
+							return resp, nil
+						},
+					})
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name:                "FetchVersionHistory: NewListKeyVersionsPager error",
+			versionHistoryLimit: 1,
+			mockKeyKVClient: &MockKeyKVClient{
+				NewListKeyVersionsPagerFunc: func(_ string, _ *azkeys.ListKeyVersionsOptions) *runtime.Pager[azkeys.ListKeyVersionsResponse] {
+					return runtime.NewPager(runtime.PagingHandler[azkeys.ListKeyVersionsResponse]{
+						More: func(_ azkeys.ListKeyVersionsResponse) bool {
+							return false
+						},
+						Fetcher: func(_ context.Context, _ *azkeys.ListKeyVersionsResponse) (azkeys.ListKeyVersionsResponse, error) {
+							return azkeys.ListKeyVersionsResponse{}, errors.New("error")
+						},
+					})
+				},
+			},
+			expectedErr: true,
+		},
+		{
+			name:                "FetchVersionHistory: Key enabled with multiple versions with nil attributes",
+			versionHistoryLimit: 2,
+			mockKeyKVClient: &MockKeyKVClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
+					return azkeys.GetKeyResponse{
+						KeyBundle: azkeys.KeyBundle{
+							Key: &azkeys.JSONWebKey{
+								KID: &keyID,
+								Kty: &keyTY,
+								N:   []byte("n"),
+								E:   []byte("e"),
+							},
+							Attributes: &azkeys.KeyAttributes{
+								Enabled: boolPtr(true),
+							},
+						},
+					}, nil
+				},
+				NewListKeyVersionsPagerFunc: func(_ string, _ *azkeys.ListKeyVersionsOptions) *runtime.Pager[azkeys.ListKeyVersionsResponse] {
+					pageCounter := 0
+					return runtime.NewPager(runtime.PagingHandler[azkeys.ListKeyVersionsResponse]{
+						More: func(resp azkeys.ListKeyVersionsResponse) bool {
+							return resp.KeyListResult.NextLink != nil
+						},
+						Fetcher: func(_ context.Context, _ *azkeys.ListKeyVersionsResponse) (azkeys.ListKeyVersionsResponse, error) {
+							var resp azkeys.ListKeyVersionsResponse
+							if pageCounter == 0 {
+								resp = azkeys.ListKeyVersionsResponse{
+									KeyListResult: azkeys.KeyListResult{
+										NextLink: stringPtr("https://testkv.vault.azure.net/keys/key1/versions?api-version=7.2"),
+										Value: []*azkeys.KeyItem{
+											{
+												KID: &keyID,
+												Attributes: &azkeys.KeyAttributes{
+													Created: nil,
+												},
+											},
+										},
+									},
+								}
+							}
+
+							if pageCounter == 1 {
+								resp = azkeys.ListKeyVersionsResponse{
+									KeyListResult: azkeys.KeyListResult{
+										Value: []*azkeys.KeyItem{
+											{
+												KID: &keyIDLatest,
+												Attributes: &azkeys.KeyAttributes{
+													Created: &keyCreatedLatest,
+												},
+											},
+										},
+									},
+								}
+							}
+
+							pageCounter++
+							return resp, nil
+						},
+					})
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name:                "FetchVersionHistory: No versions returned by pager",
+			versionHistoryLimit: 1,
+			mockKeyKVClient: &MockKeyKVClient{
+				NewListKeyVersionsPagerFunc: func(_ string, _ *azkeys.ListKeyVersionsOptions) *runtime.Pager[azkeys.ListKeyVersionsResponse] {
+					return runtime.NewPager(runtime.PagingHandler[azkeys.ListKeyVersionsResponse]{
+						More: func(_ azkeys.ListKeyVersionsResponse) bool {
+							return false
+						},
+						Fetcher: func(_ context.Context, _ *azkeys.ListKeyVersionsResponse) (azkeys.ListKeyVersionsResponse, error) {
+							return azkeys.ListKeyVersionsResponse{}, nil
+						},
+					})
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name:                "FetchVersionHistory: GetKey error",
+			versionHistoryLimit: 1,
 			mockKeyKVClient: &MockKeyKVClient{
 				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
 					return azkeys.GetKeyResponse{}, errors.New("error")
@@ -390,7 +1000,28 @@ func TestGetKeys(t *testing.T) {
 			expectedErr: true,
 		},
 		{
-			name: "Key disabled",
+			name:                "FetchVersionHistory: keyBundle attributes nil",
+			versionHistoryLimit: 1,
+			mockKeyKVClient: &MockKeyKVClient{
+				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
+					return azkeys.GetKeyResponse{
+						KeyBundle: azkeys.KeyBundle{
+							Key: &azkeys.JSONWebKey{
+								KID: &keyID,
+								Kty: &keyTY,
+								N:   []byte("n"),
+								E:   []byte("e"),
+							},
+							Attributes: nil,
+						},
+					}, nil
+				},
+			},
+			expectedErr: false,
+		},
+		{
+			name:                "FetchVersionHistory: Key disabled",
+			versionHistoryLimit: 1,
 			mockKeyKVClient: &MockKeyKVClient{
 				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
 					return azkeys.GetKeyResponse{
@@ -408,7 +1039,8 @@ func TestGetKeys(t *testing.T) {
 			expectedErr: false,
 		},
 		{
-			name: "getKeyFromKeyBundle error",
+			name:                "FetchVersionHistory: getKeyFromKeyBundle error",
+			versionHistoryLimit: 1,
 			mockKeyKVClient: &MockKeyKVClient{
 				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
 					return azkeys.GetKeyResponse{
@@ -426,7 +1058,8 @@ func TestGetKeys(t *testing.T) {
 			expectedErr: true,
 		},
 		{
-			name: "Key enabled",
+			name:                "FetchVersionHistory: Key enabled",
+			versionHistoryLimit: 1,
 			mockKeyKVClient: &MockKeyKVClient{
 				GetKeyFunc: func(_ context.Context, _ string, _ string) (azkeys.GetKeyResponse, error) {
 					return azkeys.GetKeyResponse{
@@ -453,8 +1086,9 @@ func TestGetKeys(t *testing.T) {
 			provider := &akvKMProvider{
 				keys: []types.KeyVaultValue{
 					{
-						Name:    "key1",
-						Version: "c1f03df1113d460491d970737dfdc35d",
+						Name:                "key1",
+						Version:             "c1f03df1113d460491d970737dfdc35d",
+						VersionHistoryLimit: tc.versionHistoryLimit,
 					},
 				},
 				keyKVClient: tc.mockKeyKVClient,
@@ -1013,16 +1647,6 @@ func TestInitializeKvClient_FailureInAzCertificatesClient(t *testing.T) {
 	assert.Contains(t, err.Error(), "failed to create workload identity credential")
 }
 func TestIsSecretDisabledError(t *testing.T) {
-	rawResponse := `{
-		"error": {
-			"code": "Forbidden",
-			"message": "Operation get is not allowed on a disabled secret.",
-			"innererror": {
-				"code": "SecretDisabled"
-			}
-		}
-	}`
-
 	httpErr := &azcore.ResponseError{
 		StatusCode: http.StatusForbidden,
 		RawResponse: &http.Response{