[{"data":1,"prerenderedAt":1185},["ShallowReactive",2],{"/blog/how-we-prevented-security-fixes-leaking-into-our-public-repositories":3,"navigation-en-us":400,"banner-en-us":827,"footer-en-us":837,"blog-post-authors-en-us-Robert Speicher":1082,"blog-related-posts-en-us-how-we-prevented-security-fixes-leaking-into-our-public-repositories":1096,"blog-promotions-en-us":1122,"next-steps-en-us":1175},{"id":4,"title":5,"authors":6,"body":8,"category":379,"date":380,"description":381,"extension":382,"externalUrl":383,"faq":383,"featured":384,"heroImage":385,"meta":386,"navigation":387,"path":388,"seo":389,"slug":393,"stem":394,"tags":395,"template":398,"updatedDate":383,"__hash__":399},"blogPosts/en-us/blog/how-we-prevented-security-fixes-leaking-into-our-public-repositories.md","How we prevented security fixes leaking into our public repositories",[7],"Robert Speicher",{"type":9,"value":10,"toc":365},"minimark",[11,22,25,28,33,42,51,60,76,80,83,92,105,113,117,120,129,148,161,179,182,186,194,197,200,215,219,228,231,240,263,273,284,290,305,309,312,316,351],[12,13,14,15,21],"p",{},"One of GitLab's core values is \"",[16,17,20],"a",{"href":18,"rel":19},"https://handbook.gitlab.com/handbook/values/#public-by-default",[],"public by default",",\" which means we develop in\nthe open whenever possible. One notable exception to this is security fixes,\nbecause developing security fixes in public discloses vulnerabilities before a\nfix is available, exposing ourselves and our users to attacks.",[12,23,24],{},"In order to work on these security issues in private, public GitLab projects\nhave a security mirror that's accessible only to GitLab engineers. A design flaw in GitLab's mirroring feature would cause commits from the\nSecurity repository to be exposed in the public repository before they were\nintended for release.",[12,26,27],{},"In this post we'll describe what the problem was and how we finally resolved it.",[29,30,32],"h2",{"id":31},"mirroring-setup","Mirroring setup",[12,34,35,36,41],{},"To ensure that developers working on a security fix are working against the\nlatest code for a project, we utilize GitLab's ",[16,37,40],{"href":38,"rel":39},"https://docs.gitlab.com/user/project/repository/mirror/",[],"push mirror"," feature to mirror\nthe public (\"Canonical\") repository to its private Security fork.",[12,43,44,45,50],{},"On every commit to the Canonical repository, the Security repository receives\nthe same commit. All of the mirroring is performed by the ",[16,46,49],{"href":47,"rel":48},"https://gitlab.com/gitlab-org/gitaly",[],"Gitaly","\nserver, which handles all of the Git calls made by GitLab.",[12,52,53,54,59],{},"In order to know which Git objects in the source are missing on the destination,\nGitLab would ",[16,55,58],{"href":56,"rel":57},"https://gitlab.com/gitlab-org/gitlab/blob/f5bfe5603137b8f9cf60a2db759db3dbe5c60727/app/services/projects/update_remote_mirror_service.rb#L30",[],"fetch the remote"," and then tell Gitaly to perform the push that\nwould bring the two in sync, which is where the trouble starts.",[12,61,62,63,67,68,71,72,75],{},"By performing a fetch, ",[64,65,66],"em",{},"every Git object in the Security repository was now\nknown and stored on-disk by the Canonical repository",". If someone knew the SHA\nof a commit in the ",[64,69,70],{},"private"," repository that contained a security fix, they\ncould view it in the ",[64,73,74],{},"public"," repository and discover the vulnerability we were\nfixing before it had been publicly disclosed.",[29,77,79],{"id":78},"no-guessing-necessary","No guessing necessary",[12,81,82],{},"Thankfully, even a truncated Git commit SHA is difficult to guess, so at first\nglance this might not look like a high-severity issue.",[12,84,85,86,91],{},"However, the ",[16,87,90],{"href":88,"rel":89},"https://gitlab.com/help",[],"GitLab help page"," shows exactly which\ncommit is currently running, and we always deploy security fixes to GitLab.com\nfor verification and to protect our users against the latest threats. Here's\nwhat that might look like:",[93,94,95],"blockquote",{},[96,97,99,100],"h3",{"id":98},"gitlab-enterprise-edition-1370-pre-690e4bbfe94","GitLab Enterprise Edition 13.7.0-pre ",[16,101,104],{"href":102,"rel":103},"https://gitlab.com/gitlab-org/gitlab/-/commits/690e4bbfe94",[],"690e4bbfe94",[12,106,107,108,112],{},"When a security release was in progress, any logged-in user could click on the\nrunning commit SHA and view the entire ",[16,109,111],{"href":110},"/solutions/source-code-management/","source code"," tree at that point, security\nfixes included!",[29,114,116],{"id":115},"experimenting-with-a-fix","Experimenting with a fix",[12,118,119],{},"The mirroring setup was a crucial part of our development and release process,\nand the existing fetch-based behavior was itself a crucial piece of what made\nthe mirroring functionality work. During our initial investigation, there was no\nobvious fix. One proposed workaround was to simply remove the SHA from the Help\npage, but that would only hide the problem and \"security through obscurity\"\nisn't really security at all.",[12,121,122,123,128],{},"Another workaround, which we ",[16,124,127],{"href":125,"rel":126},"https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/626",[],"ended up implementing",", was to\npause the mirroring as soon as a security fix was merged, and re-enable it\nonce the security release was published. This prevented the leak because the\nfetch was no longer happening, but it would \"stop the world\" while we worked\non a security release. The Security mirror quickly fell behind public\ndevelopment, which created a risk of new features causing merge conflicts\nwith the security fixes, or vice versa.",[12,130,131,132,137,138,147],{},"Staff engineer Jacob Vosmaer, who began the Gitaly project within GitLab,\n",[16,133,136],{"href":134,"rel":135},"https://gitlab.com/gitlab-org/gitlab/-/issues/38386#note_312363006",[],"pointed out"," that, strangely, we only used this fetch-based behavior for\nbranches; tags used Git's low-level ",[16,139,142,146],{"href":140,"rel":141},"https://git-scm.com/docs/git-ls-remote.html",[],[143,144,145],"code",{},"ls-remote"," command",".",[12,149,150,151,154,155,157,158,160],{},"Whereas Git's ",[143,152,153],{},"fetch"," command creates a local copy of every object from the\nremote repository, the ",[143,156,145],{}," command only prints the remote's available\nreferences to the terminal. If we used ",[143,159,145],{}," for branches like we did for tags, the commits from\nthe mirror would no longer be persisted on-disk, and thus wouldn't be\navailable in the public repository.",[12,162,163,164,169,170,175,176,178],{},"Because push mirroring is such a critical part of our own workflow as well as\nour users', we didn't want to just make the change and hope for the best. We\n",[16,165,168],{"href":166,"rel":167},"https://gitlab.com/gitlab-org/gitaly/-/issues/2670",[],"set up an experiment",", where the old functionality stayed exactly as it was,\nbut when a ",[16,171,174],{"href":172,"rel":173},"https://docs.gitlab.com/operations/feature_flags/",[],"feature flag"," was enabled, we'd also gather the same commit\ninformation using ",[143,177,145],{},", and compare the new results to the original,\nlogging any differences.",[12,180,181],{},"The experiment ran on GitLab.com for about a month without major discrepancies.\nIt looked like we had a solution!",[29,183,185],{"id":184},"iterating-on-the-experiment","Iterating on the experiment",[12,187,188,189,147],{},"Considering the experiment a success, but still being wary of breaking a key\npiece of functionality, we proceeded with caution. Rather than replacing the old\nbehavior outright with the new, we ",[16,190,193],{"href":191,"rel":192},"https://gitlab.com/gitlab-org/gitaly/-/merge_requests/2183",[],"split the two paths based on a feature\nflag",[12,195,196],{},"When the flag was disabled the old, tried-and-true behavior would be used. With\nthe flag enabled, we'd use the new. We shipped this change and left the flag\nenabled, watching for errors.",[12,198,199],{},"After two weeks without any reported mirroring errors, and with the security\nleak no longer occurring, we were satisfied we had found our fix.",[12,201,202,203,208,209,214],{},"First we shipped a self-managed release ",[16,204,207],{"href":205,"rel":206},"https://gitlab.com/gitlab-org/gitaly/-/merge_requests/2330",[],"with the feature flag enabled by\ndefault",", to ensure that if something unexpectedly broke for those\ninstallations it would be easy to revert to the previous behavior. Finally, after no errors reported from self-managed users, we ",[16,210,213],{"href":211,"rel":212},"https://gitlab.com/gitlab-org/gitaly/-/merge_requests/2417",[],"removed the\nfeature flag along with the old behavior",", and closed out the\nconfidential issue.",[29,216,218],{"id":217},"an-annoying-bug-emerges","An annoying bug emerges",[12,220,221,222,227],{},"Shortly after making the new behavior the default, we started getting\n",[16,223,226],{"href":224,"rel":225},"https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/914",[],"complaints from team members",". They'd receive an automated email\ntelling them that a push mirror was broken, only to go check on the mirror and\nbe told everything was fine.",[12,229,230],{},"This went on for about two months due to the transient nature of the errors.\nEvery time we'd get an email and check to see if it was accurate, the mirroring\nreported everything was fine.",[12,232,233,234,239],{},"As we began to implement ",[16,235,238],{"href":236,"rel":237},"https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/1111",[],"a new piece of tooling"," that depended on\naccurate status reporting from push mirroring, the problem became bigger than a\nfew annoying, seemingly inaccurate emails; it was causing our tooling to behave\nerratically as well.",[12,241,242,243,248,249,254,255,258,259,262],{},"Because we had absolutely no idea what was happening or why, our first step was\nto ",[16,244,247],{"href":245,"rel":246},"https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/914#note_413855603",[],"add logging"," when Gitaly was encountering an error that would mark the\nmirror as failed. The logging ",[16,250,253],{"href":251,"rel":252},"https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/914#note_416246505",[],"revealed a weird anomaly"," where it\nappeared that the Security repository – the one ",[64,256,257],{},"receiving"," updates – appeared\nto be ",[64,260,261],{},"ahead"," of its source:",[264,265,271],"pre",{"className":266,"code":268,"language":269,"meta":270},[267],"language-text","I, [2020-09-21T10:10:31] Divergent ref due to ancestry -- remote:f73bb2388a6, local:59812e04368\nI, [2020-09-21T10:26:39] Divergent ref due to ancestry -- remote:8ddcb3333da, local:f73bb2388a6\n","text","",[143,272,268],{"__ignoreMap":270},[12,274,275,276,279,280,283],{},"In this pair, the first message is saying that the remote – the Security\nrepository – was showing its latest commit as ",[143,277,278],{},"f73bb2388a6",", and that it wasn't\nan ancestor of the local ",[143,281,282],{},"59812e04368"," commit, causing the error message. On the\nnext run, we see that the local repository has \"caught up\" to the Security\nremote from the prior run.",[12,285,286,287,289],{},"It turned out that due to the number of branches and tags in this repository,\nthe ",[143,288,145],{}," command was taking so long to complete that by the time the data\nwas returned, the local repository was updated by a new push.",[12,291,292,293,298,299,304],{},"Because we gathered the remote refs after the local ones, a network delay\ncreated a window for new local commits to be written and invalidate our list\nof local refs. Luckily there was a nice ",[16,294,297],{"href":295,"rel":296},"https://handbook.gitlab.com/handbook/values/#boring-solutions",[],"boring solution",": all we had to do\nwas ",[16,300,303],{"href":301,"rel":302},"https://gitlab.com/gitlab-org/gitaly/-/merge_requests/2606",[],"swap the order"," in which we gather references.",[29,306,308],{"id":307},"wrapping-up","Wrapping up",[12,310,311],{},"As soon as we swapped the order for gathering references, the transient errors\nwent away and we finally got to close this long-standing issue. We were pleased\nwith how we were able to modify such a critical piece of functionality safely\nand without any negative user impact.",[29,313,315],{"id":314},"related-issues","Related issues",[317,318,319,327,334,345],"ul",{},[320,321,322],"li",{},[16,323,326],{"href":324,"rel":325},"https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/426",[],"Security commits available on GitLab.com",[320,328,329],{},[16,330,333],{"href":331,"rel":332},"https://gitlab.com/gitlab-org/gitlab/-/issues/38386",[],"Do not expose GitLab version on GitLab.com",[320,335,336],{},[16,337,339,340,342,343],{"href":166,"rel":338},[],"Populate remote branches in-memory via ",[143,341,145],{}," rather than using ",[143,344,153],{},[320,346,347],{},[16,348,350],{"href":224,"rel":349},[],"Transient push mirror divergence errors",[12,352,353,354,359,360],{},"Photo by ",[16,355,358],{"href":356,"rel":357},"https://unsplash.com/@imattsmart?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText",[],"iMattSmart"," on ",[16,361,364],{"href":362,"rel":363},"https://unsplash.com/s/photos/broken-lock?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText",[],"Unsplash",{"title":270,"searchDepth":366,"depth":366,"links":367},2,[368,369,374,375,376,377,378],{"id":31,"depth":366,"text":32},{"id":78,"depth":366,"text":79,"children":370},[371],{"id":98,"depth":372,"text":373},3,"GitLab Enterprise Edition 13.7.0-pre 690e4bbfe94",{"id":115,"depth":366,"text":116},{"id":184,"depth":366,"text":185},{"id":217,"depth":366,"text":218},{"id":307,"depth":366,"text":308},{"id":314,"depth":366,"text":315},"engineering","2021-01-04","Working in the open makes it difficult to work on security vulnerabilities before they're disclosed, especially when that openness discloses them early!","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667227/Blog/Hero%20Images/security-leaks-unlocked.jpg",{},true,"/en-us/blog/how-we-prevented-security-fixes-leaking-into-our-public-repositories",{"title":5,"description":381,"ogTitle":5,"ogDescription":381,"noIndex":384,"ogImage":385,"ogUrl":390,"ogSiteName":391,"ogType":392,"canonicalUrls":390},"https://about.gitlab.com/blog/how-we-prevented-security-fixes-leaking-into-our-public-repositories","https://about.gitlab.com","article","how-we-prevented-security-fixes-leaking-into-our-public-repositories","en-us/blog/how-we-prevented-security-fixes-leaking-into-our-public-repositories",[396,397],"inside GitLab","security","BlogPost","trvTRjX1Oofvw2x7B5D3JmhIrPyyYUvYs62v_fLuyQ8",{"logo":401,"freeTrial":406,"sales":411,"login":416,"items":421,"search":747,"minimal":778,"duo":797,"switchNav":806,"pricingDeployment":817},{"config":402},{"href":403,"dataGaName":404,"dataGaLocation":405},"/","gitlab logo","header",{"text":407,"config":408},"Get free trial",{"href":409,"dataGaName":410,"dataGaLocation":405},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":412,"config":413},"Request a demo",{"href":414,"dataGaName":415,"dataGaLocation":405},"/sales/?contact-topic=request-demo","sales",{"text":417,"config":418},"Sign in",{"href":419,"dataGaName":420,"dataGaLocation":405},"https://gitlab.com/users/sign_in/","sign in",[422,451,550,555,669,725],{"text":423,"config":424,"menu":426},"Platform",{"dataNavLevelOne":425},"platform",{"type":427,"columns":428},"cards",[429,435,443],{"title":423,"description":430,"link":431},"The intelligent orchestration platform for DevSecOps",{"text":432,"config":433},"Explore our Platform",{"href":434,"dataGaName":425,"dataGaLocation":405},"/platform/",{"title":436,"description":437,"link":438},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":439,"config":440},"Meet GitLab Duo",{"href":441,"dataGaName":442,"dataGaLocation":405},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":444,"description":445,"link":446},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":447,"config":448},"Learn more",{"href":449,"dataGaName":450,"dataGaLocation":405},"/why-gitlab/","why gitlab",{"text":452,"left":387,"config":453,"menu":455},"Product",{"dataNavLevelOne":454},"solutions",{"type":456,"link":457,"columns":461,"feature":529},"lists",{"text":458,"config":459},"View all Solutions",{"href":460,"dataGaName":454,"dataGaLocation":405},"/solutions/",[462,485,508],{"title":463,"description":464,"link":465,"items":470},"Automation","CI/CD and automation to accelerate deployment",{"config":466},{"icon":467,"href":468,"dataGaName":469,"dataGaLocation":405},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[471,475,478,481],{"text":472,"config":473},"CI/CD",{"href":474,"dataGaLocation":405,"dataGaName":472},"/solutions/continuous-integration/",{"text":436,"config":476},{"href":441,"dataGaLocation":405,"dataGaName":477},"gitlab duo agent platform - product menu",{"text":479,"config":480},"Source Code Management",{"href":110,"dataGaLocation":405,"dataGaName":479},{"text":482,"config":483},"Automated Software Delivery",{"href":468,"dataGaLocation":405,"dataGaName":484},"Automated software delivery",{"title":486,"description":487,"link":488,"items":493},"Security","Deliver code faster without compromising security",{"config":489},{"href":490,"dataGaName":491,"dataGaLocation":405,"icon":492},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[494,498,503],{"text":495,"config":496},"Application Security Testing",{"href":490,"dataGaName":497,"dataGaLocation":405},"Application security testing",{"text":499,"config":500},"Software Supply Chain Security",{"href":501,"dataGaLocation":405,"dataGaName":502},"/solutions/supply-chain/","Software supply chain security",{"text":504,"config":505},"Software Compliance",{"href":506,"dataGaName":507,"dataGaLocation":405},"/solutions/software-compliance/","software compliance",{"title":509,"link":510,"items":515},"Measurement",{"config":511},{"icon":512,"href":513,"dataGaName":514,"dataGaLocation":405},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[516,520,524],{"text":517,"config":518},"Visibility & Measurement",{"href":513,"dataGaLocation":405,"dataGaName":519},"Visibility and Measurement",{"text":521,"config":522},"Value Stream Management",{"href":523,"dataGaLocation":405,"dataGaName":521},"/solutions/value-stream-management/",{"text":525,"config":526},"Analytics & Insights",{"href":527,"dataGaLocation":405,"dataGaName":528},"/solutions/analytics-and-insights/","Analytics and insights",{"title":530,"type":456,"items":531},"GitLab for",[532,538,544],{"text":533,"config":534},"Enterprise",{"icon":535,"href":536,"dataGaLocation":405,"dataGaName":537},"Building","/enterprise/","enterprise",{"text":539,"config":540},"Small Business",{"icon":541,"href":542,"dataGaLocation":405,"dataGaName":543},"Work","/small-business/","small business",{"text":545,"config":546},"Public Sector",{"icon":547,"href":548,"dataGaLocation":405,"dataGaName":549},"Organization","/solutions/public-sector/","public sector",{"text":551,"config":552},"Pricing",{"href":553,"dataGaName":554,"dataGaLocation":405,"dataNavLevelOne":554},"/pricing/","pricing",{"text":556,"config":557,"menu":559},"Resources",{"dataNavLevelOne":558},"resources",{"type":456,"link":560,"columns":564,"feature":658},{"text":561,"config":562},"View all resources",{"href":563,"dataGaName":558,"dataGaLocation":405},"/resources/",[565,598,625],{"title":566,"items":567},"Getting started",[568,573,578,583,588,593],{"text":569,"config":570},"Install",{"href":571,"dataGaName":572,"dataGaLocation":405},"/install/","install",{"text":574,"config":575},"Quick start guides",{"href":576,"dataGaName":577,"dataGaLocation":405},"/get-started/","quick setup checklists",{"text":579,"config":580},"Learn",{"href":581,"dataGaLocation":405,"dataGaName":582},"https://university.gitlab.com/","learn",{"text":584,"config":585},"Product documentation",{"href":586,"dataGaName":587,"dataGaLocation":405},"https://docs.gitlab.com/","product documentation",{"text":589,"config":590},"Best practice videos",{"href":591,"dataGaName":592,"dataGaLocation":405},"/getting-started-videos/","best practice videos",{"text":594,"config":595},"Integrations",{"href":596,"dataGaName":597,"dataGaLocation":405},"/integrations/","integrations",{"title":599,"items":600},"Discover",[601,606,611,616,620],{"text":602,"config":603},"Customer success stories",{"href":604,"dataGaName":605,"dataGaLocation":405},"/customers/","customer success stories",{"text":607,"config":608},"Blog",{"href":609,"dataGaName":610,"dataGaLocation":405},"/blog/","blog",{"text":612,"config":613},"Demo Hub",{"href":614,"dataGaName":615,"dataGaLocation":405},"/demo-hub/","demo hub",{"text":617,"config":618},"The Source",{"href":619,"dataGaName":610,"dataGaLocation":405},"/the-source/",{"text":621,"config":622},"Remote",{"href":623,"dataGaName":624,"dataGaLocation":405},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":626,"items":627},"Connect",[628,633,638,643,648,653],{"text":629,"config":630},"GitLab Services",{"href":631,"dataGaName":632,"dataGaLocation":405},"/services/","services",{"text":634,"config":635},"Contribute",{"href":636,"dataGaName":637,"dataGaLocation":405},"https://contributors.gitlab.com","contribute",{"text":639,"config":640},"Community",{"href":641,"dataGaName":642,"dataGaLocation":405},"/community/","community",{"text":644,"config":645},"Forum",{"href":646,"dataGaName":647,"dataGaLocation":405},"https://forum.gitlab.com/","forum",{"text":649,"config":650},"Events",{"href":651,"dataGaName":652,"dataGaLocation":405},"/events/","events",{"text":654,"config":655},"Partners",{"href":656,"dataGaName":657,"dataGaLocation":405},"/partners/","partners",{"config":659,"title":662,"text":663,"link":664},{"background":660,"textColor":661},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":665,"config":666},"Read the latest",{"href":667,"dataGaName":668,"dataGaLocation":405},"/whats-new/","whats new",{"text":670,"config":671,"menu":673},"Company",{"dataNavLevelOne":672},"company",{"type":456,"columns":674},[675],{"items":676},[677,682,688,690,695,700,705,710,715,720],{"text":678,"config":679},"About",{"href":680,"dataGaName":681,"dataGaLocation":405},"/company/","about",{"text":683,"config":684,"footerGa":687},"Jobs",{"href":685,"dataGaName":686,"dataGaLocation":405},"/jobs/","jobs",{"dataGaName":686},{"text":649,"config":689},{"href":651,"dataGaName":652,"dataGaLocation":405},{"text":691,"config":692},"Leadership",{"href":693,"dataGaName":694,"dataGaLocation":405},"/company/team/e-group/","leadership",{"text":696,"config":697},"Handbook",{"href":698,"dataGaName":699,"dataGaLocation":405},"https://handbook.gitlab.com/","handbook",{"text":701,"config":702},"Investor relations",{"href":703,"dataGaName":704,"dataGaLocation":405},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":706,"config":707},"Trust Center",{"href":708,"dataGaName":709,"dataGaLocation":405},"/security/","trust center",{"text":711,"config":712},"AI Transparency Center",{"href":713,"dataGaName":714,"dataGaLocation":405},"/ai-transparency-center/","ai transparency center",{"text":716,"config":717},"Newsletter",{"href":718,"dataGaName":719,"dataGaLocation":405},"/company/contact/#contact-forms","newsletter",{"text":721,"config":722},"Press",{"href":723,"dataGaName":724,"dataGaLocation":405},"/press/","press",{"text":726,"config":727,"menu":728},"Contact us",{"dataNavLevelOne":672},{"type":456,"columns":729},[730],{"items":731},[732,737,742],{"text":733,"config":734},"Talk to sales",{"href":735,"dataGaName":736,"dataGaLocation":405},"/sales/","talk to sales",{"text":738,"config":739},"Support portal",{"href":740,"dataGaName":741,"dataGaLocation":405},"https://support.gitlab.com/hc/en-us","support portal",{"text":743,"config":744},"Customer portal",{"href":745,"dataGaName":746,"dataGaLocation":405},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":748,"login":749,"suggestions":756},"Close",{"text":750,"link":751},"To search repositories and projects, login to",{"text":752,"config":753},"gitlab.com",{"href":419,"dataGaName":754,"dataGaLocation":755},"search login","search",{"text":757,"default":758},"Suggestions",[759,761,765,767,771,775],{"text":436,"config":760},{"href":441,"dataGaName":436,"dataGaLocation":755},{"text":762,"config":763},"Code Suggestions (AI)",{"href":764,"dataGaName":762,"dataGaLocation":755},"/solutions/code-suggestions/",{"text":472,"config":766},{"href":474,"dataGaName":472,"dataGaLocation":755},{"text":768,"config":769},"GitLab on AWS",{"href":770,"dataGaName":768,"dataGaLocation":755},"/partners/technology-partners/aws/",{"text":772,"config":773},"GitLab on Google Cloud",{"href":774,"dataGaName":772,"dataGaLocation":755},"/partners/technology-partners/google-cloud-platform/",{"text":776,"config":777},"Why GitLab?",{"href":449,"dataGaName":776,"dataGaLocation":755},{"freeTrial":779,"mobileIcon":784,"desktopIcon":789,"secondaryButton":792},{"text":780,"config":781},"Start free trial",{"href":782,"dataGaName":410,"dataGaLocation":783},"https://gitlab.com/-/trials/new/","nav",{"altText":785,"config":786},"Gitlab Icon",{"src":787,"dataGaName":788,"dataGaLocation":783},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":785,"config":790},{"src":791,"dataGaName":788,"dataGaLocation":783},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":793,"config":794},"Get Started",{"href":795,"dataGaName":796,"dataGaLocation":783},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":798,"mobileIcon":802,"desktopIcon":804},{"text":799,"config":800},"Learn more about GitLab Duo",{"href":441,"dataGaName":801,"dataGaLocation":783},"gitlab duo",{"altText":785,"config":803},{"src":787,"dataGaName":788,"dataGaLocation":783},{"altText":785,"config":805},{"src":791,"dataGaName":788,"dataGaLocation":783},{"button":807,"mobileIcon":812,"desktopIcon":814},{"text":808,"config":809},"/switch",{"href":810,"dataGaName":811,"dataGaLocation":783},"#contact","switch",{"altText":785,"config":813},{"src":787,"dataGaName":788,"dataGaLocation":783},{"altText":785,"config":815},{"src":816,"dataGaName":788,"dataGaLocation":783},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":818,"mobileIcon":823,"desktopIcon":825},{"text":819,"config":820},"Back to pricing",{"href":553,"dataGaName":821,"dataGaLocation":783,"icon":822},"back to pricing","GoBack",{"altText":785,"config":824},{"src":787,"dataGaName":788,"dataGaLocation":783},{"altText":785,"config":826},{"src":791,"dataGaName":788,"dataGaLocation":783},{"title":828,"titleMobile":829,"button":830,"config":835},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":447,"config":831},{"href":832,"dataGaName":833,"dataGaLocation":834},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":836,"disabled":384},"release",{"data":838},{"text":839,"source":840,"edit":846,"contribute":851,"config":856,"items":861,"minimal":1071},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":841,"config":842},"View page source",{"href":843,"dataGaName":844,"dataGaLocation":845},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":847,"config":848},"Edit this page",{"href":849,"dataGaName":850,"dataGaLocation":845},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":852,"config":853},"Please contribute",{"href":854,"dataGaName":855,"dataGaLocation":845},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":857,"facebook":858,"youtube":859,"linkedin":860},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[862,909,963,1007,1039],{"title":551,"links":863,"subMenu":878},[864,868,873],{"text":865,"config":866},"View plans",{"href":553,"dataGaName":867,"dataGaLocation":845},"view plans",{"text":869,"config":870},"Why Premium?",{"href":871,"dataGaName":872,"dataGaLocation":845},"/pricing/premium/","why premium",{"text":874,"config":875},"Why Ultimate?",{"href":876,"dataGaName":877,"dataGaLocation":845},"/pricing/ultimate/","why ultimate",[879],{"title":880,"links":881},"Contact Us",[882,885,887,889,894,899,904],{"text":883,"config":884},"Contact sales",{"href":735,"dataGaName":415,"dataGaLocation":845},{"text":738,"config":886},{"href":740,"dataGaName":741,"dataGaLocation":845},{"text":743,"config":888},{"href":745,"dataGaName":746,"dataGaLocation":845},{"text":890,"config":891},"Status",{"href":892,"dataGaName":893,"dataGaLocation":845},"https://status.gitlab.com/","status",{"text":895,"config":896},"Terms of use",{"href":897,"dataGaName":898,"dataGaLocation":845},"/terms/","terms of use",{"text":900,"config":901},"Privacy statement",{"href":902,"dataGaName":903,"dataGaLocation":845},"/privacy/","privacy statement",{"text":905,"config":906},"Cookie preferences",{"dataGaName":907,"dataGaLocation":845,"id":908,"isOneTrustButton":387},"cookie preferences","ot-sdk-btn",{"title":452,"links":910,"subMenu":919},[911,915],{"text":912,"config":913},"DevSecOps platform",{"href":434,"dataGaName":914,"dataGaLocation":845},"devsecops platform",{"text":916,"config":917},"AI-Assisted Development",{"href":441,"dataGaName":918,"dataGaLocation":845},"ai-assisted development",[920],{"title":921,"links":922},"Topics",[923,928,933,938,943,948,953,958],{"text":924,"config":925},"CICD",{"href":926,"dataGaName":927,"dataGaLocation":845},"/topics/ci-cd/","cicd",{"text":929,"config":930},"GitOps",{"href":931,"dataGaName":932,"dataGaLocation":845},"/topics/gitops/","gitops",{"text":934,"config":935},"DevOps",{"href":936,"dataGaName":937,"dataGaLocation":845},"/topics/devops/","devops",{"text":939,"config":940},"Version Control",{"href":941,"dataGaName":942,"dataGaLocation":845},"/topics/version-control/","version control",{"text":944,"config":945},"DevSecOps",{"href":946,"dataGaName":947,"dataGaLocation":845},"/topics/devsecops/","devsecops",{"text":949,"config":950},"Cloud Native",{"href":951,"dataGaName":952,"dataGaLocation":845},"/topics/cloud-native/","cloud native",{"text":954,"config":955},"AI for Coding",{"href":956,"dataGaName":957,"dataGaLocation":845},"/topics/devops/ai-for-coding/","ai for coding",{"text":959,"config":960},"Agentic AI",{"href":961,"dataGaName":962,"dataGaLocation":845},"/topics/agentic-ai/","agentic ai",{"title":964,"links":965},"Solutions",[966,968,970,975,979,982,986,989,991,994,997,1002],{"text":495,"config":967},{"href":490,"dataGaName":495,"dataGaLocation":845},{"text":484,"config":969},{"href":468,"dataGaName":469,"dataGaLocation":845},{"text":971,"config":972},"Agile development",{"href":973,"dataGaName":974,"dataGaLocation":845},"/solutions/agile-delivery/","agile delivery",{"text":976,"config":977},"SCM",{"href":110,"dataGaName":978,"dataGaLocation":845},"source code management",{"text":924,"config":980},{"href":474,"dataGaName":981,"dataGaLocation":845},"continuous integration & delivery",{"text":983,"config":984},"Value stream management",{"href":523,"dataGaName":985,"dataGaLocation":845},"value stream management",{"text":929,"config":987},{"href":988,"dataGaName":932,"dataGaLocation":845},"/solutions/gitops/",{"text":533,"config":990},{"href":536,"dataGaName":537,"dataGaLocation":845},{"text":992,"config":993},"Small business",{"href":542,"dataGaName":543,"dataGaLocation":845},{"text":995,"config":996},"Public sector",{"href":548,"dataGaName":549,"dataGaLocation":845},{"text":998,"config":999},"Education",{"href":1000,"dataGaName":1001,"dataGaLocation":845},"/solutions/education/","education",{"text":1003,"config":1004},"Financial services",{"href":1005,"dataGaName":1006,"dataGaLocation":845},"/solutions/finance/","financial services",{"title":556,"links":1008},[1009,1011,1013,1015,1018,1020,1023,1025,1027,1029,1031,1033,1035,1037],{"text":569,"config":1010},{"href":571,"dataGaName":572,"dataGaLocation":845},{"text":574,"config":1012},{"href":576,"dataGaName":577,"dataGaLocation":845},{"text":579,"config":1014},{"href":581,"dataGaName":582,"dataGaLocation":845},{"text":584,"config":1016},{"href":586,"dataGaName":1017,"dataGaLocation":845},"docs",{"text":607,"config":1019},{"href":609,"dataGaName":610,"dataGaLocation":845},{"text":1021,"config":1022},"What's new",{"href":667,"dataGaName":668,"dataGaLocation":845},{"text":602,"config":1024},{"href":604,"dataGaName":605,"dataGaLocation":845},{"text":621,"config":1026},{"href":623,"dataGaName":624,"dataGaLocation":845},{"text":629,"config":1028},{"href":631,"dataGaName":632,"dataGaLocation":845},{"text":634,"config":1030},{"href":636,"dataGaName":637,"dataGaLocation":845},{"text":639,"config":1032},{"href":641,"dataGaName":642,"dataGaLocation":845},{"text":644,"config":1034},{"href":646,"dataGaName":647,"dataGaLocation":845},{"text":649,"config":1036},{"href":651,"dataGaName":652,"dataGaLocation":845},{"text":654,"config":1038},{"href":656,"dataGaName":657,"dataGaLocation":845},{"title":670,"links":1040},[1041,1043,1045,1047,1049,1051,1055,1060,1062,1064,1066],{"text":678,"config":1042},{"href":680,"dataGaName":672,"dataGaLocation":845},{"text":683,"config":1044},{"href":685,"dataGaName":686,"dataGaLocation":845},{"text":691,"config":1046},{"href":693,"dataGaName":694,"dataGaLocation":845},{"text":696,"config":1048},{"href":698,"dataGaName":699,"dataGaLocation":845},{"text":701,"config":1050},{"href":703,"dataGaName":704,"dataGaLocation":845},{"text":1052,"config":1053},"Sustainability",{"href":1054,"dataGaName":1052,"dataGaLocation":845},"/sustainability/",{"text":1056,"config":1057},"Diversity, inclusion and belonging (DIB)",{"href":1058,"dataGaName":1059,"dataGaLocation":845},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":706,"config":1061},{"href":708,"dataGaName":709,"dataGaLocation":845},{"text":716,"config":1063},{"href":718,"dataGaName":719,"dataGaLocation":845},{"text":721,"config":1065},{"href":723,"dataGaName":724,"dataGaLocation":845},{"text":1067,"config":1068},"Modern Slavery Transparency Statement",{"href":1069,"dataGaName":1070,"dataGaLocation":845},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1072},[1073,1076,1079],{"text":1074,"config":1075},"Terms",{"href":897,"dataGaName":898,"dataGaLocation":845},{"text":1077,"config":1078},"Cookies",{"dataGaName":907,"dataGaLocation":845,"id":908,"isOneTrustButton":387},{"text":1080,"config":1081},"Privacy",{"href":902,"dataGaName":903,"dataGaLocation":845},[1083],{"id":1084,"title":7,"body":383,"config":1085,"content":1087,"description":383,"extension":1090,"meta":1091,"navigation":387,"path":1092,"seo":1093,"stem":1094,"__hash__":1095},"blogAuthors/en-us/blog/authors/robert-speicher.yml",{"template":1086},"BlogAuthor",{"name":7,"config":1088},{"headshot":270,"ctfId":1089},"rspeicher","yml",{},"/en-us/blog/authors/robert-speicher",{},"en-us/blog/authors/robert-speicher","7DoEEXTBZFobqYAqIhMGZY_r6Uej_MvOCTH1SlHhdAw",[1097,1106,1114],{"title":1098,"description":1099,"heroImage":1100,"category":379,"date":1101,"authors":1102,"slug":1105,"externalUrl":383},"Confidential AI for GitLab Self-Hosted","Give developers AI coding agents in GitLab Duo without source code leaving a hardware-encrypted boundary — no GPUs needed.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773866173/vte9qh8rriznvyclhkes.png","2026-08-06",[1103,1104],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":1107,"description":1108,"heroImage":1109,"category":379,"date":1110,"authors":1111,"slug":1113,"externalUrl":383},"Green DevOps: Why carbon measurement belongs in your CI/CD pipeline","CI/CD pipelines have a hidden carbon cost. Here's why measuring it matters, and how you can get started with Eco CI and Carmen in GitLab.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1765809212/noh0mdfn9o94ry9ykura.png","2026-07-09",[1112],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":1115,"description":1116,"heroImage":1117,"category":379,"date":1118,"authors":1119,"slug":1121,"externalUrl":383},"How to build CI/CD observability at scale","This practical guide to GitLab pipeline analytics helps self-managed users gain operational insights using Prometheus and Grafana.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-04-28",[1120],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":1123},[1124,1138,1150,1161],{"id":1125,"categories":1126,"header":1128,"text":1129,"button":1130,"image":1135},"ai-modernization",[1127],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1131,"config":1132},"Get your AI maturity score",{"href":1133,"dataGaName":1134,"dataGaLocation":610},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1136},{"src":1137},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1139,"categories":1140,"header":1142,"text":1129,"button":1143,"image":1147},"devops-modernization",[1141,947],"product","Are you just managing tools or shipping innovation?",{"text":1144,"config":1145},"Get your DevOps maturity score",{"href":1146,"dataGaName":1134,"dataGaLocation":610},"/assessments/devops-modernization-assessment/",{"config":1148},{"src":1149},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1151,"categories":1152,"header":1153,"text":1129,"button":1154,"image":1158},"security-modernization",[397],"Are you trading speed for security?",{"text":1155,"config":1156},"Get your security maturity score",{"href":1157,"dataGaName":1134,"dataGaLocation":610},"/assessments/security-modernization-assessment/",{"config":1159},{"src":1160},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1162,"paths":1163,"header":1166,"text":1167,"button":1168,"image":1173},"github-azure-migration",[1164,1165],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":1169,"config":1170},"See how GitLab compares to GitHub",{"href":1171,"dataGaName":1172,"dataGaLocation":610},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1174},{"src":1149},{"header":1176,"blurb":1177,"button":1178,"secondaryButton":1183},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1179,"config":1180},"Get your free trial",{"href":1181,"dataGaName":410,"dataGaLocation":1182},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":883,"config":1184},{"href":735,"dataGaName":415,"dataGaLocation":1182},1786803752122]