diff --git a/.github/codeql/codeql-config.yaml b/.github/codeql/codeql-config.yaml new file mode 100644 index 0000000..ab6fcc3 --- /dev/null +++ b/.github/codeql/codeql-config.yaml @@ -0,0 +1,11 @@ +# CodeQL configuration +name: "CodeQL configuration" +query-filters: + - exclude: + problem.severity: + - warning + - recommendation + +paths-ignore: + - '**/tests/**' + - '**/*Tests.cs' \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d24a08d..e735327 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,25 +1,33 @@ ## Description -Please include a summary of the change. + ## Why -Please include an explanation of why this change is necessary as well as relevant motivation and context. List any dependencies that are required for this change. + -## Issue -Link to Github issue. +## Issue Link + +Refs: -## Checklist +## Checklist Please delete options that are not relevant. -- [ ] I have followed the [contributing guidelines](https://github.com/eclipse-tractusx/policy-hub/blob/main/docs/technical-documentation/dev-process/How%20to%20contribute.md) -- [ ] I have performed [IP checks](https://eclipse-tractusx.github.io/docs/release/trg-7/trg-7-04#checking-libraries-using-the-eclipse-dash-license-tool) for added or updated 3rd party libraries -- [ ] I have created and linked IP issues or requested their creation by a committer +- [ ] I have followed the contributing guidelines + +- [ ] I have performed IP checks for added or updated 3rd party libraries + +- [ ] I have added copyright and license headers, footers (for .md files) or files (for images) //open source requirement + - [ ] I have performed a self-review of my own code + - [ ] I have successfully tested my changes locally -- [ ] I have added tests that prove my changes work + +- [ ] I have added tests and updated existing tests that prove my changes work + - [ ] I have checked that new and existing tests pass locally with my changes -- [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I have added copyright and license headers, footers (for .md files) or files (for images) \ No newline at end of file + +- [ ] I have commented my code, particularly in hard-to-understand areas \ No newline at end of file diff --git a/.github/workflows/cfx-build-scan-push.yaml b/.github/workflows/cfx-build-scan-push.yaml new file mode 100644 index 0000000..dd4c959 --- /dev/null +++ b/.github/workflows/cfx-build-scan-push.yaml @@ -0,0 +1,164 @@ +# A workflow for Gradle build, Sonar scan, Code QL, Container image build, and image push +name: Policy Hub Build, scan and push + +on: + workflow_dispatch: + pull_request: + types: [ opened, synchronize, reopened ] + branches: [ main ] + paths: + - 'docker/**' + - 'src/**' + - 'tests/**' + - '.github/workflows/cfx-build-scan-push.yaml' + push: + branches: [ main ] + paths: + - 'docker/**' + - 'src/**' + - 'tests.*' + - '.github/workflows/cfx-build-scan-push.yaml' + tags: + - '[0-9]+.[0-9]+.[0-9]+-*' + +jobs: + build-test-scan-app: + permissions: + contents: read + actions: read + security-events: write + name: Build, test, and scan App + uses: Cofinity-X/central-pipelines/.github/workflows/reusable-dotnet-build.yaml@main + with: + dotnet-version: 8.0 + project: "src" + codeql-cfg-path: ./.github/codeql/codeql-config.yaml + + # The sonar job needs to use to central resusable worklows once it's available there + sonar-scan: + name: Sonar scan + runs-on: ubuntu-latest + needs: build-test-scan-app + steps: + - name: Set up JDK 17 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 + with: + distribution: 'temurin' + java-version: '17' + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Cache SonarCloud packages + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: ~/sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache SonarCloud scanner + id: cache-sonar-scanner + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: ./.sonar/scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + + - name: Install SonarCloud scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + run: | + mkdir -p ./.sonar/scanner + dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner + + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: | + dotnet tool install --global dotnet-coverage + ./.sonar/scanner/dotnet-sonarscanner begin /k:Cofinity-X_policy-hub /o:cofinity-x /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=src/coverage.xml + dotnet build src + cd src + dotnet-coverage collect 'dotnet test --no-restore --verbosity normal' -s 'settings-coverage.xml' -f xml -o 'coverage.xml' + cd .. + ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" + + # For Container image build, Trivy scan, and image push + dockerizing-application: + needs: build-test-scan-app + permissions: + contents: read + actions: read + security-events: write + id-token: write + + strategy: + matrix: + include: + - service_name: policy-hub-service + dockerfile: docker/Dockerfile-policy-hub-service + - service_name: policy-hub-migrations + dockerfile: docker/Dockerfile-policy-hub-migrations + + name: Docker build, Trivy scan, Docker push + uses: Cofinity-X/central-pipelines/.github/workflows/reusable-publish-image-to-acr.yaml@main + with: + team_name: core-services + repository_name: policy-hub + service_name: ${{ matrix.service_name }} + dockerfile_path: ${{ matrix.dockerfile }} + environment: "lower-env-acr" + push: ${{ github.event_name != 'pull_request' }} # Don't push the image in case of PR + secrets: + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + + # To dispatch a workflow in core-services-charts repo for auto image tag update. The following job could be developed as reusable workflow in the future. + auto-deploy-dispatch: + needs: dockerizing-application + name: Dispatch charts repo workflow + runs-on: ubuntu-latest + if: ${{ (github.event_name != 'pull_request') && (github.ref_type != 'tag') }} # Execute only if container image is pushed and no tag is published + steps: + # Get image tags from the previous job filter the tag that needs to be updated (Currently, it's main-{sha}) + - name: Get image tags + id: get_tag + run: | + for image_tag in $(echo "${{ needs.dockerizing-application.outputs.image_tags }}") + do + tag=$(echo $image_tag | cut -d ":" -f2) + if [[ $tag == main* ]]; + then + main_tag=$tag + fi + done + echo "main_tag=$main_tag" >> $GITHUB_OUTPUT + + # Generate a temporary token using Github app + - name: Get token + id: get_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.CORE_SERVICES_WORKFLOW_TRIGGER_GH_APP_ID }} + private-key: ${{ secrets.CORE_SERVICES_WORKFLOW_TRIGGER_GH_APP_PRIVATE_KEY }} + repositories: core-services-charts + owner: cofinity-x + + # Trigger remote workflow of core-services-chart to update the image tag in the helm values + - name: Trigger workflow + id: trigger_remote_workflow + env: + TOKEN: ${{ steps.get_token.outputs.token }} + HELM_VALUES_PATH: "policy-hub/dev/values.yaml" + IMAGE_TAG: ${{ steps.get_tag.outputs.main_tag }} + IMAGE_TAG_PROPERTY: '(.[\"service\", \"migrations\"].image.tag)' + GITHUB_RUN_ID: ${{ github.run_id }} + run: | + curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/cofinity-x/core-services-charts/dispatches \ + -d '{"event_type":"update_dev_image_tag","client_payload": { "image_tag": "'"$IMAGE_TAG"'", "helm_values_path": "'"$HELM_VALUES_PATH"'", "image_tag_property": "'"$IMAGE_TAG_PROPERTY"'", "dispatcher_info": "'"Runner id - $GITHUB_RUN_ID"'" }}' \ + --fail diff --git a/.github/workflows/cfx-codeql.yaml b/.github/workflows/cfx-codeql.yaml new file mode 100644 index 0000000..252e41f --- /dev/null +++ b/.github/workflows/cfx-codeql.yaml @@ -0,0 +1,34 @@ +name: CFX CodeQL dotnet + +on: + workflow_dispatch: + pull_request: + branches: + - 'main' + paths: + - '.github/workflows/cfx-codeql.yaml' + schedule: + - cron: "0 0 * * *" + push: + branches: + - 'main' + paths: + - '.github/workflows/cfx-codeql.yaml' +concurrency: + # cancel older running jobs on the same branch + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + dotnet-scan: + permissions: + contents: read + actions: read + security-events: write + + name: Dotnet CodeQL Scan + uses: Cofinity-X/central-pipelines/.github/workflows/reusable-codeql.yaml@main + with: + languages: "['csharp']" + codeql-cfg-path: "./.github/codeql/codeql-config.yaml" + project-directory: "src" diff --git a/.github/workflows/cfx-trivy.yaml b/.github/workflows/cfx-trivy.yaml new file mode 100644 index 0000000..f9318d0 --- /dev/null +++ b/.github/workflows/cfx-trivy.yaml @@ -0,0 +1,43 @@ +# Workflow to scan the latest container image daily +name: Trivy scan + +on: + workflow_dispatch: + pull_request: + paths: + - '.github/workflows/cfx-trivy.yaml' + schedule: + # Daily + - cron: '0 0 * * *' + +concurrency: + # cancel older running jobs on the same branch + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + scan-image: + permissions: + actions: read + contents: read + security-events: write + id-token: write + + strategy: + matrix: + include: + - service_name: policy-hub-service + dockerfile: docker/Dockerfile-policy-hub-service + - service_name: policy-hub-migrations + dockerfile: docker/Dockerfile-policy-hub-migrations + + uses: Cofinity-X/central-pipelines/.github/workflows/reusable-trivy-acr-latest-image-scan.yaml@main + name: Trivy Scan + with: + environment: lower-env-acr + image-name: ${{ matrix.service_name }} + image-namespace: core-services/policy-hub/images + secrets: + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} \ No newline at end of file diff --git a/.github/workflows/chart-test.yml b/.github/workflows/chart-test.yml index d5673d8..cd9e570 100644 --- a/.github/workflows/chart-test.yml +++ b/.github/workflows/chart-test.yml @@ -83,7 +83,7 @@ jobs: with: version: v3.9.3 - - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: '3.9' check-latest: true diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index cb5f670..af888e0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -73,7 +73,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v2.227 + uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v2.227 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -87,7 +87,7 @@ jobs: # Automates dependency installation for Python, Ruby, and JavaScript, optimizing the CodeQL analysis setup. # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@b611370bb5703a7efb587f9d136a52ea24c5c38c # v2.227 + uses: github/codeql-action/autobuild@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v2.227 # ℹī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -100,6 +100,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v2.227 + uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v2.227 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index f6c1bad..03ef1fa 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -38,13 +38,13 @@ jobs: steps: - name: Set up JDK 17 - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: 'temurin' java-version: '17' - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} - uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1 with: dotnet-version: ${{ matrix.dotnet-version }} # change to preview .NET 9 until fix for https://github.com/NuGet/Home/issues/12954 gets released for .NET 8 @@ -87,7 +87,7 @@ jobs: if: steps.dependencies-changed.outputs.changed == 'true' - name: Upload DEPENDENCIES file - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: path: DEPENDENCIES if: steps.dependencies-changed.outputs.changed == 'true' diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index 9c5b9e2..fa772cb 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: KICS scan - uses: checkmarx/kics-github-action@252e73959bd4809a14863cbfbb42d7a90d5a4860 # v2.1.1 + uses: checkmarx/kics-github-action@530ac1f8efe6202b0f12c9a6e952597ae707b755 # v2.1.2 with: # Scanning directory . path: "." @@ -69,7 +69,7 @@ jobs: # Upload findings to GitHub Advanced Security Dashboard - name: Upload SARIF file for GitHub Advanced Security Dashboard if: always() - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: kicsResults/results.sarif diff --git a/.github/workflows/migrations-docker.yml b/.github/workflows/migrations-docker.yml index 4bfe3e0..1693dcd 100644 --- a/.github/workflows/migrations-docker.yml +++ b/.github/workflows/migrations-docker.yml @@ -50,13 +50,13 @@ jobs: - name: Login to DockerHub if: github.event_name != 'pull_request' - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Docker meta id: meta diff --git a/.github/workflows/owasp-zap.yml b/.github/workflows/owasp-zap.yml index 48b4c57..a921652 100644 --- a/.github/workflows/owasp-zap.yml +++ b/.github/workflows/owasp-zap.yml @@ -126,7 +126,7 @@ jobs: - name: Upload HTML report if: success() || failure() - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: ZAP scan report path: ./report_html.html diff --git a/.github/workflows/pull_request_lint.yaml b/.github/workflows/pull_request_lint.yaml new file mode 100644 index 0000000..b351b8d --- /dev/null +++ b/.github/workflows/pull_request_lint.yaml @@ -0,0 +1,12 @@ +name: Lint +on: + pull_request: + branches: [main] + +jobs: + + pull_request_lint: + permissions: + pull-requests: write + name: PR Title + uses: Cofinity-X/central-pipelines/.github/workflows/_pull_request_lint.yaml@main \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 186a761..dd0b676 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -106,13 +106,13 @@ jobs: fetch-depth: 0 - name: Login to DockerHub - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 # Create SemVer or ref tags dependent of trigger event - name: Docker meta diff --git a/.github/workflows/release_candidate.yml b/.github/workflows/release_candidate.yml index 1b6adfe..fc7bb9d 100644 --- a/.github/workflows/release_candidate.yml +++ b/.github/workflows/release_candidate.yml @@ -47,13 +47,13 @@ jobs: - name: Login to DockerHub if: github.event_name != 'pull_request' - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Docker meta id: meta diff --git a/.github/workflows/service-docker.yml b/.github/workflows/service-docker.yml index 7052179..3f8492d 100644 --- a/.github/workflows/service-docker.yml +++ b/.github/workflows/service-docker.yml @@ -49,13 +49,13 @@ jobs: - name: Login to DockerHub if: github.event_name != 'pull_request' - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Docker meta id: meta diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 74e9f9e..fe7dfd8 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Set up JDK 17 - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/test-automation.yml b/.github/workflows/test-automation.yml index e0b56a5..19433ce 100644 --- a/.github/workflows/test-automation.yml +++ b/.github/workflows/test-automation.yml @@ -53,7 +53,7 @@ jobs: ReportPortal.config.json > temp.json mv temp.json ReportPortal.config.json - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} - uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1 with: dotnet-version: ${{ matrix.dotnet-version }} - name: Install dependencies @@ -69,7 +69,7 @@ jobs: run: dotnet test tests/endToEnd --no-restore --verbosity minimal --logger "html;logfilename=e2ePolicyHub.html" - name: 'Upload Artifact' if: always() - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: e2e-policy-hub-report path: tests/endToEnd/TestResults/e2ePolicyHub.html \ No newline at end of file diff --git a/.github/workflows/trivy-main.yml b/.github/workflows/trivy-main.yml index 0afaf5f..f7234a2 100644 --- a/.github/workflows/trivy-main.yml +++ b/.github/workflows/trivy-main.yml @@ -53,7 +53,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0 with: scan-type: "config" hide-progress: false @@ -63,7 +63,7 @@ jobs: timeout: "3600s" - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 if: always() with: sarif_file: "trivy-results1.sarif" @@ -86,7 +86,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/policy-hub-service:main" @@ -96,7 +96,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: "trivy-results2.sarif" @@ -118,7 +118,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/policy-hub-migrations:main" @@ -129,6 +129,6 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: "trivy-results3.sarif" diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index eb74e17..ad53af3 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0 with: scan-type: "config" hide-progress: false @@ -65,7 +65,7 @@ jobs: timeout: "3600s" - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 if: always() with: sarif_file: "trivy-results1.sarif" @@ -88,7 +88,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/policy-hub-service:latest" @@ -98,7 +98,7 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: "trivy-results2.sarif" @@ -120,7 +120,7 @@ jobs: # For public images, no ENV vars must be set. - name: Run Trivy vulnerability scanner if: always() - uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0 with: # Path to Docker image image-ref: "${{ env.IMAGE_NAMESPACE }}/policy-hub-migrations:latest" @@ -130,6 +130,6 @@ jobs: - name: Upload Trivy scan results to GitHub Security tab if: always() - uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: "trivy-results3.sarif" diff --git a/.github/workflows/unit.tests-formatting.yml b/.github/workflows/unit.tests-formatting.yml index 9e21da1..37425ec 100644 --- a/.github/workflows/unit.tests-formatting.yml +++ b/.github/workflows/unit.tests-formatting.yml @@ -41,7 +41,7 @@ jobs: steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} - uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1 with: dotnet-version: ${{ matrix.dotnet-version }} - name: Install dotnet-format diff --git a/src/database/PolicyHub.Entities/PolicyHub.Entities.csproj b/src/database/PolicyHub.Entities/PolicyHub.Entities.csproj index 42558ee..209b7bd 100644 --- a/src/database/PolicyHub.Entities/PolicyHub.Entities.csproj +++ b/src/database/PolicyHub.Entities/PolicyHub.Entities.csproj @@ -27,7 +27,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/database/PolicyHub.Migrations/PolicyHub.Migrations.csproj b/src/database/PolicyHub.Migrations/PolicyHub.Migrations.csproj index fa2ecd2..b2bfd4c 100644 --- a/src/database/PolicyHub.Migrations/PolicyHub.Migrations.csproj +++ b/src/database/PolicyHub.Migrations/PolicyHub.Migrations.csproj @@ -39,7 +39,7 @@ - + diff --git a/src/hub/PolicyHub.Service/BusinessLogic/PolicyHubBusinessLogic.cs b/src/hub/PolicyHub.Service/BusinessLogic/PolicyHubBusinessLogic.cs index 18f641e..9cc055b 100644 --- a/src/hub/PolicyHub.Service/BusinessLogic/PolicyHubBusinessLogic.cs +++ b/src/hub/PolicyHub.Service/BusinessLogic/PolicyHubBusinessLogic.cs @@ -64,16 +64,29 @@ private static (object rightOperand, AdditionalAttributes? additionalAttribute) AttributeKeyId.DynamicValue => (value ?? "{dynamicValue}", null), AttributeKeyId.Regex => (GetRegexValue(attributes, value), null), _ => operatorId == OperatorId.Equals - ? rightOperands.Count() > 1 ? + ? processEqualsOperator(attributes, rightOperands, value, leftOperand, useCase) + : (rightOperands, null) + }; + + private static (object rightOperand, AdditionalAttributes? additionalAttribute) processEqualsOperator((AttributeKeyId? Key, IEnumerable Values) attributes, IEnumerable rightOperands, string? value, string leftOperand, UseCaseId? useCase) + { + if (value != null) + { + if (!rightOperands.Any(r => r == value)) + { + throw new ControllerArgumentException($"Invalid values [{value}] set for key {leftOperand}. Possible values [{string.Join(",", rightOperands)}]"); + } + rightOperands = rightOperands.Where(r => r.Equals(value)); + } + return rightOperands.Count() > 1 ? ($"@{leftOperand}{(useCase != null ? useCase.Value.ToString().Insert(0, ".") : string.Empty)}-{attributes.Key}", new AdditionalAttributes($"@{leftOperand}{(useCase != null ? useCase.Value.ToString().Insert(0, ".") : string.Empty)}-{attributes.Key}", rightOperands)) : - (rightOperands.Single(), null) - : (rightOperands, null) - }; + (rightOperands.Single(), null); + } private static object GetRegexValue((AttributeKeyId? Key, IEnumerable Values) attributes, string? value) { @@ -163,7 +176,7 @@ public async Task GetPolicyContentAsync(PolicyContentRequest req if (invalidValues.Any()) { var x = missingValues.Where(x => invalidValues.Contains(x.TechnicalKey)).Select(x => - $"Key: {x.TechnicalKey}, invalid values: {string.Join(',', x.Values)}"); + $"Key: {x.TechnicalKey}, requested value[{string.Join(',', x.Values)}] Possible Values[{string.Join(',', attributeValuesForTechnicalKeys.Where(a => a.TechnicalKey.Equals(x.TechnicalKey)).Select(a => a.Values).First())}]"); throw new ControllerArgumentException($"Invalid values set for {string.Join(',', x)}"); } diff --git a/src/hub/PolicyHub.Service/Controllers/PolicyHubController.cs b/src/hub/PolicyHub.Service/Controllers/PolicyHubController.cs index cf67bb3..78a831c 100644 --- a/src/hub/PolicyHub.Service/Controllers/PolicyHubController.cs +++ b/src/hub/PolicyHub.Service/Controllers/PolicyHubController.cs @@ -23,6 +23,7 @@ using Org.Eclipse.TractusX.PolicyHub.Service.BusinessLogic; using Org.Eclipse.TractusX.PolicyHub.Service.Extensions; using Org.Eclipse.TractusX.PolicyHub.Service.Models; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Web; using System.Diagnostics.CodeAnalysis; @@ -71,6 +72,7 @@ public static RouteGroupBuilder MapPolicyHubApi(this RouteGroupBuilder group) .RequireAuthorization() .WithDefaultResponses() .Produces(StatusCodes.Status200OK, typeof(PolicyResponse), Constants.JsonContentType) + .Produces(StatusCodes.Status400BadRequest, typeof(ErrorResponse), Constants.JsonContentType) .Produces(StatusCodes.Status404NotFound, typeof(ErrorResponse), Constants.JsonContentType); policyHub.MapPost("policy-content", ([FromBody] PolicyContentRequest requestData, IPolicyHubBusinessLogic logic) => logic.GetPolicyContentAsync(requestData)) @@ -80,7 +82,8 @@ public static RouteGroupBuilder MapPolicyHubApi(this RouteGroupBuilder group) .RequireAuthorization() .WithDefaultResponses() .Produces(StatusCodes.Status200OK, typeof(PolicyResponse), Constants.JsonContentType) - .Produces(StatusCodes.Status404NotFound, typeof(ErrorResponse), Constants.JsonContentType); + .Produces(StatusCodes.Status400BadRequest, typeof(ErrorResponse), Constants.JsonContentType) + .Produces(StatusCodes.Status404NotFound, typeof(ControllerArgumentException), Constants.JsonContentType); return group; } diff --git a/src/hub/PolicyHub.Service/PolicyHub.Service.csproj b/src/hub/PolicyHub.Service/PolicyHub.Service.csproj index 83fd06b..84875fe 100644 --- a/src/hub/PolicyHub.Service/PolicyHub.Service.csproj +++ b/src/hub/PolicyHub.Service/PolicyHub.Service.csproj @@ -33,9 +33,14 @@ - - - + + + + + + + + diff --git a/src/hub/PolicyHub.Service/Program.cs b/src/hub/PolicyHub.Service/Program.cs index e71d0bf..5b78713 100644 --- a/src/hub/PolicyHub.Service/Program.cs +++ b/src/hub/PolicyHub.Service/Program.cs @@ -18,6 +18,8 @@ ********************************************************************************/ using Microsoft.AspNetCore.Authentication; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; using Org.Eclipse.TractusX.PolicyHub.DbAccess.DependencyInjection; using Org.Eclipse.TractusX.PolicyHub.Service.Authentication; using Org.Eclipse.TractusX.PolicyHub.Service.Controllers; @@ -33,6 +35,20 @@ await WebApplicationBuildRunner builder.Services.AddTransient(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddHubRepositories(builder.Configuration); + + // Configure OpenTelemetry and Prometheus Exporter + builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => resource + .AddService(serviceName: builder.Environment.ApplicationName)) + .WithMetrics(metrics => metrics + .AddAspNetCoreInstrumentation() // Capture ASP.NET Core metrics + .AddPrometheusExporter() // Add Prometheus exporter for metrics + .AddConsoleExporter((exporterOptions, metricReaderOptions) => + { + metricReaderOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = 1000; + })); + + // JSON serialization options for API responses builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); @@ -47,4 +63,7 @@ await WebApplicationBuildRunner app.MapGroup("/api") .WithOpenApi() .MapPolicyHubApi(); + + // Expose Prometheus scraping endpoint on the same port (8080) + app.UseOpenTelemetryPrometheusScrapingEndpoint(); // Exposes /metrics endpoint }).ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/tests/database/PolicyHub.DbAccess.Tests/PolicyHub.DbAccess.Tests.csproj b/tests/database/PolicyHub.DbAccess.Tests/PolicyHub.DbAccess.Tests.csproj index 28961ec..cff36e2 100644 --- a/tests/database/PolicyHub.DbAccess.Tests/PolicyHub.DbAccess.Tests.csproj +++ b/tests/database/PolicyHub.DbAccess.Tests/PolicyHub.DbAccess.Tests.csproj @@ -36,8 +36,8 @@ - - + + diff --git a/tests/database/PolicyHub.Entities.Tests/PolicyHub.Entities.Tests.csproj b/tests/database/PolicyHub.Entities.Tests/PolicyHub.Entities.Tests.csproj index 0ce74eb..b7636f4 100644 --- a/tests/database/PolicyHub.Entities.Tests/PolicyHub.Entities.Tests.csproj +++ b/tests/database/PolicyHub.Entities.Tests/PolicyHub.Entities.Tests.csproj @@ -36,8 +36,8 @@ - + diff --git a/tests/hub/PolicyHub.Service.Tests/BusinessLogic/PolicyHubBusinessLogicTests.cs b/tests/hub/PolicyHub.Service.Tests/BusinessLogic/PolicyHubBusinessLogicTests.cs index fdc291d..2d04570 100644 --- a/tests/hub/PolicyHub.Service.Tests/BusinessLogic/PolicyHubBusinessLogicTests.cs +++ b/tests/hub/PolicyHub.Service.Tests/BusinessLogic/PolicyHubBusinessLogicTests.cs @@ -169,7 +169,7 @@ public async Task GetPolicyContentWithFiltersAsync_WithMultipleValues_ReturnsExp .Returns(new ValueTuple>, string?>(true, "multipleAdditionalValues", new ValueTuple>(AttributeKeyId.Static, new[] { "value1", "value2", "value3" }), null)); // Act - var result = await _sut.GetPolicyContentWithFiltersAsync(UseCaseId.Traceability, PolicyTypeId.Usage, "multipleAdditionalValues", OperatorId.Equals, "test"); + var result = await _sut.GetPolicyContentWithFiltersAsync(UseCaseId.Traceability, PolicyTypeId.Usage, "multipleAdditionalValues", OperatorId.Equals, null); // Assert result.Content.Id.Should().Be("...."); @@ -188,6 +188,23 @@ public async Task GetPolicyContentWithFiltersAsync_WithMultipleValues_ReturnsExp result.Content.Permission.Constraint.OrOperands.Should().BeNull(); } + [Fact] + public async Task GetPolicyContentWithFiltersAsync_WithInvalidValue_ExceptionExpected() + { + // Arrange + A.CallTo(() => _policyRepository.GetPolicyContentAsync(UseCaseId.Traceability, PolicyTypeId.Usage, "multipleAdditionalValues")) + .Returns(new ValueTuple>, string?>(true, "multipleAdditionalValues", new ValueTuple>(AttributeKeyId.Static, new[] { "value1", "value2", "value3" }), null)); + + // Act + async Task Act() => await _sut.GetPolicyContentWithFiltersAsync(UseCaseId.Traceability, PolicyTypeId.Usage, "multipleAdditionalValues", OperatorId.Equals, "test"); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be("Invalid values [test] set for key multipleAdditionalValues. Possible values [value1,value2,value3]"); + } + #endregion #region GetPolicyContentAsync @@ -264,7 +281,7 @@ public async Task GetPolicyContentAsync_WithUnmatchingAttributeValues_ThrowsCont var ex = await Assert.ThrowsAsync(Act); // Assert - ex.Message.Should().Be("Invalid values set for Key: test, invalid values: abc"); + ex.Message.Should().Be("Invalid values set for Key: test, requested value[abc] Possible Values[test]"); } [Fact]