Safeguard
Supply Chain Security•

Git hosting security features by platform in 2026: a docs matrix

What GitHub, GitLab, Bitbucket, Azure DevOps and Gitea document for branch rules, signed commits, push protection, OIDC, audit logs and SBOMs, by tier.

Safeguard Security Team
37 min read

All five major Git hosts document branch protection and pull request review, but push time secret blocking, audit streaming, SBOM export and token policies sit on paid tiers or add ons on most of them. On the free tier, few controls are on by default. This matrix, built only from each platform's own docs on 23 October 2026, shows which.

The same word means different things on different hosts. "Branch protection" can be a hard block or a warning anyone can click past below a paid plan. Anyone writing a source control baseline needs the tier, the default state and the bypass path for each control, not just a yes or a no.

Key takeaways

  • Every platform documents branch protection and required review. Bypass differs: Azure DevOps has two named bypass permissions, GitHub rulesets have a bypass list, Gitea admins can force merge unless you switch that off, and Bitbucket Cloud merge checks are only warnings below Premium.
  • GitHub, GitLab and Gitea document both verifying and requiring signed commits; on GitLab, requiring them is a Premium or Ultimate push rule.
  • Push time secret blocking at the repository level is a paid feature on GitHub (it needs GitHub Secret Protection), on GitLab (Ultimate) and on Azure Repos (GitHub Advanced Security or Secret Protection for Azure DevOps). GitHub's separate user push protection is on by default for pushes to public repositories.
  • GitHub Actions, GitLab CI/CD, Bitbucket Pipelines and Azure Pipelines all document OIDC or workload identity federation for reaching clouds without stored secrets.
  • Audit retention ranges from 90 days (Azure DevOps Services) to 180 days (GitHub organization audit log) to "retained indefinitely" (GitLab audit events).
  • Native SBOM export is documented by GitHub (SPDX) and GitLab (CycloneDX, Ultimate) only, in the docs we read.
  • Counting documented controls that are on by default at the entry tier gives GitHub 5, GitLab 3, and Gitea, Bitbucket Cloud and Azure DevOps Services 1 each. That count measures defaults, not strength, and the method is explained below.

The platforms and editions in scope

We covered the five hosts named most often in source control baselines, plus the self hosted editions that share their names:

PlatformSaaS editionSelf hosted editionDocs read on 23 Oct 2026
GitHubGitHub.com (Free, Pro, Team, Enterprise Cloud)GitHub Enterprise Serverdocs.github.com, github.blog changelog
GitLabGitLab.com (Free, Premium, Ultimate) and GitLab DedicatedGitLab Self Manageddocs.gitlab.com
BitbucketBitbucket Cloud (Free, Standard, Premium)Bitbucket Data Centersupport.atlassian.com, atlassian.com licensing
Azure DevOpsAzure DevOps ServicesAzure DevOps Serverlearn.microsoft.com
GiteaHosted Gitea offerings exist; we read the open source docsGitea (open source)docs.gitea.com (version 28 docs)

AWS CodeCommit is not in the matrix. Its documentation history records that it closed to new customers on 25 July 2024 and reopened to them on 25 November 2025.

The matrix at a glance

ControlGitHubGitLabBitbucket CloudAzure DevOps ServicesGitea
Branch rulesBranch protection rules and rulesets; docs list rulesets for Team and Enterprise plansProtected branches on Free; group level rules on Premium and UltimateBranch permissions plus merge checks; checks only enforced on PremiumBranch policies and branch permissionsProtected branches with push allowlists, approvals, status checks
Signed commit verificationYes, GPG, SSH and S/MIMEYes on Free; GPG, SSH and X.509Not found in docs fetchedNot found in docs fetchedYes, GPG and SSH
Require signed commitsYes, protected branches and rulesetsPush rule "Reject unsigned commits", Premium and UltimateNot found in docs fetchedNot found in docs fetchedYes, protected branch option
Push time secret blockingRepository push protection needs Secret Protection; user push protection on by default for public reposSecret push protection, Ultimate, opt inNot found in docs fetchedPush protection with GitHub Advanced Security or Secret Protection for Azure DevOpsNot found in docs fetched
CI OIDC to cloudYes, per job tokenYes, id_tokens, Free and upYes, on by default in PipelinesWorkload identity federation for service connectionsNot found in docs fetched
Audit log and streamingOrg log covers 180 days; streaming documented for Enterprise CloudGroup and project events on Premium and Ultimate, kept indefinitely; streaming UltimateNot found in docs fetched90 day log, auditing off by default, streaming to three targetsNot found in docs fetched
Native SBOM exportSPDX from the dependency graphCycloneDX from the dependency list, UltimateNot found in docs fetchedNot found in docs fetchedNot found in docs fetched
Build attestationsArtifact attestations, GA 25 June 2024Not covered in docs fetched for this postNot found in docs fetchedNot found in docs fetchedNot found in docs fetched
Token policyFine-grained PAT approval and 366 day max on by default for orgsPATs expire in 365 days by defaultNot covered in docs fetched for this postPAT policies off by default except leaked PAT revocationNot found in docs fetched

