Securing the Software Supply Chain with Open Policy Agent: Policies for Pull Requests and Beyond

April 21, 2023

In the world of supply chain management, it’s crucial to ensure that code changes are adequately reviewed and tested before being integrated into the main codebase. Pull requests play a critical role in this process as they allow developers to review and ensure that the code adheres to established coding standards and best practices. The significance of this process cannot be overstated, as even minor errors or bugs can have far-reaching consequences for the entire supply chain.

A successful supply chain management strategy involves not only reviewing pull requests, but also incorporating automated testing and continuous integration as crucial components. Automated testing enables the early detection of bugs and errors, while continuous integration ensures that changes are integrated into the main codebase as soon as possible. Open Policy Agent (OPA) can be used to automate policy enforcement, making it easier to implement. Developers can define policies that warrants code changes adhere to established coding standards and other best practices. By enforcing these policies automatically, OPA can help guarantee that the codebase remains consistent and maintainable, minimizing the risk of errors and bugs that could harm the entire supply chain. OPA allows organizations to enforce policies that govern the software supply chain, By implementing these policies, organizations can establish a secure and transparent supply chain, which is crucial for maintaining the integrity and authenticity of software artifacts. This, in turn, helps in minimizing the risk of supply chain attacks.

OPA - Solution Architecture

Below is a Git action workflow for pull requests designed to integrate OPA into testing against policies. The workflow is triggered by various pull request events, such as opening, editing, and labeling.

name: Workflow for Pull Request OPA Policy
on:
pull_request:
types: [opened, edited, labeled]

