This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Pull Request scans

Scan pull requests created in your repository.

This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Scan pull requests created in your repository.

Scan pull requests as soon as they are raised in your repository. PR scans detect vulnerabilities in your branch when they are introduced, making it easier to identify and fix them early.

You can perform the following types of PR scans.

You can perform PR scans during the following deployments:

Scan PRs using the CLI

Run the following command to scan PRs after you commit to a pull request.

endorctl scan --pr

After you raise a pull request, the --pr flag enables scanning of the latest version of the pull request and stores the results separately from the main branches. The PR scan and its findings do not affect the main branch’s reporting.

Endor Labs stores the PR scan findings in PR Runs for 30 days, after which they are erased to accommodate new PR scans.

Set a baseline branch for PR scans

Setting up a baseline branch is recommended to establish a Git reference against which you can compare the changes introduced in pull requests. You must regularly scan the baseline branch for vulnerabilities by either scheduling it (using the GitHub app) or triggering it using the --pr-baseline flag.

Usually, the first scanned branch becomes the baseline and is continuously monitored. A successful complete scan will resolve dependencies, run analytics, and generate call graphs for supported languages. See set a default branch.

By scanning a baseline branch, you establish a qualified reference with known vulnerabilities, and understand the current state of security. This reduces the risk of introducing vulnerabilities or breaking changes to your project.

Run the following command to set a baseline branch for PR scans.

endorctl scan --pr --pr-baseline=main

In the above example, the main branch is the baseline, and all PR scans will only display findings that were not already reported when the main branch was scanned.

Perform incremental PR scan

The --pr-incremental flag scans only the parts of the codebase and dependencies that have changed since the last complete baseline scan, rather than scanning the entire codebase every time. It focuses on new or modified code that may introduce vulnerabilities or issues. The scan reports only findings that don’t exist in the baseline and are associated with changed dependencies in the pull request.

The baseline is detected automatically for GitHub App scans or when PR comments are enabled. Otherwise, you must provide it using the --pr-baseline option. You can only perform an incremental scan after scanning a baseline or the default branch.

If a finding has been fixed in the baseline by upgrading or downgrading a dependency, and a PR modifies the same package, the finding will be flagged as new since there is no matching finding in the baseline and the dependency versions don’t match. To mitigate this, you need to rebase the PR with the latest baseline content and re-run the PR check.

To initiate an incremental PR scan:

  1. Run a complete scan successfully.

  2. Run the following command to perform an incremental scan. Replace main with your baseline branch.

    endorctl scan --pr --pr-baseline=main --pr-incremental
    

During an incremental PR scan, Endor Labs first identifies packages and their dependencies. If changes are detected, only the modified packages are scanned. If the packages remain unchanged, the scan is skipped, and the No changes found message is displayed. The results of the PR incremental scan are available in Projects > PR Runs. Call graphs are generated only for the modified packages.

Incremental scans fail in the following cases.

  • There are errors when resolving dependencies.
  • The project’s path has changes.
  • The project’s packages have failures.

In these cases, Endor Labs automatically performs a complete scan.

Scan PRs during CI workflows

Configure your CI/CD tools to scan PRs and detect vulnerabilities during the workflow. You can also configure other pull request flags to enhance your PR scanning workflow.

GitHub Actions

The following example snippet shows you can set pr: true to enable PR scanning in GitHub Actions.

- name: 'Endor Labs Scan Push'
    if: ${{ github.event_name == 'push' }}
    uses: endorlabs/github-action@v1 # Replace v1 with the commit SHA of the latest version of the GitHub Action for enhanced security
    with:
    namespace: 'demo' # Replace with your Endor Labs tenant namespace
    scan_dependencies: true
    pr: true
    scan_summary_output_type: 'table'
    sarif_file: 'findings.sarif'

Azure pipelines

The following example snippet shows you can pass --pr in additionalArgs to enable PR scanning in Azure pipelines.

- task: EndorLabsScan@0
    inputs:
      serviceConnectionEndpoint: 'sanity-azure-devops-extension-staging'
      namespace: 'sanity.linux-latest'
      endorAPI: 'https://api.staging.endorlabs.com'
      logLevel: verbose
      tags: $(Build.BuildId)
      additionalArgs: '--output-type=summary --pr'
      sarifFile: scanresults.sarif

Jenkins