Branch protection and rulesets: who can bypass

GitHub: branch protection rules and rulesets

GitHub has two overlapping systems. Classic branch protection rules apply per branch pattern in a repository. Rulesets are named lists of rules that can target branches or tags in one repository or across many repositories in an organization, and they can also block pushes to a repository and its whole fork network (GitHub Docs, "About rulesets", read 23 October 2026).

The rulesets page states that rulesets are available "for customers on GitHub Team and GitHub Enterprise plans." The page we read did not mention Free or public repository availability, so check your own plan before you assume rulesets are there. A repository can carry up to 75 rulesets, and an organization can carry up to 75 organization wide rulesets.

Two details on that page matter for auditors:

  1. Rules stack. Rulesets have no priority order. When several rulesets and branch protection rules target the same branch, GitHub combines them and applies the most restrictive version of any rule defined twice.
  2. Bypass is explicit. A ruleset can carry a bypass list of roles (such as repository admins), teams or GitHub Apps. For forks, push rule bypass is limited to people with bypass permission on the root repository.

Rule types named on the page include required signed commits, blocked force pushes, file path, extension and size limits, commit metadata rules and pull request reviews.

GitLab: protected branches and push rules

GitLab's protected branches are on every tier, including Free. The docs state that "the default branch for your repository is protected by default" (GitLab Docs, "Protected branches", read 23 October 2026). New rules default to letting Maintainers push and merge, so if you want pull request only changes, you must set "Allowed to push and merge" to No one yourself.

Higher tiers add structure: group level protected branches and Code Owner approval need Premium or Ultimate. Group level rules cannot be changed at the project level.

On bypass, the protected branches page does not describe an administrator override. It does say that by default only Maintainers and Owners can unprotect a branch, and that users cannot change protection settings unless they can unprotect the branch.

Bitbucket Cloud: branch permissions and merge checks

Branch permissions control who can write to a branch and who can merge through pull requests. Merge checks add conditions: minimum approvals, default reviewer approvals, no changes requested, no unresolved tasks, and a minimum number of successful builds (Atlassian Support, "Suggest or require checks before a merge", read 23 October 2026).

The tier detail is the important part. On Free and Standard plans, merge checks are advisory: users see warnings about unresolved checks "but can still merge". The setting "Prevent a merge with unresolved merge checks" is a Premium feature, as is resetting approvals when the source branch changes.

Azure Repos: branch policies and two bypass permissions

Azure Repos documents branch policies for minimum reviewers, linked work items, comment resolution, limited merge types, build validation, status checks and automatically included reviewers (Microsoft Learn, "Set and manage branch policies", updated 15 July 2026).

Bypass is unusually explicit. Two separate permissions exist:

  • Bypass policies when completing pull requests lets a user complete a pull request that does not meet policy.
  • Bypass policies when pushing lets a user push directly to a protected branch from a local repository or the web editor.

Both can be scoped to a project, a repository or a single branch. Microsoft's security baseline page recommends limiting both to "a small admin group" and notes that these permissions "defeat the protections" created by reviewers and checks (Microsoft Learn, "Secure repositories and pull requests", 10 July 2026).

Gitea: protected branches with an admin override

Gitea's protected branch settings cover a push allowlist (users, teams and optionally deploy keys), required approvals, blocking merge on rejected reviews, required status checks by glob pattern, required signed commits and protected file patterns (Gitea Docs, "Protected branches", read 23 October 2026).

