diff --git a/.github/workflows/attest.yml b/.github/workflows/attest.yml index 06e5369..4f1d65c 100644 --- a/.github/workflows/attest.yml +++ b/.github/workflows/attest.yml @@ -84,87 +84,89 @@ jobs: echo "Using provided artifact name: ${{ inputs.artifact-name }}" echo "Encoded: $API_ARTIFACT_NAME" fi + echo "Resolved artifact name for attestation: ${{ inputs.artifact-name }}" - - name: Get SBOM - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + - name: Get and Attest SBOM + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: - args: > - sh -c " - slug=$(devguard-scanner slug ${{ github.ref_name }}) && devguard-scanner curl '${{ inputs.api-url }}/api/v1/organizations/${{ inputs.asset-name }}/refs/'$slug'/artifacts/${{ env.API_ARTIFACT_NAME }}/sbom.json/' --token='${{ secrets.devguard-token }}' > sbom.json - " - env: - API_ARTIFACT_NAME: ${{ env.API_ARTIFACT_NAME }} - - name: Get VeX - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest - with: - args: > - sh -c " - slug=$(devguard-scanner slug ${{ github.ref_name }}) && devguard-scanner curl '${{ inputs.api-url }}/api/v1/organizations/${{ inputs.asset-name }}/refs/'$slug'/artifacts/${{ env.API_ARTIFACT_NAME }}/vex.json/' --token='${{ secrets.devguard-token }}' > vex.json - " - env: - API_ARTIFACT_NAME: ${{ env.API_ARTIFACT_NAME }} - - name: Get SAST-Results - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest - with: - args: > - sh -c " - slug=$(devguard-scanner slug ${{ github.ref_name }}) && devguard-scanner curl '${{ inputs.api-url }}/api/v1/organizations/${{ inputs.asset-name }}/refs/'$slug'/sarif.json' --token='${{ secrets.devguard-token }}' > sarif.json - " - - name: Attest SBOM - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest - with: - args: > + args: | sh -c " + slug=$(devguard-scanner slug ${{ github.ref_name }}) && + artifact_name="$ARTIFACT_NAME" && + echo 'Fetching SBOM for artifact:' '${{ env.API_ARTIFACT_NAME }}' && + devguard-scanner curl '${{ inputs.api-url }}/api/v1/organizations/${{ inputs.asset-name }}/refs/'$slug'/artifacts/${{ env.API_ARTIFACT_NAME }}/sbom.json/' --token='${{ secrets.devguard-token }}' > /tmp/sbom.json && + echo 'SBOM downloaded to /tmp/sbom.json' && if [ -f image-digest.txt ]; then - devguard-scanner attest -u ${{ github.actor }} -r ghcr.io -p ${{ secrets.GITHUB_TOKEN }} sbom.json --predicateType='https://cyclonedx.org/bom' \"$(cat image-tag.txt)@$(cat image-digest.txt)\" --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName=${{ env.ARTIFACT_NAME }} + echo 'Attesting SBOM with image digest present' && + devguard-scanner attest -u ${{ github.actor }} -r ghcr.io -p ${{ secrets.GITHUB_TOKEN }} /tmp/sbom.json --predicateType='https://cyclonedx.org/bom' \"$(cat image-tag.txt)@$(cat image-digest.txt)\" --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName="$artifact_name" else - devguard-scanner attest sbom.json --predicateType='https://cyclonedx.org/bom' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName=${{ env.ARTIFACT_NAME }} + echo 'Attesting SBOM without image digest' && + devguard-scanner attest /tmp/sbom.json --predicateType='https://cyclonedx.org/bom' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName="$artifact_name" fi " env: - ARTIFACT_NAME: ${{ env.ARTIFACT_NAME }} - - name: Attest VeX - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + API_ARTIFACT_NAME: ${{ env.API_ARTIFACT_NAME }} + ARTIFACT_NAME: ${{ env.ARTIFACT_NAME }} + - name: Get and Attest VeX + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: - args: > + args: | sh -c " + slug=$(devguard-scanner slug ${{ github.ref_name }}) && + artifact_name="$ARTIFACT_NAME" && + echo 'Fetching VeX for artifact:' '${{ env.API_ARTIFACT_NAME }}' && + devguard-scanner curl '${{ inputs.api-url }}/api/v1/organizations/${{ inputs.asset-name }}/refs/'$slug'/artifacts/${{ env.API_ARTIFACT_NAME }}/vex.json/' --token='${{ secrets.devguard-token }}' > /tmp/vex.json && + echo 'VeX downloaded to /tmp/vex.json' && if [ -f image-digest.txt ]; then - devguard-scanner attest -u ${{ github.actor }} -r ghcr.io -p ${{ secrets.GITHUB_TOKEN }} vex.json \"$(cat image-tag.txt)@$(cat image-digest.txt)\" --token='${{ secrets.devguard-token }}' --predicateType='https://cyclonedx.org/vex' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName=${{ env.ARTIFACT_NAME }} + echo 'Attesting VeX with image digest present' && + devguard-scanner attest -u ${{ github.actor }} -r ghcr.io -p ${{ secrets.GITHUB_TOKEN }} /tmp/vex.json \"$(cat image-tag.txt)@$(cat image-digest.txt)\" --token='${{ secrets.devguard-token }}' --predicateType='https://cyclonedx.org/vex' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName="$artifact_name" else - devguard-scanner attest vex.json --predicateType='https://cyclonedx.org/vex' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName=${{ env.ARTIFACT_NAME }} + echo 'Attesting VeX without image digest' && + devguard-scanner attest /tmp/vex.json --predicateType='https://cyclonedx.org/vex' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName="$artifact_name" fi " env: + API_ARTIFACT_NAME: ${{ env.API_ARTIFACT_NAME }} ARTIFACT_NAME: ${{ env.ARTIFACT_NAME }} - - name: Attest SAST-Results - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + - name: Get and Attest SAST-Results + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: - args: > + args: | sh -c " + slug=$(devguard-scanner slug ${{ github.ref_name }}) && + artifact_name="$ARTIFACT_NAME" && + echo 'Fetching SAST results for artifact:' '${{ env.ARTIFACT_NAME }}' && + devguard-scanner curl '${{ inputs.api-url }}/api/v1/organizations/${{ inputs.asset-name }}/refs/'$slug'/sarif.json' --token='${{ secrets.devguard-token }}' > /tmp/sarif.json && + echo 'SAST results downloaded to /tmp/sarif.json' && if [ -f image-digest.txt ]; then - devguard-scanner attest -u ${{ github.actor }} -r ghcr.io -p ${{ secrets.GITHUB_TOKEN }} sarif.json \"$(cat image-tag.txt)@$(cat image-digest.txt)\" --predicateType='https://www.schemastore.org/schemas/json/sarif-2.1.0.json' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName=${{ env.ARTIFACT_NAME }} + echo 'Attesting SAST results with image digest present' && + devguard-scanner attest -u ${{ github.actor }} -r ghcr.io -p ${{ secrets.GITHUB_TOKEN }} /tmp/sarif.json \"$(cat image-tag.txt)@$(cat image-digest.txt)\" --predicateType='https://www.schemastore.org/schemas/json/sarif-2.1.0.json' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName="$artifact_name" else - devguard-scanner attest sarif.json --predicateType='https://www.schemastore.org/schemas/json/sarif-2.1.0.json' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName=${{ env.ARTIFACT_NAME }} + echo 'Attesting SAST results without image digest' && + devguard-scanner attest /tmp/sarif.json --predicateType='https://www.schemastore.org/schemas/json/sarif-2.1.0.json' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName="$artifact_name" fi " env: ARTIFACT_NAME: ${{ env.ARTIFACT_NAME }} - # download build-provenance.json if it exists - - name: Download build-provenance.json + - name: Download and Attest build-provenance.json uses: actions/download-artifact@v4 with: name: build${{ inputs.image-suffix }}.provenance.json - name: Attest build-provenance.json - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main continue-on-error: true with: - args: > + args: | sh -c " + artifact_name="$ARTIFACT_NAME" && + echo 'Building provenance attestation for artifact:' '${{ env.ARTIFACT_NAME }}' && if [ -f image-digest.txt ]; then - devguard-scanner attest -u ${{ github.actor }} -r ghcr.io -p ${{ secrets.GITHUB_TOKEN }} build.provenance.json \"$(cat image-tag.txt)@$(cat image-digest.txt)\" --predicateType='https://slsa.dev/provenance/v1' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName=${{ env.ARTIFACT_NAME }} + echo 'Attesting provenance with image digest present' && + devguard-scanner attest -u ${{ github.actor }} -r ghcr.io -p ${{ secrets.GITHUB_TOKEN }} build.provenance.json \"$(cat image-tag.txt)@$(cat image-digest.txt)\" --predicateType='https://slsa.dev/provenance/v1' --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName="$artifact_name" else - devguard-scanner attest build.provenance.json --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --predicateType='https://slsa.dev/provenance/v1' --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName=${{ env.ARTIFACT_NAME }} + echo 'Attesting provenance without image digest' && + devguard-scanner attest build.provenance.json --token='${{ secrets.devguard-token }}' --apiUrl=${{ inputs.api-url }} --predicateType='https://slsa.dev/provenance/v1' --assetName=${{ inputs.asset-name }} --ref=${{ github.ref_name }} --artifactName="$artifact_name" fi " env: - ARTIFACT_NAME: ${{ env.ARTIFACT_NAME }} + ARTIFACT_NAME: ${{ env.ARTIFACT_NAME }} diff --git a/.github/workflows/build-image.yml b/.github/workflows/build-image.yml index 488fe55..ce43d98 100644 --- a/.github/workflows/build-image.yml +++ b/.github/workflows/build-image.yml @@ -23,7 +23,7 @@ on: type: string required: false default: '' - description: "The name of the artifact you are building. This is useful when a single pipeline builds more than a single artifact like a container with a shell inside and one without. If you build a single artifact - leave it empty." + description: "The name of the artifact you are building. This is useful when a single pipeline builds more than a single artifact like a container with a shell inside and one without. If you build a single artifact - leave it empty." disable-artifact-registry-as-image-store: required: false default: false @@ -55,22 +55,20 @@ jobs: fi echo "BUILD_ARGS=$BUILD_ARGS --no-push --tarPath /github/workspace/tmp-image.tar" >> $GITHUB_ENV - + - name: Checkout code uses: actions/checkout@v4 with: submodules: recursive persist-credentials: false + - name: In-Toto Provenance record start id: in-toto-start - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: args: devguard-scanner intoto start --step=build --token=${{ secrets.devguard-token }} --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --supplyChainId=${{ github.sha }} continue-on-error: true - - name: Setup crane - uses: imjasonh/setup-crane@v0.1 - - name: Build Docker image with Kaniko # Building the Docker image using Kaniko id: build_image @@ -82,10 +80,14 @@ jobs: run: mv tmp-image.tar "${IMAGE_DESTINATION_PATH}" env: IMAGE_DESTINATION_PATH: ${{ inputs.image-destination-path }} - + - name: Use crane to get the digest run: | - crane digest --tarball="${IMAGE_DESTINATION_PATH}" > image-digest.txt + docker run --rm \ + -v "$GITHUB_WORKSPACE:/workspace" \ + -w /workspace \ + ghcr.io/l3montree-dev/devguard/scanner:main \ + crane digest --tarball="${IMAGE_DESTINATION_PATH}" > image-digest.txt env: IMAGE_DESTINATION_PATH: ${{ inputs.image-destination-path }} @@ -97,44 +99,55 @@ jobs: path: ${{ inputs.image-destination-path }} if: inputs.disable-artifact-registry-as-image-store == false - # Calculate a tag name - # If the image input is provided, use it as the tag - # If the workflow is triggered by a tag, use the tag as the tag - # Otherwise built GitOps compatible tags. Fallback to the branch name, commit hash, and timestamp. Those tags are sortable and unique. - - name: Set IMAGE_TAG + - name: Set image tag + id: set-image-tag + env: + IMAGE_SUFFIX: ${{ inputs.image-suffix }} + IMAGE: ${{ inputs.image }} run: | - if [ "${IMAGE}" != "" ]; then - IMAGE_TAG="${IMAGE}" - elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then - if [ "${IMAGE_SUFFIX}" != "" ]; then - IMAGE_TAG="ghcr.io/${{ github.repository }}/${IMAGE_SUFFIX}:${GITHUB_REF#refs/tags/}" + if [ -n "$IMAGE" ]; then + IMAGE_TAG="$IMAGE" + echo "$IMAGE_TAG" > image-tag.txt + echo "IMAGE_TAG=$IMAGE_TAG" >> "$GITHUB_ENV" + else + if [ -n "$IMAGE_SUFFIX" ]; then + IMAGE_PATH="ghcr.io/${GITHUB_REPOSITORY}/${IMAGE_SUFFIX}" else - IMAGE_TAG="ghcr.io/${{ github.repository }}:${GITHUB_REF#refs/tags/}" + IMAGE_PATH="ghcr.io/${GITHUB_REPOSITORY}" fi - - else - branch=${GITHUB_REF##*/} - sha=${GITHUB_SHA::8} - ts=$(date +%s) - if [ "${IMAGE_SUFFIX}" != "" ]; then - IMAGE_TAG="ghcr.io/${{ github.repository }}/${IMAGE_SUFFIX}:${branch}-${sha}-${ts}" - else - IMAGE_TAG="ghcr.io/${{ github.repository }}:${branch}-${sha}-${ts}" - fi + docker run --rm \ + -e IMAGE_PATH \ + -e GITHUB_REF_NAME \ + ghcr.io/l3montree-dev/devguard/scanner:main \ + devguard-scanner generate-tag \ + --imagePath="$IMAGE_PATH" \ + --ref="$GITHUB_REF_NAME" \ + >> image-tag-env.txt + IMAGE_TAG=$(grep '^IMAGE_TAG=' image-tag-env.txt | cut -d= -f2-) + ARTIFACT_NAME=$(grep '^ARTIFACT_NAME=' image-tag-env.txt | cut -d= -f2-) + ARTIFACT_URL_ENCODED=$(grep '^ARTIFACT_URL_ENCODED=' image-tag-env.txt | cut -d= -f2-) + echo "$IMAGE_TAG" > image-tag.txt + echo "IMAGE_TAG=$IMAGE_TAG" >> "$GITHUB_ENV" + echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> "$GITHUB_ENV" + echo "ARTIFACT_URL_ENCODED=$ARTIFACT_URL_ENCODED" >> "$GITHUB_ENV" fi - IMAGE_TAG=$(echo "$IMAGE_TAG" | tr '[:upper:]' '[:lower:]') - echo "$IMAGE_TAG" > image-tag.txt - - # necessary for the kaniko job - echo "IMAGE_TAG=$(cat image-tag.txt)" >> $GITHUB_ENV - env: - IMAGE_SUFFIX: ${{ inputs.image-suffix }} - IMAGE: ${{ inputs.image }} + - name: Log in to ghcr.io + run: | + docker run --rm \ + -v "${HOME}/.docker:/tmp/.docker" \ + ghcr.io/l3montree-dev/devguard/scanner:main \ + crane auth login ghcr.io -u ${{ github.actor }} -p ${{ github.token }} + if: inputs.disable-artifact-registry-as-image-store == true - name: Upload to container registry run: | - crane push "${IMAGE_DESTINATION_PATH}" $(cat image-tag.txt) + docker run --rm \ + -v "$GITHUB_WORKSPACE:/workspace" \ + -w /workspace \ + -v "${HOME}/.docker:/tmp/.docker:ro" \ + ghcr.io/l3montree-dev/devguard/scanner:main \ + crane push "${IMAGE_DESTINATION_PATH}" "$(cat image-tag.txt)" env: IMAGE_DESTINATION_PATH: ${{ inputs.image-destination-path }} if: inputs.disable-artifact-registry-as-image-store == true @@ -146,25 +159,21 @@ jobs: name: image-digest${{ inputs.image-suffix }} path: image-digest.txt - - name: Set Artifact purl + - name: Set artifact PURL run: | - if [ -n "$ARTIFACT_NAME" ]; then - PURL="$ARTIFACT_NAME" + if [ -n "$ARTIFACT_NAME_INPUT" ]; then + PURL="$ARTIFACT_NAME_INPUT" + SAFE_PURL=$(echo -n "$PURL" | jq -s -R -r @uri) else - IMAGE_TAG=$(cat image-tag.txt) - REGISTRY_AND_IMAGE=${IMAGE_TAG%:*} - VERSION=${IMAGE_TAG##*:} - NAMESPACE_AND_NAME=${REGISTRY_AND_IMAGE#*/} - NAME=${NAMESPACE_AND_NAME##*/} - REPOSITORY_URL="$REGISTRY_AND_IMAGE" - PURL="pkg:oci/$NAME?repository_url=$REPOSITORY_URL" + PURL="$ARTIFACT_NAME" + SAFE_PURL="$ARTIFACT_URL_ENCODED" fi - echo "$PURL" > artifact-purl.txt + echo "$SAFE_PURL" > artifact-purl-safe.txt echo "PURL=$PURL" >> $GITHUB_ENV echo "Using artifact name: $PURL" env: - ARTIFACT_NAME: ${{ inputs.artifact-name }} + ARTIFACT_NAME_INPUT: ${{ inputs.artifact-name }} - name: Upload artifact purl uses: actions/upload-artifact@v4 @@ -172,17 +181,11 @@ jobs: name: artifact-purl${{ inputs.image-suffix }} path: artifact-purl.txt - - name: create safe purl - run: | - SAFE_PURL=$(echo -n "$PURL" | jq -s -R -r @uri) - echo "$SAFE_PURL" > artifact-purl-safe.txt - echo "Safe artifact name: $SAFE_PURL" - - name: Upload safe artifact purl uses: actions/upload-artifact@v4 with: name: artifact-purl-safe${{ inputs.image-suffix }} - path: artifact-purl-safe.txt + path: artifact-purl-safe.txt # Upload the calculated image tag as an artifact - name: Upload image tag @@ -192,11 +195,11 @@ jobs: path: image-tag.txt - name: In-Toto Provenance record stop - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: args: devguard-scanner intoto stop --step=build --products=image-digest.txt --products=image-tag.txt --token=${{ secrets.devguard-token }} --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --supplyChainId=${{ github.sha }} --generateSlsaProvenance continue-on-error: true - + - name: Upload SLSA Provenance uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/build-nix-image.yml b/.github/workflows/build-nix-image.yml new file mode 100644 index 0000000..3359809 --- /dev/null +++ b/.github/workflows/build-nix-image.yml @@ -0,0 +1,217 @@ +name: Build Nix OCI Image + +on: + workflow_call: + inputs: + nix-target: + description: 'Nix flake build target (e.g. devguardOCI)' + required: true + type: string + image-name: + description: 'Full OCI image name without tag (e.g. ghcr.io/l3montree-dev/devguard/scanner)' + required: true + type: string + artifact-name-suffix: + description: 'Suffix appended to artifact names to avoid conflicts when building multiple images in the same workflow' + required: false + type: string + default: '' + asset-name: + description: 'DevGuard asset name for supply chain tracking' + required: true + type: string + api-url: + description: 'DevGuard API URL' + required: false + type: string + default: 'https://api.devguard.org' + nix-cache-substituter: + description: 'Nix binary cache substituter URL (e.g. https://nix.garage.l3montree.cloud)' + required: false + type: string + default: '' + nix-cache-public-key: + description: 'Trusted public key for the Nix binary cache' + required: false + type: string + default: '' + nix-cache-s3-endpoint: + description: 'S3 API endpoint for pushing to the cache (e.g. s3.garage.l3montree.cloud)' + required: false + type: string + default: '' + nix-cache-s3-bucket: + description: 'S3 bucket name for pushing to the cache' + required: false + type: string + default: 'nix' + nix-cache-region: + description: 'S3 region for the cache bucket' + required: false + type: string + default: 'garage' + nix-version: + description: 'Pinned Nix version used for deterministic builds (must match other CI systems)' + required: false + type: string + default: '2.34.4' + arch: + description: 'Target architecture suffix appended to the generated image tag (e.g. amd64, arm64). Leave empty for single-arch builds.' + required: false + type: string + default: '' + runner: + description: 'GitHub Actions runner label to use (e.g. ubuntu-latest, ubuntu-24.04-arm)' + required: false + type: string + default: 'ubuntu-latest' + secrets: + devguard-token: + required: false + nix-cache-secret-key: + required: false + nix-cache-aws-access-key-id: + required: false + nix-cache-aws-secret-access-key: + required: false + outputs: + image-tag: + description: 'Full image tag of the built image' + value: ${{ jobs.build.outputs.image-tag }} + image-digest: + description: 'Digest of the built image' + value: ${{ jobs.build.outputs.image-digest }} + artifact-purl: + description: 'Package URL (PURL) of the built artifact' + value: ${{ jobs.build.outputs.artifact-purl }} + +jobs: + build: + runs-on: ${{ inputs.runner }} + outputs: + image-tag: ${{ steps.set-image-tag.outputs.image_tag }} + image-digest: ${{ steps.get-digest.outputs.digest }} + artifact-purl: ${{ steps.set-purl.outputs.purl }} + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - https://github.com/actions/checkout/releases/tag/v5.0.0 + with: + fetch-depth: 0 # needed for flake.nix to resolve self.shortRev + persist-credentials: false + + - uses: cachix/install-nix-action@v31 + with: + install_url: ${{ format('https://releases.nixos.org/nix/nix-{0}/install', inputs.nix-version) }} + extra_nix_config: | + experimental-features = nix-command flakes + ${{ inputs.nix-cache-substituter != '' && format('substituters = https://cache.nixos.org {0}', inputs.nix-cache-substituter) || '' }} + ${{ inputs.nix-cache-public-key != '' && format('trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= {0}', inputs.nix-cache-public-key) || '' }} + + - name: Show Nix version + run: nix --version + + - name: Install crane and devguard-scanner + run: nix profile install nixpkgs#crane github:l3montree-dev/devguard#devguardScanner + + - name: In-Toto Provenance record start + run: devguard-scanner intoto start --step=build --token=${{ secrets.devguard-token }} --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --supplyChainId=${{ github.sha }} + continue-on-error: true + + - name: Write Nix cache secret key + if: ${{ inputs.nix-cache-s3-endpoint != '' }} + run: | + echo "${{ secrets.nix-cache-secret-key }}" > /tmp/nix-cache-priv-key.pem + + - name: Build OCI image with Nix + run: nix build .#${{ inputs.nix-target }} + + - name: Push build results to Nix cache + if: ${{ inputs.nix-cache-s3-endpoint != '' }} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.nix-cache-aws-access-key-id }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.nix-cache-aws-secret-access-key }} + run: | + mkdir -p ~/.aws + echo "[profile nix-cache]" >> ~/.aws/config + echo "s3.addressing_style = path" >> ~/.aws/config + nix copy $(nix-store -qR $(nix-store -r $(nix eval --raw .#${{ inputs.nix-target }}.drvPath 2>/dev/null || readlink result))) \ + --to 's3://${{ inputs.nix-cache-s3-bucket }}?endpoint=${{ inputs.nix-cache-s3-endpoint }}®ion=${{ inputs.nix-cache-region }}&scheme=https&profile=nix-cache&secret-key=/tmp/nix-cache-priv-key.pem' \ + || nix copy $(readlink result) \ + --to 's3://${{ inputs.nix-cache-s3-bucket }}?endpoint=${{ inputs.nix-cache-s3-endpoint }}®ion=${{ inputs.nix-cache-region }}&scheme=https&profile=nix-cache&secret-key=/tmp/nix-cache-priv-key.pem' + + - name: Prepare image.tar + run: gunzip -c "$(readlink -f result)" > image.tar + + - name: Get image digest + id: get-digest + run: | + crane digest --tarball=image.tar > image-digest.txt + echo "digest=$(cat image-digest.txt)" >> $GITHUB_OUTPUT + + - name: Upload oci-image artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2 + with: + name: oci-image${{ inputs.artifact-name-suffix }} + path: image.tar + + - name: Set image tag + id: set-image-tag + run: | + devguard-scanner generate-tag \ + --imagePath='${{ inputs.image-name }}' \ + --ref='${{ github.ref_name }}' \ + --architecture='${{ inputs.arch }}' \ + >> image-tag-env.txt + IMAGE_TAG=$(grep '^IMAGE_TAG=' image-tag-env.txt | cut -d= -f2-) + ARTIFACT_NAME=$(grep '^ARTIFACT_NAME=' image-tag-env.txt | cut -d= -f2-) + ARTIFACT_URL_ENCODED=$(grep '^ARTIFACT_URL_ENCODED=' image-tag-env.txt | cut -d= -f2-) + echo "$IMAGE_TAG" > image-tag.txt + echo "$ARTIFACT_NAME" > artifact-purl.txt + echo "$ARTIFACT_URL_ENCODED" > artifact-purl-safe.txt + echo "image_tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT" + echo "IMAGE_TAG=$IMAGE_TAG" >> "$GITHUB_ENV" + echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> "$GITHUB_ENV" + echo "ARTIFACT_URL_ENCODED=$ARTIFACT_URL_ENCODED" >> "$GITHUB_ENV" + + - name: Upload image-tag artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2 + with: + name: image-tag${{ inputs.artifact-name-suffix }} + path: image-tag.txt + + - name: Upload image-digest artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2 + with: + name: image-digest${{ inputs.artifact-name-suffix }} + path: image-digest.txt + + - name: Set artifact PURL + id: set-purl + run: | + echo "purl=$ARTIFACT_NAME" >> $GITHUB_OUTPUT + echo "$ARTIFACT_NAME" > artifact-purl.txt + echo "$ARTIFACT_URL_ENCODED" > artifact-purl-safe.txt + env: + ARTIFACT_NAME: ${{ env.ARTIFACT_NAME }} + ARTIFACT_URL_ENCODED: ${{ env.ARTIFACT_URL_ENCODED }} + + - name: Upload artifact-purl artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2 + with: + name: artifact-purl${{ inputs.artifact-name-suffix }} + path: artifact-purl.txt + + - name: Upload artifact-purl-safe artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2 + with: + name: artifact-purl-safe${{ inputs.artifact-name-suffix }} + path: artifact-purl-safe.txt + + - name: In-Toto Provenance record stop + run: devguard-scanner intoto stop --step=build --products=image-digest.txt --products=image-tag.txt --token=${{ secrets.devguard-token }} --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --supplyChainId=${{ github.sha }} --generateSlsaProvenance + continue-on-error: true + + - name: Upload SLSA Provenance + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2 + with: + name: build${{ inputs.artifact-name-suffix }}.provenance.json + path: build.provenance.json diff --git a/.github/workflows/code-risk-identification.yml b/.github/workflows/code-risk-identification.yml index 8e1f663..f8b3c73 100644 --- a/.github/workflows/code-risk-identification.yml +++ b/.github/workflows/code-risk-identification.yml @@ -46,6 +46,6 @@ jobs: if: ${{ inputs.sarif-artifact-name != '' }} - name: DevGuard Code Risk Identification - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: args: devguard-scanner sarif ${{ inputs.sarif-file }} --assetName=${{ inputs.asset-name }} --apiUrl=${{ inputs.api-url }} --token="${{ secrets.devguard-token }}" --defaultRef=${{ github.event.repository.default_branch }} --isTag=${{ github.ref_type == 'tag' }} --ref=${{ github.ref_name }} --webUI=${{ inputs.web-ui }} \ No newline at end of file diff --git a/.github/workflows/code-scanning.yml b/.github/workflows/code-scanning.yml new file mode 100644 index 0000000..4c96f9d --- /dev/null +++ b/.github/workflows/code-scanning.yml @@ -0,0 +1,93 @@ +name: Code Scanning + +on: + workflow_call: + inputs: + asset-name: + description: 'Name of the asset to be scanned' + type: string + required: true + + api-url: + type: string + required: false + default: "https://api.devguard.org" + + path: + description: 'Path to the source code to be scanned' + type: string + required: false + default: "/github/workspace" + + web-ui: + type: string + required: false + default: "https://app.devguard.org" + description: "The URL of the DevGuard Web UI. This is used to link the results in the DevGuard Web UI." + + fail-on-risk: + description: 'Fail the job if a risk is higher than the configured threshold, e.g. critical, high, medium, low' + type: string + required: false + + fail-on-cvss: + description: 'Fail the job if a CVSS score is higher than the configured threshold, e.g. critical, high, medium, low' + type: string + required: false + + continue-on-open-code-risk: + type: boolean + required: false + default: true + + secrets: + devguard-token: + description: 'DevGuard API token' + required: true + + +jobs: + call-secret-scanning: + uses: ./.github/workflows/secret-scanning.yml + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + path: ${{ inputs.path }} + web-ui: ${{ inputs.web-ui }} + continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + call-sast: + uses: ./.github/workflows/static-application-security-testing.yml + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + path: ${{ inputs.path }} + web-ui: ${{ inputs.web-ui }} + continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + call-iac: + uses: ./.github/workflows/iac.yml + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + path: ${{ inputs.path }} + web-ui: ${{ inputs.web-ui }} + continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + call-software-composition-analysis: + uses: ./.github/workflows/software-composition-analysis.yml + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + path: ${{ inputs.path }} + fail-on-risk: ${{ inputs.fail-on-risk }} + fail-on-cvss: ${{ inputs.fail-on-cvss }} + web-ui: ${{ inputs.web-ui }} + secrets: + devguard-token: ${{ secrets.devguard-token }} diff --git a/.github/workflows/container-lifecycle-nix.yml b/.github/workflows/container-lifecycle-nix.yml new file mode 100644 index 0000000..0b1e96e --- /dev/null +++ b/.github/workflows/container-lifecycle-nix.yml @@ -0,0 +1,183 @@ +name: Container Lifecycle (Nix) + +on: + workflow_call: + inputs: + nix-target: + description: 'Nix flake build target (e.g. devguardOCI)' + required: true + type: string + + image-name: + description: 'Full OCI image name without tag (e.g. ghcr.io/l3montree-dev/devguard/scanner)' + required: true + type: string + + asset-name: + description: 'DevGuard asset name for supply chain tracking' + required: true + type: string + + api-url: + description: 'DevGuard API URL' + required: false + type: string + default: 'https://api.devguard.org' + + web-ui: + description: 'DevGuard web UI URL' + required: false + type: string + default: 'https://app.devguard.org' + + artifact-name-suffix: + description: 'Suffix appended to artifact names to avoid conflicts when building multiple images in the same workflow' + required: false + type: string + default: '' + + should-deploy: + description: 'Whether to push the image to the container registry' + required: false + type: boolean + default: true + + fail-on-risk: + description: 'Fail if risk level reaches this threshold (none|low|medium|high|critical)' + required: false + type: string + + fail-on-cvss: + description: 'Fail if CVSS score reaches this threshold (none|low|medium|high|critical)' + required: false + type: string + + nix-cache-substituter: + description: 'Nix binary cache substituter URL (e.g. https://nix.garage.l3montree.cloud)' + required: false + type: string + nix-cache-public-key: + description: 'Trusted public key for the Nix binary cache' + required: false + type: string + nix-cache-s3-endpoint: + description: 'S3 API endpoint for pushing to the cache. Leave empty to skip pushing.' + required: false + type: string + nix-cache-s3-bucket: + description: 'S3 bucket name for the cache' + required: false + type: string + nix-cache-region: + description: 'S3 region for the cache bucket' + required: false + type: string + + secrets: + devguard-token: + required: true + nix-cache-secret-key: + required: false + nix-cache-aws-access-key-id: + required: false + nix-cache-aws-secret-access-key: + required: false + + outputs: + image-tag: + description: 'Full image tag of the built image' + value: ${{ jobs.build-image.outputs.image-tag }} + image-digest: + description: 'Digest of the built image' + value: ${{ jobs.build-image.outputs.image-digest }} + artifact-purl: + description: 'Package URL (PURL) of the built artifact' + value: ${{ jobs.build-image.outputs.artifact-purl }} + + +permissions: + contents: read + packages: write + +jobs: + build-image: + uses: ./.github/workflows/build-nix-image.yml + permissions: + contents: read + with: + nix-target: ${{ inputs.nix-target }} + image-name: ${{ inputs.image-name }} + artifact-name-suffix: ${{ inputs.artifact-name-suffix }} + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + nix-cache-substituter: ${{ inputs.nix-cache-substituter }} + nix-cache-public-key: ${{ inputs.nix-cache-public-key }} + nix-cache-s3-endpoint: ${{ inputs.nix-cache-s3-endpoint }} + nix-cache-s3-bucket: ${{ inputs.nix-cache-s3-bucket }} + nix-cache-region: ${{ inputs.nix-cache-region }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + nix-cache-secret-key: ${{ secrets.nix-cache-secret-key }} + nix-cache-aws-access-key-id: ${{ secrets.nix-cache-aws-access-key-id }} + nix-cache-aws-secret-access-key: ${{ secrets.nix-cache-aws-secret-access-key }} + + container-scanning: + needs: build-image + uses: ./.github/workflows/container-scanning.yml + permissions: + contents: read + security-events: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-image.outputs.artifact-purl }} + web-ui: ${{ inputs.web-ui }} + fail-on-cvss: ${{ inputs.fail-on-cvss }} + fail-on-risk: ${{ inputs.fail-on-risk }} + image-suffix: ${{ inputs.artifact-name-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + deploy: + if: ${{ inputs.should-deploy }} + needs: [build-image, container-scanning] + uses: ./.github/workflows/deploy.yml + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + image-suffix: ${{ inputs.artifact-name-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + sign: + if: ${{ inputs.should-deploy }} + needs: [build-image, container-scanning, deploy] + uses: ./.github/workflows/sign.yml + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-image.outputs.artifact-purl }} + image-suffix: ${{ inputs.artifact-name-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + attest: + if: ${{ inputs.should-deploy }} + needs: [build-image, container-scanning, deploy] + uses: ./.github/workflows/attest.yml + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-image.outputs.artifact-purl }} + image-suffix: ${{ inputs.artifact-name-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} diff --git a/.github/workflows/container-lifecycle.yml b/.github/workflows/container-lifecycle.yml new file mode 100644 index 0000000..d1d8936 --- /dev/null +++ b/.github/workflows/container-lifecycle.yml @@ -0,0 +1,145 @@ +name: Container Lifecycle + +on: + workflow_call: + inputs: + asset-name: + description: 'Name of the asset' + type: string + required: true + + api-url: + type: string + required: false + default: "https://api.devguard.org" + + web-ui: + type: string + required: false + default: "https://app.devguard.org" + description: "The URL of the DevGuard Web UI. This is used to link the results in the DevGuard Web UI." + + image-destination-path: + description: 'Path to the oci image to be scanned.' + type: string + required: false + default: "image.tar" + + image: + description: 'OCI image tag' + type: string + required: false + + disable-artifact-registry-as-image-store: + required: false + default: false + type: boolean + description: "If the artifact size is too big for your github usage quota, set this to true. This will push the image directly to the registry instead of uploading it as artifact." + + image-suffix: + description: 'Suffix for the image name. You probably need this if you are building multiple images.' + type: string + required: false + default: '' + + artifact-name: + type: string + required: false + default: '' + description: "The name of the artifact you are building. Leave empty if building a single artifact." + + should-deploy: + description: 'Should the deploy job run' + type: boolean + required: false + default: true + + fail-on-risk: + description: 'Fail the job if a risk is higher than the configured threshold, e.g. critical, high, medium, low' + type: string + required: false + + fail-on-cvss: + description: 'Fail the job if a CVSS score is higher than the configured threshold, e.g. critical, high, medium, low' + type: string + required: false + + secrets: + devguard-token: + description: 'DevGuard API token' + required: true + build-args: + description: 'Build arguments. Useful to overwrite context and dockerfile.' + required: false + + +permissions: + contents: read + packages: write + +jobs: + call-build-image: + uses: ./.github/workflows/build-image.yml + with: + image-destination-path: ${{ inputs.image-destination-path }} + image: ${{ inputs.image }} + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + disable-artifact-registry-as-image-store: ${{ inputs.disable-artifact-registry-as-image-store }} + artifact-name: ${{ inputs.artifact-name }} + image-suffix: ${{ inputs.image-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + build-args: ${{ secrets.build-args }} + + call-container-scanning: + needs: call-build-image + uses: ./.github/workflows/container-scanning.yml + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + image-path: ${{ inputs.image-destination-path }} + fetch-image-from-registry: ${{ inputs.disable-artifact-registry-as-image-store }} + fail-on-risk: ${{ inputs.fail-on-risk }} + fail-on-cvss: ${{ inputs.fail-on-cvss }} + web-ui: ${{ inputs.web-ui }} + artifact-name: ${{ inputs.artifact-name }} + image-suffix: ${{ inputs.image-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + call-deploy: + needs: [call-build-image, call-container-scanning] + uses: ./.github/workflows/deploy.yml + with: + should-deploy: ${{ inputs.should-deploy }} + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + image-already-in-registry: ${{ inputs.disable-artifact-registry-as-image-store }} + image-suffix: ${{ inputs.image-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + call-sign: + needs: [call-build-image, call-container-scanning, call-deploy] + uses: ./.github/workflows/sign.yml + with: + should-deploy: ${{ inputs.should-deploy }} + api-url: ${{ inputs.api-url }} + asset-name: ${{ inputs.asset-name }} + artifact-name: ${{ inputs.artifact-name }} + image-suffix: ${{ inputs.image-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + call-attest: + needs: [call-build-image, call-container-scanning, call-deploy] + uses: ./.github/workflows/attest.yml + with: + should-deploy: ${{ inputs.should-deploy }} + api-url: ${{ inputs.api-url }} + asset-name: ${{ inputs.asset-name }} + artifact-name: ${{ inputs.artifact-name }} + image-suffix: ${{ inputs.image-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} diff --git a/.github/workflows/container-scanning.yml b/.github/workflows/container-scanning.yml index e74352f..edd5f55 100644 --- a/.github/workflows/container-scanning.yml +++ b/.github/workflows/container-scanning.yml @@ -97,7 +97,7 @@ jobs: if: inputs.fetch-image-from-registry == true - name: DevGuard Container-Scanning - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: args: devguard-scanner container-scanning --assetName=${{ inputs.asset-name }} --apiUrl=${{ inputs.api-url }} --token="${{ secrets.devguard-token }}" --path=${{ inputs.image-path }} --defaultRef=${{ github.event.repository.default_branch }} --isTag=${{ github.ref_type == 'tag' }} --ref=${{ github.ref_name }} --failOnRisk=${{ inputs.fail-on-risk }} --failOnCVSS=${{ inputs.fail-on-cvss }} --artifactName=${{ env.ARTIFACT_NAME }} --webUI=${{ inputs.web-ui }} env: diff --git a/.github/workflows/dependency-risk-identification.yml b/.github/workflows/dependency-risk-identification.yml index 3ede3e2..19d4b8d 100644 --- a/.github/workflows/dependency-risk-identification.yml +++ b/.github/workflows/dependency-risk-identification.yml @@ -63,6 +63,6 @@ jobs: if: ${{ inputs.sbom-artifact-name != '' }} - name: DevGuard Dependency Risk Identification - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: args: devguard-scanner sbom ${{ inputs.sbom-file }} --assetName=${{ inputs.asset-name }} --apiUrl=${{ inputs.api-url }} --token="${{ secrets.devguard-token }}" --defaultRef=${{ github.event.repository.default_branch }} --isTag=${{ github.ref_type == 'tag' }} --ref=${{ github.ref_name }} --artifactName=${{ inputs.artifact-name }} --webUI=${{ inputs.web-ui }} --failOnRisk=${{ inputs.fail-on-risk }} --failOnCVSS=${{ inputs.fail-on-cvss }} \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 80554d4..5fa8c2d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,6 +27,11 @@ on: required: false default: 'container' description: "The name of the artifact you are building. This is useful when a single pipeline builds more than a single artifact like a container with a shell inside and one without. If you build a single artifact - leave it empty." + artifact-suffix: + type: string + required: false + default: '' + description: "Suffix used to look up build artifacts by name. Defaults to image-suffix when not set." secrets: devguard-token: @@ -68,7 +73,7 @@ jobs: run: echo "DIGEST=$(cat image-digest.txt)" >> $GITHUB_ENV - name: In-Toto Provenance run - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: args: devguard-scanner intoto run --step=deploy --materials=image-tag.txt --products=image-tag.txt --products=image-digest.txt --token=${{ secrets.devguard-token }} --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --supplyChainId=${{ github.sha }} --supplyChainOutputDigest="${{ env.DIGEST }}" continue-on-error: true @@ -80,17 +85,3 @@ jobs: run: crane push image.tar $(cat image-tag.txt) if: inputs.image-already-in-registry == false - - name: Push oci image to GitHub image Registry with latest - run: | - branch=${GITHUB_REF##*/} - - if [ "${IMAGE_SUFFIX}" != "" ]; then - name="ghcr.io/${{ github.repository }}/${IMAGE_SUFFIX}:$branch-latest" - else - name="ghcr.io/${{ github.repository }}:$branch-latest" - fi - - name=$(echo "$name" | tr '[:upper:]' '[:lower:]') - crane copy $(cat image-tag.txt) $name - env: - IMAGE_SUFFIX: ${{ inputs.image-suffix }} \ No newline at end of file diff --git a/.github/workflows/full-nix.yml b/.github/workflows/full-nix.yml new file mode 100644 index 0000000..ca32a86 --- /dev/null +++ b/.github/workflows/full-nix.yml @@ -0,0 +1,365 @@ +name: Full Nix Multi-Arch Pipeline + +on: + workflow_call: + inputs: + nix-target-amd64: + description: 'Nix flake build target for amd64 (e.g. devguard-0-amd64)' + required: true + type: string + nix-target-arm64: + description: 'Nix flake build target for arm64 (e.g. devguard-0-arm64)' + required: true + type: string + image-name: + description: 'Full OCI image name without tag (e.g. ghcr.io/l3montree-dev/devguard)' + required: true + type: string + artifact-name-suffix: + description: 'Suffix appended to artifact names to avoid conflicts when building multiple images in the same workflow' + required: false + type: string + default: '' + asset-name: + description: 'DevGuard asset name for supply chain tracking' + required: true + type: string + api-url: + description: 'DevGuard API URL' + required: false + type: string + default: 'https://api.devguard.org' + web-ui: + description: 'DevGuard web UI URL' + required: false + type: string + default: 'https://app.devguard.org' + fail-on-risk: + description: 'Fail if risk level reaches this threshold (none|low|medium|high|critical)' + required: false + type: string + default: 'critical' + fail-on-cvss: + description: 'Fail if CVSS score reaches this threshold (none|low|medium|high|critical)' + required: false + type: string + default: 'critical' + should-deploy: + description: 'Whether to push the images and manifest to the container registry' + required: false + type: boolean + default: true + runner-amd64: + description: 'GitHub Actions runner label for the amd64 build' + required: false + type: string + default: 'ubuntu-latest' + runner-arm64: + description: 'GitHub Actions runner label for the arm64 build' + required: false + type: string + default: 'ubuntu-24.04-arm' + nix-cache-substituter: + description: 'Nix binary cache substituter URL' + required: false + type: string + default: '' + nix-cache-public-key: + description: 'Trusted public key for the Nix binary cache' + required: false + type: string + default: '' + nix-cache-s3-endpoint: + description: 'S3 API endpoint for pushing to the Nix cache' + required: false + type: string + default: '' + nix-cache-s3-bucket: + description: 'S3 bucket name for the Nix cache' + required: false + type: string + default: 'nix' + nix-cache-region: + description: 'S3 region for the Nix cache bucket' + required: false + type: string + default: 'garage' + nix-version: + description: 'Pinned Nix version used by reusable build jobs' + required: false + type: string + default: '2.34.4' + secrets: + devguard-token: + required: true + nix-cache-secret-key: + required: false + nix-cache-aws-access-key-id: + required: false + nix-cache-aws-secret-access-key: + required: false + outputs: + manifest-tag: + description: 'Full image tag of the multi-arch manifest' + value: ${{ jobs.create-manifest.outputs.manifest-tag }} + +permissions: + contents: read + packages: write + +jobs: + build-amd64: + uses: l3montree-dev/devguard-action/.github/workflows/build-nix-image.yml@nix + permissions: + contents: read + with: + nix-target: ${{ inputs.nix-target-amd64 }} + image-name: ${{ inputs.image-name }} + artifact-name-suffix: ${{ inputs.artifact-name-suffix }}-amd64 + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + arch: amd64 + runner: ${{ inputs.runner-amd64 }} + nix-cache-substituter: ${{ inputs.nix-cache-substituter }} + nix-cache-public-key: ${{ inputs.nix-cache-public-key }} + nix-cache-s3-endpoint: ${{ inputs.nix-cache-s3-endpoint }} + nix-cache-s3-bucket: ${{ inputs.nix-cache-s3-bucket }} + nix-cache-region: ${{ inputs.nix-cache-region }} + nix-version: ${{ inputs.nix-version }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + nix-cache-secret-key: ${{ secrets.nix-cache-secret-key }} + nix-cache-aws-access-key-id: ${{ secrets.nix-cache-aws-access-key-id }} + nix-cache-aws-secret-access-key: ${{ secrets.nix-cache-aws-secret-access-key }} + + build-arm64: + uses: l3montree-dev/devguard-action/.github/workflows/build-nix-image.yml@nix + permissions: + contents: read + with: + nix-target: ${{ inputs.nix-target-arm64 }} + image-name: ${{ inputs.image-name }} + artifact-name-suffix: ${{ inputs.artifact-name-suffix }}-arm64 + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + arch: arm64 + runner: ${{ inputs.runner-arm64 }} + nix-cache-substituter: ${{ inputs.nix-cache-substituter }} + nix-cache-public-key: ${{ inputs.nix-cache-public-key }} + nix-cache-s3-endpoint: ${{ inputs.nix-cache-s3-endpoint }} + nix-cache-s3-bucket: ${{ inputs.nix-cache-s3-bucket }} + nix-cache-region: ${{ inputs.nix-cache-region }} + nix-version: ${{ inputs.nix-version }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + nix-cache-secret-key: ${{ secrets.nix-cache-secret-key }} + nix-cache-aws-access-key-id: ${{ secrets.nix-cache-aws-access-key-id }} + nix-cache-aws-secret-access-key: ${{ secrets.nix-cache-aws-secret-access-key }} + + container-scanning-amd64: + needs: build-amd64 + uses: l3montree-dev/devguard-action/.github/workflows/container-scanning.yml@nix + permissions: + contents: read + security-events: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-amd64.outputs.artifact-purl }} + web-ui: ${{ inputs.web-ui }} + fail-on-cvss: ${{ inputs.fail-on-cvss }} + fail-on-risk: ${{ inputs.fail-on-risk }} + image-suffix: ${{ inputs.artifact-name-suffix }}-amd64 + secrets: + devguard-token: ${{ secrets.devguard-token }} + + container-scanning-arm64: + needs: build-arm64 + uses: l3montree-dev/devguard-action/.github/workflows/container-scanning.yml@nix + permissions: + contents: read + security-events: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-arm64.outputs.artifact-purl }} + web-ui: ${{ inputs.web-ui }} + fail-on-cvss: ${{ inputs.fail-on-cvss }} + fail-on-risk: ${{ inputs.fail-on-risk }} + image-suffix: ${{ inputs.artifact-name-suffix }}-arm64 + secrets: + devguard-token: ${{ secrets.devguard-token }} + + deploy-amd64: + if: ${{ inputs.should-deploy }} + needs: [build-amd64, container-scanning-amd64] + uses: l3montree-dev/devguard-action/.github/workflows/deploy.yml@nix + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + image-suffix: ${{ inputs.artifact-name-suffix }}-amd64 + secrets: + devguard-token: ${{ secrets.devguard-token }} + + deploy-arm64: + if: ${{ inputs.should-deploy }} + needs: [build-arm64, container-scanning-arm64] + uses: l3montree-dev/devguard-action/.github/workflows/deploy.yml@nix + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + image-suffix: ${{ inputs.artifact-name-suffix }}-arm64 + secrets: + devguard-token: ${{ secrets.devguard-token }} + + create-manifest: + if: ${{ inputs.should-deploy }} + needs: [build-amd64, build-arm64, deploy-amd64, deploy-arm64] + runs-on: ubuntu-latest + outputs: + manifest-tag: ${{ steps.manifest.outputs.manifest-tag }} + steps: + - uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + experimental-features = nix-command flakes + + - name: Install crane + run: nix profile install nixpkgs#crane + + - name: Log in to ghcr.io + run: crane auth login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} + + - name: Download amd64 image-tag artifact + uses: actions/download-artifact@v4 + with: + name: image-tag${{ inputs.artifact-name-suffix }}-amd64 + path: amd64 + + - name: Download arm64 image-tag artifact + uses: actions/download-artifact@v4 + with: + name: image-tag${{ inputs.artifact-name-suffix }}-arm64 + path: arm64 + + - name: Create and push multi-arch manifest + id: manifest + run: | + AMD64_TAG=$(cat amd64/image-tag.txt) + ARM64_TAG=$(cat arm64/image-tag.txt) + + # Derive manifest tag by stripping the -amd64 arch suffix + MANIFEST_TAG="${AMD64_TAG%-amd64}" + + crane index append \ + --tag "$MANIFEST_TAG" \ + -m "$AMD64_TAG" \ + -m "$ARM64_TAG" + + echo "manifest-tag=$MANIFEST_TAG" >> $GITHUB_OUTPUT + echo "MANIFEST_TAG=$MANIFEST_TAG" >> $GITHUB_ENV + echo "$MANIFEST_TAG" > manifest-tag.txt + + - name: Upload manifest-tag artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: manifest-tag${{ inputs.artifact-name-suffix }} + path: manifest-tag.txt + + - name: Get manifest digest and prepare signing artifacts + run: | + MANIFEST_DIGEST=$(crane digest "$MANIFEST_TAG") + echo "$MANIFEST_TAG" > image-tag.txt + echo "$MANIFEST_DIGEST" > image-digest.txt + + - name: Upload manifest image-tag artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: image-tag${{ inputs.artifact-name-suffix }}-manifest + path: image-tag.txt + + - name: Upload manifest image-digest artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: image-digest${{ inputs.artifact-name-suffix }}-manifest + path: image-digest.txt + + sign-manifest: + if: ${{ inputs.should-deploy }} + needs: [create-manifest] + uses: l3montree-dev/devguard-action/.github/workflows/sign.yml@nix + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + image-suffix: ${{ inputs.artifact-name-suffix }}-manifest + secrets: + devguard-token: ${{ secrets.devguard-token }} + + sign-amd64: + if: ${{ inputs.should-deploy }} + needs: [build-amd64, create-manifest] + uses: l3montree-dev/devguard-action/.github/workflows/sign.yml@nix + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-amd64.outputs.artifact-purl }} + image-suffix: ${{ inputs.artifact-name-suffix }}-amd64 + secrets: + devguard-token: ${{ secrets.devguard-token }} + + sign-arm64: + if: ${{ inputs.should-deploy }} + needs: [build-arm64, create-manifest] + uses: l3montree-dev/devguard-action/.github/workflows/sign.yml@nix + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-arm64.outputs.artifact-purl }} + image-suffix: ${{ inputs.artifact-name-suffix }}-arm64 + secrets: + devguard-token: ${{ secrets.devguard-token }} + + attest-amd64: + if: ${{ inputs.should-deploy }} + needs: [build-amd64, create-manifest] + uses: l3montree-dev/devguard-action/.github/workflows/attest.yml@nix + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-amd64.outputs.artifact-purl }} + image-suffix: ${{ inputs.artifact-name-suffix }}-amd64 + secrets: + devguard-token: ${{ secrets.devguard-token }} + + attest-arm64: + if: ${{ inputs.should-deploy }} + needs: [build-arm64, create-manifest] + uses: l3montree-dev/devguard-action/.github/workflows/attest.yml@nix + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-arm64.outputs.artifact-purl }} + image-suffix: ${{ inputs.artifact-name-suffix }}-arm64 + secrets: + devguard-token: ${{ secrets.devguard-token }} diff --git a/.github/workflows/full-with-nix.yml b/.github/workflows/full-with-nix.yml new file mode 100644 index 0000000..6ebcc62 --- /dev/null +++ b/.github/workflows/full-with-nix.yml @@ -0,0 +1,158 @@ +name: Full Nix Image Pipeline + + +on: + workflow_call: + inputs: + nix-target: + description: 'Nix flake build target (e.g. devguardOCI)' + required: true + type: string + image-name: + description: 'Full OCI image name without tag (e.g. ghcr.io/l3montree-dev/devguard/scanner)' + required: true + type: string + artifact-name-suffix: + description: 'Suffix appended to artifact names to avoid conflicts when building multiple images in the same workflow' + required: false + type: string + default: '' + asset-name: + description: 'DevGuard asset name for supply chain tracking' + required: true + type: string + api-url: + description: 'DevGuard API URL' + required: false + type: string + default: 'https://api.devguard.org' + web-ui: + description: 'DevGuard web UI URL' + required: false + type: string + default: 'https://app.devguard.org' + fail-on-risk: + description: 'Fail if risk level reaches this threshold (none|low|medium|high|critical)' + required: false + type: string + default: 'critical' + fail-on-cvss: + description: 'Fail if CVSS score reaches this threshold (none|low|medium|high|critical)' + required: false + type: string + default: 'critical' + should-deploy: + description: 'Whether to push the image to the container registry' + required: false + type: boolean + default: true + path: + description: 'Path to the source code to be scanned' + type: string + required: false + default: "/github/workspace" + continue-on-open-code-risk: + type: boolean + required: false + default: true + secrets: + devguard-token: + required: true + outputs: + image-tag: + description: 'Full image tag of the built image' + value: ${{ jobs.build-image.outputs.image-tag }} + image-digest: + description: 'Digest of the built image' + value: ${{ jobs.build-image.outputs.image-digest }} + artifact-purl: + description: 'Package URL (PURL) of the built artifact' + value: ${{ jobs.build-image.outputs.artifact-purl }} + +jobs: + call-code-scanning: + uses: l3montree-dev/devguard-action/.github/workflows/code-scanning.yml@main + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + path: ${{ inputs.path }} + web-ui: ${{ inputs.web-ui }} + fail-on-risk: ${{ inputs.fail-on-risk }} + fail-on-cvss: ${{ inputs.fail-on-cvss }} + continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + build-image: + uses: l3montree-dev/devguard-action/.github/workflows/build-nix-image.yml@main + permissions: + contents: read + with: + nix-target: ${{ inputs.nix-target }} + image-name: ${{ inputs.image-name }} + artifact-name-suffix: ${{ inputs.artifact-name-suffix }} + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + container-scanning: + uses: l3montree-dev/devguard-action/.github/workflows/container-scanning.yml@main + permissions: + contents: read + security-events: write + needs: [build-image] + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-image.outputs.artifact-purl }} + web-ui: ${{ inputs.web-ui }} + fail-on-cvss: ${{ inputs.fail-on-cvss }} + fail-on-risk: ${{ inputs.fail-on-risk }} + image-suffix: ${{ inputs.artifact-name-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + deploy: + if: ${{ inputs.should-deploy }} + needs: [build-image, container-scanning, call-code-scanning] + uses: l3montree-dev/devguard-action/.github/workflows/deploy.yml@main + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + image-suffix: ${{ inputs.artifact-name-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + sign: + if: ${{ inputs.should-deploy }} + needs: [build-image, container-scanning, call-code-scanning] + uses: l3montree-dev/devguard-action/.github/workflows/sign.yml@main + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-image.outputs.artifact-purl }} + image-suffix: ${{ inputs.artifact-name-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} + + attest: + if: ${{ inputs.should-deploy }} + needs: [build-image, container-scanning, call-code-scanning] + uses: l3montree-dev/devguard-action/.github/workflows/attest.yml@main + permissions: + contents: read + packages: write + with: + asset-name: ${{ inputs.asset-name }} + api-url: ${{ inputs.api-url }} + artifact-name: ${{ needs.build-image.outputs.artifact-purl }} + image-suffix: ${{ inputs.artifact-name-suffix }} + secrets: + devguard-token: ${{ secrets.devguard-token }} diff --git a/.github/workflows/full.yml b/.github/workflows/full.yml index 6404438..3434f0f 100644 --- a/.github/workflows/full.yml +++ b/.github/workflows/full.yml @@ -89,49 +89,16 @@ permissions: packages: write jobs: - call-secret-scanning: - uses: ./.github/workflows/secret-scanning.yml + call-code-scanning: + uses: ./.github/workflows/code-scanning.yml with: asset-name: ${{ inputs.asset-name }} api-url: ${{ inputs.api-url }} path: ${{ inputs.path }} web-ui: ${{ inputs.web-ui }} - continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }} - secrets: - devguard-token: ${{ secrets.devguard-token }} - - call-sast: - uses: ./.github/workflows/static-application-security-testing.yml - with: - asset-name: ${{ inputs.asset-name }} - api-url: ${{ inputs.api-url }} - path: ${{ inputs.path }} - web-ui: ${{ inputs.web-ui }} - continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }} - secrets: - devguard-token: ${{ secrets.devguard-token }} - - call-iac: - uses: ./.github/workflows/iac.yml - with: - asset-name: ${{ inputs.asset-name }} - api-url: ${{ inputs.api-url }} - path: ${{ inputs.path }} - web-ui: ${{ inputs.web-ui }} - continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }} - secrets: - devguard-token: ${{ secrets.devguard-token }} - - call-software-compsition-analysis: - uses: ./.github/workflows/software-composition-analysis.yml - with: - asset-name: ${{ inputs.asset-name }} - api-url: ${{ inputs.api-url }} - path: ${{ inputs.path }} fail-on-risk: ${{ inputs.fail-on-risk }} fail-on-cvss: ${{ inputs.fail-on-cvss }} - web-ui: ${{ inputs.web-ui }} -# artifact-name: // WE ARE NOT!! PROVIDING THE ARTIFACT NAME RIGHT HERE - OTHERWISE CONTAINER AND SCA WOULD DO THE SAME ARTIFACT + continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }} secrets: devguard-token: ${{ secrets.devguard-token }} @@ -169,10 +136,7 @@ jobs: needs: - call-build-image - call-container-scanning - - call-software-compsition-analysis - - call-sast - - call-secret-scanning - - call-iac + - call-code-scanning uses: ./.github/workflows/deploy.yml with: should-deploy: ${{ inputs.should-deploy }} @@ -187,10 +151,7 @@ jobs: needs: - call-build-image - call-container-scanning - - call-software-compsition-analysis - - call-sast - - call-secret-scanning - - call-iac + - call-code-scanning - call-deploy uses: ./.github/workflows/sign.yml with: @@ -206,10 +167,7 @@ jobs: needs: - call-build-image - call-container-scanning - - call-software-compsition-analysis - - call-sast - - call-secret-scanning - - call-iac + - call-code-scanning - call-deploy uses: ./.github/workflows/attest.yml with: diff --git a/.github/workflows/iac.yml b/.github/workflows/iac.yml index 230e1c8..50fed3b 100644 --- a/.github/workflows/iac.yml +++ b/.github/workflows/iac.yml @@ -39,7 +39,7 @@ jobs: persist-credentials: false fetch-depth: 0 - name: DevGuard Infrastructure as Code - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main continue-on-error: ${{ inputs.continue-on-open-code-risk }} with: args: devguard-scanner iac --assetName=${{ inputs.asset-name }} --apiUrl=${{ inputs.api-url }} --token="${{ secrets.devguard-token }}" --path=${{ inputs.path }} --defaultRef=${{ github.event.repository.default_branch }} --isTag=${{ github.ref_type == 'tag' }} --ref=${{ github.ref_name }} --webUI=${{ inputs.web-ui }} diff --git a/.github/workflows/secret-scanning.yml b/.github/workflows/secret-scanning.yml index 2951ba2..54fd76f 100644 --- a/.github/workflows/secret-scanning.yml +++ b/.github/workflows/secret-scanning.yml @@ -40,7 +40,7 @@ jobs: persist-credentials: false uses: actions/checkout@v4 - name: DevGuard Secret-Scanning - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main continue-on-error: ${{ inputs.continue-on-open-code-risk }} with: args: devguard-scanner secret-scanning --assetName=${{ inputs.asset-name }} --apiUrl=${{ inputs.api-url }} --token="${{ secrets.devguard-token }}" --path=${{ inputs.path }} --defaultRef=${{ github.event.repository.default_branch }} --isTag=${{ github.ref_type == 'tag' }} --ref=${{ github.ref_name }} --webUI=${{ inputs.web-ui }} \ No newline at end of file diff --git a/.github/workflows/sign.yml b/.github/workflows/sign.yml index eeea5b7..499bd72 100644 --- a/.github/workflows/sign.yml +++ b/.github/workflows/sign.yml @@ -58,6 +58,6 @@ jobs: run: echo "IMAGE_TAG_AND_DIGEST=$(cat image-tag.txt)@$(cat image-digest.txt)" >> $GITHUB_ENV - name: DevGuard Image-Signing - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: args: devguard-scanner sign -u ${{ github.actor }} -r ghcr.io -p ${{ secrets.GITHUB_TOKEN }} --token="${{ secrets.devguard-token }}" ${{ env.IMAGE_TAG_AND_DIGEST }} --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} \ No newline at end of file diff --git a/.github/workflows/software-composition-analysis.yml b/.github/workflows/software-composition-analysis.yml index bce5448..97f27d4 100644 --- a/.github/workflows/software-composition-analysis.yml +++ b/.github/workflows/software-composition-analysis.yml @@ -47,7 +47,7 @@ jobs: persist-credentials: true # we need the credentials to be able to use the devguard-scanner in private repositories uses: actions/checkout@v4 # Check out the repository content to the runner - name: DevGuard SCA - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main with: args: devguard-scanner sca --assetName=${{ inputs.asset-name }} --apiUrl=${{ inputs.api-url }} --token="${{ secrets.devguard-token }}" --path=${{ diff --git a/.github/workflows/static-application-security-testing.yml b/.github/workflows/static-application-security-testing.yml index c5f701c..c0a6987 100644 --- a/.github/workflows/static-application-security-testing.yml +++ b/.github/workflows/static-application-security-testing.yml @@ -39,7 +39,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: DevGuard Static application security testing - uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main-latest + uses: docker://ghcr.io/l3montree-dev/devguard/scanner:main continue-on-error: ${{ inputs.continue-on-open-code-risk }} with: args: devguard-scanner sast --assetName=${{ inputs.asset-name }} --apiUrl=${{ inputs.api-url }} --token="${{ secrets.devguard-token }}" --path=${{ inputs.path }} --defaultRef=${{ github.event.repository.default_branch }} --isTag=${{ github.ref_type == 'tag' }} --ref=${{ github.ref_name }} --webUI=${{ inputs.web-ui }}