The following example snippet shows how you can enable PR scanning using endorctl in Jenkins.

stage('endorctl Scan') {
    steps {
        // Download and install endorctl.
        sh '''#!/bin/bash
            echo "Downloading latest version of endorctl"
            VERSION=$(curl $ENDOR_API/meta/version | jq -r '.ClientVersion')
            ENDORCTL_SHA=$(curl $ENDOR_API/meta/version | jq -r '.ClientChecksums.ARCH_TYPE_LINUX_AMD64')
            curl $ENDOR_API/download/endorlabs/"$VERSION"/binaries/endorctl_"$VERSION"_linux_amd64 -o endorctl
            echo "$ENDORCTL_SHA  endorctl" | sha256sum -c
            if [ $? -ne 0 ]; then
                echo "Integrity check failed"
                exit 1
            fi
            chmod +x ./endorctl
            // Check endorctl version and installation.
            ./endorctl --version
            // Run the scan.
            ./endorctl scan -a $ENDOR_API -n $ENDOR_NAMESPACE --api-key $ENDOR_API_CREDENTIALS_KEY --api-secret $ENDOR_API_CREDENTIALS_SECRET --pr $ENABLE_PR_SCAN
        '''
    }

GitLab pipelines

The following example snippet shows how you can enable PR scanning using endorctl in GitLab pipelines.

script:
    - curl https://api.endorlabs.com/download/latest/endorctl_linux_amd64 -o endorctl;
    - echo "$(curl -s https://api.endorlabs.com/sha/latest/endorctl_linux_amd64)  endorctl" | sha256sum -c;
      if [ $? -ne 0 ]; then
       echo "Integrity check failed";
       exit 1;
      fi
    - chmod +x ./endorctl
    - if [ "$DEBUG" == "true" ]; then
        export ENDOR_LOG_VERBOSE=true;
        export ENDOR_LOG_LEVEL=debug;
      fi
    - if [ "$CI_COMMIT_REF_NAME" == "$CI_DEFAULT_BRANCH" ]; then
        export ENDOR_SCAN_AS_DEFAULT_BRANCH=true;
        export ENDOR_SCAN_DETACHED_REF_NAME="$CI_COMMIT_REF_NAME";
      else
        export ENDOR_SCAN_PR=true;
      fi
    - ./endorctl scan ${ENDOR_ARGS}

Bitbucket pipelines

The following example snippet shows how you can enable PR scanning using endorctl in Bitbucket pipelines.

pull-requests:
    '**':
      - step:
          name: "Build and Test on PR"
          script:
            - mvn install -DskipTests
            - echo "Running Endor Labs PR Scan"
            - curl https://api.endorlabs.com/download/latest/endorctl_linux_amd64 -o endorctl
            - echo "$(curl -s https://api.endorlabs.com/sha/latest/endorctl_linux_amd64)  endorctl" | sha256sum -c
            - chmod +x ./endorctl
            - ./endorctl scan --pr --pr-baseline=main --languages=java --output-type=json -n $ENDOR_NAMESPACE --api-key $ENDOR_API_CREDENTIALS_KEY --api-secret $ENDOR_API_CREDENTIALS_SECRET | tee output.json

CircleCI

The following example snippet shows how you can enable PR scanning using endorctl in CircleCI.

- run:
    name: "Endor Labs Scan"
    command: |
    ./endorctl scan --dependencies --pr    

Google Cloud Build

The following example snippet shows how you can enable PR scanning using endorctl in Google Cloud Build.

# Step 4: SCA Scan With EndorLabs
  - name: 'SCA scan'
    entrypoint: 'bash'
    args: ["-c", "./endorctl scan -n $$ENDOR_NAMESPACE --api-key=$$ENDOR_API_CREDENTIALS_KEY --api-secret=$$ENDOR_API_CREDENTIALS_SECRET --as-default-branch=true --pr"]
    secretEnv: ['ENDOR_API_CREDENTIALS_KEY', 'ENDOR_API_CREDENTIALS_SECRET']
    env:
      - 'ENDOR_NAMESPACE=demo'
    id: 'SCA Scan With EndorLabs'

See Google Cloud Build configuration example for more information.

Scan PRs using the Endor Labs GitHub app

To automatically scan the PRs when they are raised, set the pull request preferences during the installation of the GitHub App or edit the integration preferences afterwards.

The Endor Labs GitHub App provides a scan report with details about scan failures. The report includes warning and error logs, recommended actions when available, and a link to the full scan history for additional context.

To view the scan report:

  1. Open the pull request where the scan failed.
  2. Click on the three vertical dots and select View Details from the Endor Labs Automated Scan to view the scan report.

View PR scan findings

To view the PR scan findings:

  1. Sign in to Endor Labs.
  2. Click Projects from the left sidebar.
  3. Search for and select the project.
  4. Select PR runs to view the PR scan findings.

PR Runs captures the commit ID, Commit SHA, the referenced branch, its findings, and the tags added to the scan as configured in the policies. Select the specific PR scan to view its findings in detail.

PR scan results in PR Runs

Pull Request comments

PR comments are automated comments added to pull requests when Endor Labs detects policy violations or security issues during scans. When a PR is raised or updated, Endor Labs runs scans on the proposed changes and adds a comment if any violations are detected based on the configured action policies.

Types of PR comments

Endor Labs generates the following types of PR comments based on the nature of the findings in a scan:

  • PR comments for Secrets: For findings of type FINDING_CATEGORY_SECRETS, Endor Labs adds a comment directly on the specific line where the secret is detected, using the line number provided in the finding object. These comments remain visible even if the secret is removed in a later scan.
  • PR comments for SCA: For SCA findings, Endor Labs adds a single comment that applies to the entire PR. It summarizes all findings from the policy evaluation results. The comment is updated with each scan run to reflect only the latest findings.
  • PR comments for SAST: For findings of type FINDING_CATEGORY_SAST, Endor Labs adds a single comment that applies to the entire PR. It summarizes all SAST-related policy violations detected during the scan. The comment is updated with each run and reflects only the latest findings.

Enable PR comments

You can enable PR comments through one of the following methods.

After enabling PR comments, you must Configure an action policy to allow comments to be posted on pull requests.

GitHub App

You can enable PR comments during the initial setup of the GitHub App or GitHub App (Pro), or by editing an existing integration. Once enabled, Endor Labs automatically adds comments to pull requests when policy violations are detected.

GitHub Actions

You can configure GitHub actions to comment on PRs if there are any policy violations. Make sure that your GitHub action workflow includes the following configuration.

  • The workflow must have a with clause including: enable_pr_comments to true to publish new findings as review comments and github_token: ${{ secrets.GITHUB_TOKEN }}. This token is automatically provisioned by GitHub when using GitHub actions. See GitHub configuration parameters for more information.
  • To grant Endor Labs the ability to comment on PRs you must include the permission pull-requests: write.

The following example configuration comments on PRs if a policy violation is detected.

      - name: Endor Labs Scan PR to Default Branch
        if: github.event_name == 'pull_request'
        uses: endorlabs/github-action@v1 # Replace v1 with the commit SHA of the latest version of the GitHub Action for enhanced security
        with:
          namespace: 'example' # Update with your Endor Labs namespace
          scan_summary_output_type: 'table'
          scan_dependencies: true
          scan_secrets: true
          pr: true
          enable_pr_comments: true
          github_token: ${{ secrets.GITHUB_TOKEN }}
PR comments example

The main.yaml file in this sample repository contains the following configuration to enable PR comments.

name: Build Release
on:
  pull_request:
    branches: [main]
  workflow_dispatch:
  push:
    branches: [main]
  schedule:
    - cron: "23 23 * * 0"
jobs:
  build:
    permissions:
      pull-requests: write
      security-events: write
      contents: read
      id-token: write
      actions: read
    runs-on: ubuntu-latest
    env:
      ENDOR_NAMESPACE: "endorlabs-hearts-github"
    steps:
      - name: Endor Labs Scan PR to Default Branch
        if: github.event_name == 'pull_request'
        uses: endorlabs/github-action@v1 # Replace v1 with the commit SHA of the latest version of the GitHub Action for enhanced security
        with:
          namespace: ${{ env.ENDOR_NAMESPACE }}
          pr: true
          enable_pr_comments: true
          github_token: ${{ secrets.GITHUB_TOKEN }}

The PR #10 introduced a reachable vulnerability. Since the workflow has enable_pr_comments set as true, a comment is added to the PR on the policy violation.

You can expand the comment to view the following details:

  • Issue type: Describes the category of the security or policy violation
  • Severity: Indicates how critical the issue is.
  • Impacted files or dependencies: Specifies the files and packages affected by the issue.
  • Remediation steps: Specifies the required fix to resolve the detected issue.

PR Comment Details

Endor Labs CLI

You can generate PR comments using the CLI by including the following flags in the endorctl scan command.

endorctl scan \
  --pr \
  --enable-pr-comments \
  --github-token <your-token> \
  --github-pr-id <pull-request-id> \
  --namespace <your-namespace>

Ensure the following:

  • Set --enable-pr-comments to true to activate PR comment generation.
  • Use --github-pr-id to specify the pull request to comment on.
  • Set the pull-requests permission to write for the --github-token.

Configure Action policy for PR comments

You must create an Action policy to receive comments on your pull request after enabling PR comments.

  1. Create an Action policy.
  2. Set the Branch Type to PR so the policy applies specifically to pull request scans.
  3. Under Action, select Enforce Policy, then choose:
    • Warn to post a comment without breaking the build.
    • Break the Build to fail the build and block the pull request.
  4. Define the scope of the policy using tags. Only projects that match the specified tags will receive PR comments.

Customize PR comments templates

Endor Labs provides a default template with standard information that will be included in your pull requests as comments. You can use the default template, or you can choose to edit and customize this template to fit your organization’s specific requirements. You can also create custom templates using Go Templates.

  1. Sign in to Endor Labs and navigate to Manage>Integrations
  2. Look for GitHub PR comments under Notifications.
  3. Click Edit Template.
  4. Make the required changes and click Save Template.
  5. Click Restore to Default to revert the changes.
  6. Use the download icon in the top right corner to download this template.
  7. Use the copy icon to copy the information in the template.

Data model

To create custom templates for PR comments, you must understand the data supplied to the template.

See the following protobuf specification for the GithubCommentData message that this template uses.

syntax = "proto3";

package internal.endor.ai.endor.v1;

import "google/protobuf/wrappers.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "spec/internal/endor/v1/common.proto";
import "spec/internal/endor/v1/finding.proto";
import "spec/internal/endor/v1/package_version.proto";

option go_package = "github.com/endorlabs/monorepo/src/golang/spec/internal.endor.ai/endor/v1";
option java_package = "ai.endor.internal.spec";

// The list of finding UUIDs.
message FindingUuids {
  repeated string uuids = 1;
}

// The map of dependency name to findings.
message DependencyToFindings {
  map<string, FindingUuids> dependency_to_findings = 1;
}

// The map of PackageVersion UUID to DependencyToFindings.
message PackageToDependencies {
  map<string, DependencyToFindings> package_to_dependencies = 1;
}

message GithubCommentData {
  // The header of the PR comment. Identifies the PR comment published by Endor Labs.
  // It should always be at top of the template.
  google.protobuf.StringValue comment_header = 1;

  // The footer of the PR comment.
  google.protobuf.StringValue comment_footer = 2;

  // The map of finding UUID to finding object.
  map<string, internal.endor.ai.endor.v1.Finding> findings_map = 3;

  // The map of policy UUID to policy name.
  // This will contain only the policies that are triggered or violated.
  map<string, string> policies_map = 4;

  // The map of policy UUID to the list of finding UUIDs.
  map<string, FindingUuids> policy_findings_map = 5;

  // The map of PackageVersion UUID to PackageVersion object.
  map<string, internal.endor.ai.endor.v1.PackageVersion> package_versions_map = 6;

  // The data needs to be grouped as follows:
  //
  // - Policy 1
  // 		- Package 1
  //			- Dependency Package 1
  //				- Finding 1
  //				- Finding 2
  //			- Dependency Package 2
  //				- Finding 3
  //				- Finding 4
  // 		- Package 2
  //			- Dependency Package 1
  //				- Finding 1
  //				- Finding 5
  // - Policy 2
  //		....
  //
  //		Map 0[PolicyUUID]/Map 1[PkgVerUUID]/Map 2 [Dep Names]/Finding UUID
  map<string, PackageToDependencies> data_map = 7;

  google.protobuf.StringValue api_endpoint = 8;
}

// Data structure for security review comments on pull requests.
message SecurityReviewCommentData {
  option (internal.endor.ai.endor.v1.parent_kinds) = {};
  option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
    json_schema: {
      extensions: {
        key: "x-internal";
        value {bool_value: true}
      }
    }
  };

  // Represents a specific security risk identified in the code review.
  message SecurityRisk {
    // Icon representing the severity level of the risk.
    google.protobuf.StringValue severity_icon = 1;

    // The category or type of the security risk.
    google.protobuf.StringValue category = 2;

    // The title or name of the security risk.
    google.protobuf.StringValue title = 3;

    // Link to the specific code location where the risk was identified.
    google.protobuf.StringValue code_link = 4;

    // Detailed description of the security risk and potential impact.
    google.protobuf.StringValue description = 5;

    // The level of the security risk.
    google.protobuf.StringValue level = 6;

    // The type of impact (improvement or regression).
    google.protobuf.StringValue impact_type = 7;
  }

  // The header of the security review comment.
  // It should always be at the top of the template.
  google.protobuf.StringValue comment_header = 1;

  // The footer of the security review comment.
  google.protobuf.StringValue comment_footer = 2;

  // A description of the changes made in the pull request.
  google.protobuf.StringValue changes_description = 3;

  // A general security assessment description.
  google.protobuf.StringValue security_description = 4;

  // The list of identified security risks in the pull request.
  repeated SecurityRisk security_risks = 5;
}