By default, repository administrators can override the rules with "Force merge". A separate option makes administrators follow branch protection rules too, which removes that override.

Signed commits: verify versus require

Verification and enforcement are different controls. Verification means the host checks a signature and shows a badge. Enforcement means the host rejects unsigned commits on a protected branch.

PlatformSignature types verifiedPlatform signs its own web commitsEnforcement optionTier for enforcement
GitHubGPG, SSH (Git 2.34 or later), S/MIME (Git 2.19 or later)Yes, with GitHub's web-flow GPG keyRequire signed commits via protected branches; rulesets also list itRulesets: Team and Enterprise per the rulesets page
GitLabGPG, SSH, X.509Yes, with an instance keyPush rule "Reject unsigned commits"Premium and Ultimate
GiteaGPG, SSHConfigurable via [repository.signing]Protected branch "Require signed commits"Open source, no tiers
Bitbucket CloudNot found in docs fetchedNot foundNot foundn/a
Azure ReposNot found in docs fetchedNot foundNot foundn/a

Behaviors that change what an auditor sees in history:

  • GitHub persistent verification. When GitHub verifies a signature on push, it stores an immutable record with a timestamp, exposed as verified_at in the REST API. The status survives key rotation, revocation or expiry.
  • GitHub rebase and merge. The docs warn that GitHub's Rebase and Merge creates new commits it cannot sign for the author, so those commits are not verified. Their suggested workaround is to rebase locally and push.
  • GitLab web commits pass the push rule. "Reject unsigned commits" applies to git push. Commits created through the GitLab UI or API, including merge request actions, are allowed without signatures, so unsigned GitLab created commits can still appear in history. Web IDE commits are blocked by default unless an administrator disables a feature flag.
  • Gitea does not check revocation. Gitea checks signatures against keys in its database or its default signing key, but the docs say it does not check whether keys have expired or been revoked and does not query keyservers.

The Azure Repos security baseline page we read (10 July 2026) lists repository permissions, branch permissions, branch policies and GitHub Advanced Security status checks. It does not list commit signature verification or enforcement. The Bitbucket Cloud pages we fetched did not cover commit signatures.

Push time secret blocking: a platform feature, by tier

Push protection stops a credential at push time, before it lands in history.

PlatformFeature nameTier or product neededDefault stateHow a user bypasses
GitHub (repository)Push protectionGitHub Secret ProtectionOff by defaultAnyone with write access gives a reason, unless delegated bypass is on
GitHub (user)User push protectionPersonal account on GitHub.comOn by default, for pushes to public reposNot detailed on the page
GitLabSecret push protectionUltimate (GA in 17.5)Opt in per project; admin must allow on Self Managed and DedicatedPush option secret_push_protection.skip_all or [skip secret push protection] in a commit message
Azure ReposPush protectionGitHub Advanced Security for Azure DevOps, or Secret Protection for Azure DevOpsActive once the product is enabledskip-secret-scanning:true in the commit message
Bitbucket CloudNot found in docs fetchedn/an/an/a
GiteaNot found in docs fetchedn/an/an/a

GitHub

GitHub's push protection page describes two types (GitHub Docs, "About push protection", read 23 October 2026). Repository push protection can be enabled at repository, organization or enterprise level, is off by default, and "Requires GitHub Secret Protection to be enabled." User push protection belongs to a personal account, exists only on GitHub.com, "Is enabled by default", and stops that person pushing secrets to public repositories.

Bypass is recorded. By default, anyone with write access can bypass by giving a reason. Each bypass creates an alert, lands in the audit log and emails owners, security managers and watching admins. Delegated bypass limits who can push past a block.

GitHub's Secret Protection product page gives two 2024 figures: "4.4M Secrets prevented from leaking on GitHub in 2024" and "39M Secret leaks detected with Secret Protection in 2024" (github.com Secret Protection page, read 23 October 2026). Those are GitHub's own counts for its own platform and cannot be compared with any other host.

GitLab

GitLab's secret push protection is Ultimate on all offerings, GA from 17.5 (GitLab Docs, read 23 October 2026). It scans pushed diffs in the pre receive hook with high confidence patterns, so some secrets will be missed. Documented limits:

  • Binary files are not checked.
  • Files or diff patches larger than 1 MiB are not checked.
  • The initial push that creates a repository is not checked.
  • Pushes that change more than 3,150 paths or 350,000 lines are skipped entirely.

