Browse Source

chore(actions): fix security alerts for github actions (#5504)

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

On-behalf-of: Gergely Brautigam <gergely.brautigam@sap.com>
Gergely Brautigam 5 months ago
parent
commit
560fa94c44

+ 4 - 4
.github/actions/e2e/action.yml

@@ -6,7 +6,7 @@ runs:
   steps:
   steps:
 
 
     - name: Configure AWS Credentials
     - name: Configure AWS Credentials
-      uses: aws-actions/configure-aws-credentials@v1
+      uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v1
       with:
       with:
         role-to-assume: ${{ env.AWS_OIDC_ROLE_ARN }}
         role-to-assume: ${{ env.AWS_OIDC_ROLE_ARN }}
         aws-region: ${{ env.AWS_REGION }}
         aws-region: ${{ env.AWS_REGION }}
@@ -31,14 +31,14 @@ runs:
         restore-keys: ${{ runner.os }}-build-unit-tests-${{ github.sha }}-
         restore-keys: ${{ runner.os }}-build-unit-tests-${{ github.sha }}-
 
 
     - name: Cache Go Dependencies
     - name: Cache Go Dependencies
-      uses: actions/cache@v3
+      uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
       with:
       with:
         path: ${{ steps.go.outputs.mod-cache }}
         path: ${{ steps.go.outputs.mod-cache }}
         key: ${{ runner.os }}-pkg-${{ github.sha }}-${{ hashFiles('**/go.sum') }}
         key: ${{ runner.os }}-pkg-${{ github.sha }}-${{ hashFiles('**/go.sum') }}
         restore-keys: ${{ runner.os }}-pkg-${{ github.sha }}-
         restore-keys: ${{ runner.os }}-pkg-${{ github.sha }}-
 
 
     - name: Setup kind
     - name: Setup kind
-      uses: engineerd/setup-kind@v0.5.0
+      uses: engineerd/setup-kind@aa272fe2a7309878ffc2a81c56cfe3ef108e7d49 # v0.5.0
       with:
       with:
         version: ${{ env.KIND_VERSION }}
         version: ${{ env.KIND_VERSION }}
         wait: 10m
         wait: 10m
@@ -46,7 +46,7 @@ runs:
         name: external-secrets
         name: external-secrets
 
 
     - name: Setup Docker Buildx
     - name: Setup Docker Buildx
-      uses: docker/setup-buildx-action@v2
+      uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232e5 # v2
       with:
       with:
         version: ${{ env.DOCKER_BUILDX_VERSION }}
         version: ${{ env.DOCKER_BUILDX_VERSION }}
         install: true
         install: true

+ 26 - 14
.github/actions/sign/action.yml

@@ -18,12 +18,12 @@ runs:
   steps:
   steps:
 
 
     - name: Install cosign
     - name: Install cosign
-      uses: sigstore/cosign-installer@v2
+      uses: sigstore/cosign-installer@42ab207638e63e893c6ead51f15e19ac3e46e6cc # v2
       with:
       with:
         cosign-release: v1.13.6
         cosign-release: v1.13.6
 
 
     - name: Install Syft
     - name: Install Syft
-      uses: anchore/sbom-action/download-syft@v0.7.0
+      uses: anchore/sbom-action/download-syft@f86d330ae046b4a16d86ccc1d9c92e16fefcf304 # v0.7.0
 
 
     - name: Check Cosign install
     - name: Check Cosign install
       shell: bash
       shell: bash
@@ -48,33 +48,42 @@ runs:
     - name: Get docker image tag
     - name: Get docker image tag
       id: container_info
       id: container_info
       shell: bash
       shell: bash
-      run: echo "digest=$(crane digest ${{ inputs.image-name }}:${{ inputs.image-tag }})" >> $GITHUB_OUTPUT
+      env:
+        IMAGE_NAME: ${{ inputs.image-name }}
+        IMAGE_TAG: ${{ inputs.image-tag }}
+      run: echo "digest=$(crane digest ${IMAGE_NAME}:${IMAGE_TAG})" >> $GITHUB_OUTPUT
 
 
     - name: Sign image
     - name: Sign image
       shell: bash
       shell: bash
       env:
       env:
         COSIGN_EXPERIMENTAL: "1"
         COSIGN_EXPERIMENTAL: "1"
-      run: cosign sign -a GITHUB_ACTOR=${{ github.triggering_actor }} "${{ inputs.image-name }}@${{ steps.container_info.outputs.digest }}"
+        IMAGE_NAME: ${{ inputs.image-name }}
+        CONTAINER_DIGEST: ${{ steps.container_info.outputs.digest }}
+        GITHUB_TRIGGERING_ACTOR: ${{ github.triggering_actor }}
+      run: cosign sign -a GITHUB_ACTOR=${GITHUB_TRIGGERING_ACTOR} "${IMAGE_NAME}@${CONTAINER_DIGEST}"
 
 
     - name: Attach SBOM to image
     - name: Attach SBOM to image
       shell: bash
       shell: bash
       id: sbom
       id: sbom
       env:
       env:
         COSIGN_EXPERIMENTAL: "1"
         COSIGN_EXPERIMENTAL: "1"
+        IMAGE_NAME: ${{ inputs.image-name }}
+        IMAGE_TAG: ${{ inputs.image-tag }}
+        CONTAINER_DIGEST: ${{ steps.container_info.outputs.digest }}
       run: |
       run: |
         # Image SBOM (OS + application libs contained in the image)
         # Image SBOM (OS + application libs contained in the image)
-        syft "${{ inputs.image-name }}@${{ steps.container_info.outputs.digest }}" -o spdx-json=sbom.${{ inputs.image-tag }}.spdx.json
-        cosign attest --predicate sbom.${{ inputs.image-tag }}.spdx.json --type spdx "${{ inputs.image-name }}@${{ steps.container_info.outputs.digest }}"
-        cosign verify-attestation --type spdx ${{ inputs.image-name }}@${{ steps.container_info.outputs.digest }} | jq '.payload |= @base64d | .payload | fromjson'
+        syft "${IMAGE_NAME}@${CONTAINER_DIGEST}" -o spdx-json=sbom.${IMAGE_TAG}.spdx.json
+        cosign attest --predicate sbom.${IMAGE_TAG}.spdx.json --type spdx "${IMAGE_NAME}@${CONTAINER_DIGEST}"
+        cosign verify-attestation --type spdx ${IMAGE_NAME}@${CONTAINER_DIGEST} | jq '.payload |= @base64d | .payload | fromjson'
 
 
         # Go modules SBOM (dependencies from the source tree)
         # Go modules SBOM (dependencies from the source tree)
         # Requires repository to be checked out before this composite action runs.
         # Requires repository to be checked out before this composite action runs.
-        syft dir:. -o spdx-json=sbom.gomod.${{ inputs.image-tag }}.spdx.json
-        cosign attest --predicate sbom.gomod.${{ inputs.image-tag }}.spdx.json --type spdx "${{ inputs.image-name }}@${{ steps.container_info.outputs.digest }}"
-        cosign verify-attestation --type spdx ${{ inputs.image-name }}@${{ steps.container_info.outputs.digest }} | jq '.payload |= @base64d | .payload | fromjson'
+        syft dir:. -o spdx-json=sbom.gomod.${IMAGE_TAG}.spdx.json
+        cosign attest --predicate sbom.gomod.${IMAGE_TAG}.spdx.json --type spdx "${IMAGE_NAME}@${CONTAINER_DIGEST}"
+        cosign verify-attestation --type spdx ${IMAGE_NAME}@${CONTAINER_DIGEST} | jq '.payload |= @base64d | .payload | fromjson'
 
 
     - name: Generate provenance
     - name: Generate provenance
-      uses: philips-labs/slsa-provenance-action@v0.7.2
+      uses: philips-labs/slsa-provenance-action@c6e428e3b9ea5ab10fa23efc10d6cbf5f0fe62b1 # v0.7.2
       with:
       with:
         command: generate
         command: generate
         subcommand: container
         subcommand: container
@@ -88,7 +97,10 @@ runs:
       id: provenance
       id: provenance
       env:
       env:
         COSIGN_EXPERIMENTAL: "1"
         COSIGN_EXPERIMENTAL: "1"
+        IMAGE_NAME: ${{ inputs.image-name }}
+        IMAGE_TAG: ${{ inputs.image-tag }}
+        CONTAINER_DIGEST: ${{ steps.container_info.outputs.digest }}
       run: |
       run: |
-        jq '.predicate' provenance.${{ inputs.image-tag }}.intoto.jsonl > provenance-predicate.att
-        cosign attest --predicate provenance-predicate.att --type slsaprovenance "${{ inputs.image-name }}@${{ steps.container_info.outputs.digest }}"
-        cosign verify-attestation --type slsaprovenance ${{ inputs.image-name }}@${{ steps.container_info.outputs.digest }}
+        jq '.predicate' provenance.${IMAGE_TAG}.intoto.jsonl > provenance-predicate.att
+        cosign attest --predicate provenance-predicate.att --type slsaprovenance "${IMAGE_NAME}@${CONTAINER_DIGEST}"
+        cosign verify-attestation --type slsaprovenance ${IMAGE_NAME}@${CONTAINER_DIGEST}

+ 5 - 0
.github/dependabot.yml

@@ -5,23 +5,28 @@ updates:
     directory: "/"
     directory: "/"
     schedule:
     schedule:
       interval: "weekly"
       interval: "weekly"
+    open-pull-requests-limit: 10
 
 
   - package-ecosystem: "docker"
   - package-ecosystem: "docker"
     directory: "/"
     directory: "/"
     schedule:
     schedule:
       interval: "weekly"
       interval: "weekly"
+    open-pull-requests-limit: 10
 
 
   - package-ecosystem: docker
   - package-ecosystem: docker
     directory: /e2e
     directory: /e2e
     schedule:
     schedule:
       interval: weekly
       interval: weekly
+    open-pull-requests-limit: 10
 
 
   - package-ecosystem: docker
   - package-ecosystem: docker
     directory: /hack/api-docs
     directory: /hack/api-docs
     schedule:
     schedule:
       interval: weekly
       interval: weekly
+    open-pull-requests-limit: 10
 
 
   - package-ecosystem: pip
   - package-ecosystem: pip
     directory: /hack/api-docs
     directory: /hack/api-docs
     schedule:
     schedule:
       interval: weekly
       interval: weekly
+    open-pull-requests-limit: 10

+ 12 - 4
.github/workflows/e2e-managed.yml

@@ -61,11 +61,14 @@ jobs:
       - name: Create status check
       - name: Create status check
         id: create_check
         id: create_check
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
+        env:
+          PROVIDER: ${{ github.event.client_payload.slash_command.args.named.provider }}
+          HEAD_SHA: ${{ github.event.client_payload.pull_request.head.sha }}
         with:
         with:
           github-token: ${{ secrets.GITHUB_TOKEN }}
           github-token: ${{ secrets.GITHUB_TOKEN }}
           script: |
           script: |
-            const job_name = "e2e-managed-" + "${{ github.event.client_payload.slash_command.args.named.provider }}"
-            const ref = "${{ github.event.client_payload.pull_request.head.sha }}"
+            const job_name = "e2e-managed-" + process.env.PROVIDER
+            const ref = process.env.HEAD_SHA
             const { data: checks } = await github.rest.checks.listForRef({
             const { data: checks } = await github.rest.checks.listForRef({
               ...context.repo,
               ...context.repo,
               ref
               ref
@@ -347,11 +350,16 @@ jobs:
     steps:
     steps:
       - name: Update status check
       - name: Update status check
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
+        env:
+          AWS_RESULT: ${{ needs.test-aws.result }}
+          GCP_RESULT: ${{ needs.test-gcp.result }}
+          AZURE_RESULT: ${{ needs.test-azure.result }}
+          CHECK_RUN_ID: ${{ needs.setup.outputs.check_run_id }}
         with:
         with:
           github-token: ${{ secrets.GITHUB_TOKEN }}
           github-token: ${{ secrets.GITHUB_TOKEN }}
           script: |
           script: |
-            const conclusion = '${{ needs.test-aws.result }}${{ needs.test-gcp.result }}${{ needs.test-azure.result }}';
-            const checkRunId = '${{ needs.setup.outputs.check_run_id }}';
+            const conclusion = process.env.AWS_RESULT + process.env.GCP_RESULT + process.env.AZURE_RESULT;
+            const checkRunId = process.env.CHECK_RUN_ID;
             
             
             // Determine the overall conclusion
             // Determine the overall conclusion
             let finalConclusion = 'success';
             let finalConclusion = 'success';

+ 5 - 3
.github/workflows/publish.yml

@@ -100,13 +100,15 @@ jobs:
         shell: bash
         shell: bash
         env:
         env:
           GITHUB_REF: ${{ github.ref }}
           GITHUB_REF: ${{ github.ref }}
+          INPUT_IMAGE_TAG: ${{ inputs.image-tag }}
+          INPUT_TAG_SUFFIX: ${{ inputs.tag-suffix }}
         run: |
         run: |
           # rebuild-image
           # rebuild-image
-          if [ "${{ inputs.image-tag }}" != "" ]; then
-            TAG="${{ inputs.image-tag }}${{ inputs.tag-suffix }}"
+          if [ "$INPUT_IMAGE_TAG" != "" ]; then
+            TAG="${INPUT_IMAGE_TAG}${INPUT_TAG_SUFFIX}"
           # main
           # main
           elif [[ "$GITHUB_REF" == "refs/heads/main" ]]; then
           elif [[ "$GITHUB_REF" == "refs/heads/main" ]]; then
-            TAG=${GITHUB_REF#refs/heads/}${{ inputs.tag-suffix }}
+            TAG=${GITHUB_REF#refs/heads/}${INPUT_TAG_SUFFIX}
           # Pull Request
           # Pull Request
           else
           else
             TAG=$(make docker.tag)
             TAG=$(make docker.tag)

+ 9 - 4
.github/workflows/release.yml

@@ -36,8 +36,10 @@ jobs:
           fetch-depth: 0
           fetch-depth: 0
           ref: ${{ github.event.inputs.source_ref }}
           ref: ${{ github.event.inputs.source_ref }}
       - name: check-docs
       - name: check-docs
+        env:
+          DOCS_VERSION: ${{ github.event.inputs.version }}
         run: |
         run: |
-          DOCS_VERSION=${{ github.event.inputs.version }} make docs.check
+          make docs.check
   release:
   release:
     name: Create Release
     name: Create Release
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
@@ -75,9 +77,10 @@ jobs:
 
 
       - name: Update Docs
       - name: Update Docs
         if: github.ref == 'refs/heads/main'
         if: github.ref == 'refs/heads/main'
-        run: make docs.publish DOCS_VERSION=${{ github.event.inputs.version }} DOCS_ALIAS=latest
         env:
         env:
+          DOCS_VERSION: ${{ github.event.inputs.version }}
           GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
           GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+        run: make docs.publish DOCS_ALIAS=latest
 
 
   promote:
   promote:
     name: Promote Container Image
     name: Promote Container Image
@@ -128,10 +131,12 @@ jobs:
         run: make docker.promote
         run: make docker.promote
 
 
       - name: Build release manifests
       - name: Build release manifests
+        env:
+          RELEASE_VERSION: ${{ github.event.inputs.version }}
         run: |
         run: |
           # temporarily patch the version so we generate manifests with the new version
           # temporarily patch the version so we generate manifests with the new version
-          yq e -i '.version = "${{ github.event.inputs.version }}"' ./deploy/charts/external-secrets/Chart.yaml
-          yq e -i '.appVersion = "${{ github.event.inputs.version }}"' ./deploy/charts/external-secrets/Chart.yaml
+          yq e -i ".version = \"$RELEASE_VERSION\"" ./deploy/charts/external-secrets/Chart.yaml
+          yq e -i ".appVersion = \"$RELEASE_VERSION\"" ./deploy/charts/external-secrets/Chart.yaml
           make manifests
           make manifests
 
 
       - name: Sign promoted image
       - name: Sign promoted image

+ 5 - 2
.github/workflows/release_esoctl.yml

@@ -55,16 +55,19 @@ jobs:
 
 
       - name: Check if Tag Exists
       - name: Check if Tag Exists
         id: check_tag
         id: check_tag
+        env:
+          VERSION: ${{ github.event.inputs.version }}
         run: |
         run: |
-          if git rev-parse "${{ github.event.inputs.version }}" >/dev/null 2>&1; then
+          if git rev-parse "$VERSION" >/dev/null 2>&1; then
             echo "Tag exists."
             echo "Tag exists."
             exit 1
             exit 1
           fi
           fi
 
 
       - name: Create Tag if Not Exists
       - name: Create Tag if Not Exists
         if: success()
         if: success()
+        env:
+          TAG: ${{ github.event.inputs.version }}
         run: |
         run: |
-          TAG="${{ github.event.inputs.version }}"
           git tag $TAG
           git tag $TAG
           git push origin $TAG
           git push origin $TAG
 
 

+ 4 - 3
.github/workflows/update-deps.yml

@@ -66,6 +66,9 @@ jobs:
         go-version-file: go.mod
         go-version-file: go.mod
 
 
     - name: create pull request
     - name: create pull request
+      env:
+        BASE_BRANCH: ${{ matrix.branch }}
+        GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
       run: |
       run: |
         git config --global user.email "ExternalSecretsOperator@users.noreply.github.com"
         git config --global user.email "ExternalSecretsOperator@users.noreply.github.com"
         git config --global user.name "External Secrets Operator"
         git config --global user.name "External Secrets Operator"
@@ -81,6 +84,4 @@ jobs:
         git add -A
         git add -A
         git commit -m "update dependencies" -s
         git commit -m "update dependencies" -s
         git push origin $BRANCH
         git push origin $BRANCH
-        gh pr create -B ${{ matrix.branch }} -H ${BRANCH} --title 'chore: update dependencies' --body 'Update dependencies'
-      env:
-        GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
+        gh pr create -B $BASE_BRANCH -H ${BRANCH} --title 'chore: update dependencies' --body 'Update dependencies'

+ 3 - 0
.github/workflows/zizmor.yml

@@ -12,6 +12,9 @@ on:
     paths:
     paths:
       - '.github/workflows/**'
       - '.github/workflows/**'
 
 
+permissions:
+  contents: read
+
 jobs:
 jobs:
   detect-noop:
   detect-noop:
     permissions:
     permissions:

+ 5 - 0
pkg/provider/cloudru/secretmanager/endpoints.go

@@ -39,6 +39,11 @@ type Endpoint struct {
 
 
 // GetEndpoints returns the actual Cloud.ru API endpoints.
 // GetEndpoints returns the actual Cloud.ru API endpoints.
 func GetEndpoints(url string) (*EndpointsResponse, error) {
 func GetEndpoints(url string) (*EndpointsResponse, error) {
+	// Validate that the URL is the expected Cloud.ru endpoints URL to prevent SSRF
+	if url != EndpointsURI {
+		return nil, fmt.Errorf("invalid endpoints URL: expected %s, got %s", EndpointsURI, url)
+	}
+	
 	req, err := http.NewRequest(http.MethodGet, url, http.NoBody)
 	req, err := http.NewRequest(http.MethodGet, url, http.NoBody)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("construct HTTP request for cloud.ru endpoints: %w", err)
 		return nil, fmt.Errorf("construct HTTP request for cloud.ru endpoints: %w", err)

+ 4 - 1
pkg/provider/vault/auth_iam.go

@@ -193,7 +193,10 @@ func (c *client) getCredsFromIRSAToken(ctx context.Context, tokenFile, region st
 		return nil, fmt.Errorf(errIrsaTokenFileNotReadable, tokenFile, err)
 		return nil, fmt.Errorf(errIrsaTokenFileNotReadable, tokenFile, err)
 	}
 	}
 
 
-	// let's parse the jwt token
+	// Parse the JWT token to extract metadata (namespace and service account).
+	// Note: Signature verification is intentionally skipped here as we only need to extract
+	// claims from the IRSA token that comes from a trusted source (AWS-mounted file).
+	// The token itself will be validated by AWS STS when used for authentication.
 	parser := jwt.NewParser(jwt.WithoutClaimsValidation())
 	parser := jwt.NewParser(jwt.WithoutClaimsValidation())
 
 
 	token, _, err := parser.ParseUnverified(string(jwtByte), jwt.MapClaims{})
 	token, _, err := parser.ParseUnverified(string(jwtByte), jwt.MapClaims{})