To connect GitHub to Safeguard, you create a GitHub personal access token, paste it into the GitHub card under Integrations, pick repositories and a branch or tag for each, and Safeguard clones and scans them into projects with an SBOM and findings. Scanning needs only Metadata and Contents read access. Write permissions are optional and switch on the pull request features: fix and upgrade pull requests, pull request comments and reviews, and a quality gate commit status.
Below: the connection screen by screen, every permission and why, the two ways Safeguard sits on a pull request, and fix pull requests with a human on the merge, then the limits.
Key takeaways
- GitHub repository connection (GA) works with GitHub.com, GitHub Enterprise Cloud (including data residency addresses under ghe.com) and GitHub Enterprise Server that is reachable over a public address.
- Tokens only. The dialog takes a fine-grained personal access token (recommended) or a classic token. Safeguard's docs say a GitHub App installation and "Sign in with GitHub" are not available.
- Read to scan, write only for pull request features. Metadata and Contents read-only is the least privilege set. Contents, Pull requests and Commit statuses write are optional.
- No webhooks to set up. Each repository is scanned when you connect it; schedule rescans under Integrations › Scheduled or use Rescan.
- Pull request checks come from two places: the quality gate commit status
safeguard/code-analysis(needs Commit statuses write), and a GitHub Actions job that runs the Safeguard CLI (GA) and fails on a severity or a policy gate (Policies, Gates, Guardrails: GA). - Fix pull requests come from autonomous remediation with Griffin (GA, ships off). It ships switched off, with every category set to Manual, so a person starts and reviews every fix pull request until you decide otherwise.
- Prioritisation inside the findings you act on uses SCA (GA), reachability analysis (GA) and EPSS and CISA KEV enrichment from vulnerability management (GA).
- Source code is not stored. The scanner makes a shallow clone into a temporary folder and deletes it when the scan ends.
The problem: a security tool on your repositories is a privileged integration
Any tool that reads your source code holds a key to it, usually a token. Tokens leak, outlive the person who made them, and quietly gain reach as repositories are added. A scanner that only needs to read ends up with write access because write was the default. The other half of the problem is the merge: a fix that lands as a pull request still needs a review, and a check on every pull request only helps if a failed check stops the merge.
What NIST's Secure Software Development Framework says
NIST SP 800-218, the Secure Software Development Framework (SSDF) version 1.1 from February 2022, speaks to both halves.
- PS.1.1 asks you to store all forms of code, including source and configuration as code, based on the principle of least privilege so that only authorised personnel, tools and services have access. A scanning token is one of those tools. It should reach the repositories you chose and nothing else, at the lowest permission level that does the job.
- PO.5.1 asks you to separate and protect each environment involved in software development. A third party service that clones your repositories is part of that environment, and its credential belongs in the same inventory as your CI secrets.
- RV.2.2 asks you to plan and implement risk responses for vulnerabilities, making a risk based decision about whether each one is remediated or handled another way, and prioritising the actions. A fix pull request is one risk response; an exception with an expiry date is another. Both need a record.
What the OWASP Top 10 CI/CD Security Risks adds
The OWASP Top 10 CI/CD Security Risks names four failure modes that matter here:
- CICD-SEC-1, Insufficient Flow Control Mechanisms: code reaches a protected branch without the checks or approvals that should stand in the way.
- CICD-SEC-2, Inadequate Identity and Access Management: identities with more access than they need, or identities nobody owns.
- CICD-SEC-6, Insufficient Credential Hygiene: long lived, broad, unrotated credentials.
- CICD-SEC-8, Ungoverned Usage of 3rd Party Services: third party services connected to source control with broad permissions and no review.
What GitHub, OpenSSF and CISA add
GitHub's documentation on personal access tokens says it recommends fine-grained tokens instead of classic tokens "whenever possible". The reason is reach: a classic token can access every repository the account can access, while a fine-grained token is limited to one resource owner and to the repositories and permissions you pick. GitHub also notes that a classic token must be authorised for an organisation that uses SAML single sign-on before it can reach that organisation's resources, while fine-grained tokens are authorised during creation.
The OpenSSF Scorecard project checks open source repositories for the same habits from the other side: its Branch-Protection check looks at settings such as required status checks, and its Token-Permissions check looks at whether workflow tokens are restricted. CISA's Secure by Design initiative asks technology providers to own their products' security; for a vendor's integration, that starts with documenting what access it takes.
Safeguard's GitHub page follows the same line as GitHub. It recommends a fine-grained token by default and lists the classic token as the fallback for organisations that block fine-grained tokens or for GitHub Enterprise Server setups without them.
How the GitHub connection works
The GitHub card under Integrations on the Project/Product tab connects Safeguard to repositories on GitHub.com, GitHub Enterprise Cloud or your own GitHub Enterprise Server. Safeguard lists the repositories a token can see. Each repository you select, at a branch or a tag, becomes a project that Safeguard clones and scans for an SBOM and findings.
If you are still deciding whether Safeguard should clone your code at all, or whether the CLI or local runner on your own machines is a better fit, read Repo connection, CLI or local runner: how Safeguard touches your code first. This post assumes you have chosen the repository connection for GitHub.
Public and private connections
The dialog has two tabs. Public takes one or more public repository URLs, such as https://github.com/user/repo, and needs no credential. Private takes where GitHub runs and a personal access token, and can scan every repository the token can read. Only the Private tab can open pull requests: asking for a fix on a public connection returns an error that starts "No credentials configured for GITHUB. Please provide a personal access token to create PRs."
Token types Safeguard accepts
| Token type | Works? | What to know |
|---|---|---|
| Fine-grained personal access token | Yes, recommended | Pick the organisation that owns the repositories as Resource owner. If the organisation requires approval, the token stays pending and can only read public resources until an owner approves it |
| Personal access token (classic) | Yes | For an organisation that uses SAML single sign-on, authorise the token for that organisation with Configure SSO, then Authorize, or GitHub refuses it |
| GitHub App or OAuth app | Not available yet | The dialog takes a personal access token only |
GitHub Actions GITHUB_TOKEN | No | It exists only inside a workflow run |
| A token for a machine user or service account | Yes | A fine-grained or classic token of a dedicated GitHub account works the same way; give that account read access to the repositories |
A token belongs to a GitHub account. If that account is a person, the connection breaks the day they leave. A dedicated machine account, owned by the team and in your access reviews, avoids that and answers CICD-SEC-2.
Permissions requested, and what each one is for
For scanning, Safeguard only reads. It writes to GitHub only for the optional features below, and only when you use them. This is the fine-grained permission table from Safeguard's GitHub page, in plain terms.
| Repository permission | Required? | What Safeguard uses it for |
|---|---|---|
| Metadata: Read-only | Required | Listing your repositories and reading repository details and tags. GitHub adds it to every fine-grained token |
| Contents: Read-only | Required | Listing branches, reading files and cloning the repository for the scan |
| Contents: Read and write | Optional | Fix and upgrade pull requests: creating the branch and committing the changed files. Also merging a pull request from Safeguard |
| Pull requests: Read and write | Optional | Opening fix pull requests, requesting reviewers, adding labels, reading changed files, and posting pull request reviews and comments |
| Commit statuses: Read and write | Optional | The quality gate commit status safeguard/code-analysis on pull requests |
The least privilege set, in the docs' own words, is Metadata (Read-only) and Contents (Read-only), with Repository access set to Only select repositories.
For a classic token the picture is coarser:
| Classic scope | Required? | What Safeguard uses it for |
|---|---|---|
repo | Required for private repositories | Listing, reading and cloning private repositories. GitHub has no read-only classic scope for private repositories, so this also covers fix pull requests, comments and commit statuses |
public_repo | Alternative to repo | Enough when the token only needs public repositories, for example to open fix pull requests on them |
read:org, admin:repo_hook, write:repo_hook, workflow | Not needed | Safeguard does not list organisations or create webhooks, and fix pull requests do not edit workflow files |
Because fix pull requests do not edit files under .github/workflows, a token without the workflow scope is enough, and it cannot change your CI definitions through this integration.
What Safeguard never needs
The GitHub page lists what Safeguard never needs: admin or organisation scopes, webhook scopes, Actions, Secrets, Administration, deleting repositories, or write access of any kind when you only scan.
What Safeguard reads, stores and does not store
- Read: your login and repository list, repository details, branches and tags, and the contents of each selected repository at the chosen branch or tag. For pull request features, also the pull request, its changed files and its comments.
- Stored: the connection's settings, the repositories you selected, the SBOM and findings of each scan, and the token. The token is encrypted at rest with a per workspace key and never shown again after you save it.
- Not stored: source code. The scanner makes a shallow clone into a temporary folder and deletes it when the scan ends.
The docs also state that no customer data from this integration is sent to third party LLMs.
Step by step: connect GitHub to Safeguard
The steps below follow Safeguard's GitHub page and GitHub's own token documentation. Where a screen is named, it is named as the docs name it.
Step 1: decide the account and the scope
Decide which account owns the token (a machine account for an organisation, a personal account for a trial), which repositories it reaches, and whether you need pull request features yet. You can add write permissions later.
Step 2: create a fine-grained token in GitHub
- Select your profile picture, then Settings › Developer settings › Personal access tokens › Fine-grained tokens.
- Click Generate new token, enter a Token name and choose an Expiration. Safeguard's docs suggest an expiry such as 90 days.
- Under Resource owner, choose the account or organisation that owns the repositories. If the organisation requires approval, add a justification.
- Under Repository access, choose Only select repositories and pick the repositories, or choose all repositories of that owner.
- Under Permissions, set Contents to Read-only. Metadata is added automatically. Add the optional write permissions from the table above only if you want fix pull requests or the quality gate.
- Click Generate token and copy it. GitHub shows it only once.
Step 2, alternative: create a classic token
Use this only if your organisation blocks fine-grained tokens, or your GitHub Enterprise Server setup does not have them.
- Select your profile picture, then Settings › Developer settings › Personal access tokens › Tokens (classic).
- Select Generate new token, then Generate new token (classic).
- Enter a Note, choose an Expiration, and select the
reposcope, orpublic_repofor public repositories only. - Click Generate token and copy it.
- If the repositories belong to an organisation with SAML single sign-on, go back to Personal access tokens, click Configure SSO next to the token, and click Authorize next to the organisation.
On GitHub Enterprise Server the same pages are on your server, under your profile's Settings › Developer settings.
If you must use repo, put it on a machine account whose own repository access is limited.
Step 3: check what the token can see
GitHub has no CLI command that creates a personal access token, but the GitHub CLI can tell you what a token reaches. These are the checks from Safeguard's docs. Replace the placeholder with your token, and never paste a real token into a shared terminal history.
# Who the token belongs to
GH_TOKEN=<token> gh api user --jq .login
# A classic token's scopes (the X-OAuth-Scopes header; fine-grained tokens have none)
GH_TOKEN=<token> gh api -i user | grep -i '^x-oauth-scopes'
# The repositories it can list, as Safeguard lists them
GH_TOKEN=<token> gh api "user/repos?visibility=all&per_page=100" --jq '.[].full_name'
# GitHub Enterprise Server: add --hostname github.example.com to each command
The third command prints the list Safeguard will show you next. A missing repository means the token is too narrow; an unexpected one means it reaches too far.
Step 4: connect the token in Safeguard
- Open Integrations and choose the Project/Product tab.
- Click Connect on the GitHub card.
- For public repositories, stay on Public, enter a Name, paste each URL under Enter a public URL and click Add. Then click Next.
- For private repositories, choose Private and enter a Name, and an optional Description.
- Under Where does GitHub run?, keep GitHub.com, which includes GitHub Enterprise Cloud, or choose GitHub Enterprise Server and enter its Server URL, such as
https://github.example.com. For GitHub Enterprise Cloud with data residency, an address under ghe.com, keep GitHub.com, click Edit and enter your address. - Paste the token into Access Token and click Check. The message "Access token validated successfully" means GitHub accepted it.
- Click Next, select the repositories and the branch or tag of each, and finish with the Connect button.
Step 5: choose how the first scan runs
The Connect button has a menu with three options:
- Connect: Fast scan is the quicker scan.
- Connect: Deep scan is the full scan.
- Connect: Progressive scan runs fast now and reaches full depth after.
For a large organisation, progressive gives findings sooner without giving up depth. Each selected repository becomes a project with an SBOM and findings, and the findings are prioritised with reachability analysis (GA) and EPSS and KEV enrichment. How that ranking works is covered in Prioritise vulnerabilities with reachability, EPSS and KEV in Safeguard.
Step 6: keep scans fresh without webhooks
Safeguard's GitHub page is direct about this: the integration has no webhooks to set up. Each selected repository is scanned when you connect it. To rescan new commits, you have two options:
- Set a schedule under Integrations › Scheduled.
- Choose Rescan from the connection's menu under Project Configurations.
That is why webhook scopes are not needed, and there is no hook left firing after you disconnect. The trade-off is freshness: a scheduled scan sees main as of the last run. For a result on every pull request, add the CI job in the next section.
GitHub Enterprise Server and network reachability
A GitHub Enterprise Server must be reachable from the internet over a public address. Safeguard does not send a token to private or internal addresses, so an internal only server fails with "This server address isn't allowed". Use the CLI or the local runner on your network for it.
Pull request checks and gates
There are two ways for Safeguard to show up on a GitHub pull request. They do different jobs, and many teams use both.
Route A: the quality gate commit status from the connection
With Commit statuses: Read and write on the token, Safeguard can set a commit status on pull requests for the quality gate. The status appears under the context name safeguard/code-analysis.
GitHub's REST documentation describes a commit status as a way for external services to mark a commit error, failure, pending or success, with a context naming the service.
Safeguard's docs say quality thresholds are evaluated by the platform on the scans it runs for your connected repositories, and that there is no CLI step to add for this. What the docs do not describe is the exact moment the status is written for a given pull request. Before you make safeguard/code-analysis a required check (see below), open a few pull requests and confirm that the status appears on each one. A required check that is never reported blocks every merge.
Route B: a GitHub Actions job on every pull request
The documented way to scan every push and pull request, and to fail the build on what it finds, is a GitHub Actions workflow that runs the Safeguard CLI. This does not use the GitHub token from the connection at all. It uses a Safeguard API key stored as a GitHub secret.
- Create a secret key in Safeguard under Settings › Developer with the scopes
scans:triggerandscans:read. - In GitHub, add it under Settings › Secrets and variables › Actions › New repository secret with the name
SAFEGUARD_API_KEY. An organisation secret works too. - Save this workflow as
.github/workflows/safeguard.yml. It is copied from Safeguard's GitHub Actions page; we did not run it for this post.
name: Safeguard
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Safeguard scan
env:
SAFEGUARD_API_KEY: ${{ secrets.SAFEGUARD_API_KEY }}
SAFEGUARD_INSTALL_DIR: ${{ runner.temp }}/safeguard-bin
run: |
set -eu
curl -fsSL https://cli.safeguard.sh/install | bash
"$SAFEGUARD_INSTALL_DIR/safeguard" ci scan --wait --fail-on critical
No checkout step is needed, because Safeguard clones the repository. On a pull request the head branch is scanned and the version is named after its head commit. The run shows the scan's result and a link to it in Safeguard.
The docs list the options you are most likely to change:
| Change | How |
|---|---|
| Report only | Drop --fail-on critical |
| Another threshold | --fail-on high (or medium, low) |
| Fail on a policy gate | Add --fail-on-gate all |
| Scan on the runner | Add - uses: actions/checkout@v4 before the step and --local to the command |
| A specific project | Add --project-id <id> |
| Pin the CLI version | Add SAFEGUARD_VERSION: "<version>" to env |
on.pull_request.branches lists the target branches of pull requests that are scanned, and wildcards work, for example [main, 'release/**']. Delete the pull_request block to scan pushes only.
With checkout and --local, the scan runs on the GitHub runner instead of Safeguard cloning the repository.
Make the check a merge requirement in GitHub
GitHub's documentation on protected branches says that required status checks must have a successful, skipped or neutral status before collaborators can make changes to a protected branch, and that required status checks can be checks or commit statuses. Both Safeguard routes fit: the Actions job reports as a check, and safeguard/code-analysis is a commit status.
A practical sequence:
- Add the workflow and let it run on a test pull request so GitHub has seen the check.
- In the repository's branch protection rule or ruleset for
main, require status checks to pass and select the Safeguard job. - Add
safeguard/code-analysisas well, once you have confirmed it is reported on every pull request. - Decide between GitHub's "strict" and "loose" modes. Strict requires the branch to be up to date with the base branch before merging, which means the scan result reflects what will actually land.
GitHub also lets you require that a status check comes from a specific GitHub App. Safeguard's connection uses a personal access token rather than an App, so the status is set by the token's account.
How Safeguard decides pass or fail: policies, gates and actions
--fail-on critical is a severity threshold. --fail-on-gate is a policy decision, and it is where Policies, Gates, Guardrails (GA) come in.
Safeguard's docs separate the two ideas. Policies are rules that evaluate your SBOMs and generate findings. Gates are checkpoints that enforce policies, for example in a CI pipeline. Policies are built under Settings › Policies with the visual builder or YAML, and conditions can cover vulnerability severity, CVSS score, EPSS score, license, component age and maintenance status.
Each policy has one Action, which decides what a failed policy does to the gate:
| Action | When the policy fails |
|---|---|
| Block | The gate fails |
| Warn | The gate still passes, with a warning that names the policy |
| Notify | The gate still passes, and Safeguard sends a notification to your workspace's configured channels |
The gate is Failed if any Block policy fails, Error if a policy could not be evaluated, Not evaluated if nothing has run or no policy is linked, Warning if only Warn policies fail, and otherwise Passed. An Error never counts as a pass. The docs say safeguard ci scan --wait --fail-on-gate <gate name or id | all> waits for the scan and for the gate's policies to be evaluated on the new data, then:
- exits 0 when the gate is Passed or Warning, printing warnings and notified policies;
- exits non-zero when the gate is Failed, printing the Block policies that failed;
- exits non-zero when the gate is Error or Not evaluated, or its policies were not evaluated before
--timeout, with a message that says why.
A Warn policy is a good way to introduce a new rule without blocking anyone, and a gate with no linked policies fails the job as Not evaluated.
One more detail from the docs: policy outcomes are turned on per workspace by Safeguard. If your workspace does not show an Outcome column on the Gates and Policies tables, ask your Safeguard contact to turn it on. The --fail-on-gate flag depends on it. Writing policies in plain English and linking them to release gates is covered in Plain English security policies to release gates in Safeguard.
Exceptions instead of bypasses
When a pull request fails a gate for a reason the team accepts, the answer should be an exception, not an admin bypass of branch protection. Safeguard's docs describe the flow: click Request Exception, select the policy and finding, give a business justification, set an expiration date and submit for approval. Approvers are notified, decide, and the decision is recorded in an audit trail.
That record is what SSDF RV.2.2 asks for; an admin bypass leaves none.
Pull requests from forks
Open source maintainers and teams with external contributors hit this first. Safeguard's GitHub Actions page notes that pull requests from forks fail, because GitHub gives fork pull requests no secrets, so the job has no API key. The docs offer two options: scan pushes only, or use pull_request_target with care. That event runs with your secrets, so never run the fork's code in that job. GitHub's own documentation on workflow triggers carries the same warning about pull_request_target.
Fix pull requests
The pull request features of the connection exist mainly for one thing: fix pull requests from autonomous remediation with Griffin (GA, ships off). Safeguard's docs call this AI Remediate.
How Griffin builds a fix pull request
Safeguard's AI Remediate page describes five stages:
- Analysis: Griffin analyses the vulnerability and your codebase.
- Solution generation: it determines the safest upgrade path.
- Impact assessment: breaking changes and compatibility are checked.
- Pull request creation: a pull request is opened with all the changes.
- Validation: automated tests run to verify the fix.
For dependency upgrades, Griffin can upgrade vulnerable packages to fixed versions, handle transitive dependency conflicts, update lock files and keep compatibility with existing code. The docs list full AI Remediate support for npm, yarn and pnpm, pip, poetry and pipenv, Maven and Gradle, Go modules, Cargo, Bundler, NuGet and Composer.
Three ways to start a fix
- Single vulnerability: open the vulnerability, click AI Remediate, review the proposed changes, click Create Pull Request.
- Bulk: select vulnerabilities, click AI Remediate Selected. Griffin batches compatible fixes; you review all changes and create pull requests, one per repository.
- Page level: from the Vulnerabilities page, click AI Remediate at the top. Griffin analyses all fixable vulnerabilities and shows a remediation plan; you approve and create the pull requests.
Switched off until you turn it on
Autonomous remediation ships switched off, with every category set to Manual. Nothing opens a pull request in your GitHub repositories on its own until someone changes that.
The AI Remediate settings in the docs, under Settings › AI Remediate, include Auto-Create PRs (create pull requests for safe fixes automatically), Require Approval (manual approval before pull requests), Test Integration (run tests before creating pull requests), Breaking Change Policy and Branch Naming. The eight remediation categories and their strategies are covered in Autonomous remediation in Safeguard: eight categories, four strategies, off by default.
A sensible order for a GitHub rollout:
- Connect with read only permissions and live with the findings for a sprint.
- Update the token to add Contents and Pull requests write on a few repositories.
- Create fix pull requests by hand from single vulnerabilities, and review them like any other change.
- Turn on Test Integration, so pull requests arrive with test results.
- Only then consider automatic creation for fixes Griffin rates as Safe, and keep Require Approval on.
What a fix pull request contains
The docs say auto generated pull requests include a clear title with the CVE reference, a description of the vulnerability, a summary of the changes, breaking change warnings if any, test results if configured, and links to the relevant advisories.
Each fix also carries a breaking change rating:
| Rating | Meaning in the docs | Sensible review |
|---|---|---|
| Safe | No breaking changes expected | Normal review |
| Minor | Minor API changes, low risk | Normal review, check the changelog |
| Major | Significant changes, review recommended | Owner review and a full test run |
| Critical | High risk of breaking, manual review required | Treat as a planned upgrade, not a quick merge |
Keeping a human on the merge
GitHub's protections apply to Safeguard's pull requests as to anyone's.
- Required reviews. If
mainrequires an approving review, a Safeguard fix pull request waits for one. Safeguard can request reviewers and add labels with Pull requests write, which helps routing; it does not approve its own work. - Required checks. The same required status checks run on a fix pull request. A dependency bump that breaks your tests fails the build like any other change.
- Merging from Safeguard. Contents write also lets Safeguard merge a pull request. If you would rather all merges happen in GitHub, leave that to your developers; the permission is still needed to create the branch and commit.
- Admin bypass. If the token's account is an organisation or repository admin, branch protection may let it bypass rules unless your settings prevent that. A machine account without admin rights avoids the question.
What a fix pull request needs from the token
A fix pull request needs the Private tab, a token, and write permissions. In practice:
- Fine-grained: Contents: Read and write, plus Pull requests: Read and write.
- Classic:
repofor private repositories, orpublic_repofor public ones.
Fix pull requests do not edit workflow files, so a token without the classic workflow scope, or without a fine-grained Workflows permission, is enough. If a fix would need a change under .github/workflows, that change is yours to make.
Rotation, expiry and disconnect
Tokens expire, and when one does, scans and repository listing fail until you paste a new one. Plan for it.
Rotate:
- Create a new token with the same permissions.
- Under Project Configurations on the Project/Product tab, open the connection's menu and choose Update.
- Paste the new token into Access Token, click Check, and click Save Changes.
- Delete the old token in GitHub under Settings › Developer settings › Personal access tokens.
Disconnect:
- Choose Delete from the connection's menu under Project Configurations and confirm Delete configuration.
- Delete the token in GitHub.
Put the expiry date in a calendar; a stopped scan still leaves the last results on the dashboard.
Troubleshooting messages you might see
| Message | Likely cause | Fix |
|---|---|---|
| Invalid or expired access token | Mistyped, expired or revoked token; a fine-grained token still pending approval; a classic token not authorised for SAML single sign-on | Create a new token, ask an organisation owner to approve it, or authorise it with Configure SSO |
| Failed to validate access token | Safeguard could not reach GitHub, often a GitHub Enterprise Server address that is wrong or not reachable from the internet | Check the Server URL and that the server is reachable from outside your network |
| This server address isn't allowed | The address is private, internal or contains a user name and password | Use the server's public https address |
| git clone failed (on a scan) | The token lost access to the repository, or the branch or tag no longer exists | Check the token's repository access and expiry, then update the connection |
Decision table: which permissions for which goal
| Goal | Token | Permissions | Extra setup |
|---|---|---|---|
| Try Safeguard on an open source repository | None | None | Public tab |
| Inventory and findings for private repositories | Fine-grained, machine account | Metadata and Contents read-only, selected repositories | Schedule under Integrations › Scheduled |
| Findings plus fix pull requests you start by hand | Fine-grained, machine account | Add Contents and Pull requests read and write | Branch protection with required reviews |
| A gate on every pull request | Safeguard API key in Actions | scans:trigger and scans:read on the Safeguard key | Workflow plus a required status check |
| Quality gate status on pull requests | Fine-grained | Add Commit statuses read and write | Confirm it appears before requiring it |
| Organisation that blocks fine-grained tokens | Classic, machine account | repo | Configure SSO and Authorize for SAML organisations |
| GitHub Enterprise Server with no public address | Not supported by the connection | Not applicable | Use the CLI or the local runner instead |
Requirement versus what Safeguard does versus what you still own
| Requirement | What Safeguard does | What you still own |
|---|---|---|
| SSDF PS.1.1: least privilege access to code | Works with a fine-grained token limited to selected repositories and read-only permissions for scanning | Choosing the account, the repositories and the expiry; reviewing the token in access reviews |
| SSDF PO.5.1: separate and protect development environments | Stores the token encrypted at rest with a per workspace key; deletes the temporary clone after each scan | Your GitHub organisation settings, SSO, and approval policy for fine-grained tokens |
| SSDF RV.2.2: plan and implement risk responses | Prioritises findings, opens fix pull requests when asked, records gate exceptions with justification and expiry | Reviewing and merging fixes; deciding which risks to accept |
| OWASP CICD-SEC-1: flow control | Reports a pass or fail per pull request through the CI job and the quality gate status | Making those checks required in branch protection |
| OWASP CICD-SEC-2 and SEC-6: identity and credential hygiene | Accepts machine account tokens; never needs admin, webhook, Actions or Secrets permissions | Rotation before expiry, revoking old tokens, owning the machine account |
| OWASP CICD-SEC-8: third party services | Documents every permission and what it is used for | Reviewing that list against your third party policy |
What Safeguard does not do
- No GitHub App or OAuth install today. The connection takes a personal access token. You cannot install Safeguard organisation wide as an app, and there is no "Sign in with GitHub" for the connection. Plan for token ownership and rotation.
- No push webhooks from the connection. Repositories are scanned on connect, on a schedule or on Rescan. If you need a result on every pull request, you add a CI job; the connection alone will not rescan on each push.
- No private network reach. A GitHub Enterprise Server with only an internal address cannot be connected. Safeguard does not send a token to private or internal addresses. Use the CLI or the local runner for those repositories.
- No fix pull requests without write access. Public connections and read only tokens produce findings, not pull requests.
- No workflow file edits. Fix pull requests do not change files under
.github/workflows. If a GitHub Action version needs updating, that is your change. - No automatic merging by default. Autonomous remediation ships switched off with every category on Manual, and your branch protection still applies. Safeguard does not replace your reviewers.
- No fork pull request scanning with secrets by default. GitHub withholds secrets from fork pull requests, so the documented workflow cannot scan them without the
pull_request_targettrade-offs. - Not covered here: AI review of pull request diffs. The offering's PR Guard, Griffin AI code review of pull request diffs with GitHub first, is generally available. This post does not describe it as part of the GitHub connection.
- Not covered here: outbound event webhooks from Safeguard. Safeguard's events page describes remediation events such as a fix pull request being opened, but that page carries a notice that its
/v1REST endpoints are not available yet. Do not build automation on them until the notice is gone.
Common mistakes
- Using a classic
repotoken from an admin's personal account. It reaches every private repository the admin can see, with write access, and it dies when the admin leaves. Use a fine-grained token on a machine account. - Forgetting the SSO step. A classic token that has not been authorised for a SAML organisation fails with "Invalid or expired access token" even though it was created a minute ago.
- Waiting on an approval nobody knows about. A fine-grained token for an organisation that requires approval stays pending until an owner approves it. Tell the owner before you start.
- Expecting rescans on push. There are no webhooks. Without a schedule or a CI job, Safeguard shows the repository as it was when you connected it.
- Requiring a check that never reports. Confirm
safeguard/code-analysisappears on several pull requests before you make it required, and link at least one policy to a gate before you require a--fail-on-gatejob. - Treating Warn as Block. A Warn policy passes the gate. If you want a merge stopped, the policy's Action must be Block.
- Using
pull_request_targetto make fork scans work, then checking out the fork's code. That runs untrusted code with your secrets. The docs say never run the fork's code in that job. - Turning on automatic pull request creation before reviewing any by hand. Start with single fixes, read the breaking change rating, and build trust in the output first.
FAQ
How do I connect GitHub to Safeguard?
Create a fine-grained personal access token with Metadata and Contents read-only on the repositories you want, then open Integrations, choose the Project/Product tab, click Connect on the GitHub card, choose Private, paste the token, click Check, select repositories and a branch or tag, and click Connect. Public repositories can be added by URL with no token.
What GitHub permissions does Safeguard need?
For scanning, Metadata: Read-only and Contents: Read-only, with Repository access set to Only select repositories. Optional: Contents and Pull requests read and write for fix pull requests, comments and reviews, and Commit statuses read and write for the quality gate commit status. Safeguard never needs admin, organisation, webhook, Actions, Secrets or Administration permissions.
Does Safeguard have a GitHub App?
Not today. Safeguard's GitHub page says a GitHub App installation and "Sign in with GitHub" are not available, and the dialog takes a personal access token only. A token from a dedicated machine account is the closest equivalent for an organisation.
Does Safeguard use GitHub webhooks?
No. The GitHub integration has no webhooks to set up. Each repository is scanned when you connect it, and you rescan with a schedule under Integrations › Scheduled or the Rescan action. For a result on every pull request, add the Safeguard CLI to a GitHub Actions workflow.
Can Safeguard block a pull request from merging?
Safeguard reports the result; GitHub blocks the merge. Run safeguard ci scan --wait --fail-on critical or --fail-on-gate in a GitHub Actions job on pull requests, then make that job a required status check in branch protection. The quality gate commit status safeguard/code-analysis can be required the same way once you have confirmed it reports on your pull requests.
Does Safeguard open pull requests automatically?
Not by default. Autonomous remediation ships switched off with every category set to Manual. A person starts each fix from a vulnerability, a bulk selection or the Vulnerabilities page, reviews the change and clicks to create the pull request. Automatic creation is a setting you choose to turn on.
Does Safeguard store my source code?
No. The scanner makes a shallow clone into a temporary folder and deletes it when the scan ends. Safeguard stores the connection settings, the repositories you selected, the SBOM and findings, and the token, encrypted at rest with a per workspace key.
Why do pull requests from forks fail in the Safeguard workflow?
GitHub does not pass secrets to workflows triggered by fork pull requests, so the job has no SAFEGUARD_API_KEY. Scan pushes only, or use pull_request_target carefully and never run the fork's code in that job.
Try it on one repository
Test on one repository you know well: connect it read only, read the findings, then add write permissions and create one fix pull request by hand. Get the Safeguard CLI (GA) and the other clients at safeguard.sh/download, and keep docs.safeguard.sh open for the GitHub and CI pages as you go. For an overview of every capability and its maturity label, see What Safeguard does today.
A fix pull request is only possible when a fixed version exists. When it does not, or when the fix is slow to arrive, the options below are what Safeguard offers on top of the GitHub connection.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.
3. Gold Registry: start from components that are already fixed
Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | Safeguard policies and gates in CI |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real Safeguard CLI session. It installs the CLI, opens the interactive terminal with safeguard and asks it to fix the lodash CVE in a repository; the assistant reads the finding and advisory, asks before any command that changes something, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, prints a summary and pushes nothing. It then shows /permissions, /model and /scan .. It is the local counterpart of a fix pull request.
Click through it at your own pace, or open it full screen.
The second demo sets up the local runner on your own machine, approves it with its device code, then scans from the terminal. It is the route for repositories the GitHub connection cannot reach, such as a GitHub Enterprise Server with only an internal address.
Click through it at your own pace, or open it full screen.
The third demo connects Claude to the Safeguard MCP server with OAuth and chooses which tools Claude can use. In Claude, add Safeguard from the connector directory rather than configuring a custom server; Safeguard is listed there as a community connector. Safeguard's docs say the MCP server has read-only tools for gate and policy outcomes when policy outcomes are on for your workspace. The connector directory post covers the install.
Click through it at your own pace, or open it full screen.
Quick check
You only want SBOMs and findings for ten private repositories. Which fine-grained permissions should the token have?
Metadata: Read-only and Contents: Read-only, with Repository access set to Only select repositories and those ten repositories picked. Write permissions are only for fix pull requests, pull request comments and the quality gate commit status.
A policy linked to your gate has its Action set to Warn and it fails on a pull request. Does safeguard ci scan --fail-on-gate fail the job?
No. A failed Warn policy leaves the gate at Warning, which passes, so the command exits 0 and prints the warning. Only a failed Block policy, an Error or a gate that was not evaluated makes it exit non-zero.
Sources
- Safeguard docs: GitHub integration
- Safeguard docs: Integrations
- Safeguard docs: GitHub Actions
- Safeguard docs: AI Remediate
- Safeguard docs: Policies and Gates
- Safeguard docs: Policy outcomes and actions
- Safeguard docs: Code Quality
- Safeguard docs: Findings
- Safeguard docs: Webhooks and Events
- Safeguard download page
- GitHub Docs: Managing your personal access tokens
- GitHub Docs: Permissions required for fine-grained personal access tokens
- GitHub Docs: Authorizing a personal access token for use with SAML single sign-on
- GitHub Docs: REST API endpoints for commit statuses
- GitHub Docs: About protected branches
- GitHub Docs: Events that trigger workflows
- NIST SP 800-218, Secure Software Development Framework version 1.1
- NIST SP 800-218 full text (PDF)
- OWASP Top 10 CI/CD Security Risks
- CISA: Secure by Design
- OpenSSF Scorecard checks
Last reviewed: 25 October 2026. Product capabilities change; check docs.safeguard.sh for the current state.