Skipped pushes are recorded in audit events, and pipeline secret detection, if enabled, still scans afterwards.

Azure Repos

Microsoft's page says push protection is "built natively into Azure DevOps Git" once GitHub Advanced Security for Azure DevOps, or the standalone Secret Protection for Azure DevOps, is enabled (Microsoft Learn, updated 27 February 2026). A user who believes a block is a false positive can add skip-secret-scanning:true to the commit message; an alert is still generated. The page notes legacy token formats might not be blocked, and GitHub Advanced Security for Azure DevOps is Services only.

Related reading: GitHub push protection bypass policy for teams and GitLab secret detection: push protection versus pipeline versus client side.

OIDC tokens for CI: what the claims carry

OIDC lets a CI job prove its identity to a cloud and get short lived credentials. The claims matter because they are what your cloud trust policy can match on.

PlatformMechanismTierOpt in?Claims named in docsLifetime in docs
GitHub ActionsOIDC token, issuer token.actions.githubusercontent.comNot stated on pageJob requests itsub, aud, repository_id, ref, sha, environment, job_workflow_ref, runner_environment, actor, custom repo_property_ claimsPer job, expires automatically
GitLab CI/CDid_tokens keyword, RS256 JWTFree, Premium, UltimateDeclared per jobproject_id, namespace_id, ref, ref_protected, environment_protected, runner_environment, sha, ci_config_ref_uri, user_login and moreJob timeout if set, otherwise 5 minutes
Bitbucket PipelinesOIDC via BITBUCKET_STEP_OIDC_TOKENPipelines requiredOn by default (oidc: true)Workspace, repository and deployment environment UUIDs; full payload shown as an imageNot stated; step max-time plus a 5 minute buffer
Azure PipelinesWorkload identity federation for Azure Resource Manager service connectionsAzure DevOps ServicesRecommended for new connectionsNot listed on the pageNot stated

Sources: GitHub Docs "OpenID Connect"; GitLab Docs "OpenID Connect (OIDC) Authentication Using ID Tokens"; Atlassian Support "Integrate Pipelines with resource servers using OIDC"; Microsoft Learn "Use an Azure Resource Manager service connection" (updated 18 August 2026).

Platform notes worth recording:

  • GitHub's new default subject. The GitHub OIDC page states that repositories created after 15 July 2026 use an immutable default sub format that includes owner and repository IDs, which is not available on GitHub Enterprise Server.
  • GitLab stable IDs. GitLab recommends trust policies use project_id and namespace_id, which do not change when a path is renamed. On GitLab.com several claims are available as AWS condition keys; elsewhere only sub is.
  • Bitbucket audiences. Custom audiences can be set globally or per step, up to 10 audiences of up to 150 characters each.
  • Azure issuer change. For workload identity federation in Azure public cloud, Microsoft says the Azure DevOps issuer (vstoken.dev.azure.com) retires on 1 July 2027, and new connections use the Microsoft Entra issuer by default. Azure Pipelines may also automatically disable Azure Resource Manager service connections unused for 100 days.

For token lifetime comparisons in more depth, see how long a CI OIDC token is valid on GitHub versus GitLab.

Audit logs: retention and streaming by tier

PlatformWhere the log livesRetention in docsStreaming targetsTier or prerequisite
GitHubOrganization audit log, owners only180 daysAmazon S3, Azure Blob Storage, Azure Event Hubs, Google Cloud Storage, Datadog, Splunk; Microsoft Purview in preview for Copilot agent eventsAPI access: Enterprise Cloud; streaming documented under Enterprise Cloud
GitLabGroup, project and instance audit eventsRetained indefinitelyHTTP endpoint, Google Cloud Logging, AWS S3Events: Premium and Ultimate; streaming: Ultimate
Azure DevOps ServicesOrganization auditing90 daysSplunk, Azure Monitor Logs, Azure Event GridMicrosoft Entra backed org; auditing off by default; public preview
Bitbucket CloudNot found in docs fetchedn/an/an/a
GiteaNot found in docs fetchedn/an/an/a