To understand Finding and PackageVersion definitions that are used in this protobuf specification, see:

See the following specification to understand the additional functions that are also available. You can access these functions by using their corresponding keys.


// FuncMap contains the additional functions that are available to GithubCommentTemplate.
var FuncMap = template.FuncMap{
	"now": toTime, // 'now' gives the current time

	// 'enumToString' coverts the enums for finding level, finding category and finding tags to string
	"enumToString": enumToString,

	// 'getPackageVersionURL' returns the URL for a given PackageVersion
	"getPackageVersionURL": func(apiURL string, packageVersion *endorpb.PackageVersion) string {
		result, err := common.GetPackageVersionURL(apiURL, packageVersion)
		if err != nil {
			return ""
		}
		return result
	},

	// 'getFindingURL' returns the URL for a given Finding
	"getFindingURL": func(apiURL string, finding *endorpb.Finding) string {
		result, err := common.GetFindingURL(apiURL, finding)
		if err != nil {
			return ""
		}
		return result
	},

	// 'add' returns the sum of two integers
	"add": func(n int, incr int) int {
		return n + incr
	},

	// 'getOtherFindingsPackageMarker' returns the key for _findingsWithNoPackages for lookup in DataMap
	// Not all findings are associated with a PackageVersion, such findings are grouped under this key
	// in the DataMap
	"getOtherFindingsPackageMarker": func() string { return _findingsWithNoPackages },

	// 'getOtherFindingsDependencyMarker' returns the key for _findingsWithNoDeps for lookup in DataMap
	// Not all findings are associated with a dependency, such findings are grouped under this key
	// in the DataMap
	"getOtherFindingsDependencyMarker": func() string { return _findingsWithNoDeps },

	// 'getFindingsCountString' returns a string with number of findings, example - "5 findings"
	"getFindingsCountString": func(dataMap *endorpb.PackageToDependencies) string {
		count := 0

		for _, depMap := range dataMap.PackageToDependencies {
			for _, findingMap := range depMap.DependencyToFindings {
				count += len(findingMap.Uuids)
			}
		}

		findingsStr := "findings"
		if count == 1 {
			findingsStr = "finding"
		}

		return fmt.Sprintf("%d %s", count, findingsStr)
	},

	// 'hasFindingCategory' checks if a finding has a specific category
	"hasFindingCategory": func(finding *endorpb.Finding, targetCategory string) bool {
		for _, category := range finding.GetSpec().GetFindingCategories() {
			if enumToString(category) == targetCategory {
				return true
			}
		}
		return false
	},

	// 'isNotEmptyString' checks if a string is not empty
	"isNotEmptyString": func(value string) bool {
		return value != ""
	},

	// 'getCustomLocation' extracts the location from Custom field
	"getCustomLocation": func(finding *endorpb.Finding) string {
		return getCustomFieldValue(finding, "location")
	},

	// 'getCustomCodeSnippet' extracts the code snippet from Custom field
	"getCustomCodeSnippet": func(finding *endorpb.Finding) string {
		return getCustomFieldValue(finding, "code_snippet")
	},

	"fixBackticks": fixUnclosedBackticks,
}