How to best handle builds from forks?

I have an open source project on GitHub that triggers Buildkite builds with webhooks. It’s useful to trigger builds for PRs coming from forks as most contributors don’t have commit access to the repo. However, this opens up some attack vectors such as in this case where a malicious actor wants to gain control of the Buildkite agents.

What’s the recommended solution to prevent these kinds of attacks? Ideally would strike a balance between convenience and security. Perhaps by only blocking builds from first-time contributors.

Thanks!
JP

1 Like

Hi @jpsim

First of all welcome to Buildkite community and thank you for reaching out to us with your question.

I agree that allowing builds for PR with source being a third party fork can open up to risks like you mentioned.

One option would be like you mentioned have a block step if the build is for a third party fork by having a step like shown in below example pipeline config

steps:

  • block: Run untrusted build
    if: build.pull_request.repository.fork

This example blocks builds if PR is from a fork so then you can control if the changes proposed in the PR are fine to allow the build and at same time it does not disrupt builds for the main repo.

Thanks,
Suma

Thanks for the suggestion, Suma. I was looking at block steps to handle this but because my pipeline is defined in yaml in the git repo itself, couldn’t a malicious actor simply open a PR removing that step to cause their untrusted code to run?

Yes, if the pipeline definition is also present in git repo then malicious actor can modify it and above option will not help. May be something on agent hook to validate before running the steps but let me check for this scenario what is the best option.

Hi @jpsim

In this case I think option is to use hooks on agent end to validate them before running the steps as these hooks will be local to your agent this is one way you can ensure even if someone modifies the pipeline definition you can make sure only after certain checks are passed further steps are run.

Agent hooks seems like it could work. However those are scripts so I can’t use the yaml you provided. How would you suggest writing this hook? Should it be a pre-bootstrap or pre-checkout hook or some other hook?

Hi @jpsim

Yes, the conditional check I shared earlier was for scenario where you want to perform check in pipeline itself but as you mentioned later that pipeline is also exposed on public repo that wont fit your usecase.

For agent hook, I would say use post checkout hook and perform below validation

  1. Check for environment variable BUILDKITE_PULL_REQUEST and if build is for a PR then check BUILDKITE_PULL_REQUEST_REPO is your repo.
    2.If BUILDKITE_PULL_REQUEST_REPO is your repo then allow the build to progress
  2. If it is not then you will check for the pipeline definition in the checked out repo and make sure it does have the block step like you expect and is not modified

Please let me know if there are any questions.

Thanks for your suggestions. I’ll take that into consideration when deciding what to do for my project.

I noticed that you might work at Buildkite. I think this protection is a significant gap in the current offering and having a Pipelines settings option to require manual approval to run builds for forks would be extremely useful.

Have you decided you on how to proceed with this? We are currently looking at Buildkite for our open source project Trixi.jl and are considering similar questions. Specifically, we would like to have the ability to manually approve pipelines running on forks without worrying about the pipeline.yml file being modified in the fork itself.

I am wondering if another approach could be to add this check in the “default pipeline” (or whatever it is called) that usually does nothing but to upload the pipeline file from the repo? If the check were added before the pipeline document is even uploaded, this should be safe from malicious users, right?

Alternatively, it would be nice if one could easily verify the user of the fork against a list of pre-approved users (e.g. members of the GitHub org or a particular team of that org), such that regular developer do not need to repeatedly approve their own fork builds.