Sources: GitHub Docs "Reviewing the audit log for your organization" and "Streaming the audit log for your enterprise"; GitLab Docs "Audit events" and "Audit event streaming"; Microsoft Learn "Create audit streaming for Azure DevOps" (updated 31 August 2026).

  • GitHub pauses. A paused GitHub stream holds data for up to seven days. After three weeks paused, nothing is retained. Datadog only accepts logs up to 18 hours old.
  • GitLab Free. GitLab Free users get only successful sign in events in their own authentication log, not group or project audit events.
  • Azure DevOps. Microsoft states auditing is "turned off by default for all Azure DevOps Services organizations", is still in public preview, is only available for Microsoft Entra backed organizations, and is not available for on premises deployments. Azure Monitor Logs keeps streamed data for 30 days by default unless you pay for longer.

If your control framework asks for a year of source control audit evidence, the Azure DevOps 90 day window and GitHub 180 day window both mean you need a stream and your own storage.

Dependency graph and SBOM export

PlatformNative inventoryExport formatsTier
GitHubDependency graph from manifests, lock files and the dependency submission APISPDX (docs link the SPDX 2.3 spec), from the UI or REST APINot stated on the pages read
GitLabDependency list, populated by dependency or container scanning on the default branch, or by uploaded CycloneDX 1.4 to 1.6 reportsJSON, CSV, CycloneDX (CycloneDX for projects only)Ultimate
Bitbucket CloudNot found in docs fetchedn/an/a
Azure ReposNot found in docs fetchedn/an/a
GiteaNot found in docs fetchedn/an/a

On GitHub you export from Insights, Dependency graph, Export SBOM, or through the REST API, where the docs describe requesting generation and polling an sbom_url until a 302 redirect hands you the file (GitHub Docs, "Exporting a software bill of materials for your repository", read 23 October 2026). GitHub's dependency graph page says repository administrators can enable or disable the graph, but it did not state the default for public repositories on the day we read it.

A repository level SBOM describes what the manifests declare. It is not the same as an SBOM of the container image or binary you ship, which can contain operating system packages and vendored code the manifests never mention.

Build provenance and artifact attestations

GitHub announced artifact attestations as generally available on 25 June 2024 (GitHub changelog, read 23 October 2026). The feature uses Sigstore to create what GitHub calls "an unforgeable link between artifacts and their build process". You add the attest-build-provenance action with id-token: write, contents: read and attestations: write permissions, and verify with gh attestation verify. The changelog says it works with public and private repositories. It did not state plan availability or a SLSA level.

We did not find a native artifact attestation feature in the Bitbucket Cloud, Azure Repos or Gitea pages we fetched. Teams there can sign inside the pipeline with open tooling such as cosign.

Tokens: types and admin policies

PlatformToken types covered in docs readDefault policyAdmin controls
GitHubFine-grained PATs and classic PATsOrg approval required for fine-grained PATs; fine-grained max lifetime 366 days; classic tokens have no expiry requirementRestrict classic or fine-grained PAT access to org resources; set separate max lifetimes; non-compliant tokens are blocked, not revoked
GitLabPersonal access tokens (fine-grained tokens on a separate page)Expiry required; 365 days if none set; maximum 365 days from today by defaultAdmins can change the maximum from 17.6; one section says only Ultimate admins can set a maximum lifetime
Azure DevOps ServicesPersonal access tokens, with Microsoft Entra tokens recommended insteadRestriction policies off by default; automatic revocation of leaked PATs on by defaultRestrict global PATs, restrict full scoped PATs, enforce max lifespan (tenant); restrict PAT creation (org)
Bitbucket CloudNot covered in docs fetched for this postn/aSee the app password migration checklist linked below
GiteaNot found in docs fetchedn/an/a

Sources: GitHub Docs "Setting a personal access token policy for your organization"; GitLab Docs "Personal access tokens"; Microsoft Learn "Manage personal access tokens using policies" (updated 22 April 2026).

An Azure DevOps PAT pushed to a public GitHub repository is revoked automatically unless an administrator turned that policy off. For dated migration steps, see the Azure DevOps global PAT retirement checklist and the Bitbucket app passwords to API tokens migration checklist.

Self hosted editions: what changes