jobs:
opa_review_policy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Installation of OPA
run: |
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64
chmod 755 ./opa
- name: Check Pull Request
run: |
./opa eval -i ${GITHUB_EVENT_PATH} -d repo/policy.rego
}
.code-container { position: relative; margin-top: 20px;}.copy-btn { position: absolute; right: 8px; top: 20px; /* Adjust as needed to position above the code block */ padding: 6px 12px; cursor: pointer; background-color: #777777; /* Button background color */ color: white; /* Button text color */ border: none; border-radius: 4px; font-size: 12px;}.code-block { font-family: monospace; background-color: rgba(255, 255, 255, 1); padding: 24px; margin-top: 12px; margin-bottom: 12px; border-radius: 8px; overflow-x: auto;}

Below are some policies that can be enforced with Open Policy Agent (OPA) to establish a secure software supply chain.

Code Review Policies:
Two-person review is an industry best practice for catching mistakes and deterring bad behavior, and it is also a part of `SLSA L4(https://slsa.dev/spec/v0.1/levels)`. This can be achieved using the Rego policy, which can be written to ensure that a certain number of reviewers review all pull request changes before they are merged.

package pr_review_policy
min_reviewers = 2
default allow = false
allow[msg]{
reviewers := input.review.approved_reviewers
num_reviewers := count(reviewers)
num_reviewers >= min_reviewers
msg := "reviewers not satisfied"
}
.code-container { position: relative; margin-top: 20px;}.copy-btn { position: absolute; right: 8px; top: 20px; /* Adjust as needed to position above the code block */ padding: 6px 12px; cursor: pointer; background-color: #777777; /* Button background color */ color: white; /* Button text color */ border: none; border-radius: 4px; font-size: 12px;}.code-block { font-family: monospace; background-color: rgba(255, 255, 255, 1); padding: 24px; margin-top: 12px; margin-bottom: 12px; border-radius: 8px; overflow-x: auto;}

It sets a minimum number of reviewers required for a pull request to be approved, which is 2 in this case. The default value for allowing a pull request is set to false. The policy also defines an "allow" rule with an error message that will be displayed if the number of approved reviewers is less than the minimum required. The rule checks the number of approved reviewers and compares it with the minimum required number. If the minimum criteria are met, the rule allows the pull request to be merged.

Access Control Policies:
Access control policies are a crucial aspect of any secure system. They define the users or groups are authorized to perform specific actions and those that are not. Without access control policies, any user be able to perform any action, making the system vulnerable to security threats. For example, it's important to ensure that only authorized users can create, merge, or approve pull requests. Access control policies can be used to manage users or groups with permission to perform these actions.

Let's write a policy that govern who has access to review and merge pull requests.  


package pr_access_control
default allow = false
allowed_reviewers = ["r1", "r2"]
allowed_approvers = ["a1", "a2"]

allow {
event := input.pull_request
input.action == "opened"
event.merged == false
event.requested_reviewers[_].login == allowed_reviewers[_]
event.head.repo.owner.login == event.base.repo.owner.login
approver := event.requested_reviewers[_].login
approver == allowed_approvers[_]
}
.code-container { position: relative; margin-top: 20px;}.copy-btn { position: absolute; right: 8px; top: 20px; /* Adjust as needed to position above the code block */ padding: 6px 12px; cursor: pointer; background-color: #777777; /* Button background color */ color: white; /* Button text color */ border: none; border-radius: 4px; font-size: 12px;}.code-block { font-family: monospace; background-color: rgba(255, 255, 255, 1); padding: 24px; margin-top: 12px; margin-bottom: 12px; border-radius: 8px; overflow-x: auto;}

The policy then defines two arrays, "allowed_reviewers" and "allowed_approvers", which contain the usernames of the reviewers and approvers who are allowed to access the pull request. The "allow" rule is defined with a set of conditions that must be met for the pull request to be allowed. The conditions check to see if

  1. the requested reviewers are in the "allowed_reviewers"
  2. the owner of the head repository is the same as the owner of the base repository, and
  3. the approvers are in the "allowed_approvers"

Security Impact Assessment Policy:
Rego can be used for defining policies that specify the criteria that must be met to pass a security impact assessment. These policies can include rules that check for various security requirements, such as

  1. encryption requirements
  2. compliance with regulatory requirements and secure coding practices
  3. protection of sensitive data, and
  4. detection and response to security incidents

Below policy that can detect the usage of deprecated or vulnerable libraries and configuration changes in a pull request.

package pr_security_policy
deny[msg] {
input.type == "pull_request"
lib := input.files[_].name
deprecated_libs := ["lib1", "lib2", "lib3"]
vulnerable_libs := ["lib4", "lib5", "lib6"]
lib in deprecated_libs
lib in vulnerable_libs
msg := sprintf("use of deprecated/vulnerable library %v", [lib])
}
deny[msg] {
input.type == "pull_request"
infra_config_files := ["network_security.yaml", "database_config.yaml"]
infra_config_files[_] == input.files[_].name
msg := "changes to infrastructure config"
}
deny[msg] {
input.type == "pull_request"
auth_files := ["auth.py", "auth_config.yaml"]
auth_files[_] == input.files[_].name
authz_files := ["authz.py", "authz_config.yaml"]
authz_files[_] == input.files[_].name
msg := "changes to auth code are not allowed"
}
}
.code-container { position: relative; margin-top: 20px;}.copy-btn { position: absolute; right: 8px; top: 20px; /* Adjust as needed to position above the code block */ padding: 6px 12px; cursor: pointer; background-color: #777777; /* Button background color */ color: white; /* Button text color */ border: none; border-radius: 4px; font-size: 12px;}.code-block { font-family: monospace; background-color: rgba(255, 255, 255, 1); padding: 24px; margin-top: 12px; margin-bottom: 12px; border-radius: 8px; overflow-x: auto;}

Explanation:

The pr_security_policy is a sample policy that denies pull requests that include the use of deprecated or vulnerable libraries, changes to infrastructure config files, or changes to auth code. It provides error messages to indicate why the pull request was denied.

We should develop a new Rego policy to identify any occurrences of sensitive data disclosure in code. This policy will leverage a code review tool to review the changes made in pull requests and provide a verdict.

package pr_sensitive_data
default allow = false
allow {
input.action == "opened"
not sensitive_data_found
}
deny[msg] {
input.action == "opened"
sensitive_data_found
msg = "sensitive data found in pr"
}

sensitive_data_found {
scan_request := sprintf("YOUR_TOOL_API=%v", [input.pull_request.number])
response := http.send({
"method": "GET",
"url": scan_request,
})
response.status_code == 200
response.raw_body == "sensitive_info_response"
response.body.sensitive_data_found == true
}
.code-container { position: relative; margin-top: 20px;}.copy-btn { position: absolute; right: 8px; top: 20px; /* Adjust as needed to position above the code block */ padding: 6px 12px; cursor: pointer; background-color: #777777; /* Button background color */ color: white; /* Button text color */ border: none; border-radius: 4px; font-size: 12px;}.code-block { font-family: monospace; background-color: rgba(255, 255, 255, 1); padding: 24px; margin-top: 12px; margin-bottom: 12px; border-radius: 8px; overflow-x: auto;}

*you will need to replace the YOUR_TOOL_API  with the URL of your code scanning tool.

Explanation:

The "sensitive_data_found" rule defines a method to scan the pull request using a tool API and checks whether the response contains sensitive information. If the response contains sensitive data, the rule sets the "sensitive_data_found" variable to true, which triggers the "deny" rule and rejects the pull request. This policy can help prevent sensitive data leaks and ensure that pull requests are secure before they are merged into the main codebase.


Compliance Policies:

OPA can be used to set compliance policies, such as restricting access to sensitive data to authorized users and policies that ensure that pull requests comply with relevant regulations and standards, such as data privacy regulations.

Let us write a policy based on best practice that checks that artifacts are signed by a trusted entity before deployment.  

package pr_signing_policy
default allow = false
allow {
input.pull_request.base.ref == "main"
input.artifacts[_].signed_by == "trusted_entity"
}
deny {
input.pull_request.base.ref == "main"
not input.artifacts[_].signed_by == "trusted_entity"
}
.code-container { position: relative; margin-top: 20px;}.copy-btn { position: absolute; right: 8px; top: 20px; /* Adjust as needed to position above the code block */ padding: 6px 12px; cursor: pointer; background-color: #777777; /* Button background color */ color: white; /* Button text color */ border: none; border-radius: 4px; font-size: 12px;}.code-block { font-family: monospace; background-color: rgba(255, 255, 255, 1); padding: 24px; margin-top: 12px; margin-bottom: 12px; border-radius: 8px; overflow-x: auto;}

In conclusion, having policies for code review, access control, security impact assessment, and compliance is crucial for ensuring that pull requests are properly vetted before they are merged into the codebase. These policies help to prevent unauthorized changes, reduce the risk of security vulnerabilities, and ensure compliance with relevant regulations and standards. By leveraging OPA, organizations can improve their overall development processes and ensure that their code is secure, compliant, and of the highest quality. In addition, OPA provides a flexible and scalable way to define and enforce policies across the entire infrastructure, making it easier to maintain a secure and compliant supply chain.  

References

Author:

Pruthvi T - Lead Security Researcher, Loginsoft

Get notified

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

BLOGS AND RESOURCES

Latest Articles

RansomHub Revealed: Threats, Tools, and Tactics

December 9, 2024

The Rise of INTERLOCK Ransomware

November 13, 2024

Fortifying the Cloud: A Guide to Securing Vulnerable Cloud Environments

October 23, 2024