EditionWhat the docs say differsSource
GitHub Enterprise ServerThe new immutable OIDC sub format for repositories created after 15 July 2026 is not available on GHESGitHub Docs, OpenID Connect
GitLab Self ManagedSecret push protection needs an administrator to allow it instance wide first; security fixes ship for the current release and the two before itGitLab Docs, secret push protection; maintenance policy
Bitbucket Data CenterNot on Atlassian's Data Center end of life list; existing customers get a Bitbucket Hybrid LicenseAtlassian Data Center end of life page
Azure DevOps ServerAuditing is not available on premises; GitHub Advanced Security for Azure DevOps is Services only; PAT policy pages are Services onlyMicrosoft Learn auditing, secure repositories, PAT policy pages
GiteaAll protected branch and signing settings are in app.ini and the UI, with no tiersGitea Docs

GitLab's maintenance policy (read 23 October 2026) sets the self managed patch cadence: monthly minor releases, patch releases twice a month, and security fixes for the current release plus the two previous ones. On that day the maintained versions were 19.5, 19.4 and 19.3.

Atlassian's Data Center end of life page sets 28 March 2029 as end of life for impacted Data Center products, but states that "Bitbucket and Bamboo Data Center will not end of life."

Ranked: documented controls on by default at the entry tier

This table counts, it does not judge. It ranks platforms by the number of security relevant controls that their docs, read on 23 October 2026, describe as on by default without any setup at the lowest tier we could identify. A higher count means more defaults, nothing more.

RankPlatform (tier counted)Documented default-on controlsCountSource and date
1GitHub (Free, organization)User push protection for public repos; signature verification badges; GitHub signs web UI commits; fine-grained PAT approval and 366 day max for orgs; 180 day org audit log5GitHub Docs pages listed in Sources, 23 Oct 2026
2GitLab (Free)Default branch protected; signature verification on Free; GitLab signs web UI commits3GitLab Docs, 23 Oct 2026
3Gitea (open source, version 28 docs)Signature verification against keys stored in Gitea1Gitea Docs, 23 Oct 2026
3Bitbucket Cloud (Free)Pipelines OIDC set to true by default1Atlassian Support, 23 Oct 2026
3Azure DevOps ServicesAutomatic revocation of leaked PATs1Microsoft Learn, updated 22 Apr 2026

How to read it: Gitea's server side signing of its own commits depends on how the instance administrator sets SIGNING_KEY, and its admin force merge is on by default, so neither is counted. Bitbucket Cloud's 1 partly reflects Atlassian pages that returned 404. Azure DevOps documents many controls that start off, including auditing. Some GitHub items had no plan stated on the page.

How we measured

Source set. Only each platform's own docs, changelogs and product pages (GitHub, GitLab, Atlassian, Microsoft Learn, Gitea, AWS). No third party comparisons, analyst reports or vendor research.

Date. Every page was fetched on 23 October 2026. Where Microsoft Learn exposes an update date, we give it.

What counts as documented. A feature is "documented" if the platform's page describes it as existing for that product. "Not found in docs fetched" means one of three things: the page we tried returned 404, the page did not mention it, or we did not fetch a page on it in the time box.

What counts as on by default. A control counts only if the doc says, in words, that it is on, enabled, protected or required by default, or describes the behavior as automatic with no setup step. A control that requires a per job declaration (GitHub and GitLab OIDC) or a toggle (GitLab secret push protection) does not count. A control that is on by default but only on a paid tier does not count toward the entry tier.

Why the numbers cannot be added. The figures in this post count different things. GitHub's 4.4 million and 39 million are 2024 secret counts on GitHub only. Retention windows are days; rule limits are product limits; the ranked table counts defaults. None share a unit, so this post has no total, average or score.

Known gaps. Bitbucket Cloud pages for branch restrictions, secret scanning and the audit log returned 404 on 23 October 2026. GitHub's rulesets page did not mention Free plan availability. GitHub's audit streaming page did not name a plan in the text we received. GitLab's PAT page gave two different statements about which tier can set a maximum lifetime.

A step by step baseline check, with commands

These commands read configuration; none of them change it. Replace the placeholders with your own names.

1. GitHub: list rulesets and check protection on the default branch.

# Rulesets that apply to the repository
gh api repos/OWNER/REPO/rulesets

# Classic branch protection on main, if any
gh api repos/OWNER/REPO/branches/main/protection

# Organization fine-grained PAT requests waiting for approval
gh api orgs/ORG/personal-access-token-requests

2. GitHub: export the SBOM and verify an attestation.

# SPDX SBOM from the dependency graph
gh api repos/OWNER/REPO/dependency-graph/sbom > sbom.spdx.json

# Verify the provenance attestation for a downloaded artifact
gh attestation verify ./dist/app.tar.gz -o ORG

The workflow that produces the attestation looks like this:

permissions:
  id-token: write
  contents: read
  attestations: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: make dist
      - uses: actions/attest-build-provenance@v2
        with:
          subject-path: dist/app.tar.gz

3. GitLab: protected branches, push rules and an ID token.

glab api projects/:id/protected_branches
glab api projects/:id/push_rule
deploy:
  id_tokens:
    AWS_ID_TOKEN:
      aud: sts.amazonaws.com
  script:
    - aws sts assume-role-with-web-identity --role-arn "$ROLE_ARN" --role-session-name gitlab --web-identity-token "$AWS_ID_TOKEN"

4. Bitbucket Pipelines: OIDC to AWS.

pipelines:
  branches:
    main:
      - step:
          oidc: true
          script:
            - aws sts assume-role-with-web-identity --role-arn "$ROLE_ARN" --role-session-name bitbucket --web-identity-token "$BITBUCKET_STEP_OIDC_TOKEN"

Then confirm in the workspace settings whether "Prevent a merge with unresolved merge checks" is available and on. If it is not, the merge checks are warnings.

5. Azure Repos: list branch policies and audit who can bypass.

az repos policy list --repository-id REPO_ID --branch main --output table

Then open Project settings, Repositories, the branch, Security, and list every identity with "Bypass policies when completing pull requests" or "Bypass policies when pushing". In Organization settings, check that Auditing is turned on and that a stream exists if you need more than 90 days.

6. Gitea: check signing and admin override.

[repository.signing]
SIGNING_KEY = default
SIGNING_FORMAT = openpgp
MERGES = pubkey, twofa, basesigned, commitssigned

Then open the protected branch rule and confirm both "Require signed commits" and the option that makes administrators follow the rules are set the way your policy says.

Where the docs leave gaps that process must cover

Several gaps show up across all five:

  1. Bypass review. None of the docs we read schedules a review of who holds bypass rights. Make it a quarterly access review task.
  2. Defaults on new repositories. GitLab protects the default branch automatically. Elsewhere, apply rules through org level rulesets, group rules or provisioning scripts.
  3. Web created commits. GitHub and GitLab sign their own web commits, but GitLab's push rule lets UI and API commits through unsigned, and GitHub's rebase and merge produces unverified commits.
  4. Secret blocking limits. Each host documents gaps in push protection. It reduces leaks; it does not prove none happened.
  5. Dependencies and images. Branch rules and push protection govern what enters the repository. They say nothing about known vulnerabilities in the lock file or base image.

Common mistakes

Reading "available" as "enabled". Push protection on Azure Repos exists, but it needs a paid product switched on. Check the setting, not the docs.

Mixing tiers in one column. GitLab protected branches are Free, but group level protected branches and Code Owner approval are Premium. Bitbucket merge checks exist on every plan, but only block on Premium. A single cell per platform hides that.

Comparing GitHub's secret counts with another host. No other platform published a comparable figure in the docs we read, so there is no cross platform rate.

Assuming a self hosted edition matches SaaS. The self hosted table above lists documented differences; check it before copying a SaaS baseline.

Writing OIDC trust policies on names. Path and name based subjects can change when a project is renamed or moved. GitLab recommends ID based claims, and GitHub moved new repositories to an ID based default subject in July 2026.

Treating branch rules as supply chain coverage. A protected branch with two reviewers can still merge a lock file that pulls in a vulnerable transitive dependency.

FAQ

Which Git hosting platforms support push protection for secrets?

GitHub, GitLab and Azure Repos document it. GitHub's repository push protection needs Secret Protection and is off by default; user push protection is on by default for public repos. GitLab's is Ultimate. Azure Repos needs GitHub Advanced Security or Secret Protection for Azure DevOps.

Can I require signed commits on GitLab Free?

GitLab Free verifies GPG, SSH and X.509 signatures and shows badges. Rejecting unsigned commits is a push rule, and push rules need Premium or Ultimate.

Does Bitbucket Cloud enforce merge checks on the Free plan?

No. Atlassian's docs say that without Premium, users see warnings about unresolved merge checks but can still merge. "Prevent a merge with unresolved merge checks" is a Premium setting.

Who can bypass branch policies in Azure DevOps?

Anyone granted "Bypass policies when completing pull requests" or "Bypass policies when pushing", scoped to a project, repository or branch. Microsoft recommends limiting both to a small admin group.

How long is a GitLab CI ID token valid?

GitLab's docs say the token expires when the job's timeout ends if one is set, otherwise after 5 minutes. It is signed with RS256.

How long does GitHub keep the organization audit log?

GitHub's organization audit log page covers events from the last 180 days. Streaming is documented under Enterprise Cloud.

Can I export an SBOM from GitHub or GitLab without extra tools?

GitHub exports SPDX from the dependency graph through the UI or REST API. GitLab's dependency list exports JSON, CSV and CycloneDX, on Ultimate, with CycloneDX available for projects only.

Does Gitea support required signed commits?

Yes. Gitea's protected branch settings include "Require signed commits", which rejects pushes containing unsigned or unverifiable commits. Note that Gitea's docs say it does not check key expiry or revocation.

Where Safeguard fits

Everything in the matrix above protects the repository: who can change it, how changes are verified, and what gets logged. Safeguard works on a different layer, the code and components that pass through those controls.

Safeguard connects to GitHub, GitLab, Bitbucket and Azure DevOps and scans repositories, container images and SBOM files for dependencies and known vulnerabilities, matched against vulnerability databases, with reachability analysis to show which vulnerable code paths your application actually calls. It generates SBOMs in SPDX and CycloneDX, shows which projects contain a given component and version, and evaluates policies and gates in the platform so a pull request that introduces a disallowed component or a vulnerability over your threshold can be flagged. When a fixed version exists, autonomous remediation opens a fix pull request in the same SCM; it ships switched off, so your team decides what runs on its own.

What Safeguard does not do here: it does not replace any platform control in this post. Branch rules, signed commit enforcement, audit logs, token policies and push protection are configured in your Git host. Gitea is not one of the four SCMs Safeguard connects to. If you want to try the scanner locally first, the CLI is at safeguard.sh/download, with setup in the CLI docs.

For narrower comparisons, see hardening GitLab versus GitHub default settings, GitHub branch protection rules versus rulesets, who can bypass Azure DevOps branch policies and Bitbucket branch permission patterns.

Fix it without waiting for upstream

The source control controls above decide who can change your code. They do not decide whether the dependencies and images in that code are already fixed, and that is where the next block applies.

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 covers eight categories, including dependencies, licenses, security posture and findings. 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. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature 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. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull 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

This is a real Safeguard CLI session: asked to fix a lodash CVE, it reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, and pushes nothing. On any host in this matrix, that fix is a normal change your branch rules, reviews and signing settings still govern.

Click through it at your own pace, or open it full screen.

This one sets up the Safeguard runner on your own machine, approves it with its device code, and then runs a scan from the terminal. It lets you scan a checked out repository from any of these Git hosts without first connecting the SCM to the platform.

Click through it at your own pace, or open it full screen.

The last demo connects Claude to the Safeguard MCP server with OAuth and lets you choose which tools Claude can use, so a platform team can ask about findings across connected repositories with tool access it approves.

Click through it at your own pace, or open it full screen.

Quick check

On Bitbucket Cloud Standard, will a merge check requiring two approvals stop a merge with one approval?

No. Atlassian's docs say merge checks are advisory without Premium: users see a warning but can still merge. Blocking needs the Premium setting "Prevent a merge with unresolved merge checks".

Does GitLab's "Reject unsigned commits" push rule guarantee every commit in history is signed?

No. GitLab's docs say the rule applies to commits pushed with git push, while commits created through the GitLab UI or API, including merge request actions, are allowed without signatures.

Sources

Last reviewed: 23 October 2026. Platform features, tiers and defaults change often; check the linked docs for the current state.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.