[{"data":1,"prerenderedAt":718},["ShallowReactive",2],{"/en-us/blog/building-gitlab-with-gitlab-api-fuzzing-workflow/":3,"navigation-en-us":38,"banner-en-us":455,"footer-en-us":470,"Mike Eddington-Eugene Lim":681,"next-steps-en-us":703},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":28,"_id":31,"_type":32,"title":33,"_source":34,"_file":35,"_stem":36,"_extension":37},"/en-us/blog/building-gitlab-with-gitlab-api-fuzzing-workflow","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"Building GitLab with GitLab: Web API Fuzz Testing","Our new series shows how we dogfood new DevSecOps platform features to ready them for you. First up, security testing.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659740/Blog/Hero%20Images/building-gitlab-with-gitlab-no-type.png","https://about.gitlab.com/blog/building-gitlab-with-gitlab-api-fuzzing-workflow","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Building GitLab with GitLab: Web API Fuzz Testing\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Mike Eddington\"},{\"@type\":\"Person\",\"name\":\"Eugene Lim\"}],\n        \"datePublished\": \"2023-05-09\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":20,"body":21,"category":22,"tags":23},[18,19],"Mike Eddington","Eugene Lim","2023-05-09","\n\nAt GitLab, we try to [dogfood everything](/handbook/product/product-processes/#dogfood-everything) to help us better understand the product, pain points, and configuration issues. We use what we learn to build a more efficient, feature-rich platform and user experience. In this first installment of our “Building GitLab with GitLab” series, we will focus on security testing. We constantly strive to improve our security testing coverage and integrate it into our DevSecOps lifecycle. These considerations formed the motivation for the API fuzzing dogfooding project at GitLab. By sharing our lessons from building this workflow, we hope other teams can also learn how to integrate GitLab’s Web API Fuzz Testing and solve some common challenges.\n\n## What is Web API Fuzz Testing?\n\nWeb API Fuzz Testing involves generating and sending various unexpected input parameters to a web API in an attempt to trigger unexpected behavior and errors in the API backend. By analyzing these errors, you can discover bugs and potential security issues missed by other scanners that focus on specific vulnerabilities. GitLab's Web API Fuzz Testing complements and should be run in addition to GitLab Secure’s other security scanners such as static application security testing ([SAST](https://docs.gitlab.com/ee/user/application_security/sast/)) and dynamic application security testing ([DAST](https://docs.gitlab.com/ee/user/application_security/dast/)) APIs.\n\n## Auto-generating an OpenAPI specification\nTo run the Web API Fuzzing Analyzer, you need one of the following:\n* OpenAPI Specification - Version 2 or 3\n* GraphQL Schema\n* HTTP Archive (HAR)\n* Postman Collection - Version 2.0 or 2.1\n\nAt the start of the API fuzzing project, the [API Vision working group](/company/team/structure/working-groups/api-vision/) was also working on an issue to automatically document [GitLab’s REST API endpoints in an OpenAPI specification](https://gitlab.com/groups/gitlab-org/-/epics/8636), so we worked with our colleague Andy Soiron on implementing it. Because GitLab uses the [grape](https://github.com/ruby-grape/grape) API framework, Andy had already identified and [tested](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95877) the [grape-swagger](https://github.com/ruby-grape/grape-swagger) gem that auto-generates an OpenAPI v2 specification based on existing grape annotations. For example, the following API endpoint code:\n\n```\n     Class.new(Grape::API) do\n       format :json\n       desc 'This gets something.'\n       get '/something' do\n         { bla: 'something' }\n       end\n       add_swagger_documentation\n     end\n``` \nWill be parsed by grape-swagger into:\n\n```\n{\n  // rest of OpenAPI v2 specification\n  …\n  \"paths\": {\n    \"/something\": {\n      \"get\": {\n        \"description\": \"This gets something.\",\n        \"produces\": [\n          \"application/json\"\n        ],\n        \"operationId\": \"getSomething\",\n        \"responses\": {\n          \"200\": {\n            \"description\": \"This gets something.\"\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n\nHowever, with almost 2,000 API operations with different requirements and formats, a lot of additional work needed to be done to resolve edge cases that did not meet the requirements of grape-swagger or the OpenAPI format. For example, one simple case was API endpoints that accept file parameters, such as the [upload metric image endpoint](https://docs.gitlab.com/ee/api/issues.html#upload-metric-image). GitLab uses the [Workhorse](https://gitlab.com/gitlab-org/gitlab/tree/master/workhorse) smart reverse proxy to handle \"large\" HTTP requests such as file uploads. As such, file parameters must be of the type WorkhorseFile:\n\n\n```\nnamespace ':id/issues/:issue_iid/metric_images' do\n            …\n            desc 'Upload a metric image for an issue' do\n              success Entities::IssuableMetricImage\n            end\n            params do\n              requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The image file to be uploaded'\n              optional :url, type: String, desc: 'The url to view more metric info'\n              optional :url_text, type: String, desc: 'A description of the image or URL'\n            end\n            post do\n              require_gitlab_workhorse!\n```\n\nBecause grape-swagger does not recognize what OpenAPI type WorkhorseFile corresponds to, it excludes the parameter from its output. We fixed this by adding a grape-swagger-specific documentation to override the type during generation:\n\n```\n             requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The image file to be uploaded', documentation: { type: 'file' }\n```\n\nHowever, not all edge cases could be resolved with a simple match-and-replace in the grape annotations. For example, Ruby on Rails supports wildcard segment parameters. A route like `get 'books/*section/:title'` would match`books/some/section/last-words-a-memoir`. In addition, the URI would be parsed such that the `section` path parameter would have the value `some/section` and the `title` path parameter would have the value `last-words-a-memoir`.\n\nCurrently, grape-swagger does not recognize these wildcard segments as path parameters. For example, the route would generate:\n\n```\n\"paths\": {\n  \"/api/v2/books/*section/{title}\": {\n    \"get\": {\n    ...\n      \"parameters\": [\n         {\n           \"in\": \"query\", \"name\": \"*section\"\n           ...\n  }\n}\n```\n\nInstead of the expected:\n\n```\n\"paths\": {\n  \"/api/v2/books/{section}/{title}\": {\n    \"get\": {\n    ...\n      \"parameters\": [\n         {\n           \"in\": \"path\", \"name\": \"section\"\n           ...\n  }\n}\n```\n\nAs such, we also needed to make several patches to grape-swagger, which we forked while waiting for the changes to be accepted upstream. Nevertheless, with lots of careful checking and cooperation across teams, we managed to get the OpenAPI specification generated for most of the endpoints.\n\n## Performance tuning\n\nWith the OpenAPI specification, we could now begin with the API fuzzing. GitLab already uses the [Review Apps](https://docs.gitlab.com/ee/ci/review_apps/) feature to generate testing environments for some feature changes, providing a readily available fuzzing target. However, given the large number of endpoints, it would be impossible to expect a standard shared runner to complete fuzzing in a single job. The Web API Fuzz Testing documentation includes a [performance tuning section](https://docs.gitlab.com/ee/user/application_security/api_fuzzing/#performance-tuning-and-testing-speed) that recommends the following:\n\n* using a multi-CPU Runner\n* excluding slow operations\n* splitting a test into multiple jobs\n* excluding operations in feature branches, but not default branch\n\nThe first recommendation was easy to implement with a dedicated fuzzing runner. We recommend doing this for large scheduled fuzzing workflows, especially if you select the Long-100 fuzzing profile. We also began excluding slow operations by checking the job logs for the time taken to complete each operation. Along the way, we identified other endpoints that needed to be excluded, such as the [revoke token endpoint](https://docs.gitlab.com/ee/api/personal_access_tokens.html#revoke-a-personal-access-token) that prematurely ended the fuzzing session.\n\nSplitting the test into multiple jobs took the most effort due to the requirements of the OpenAPI format. Each OpenAPI document includes a required set of objects and fields, so it is not simply a matter of splitting after a fixed number of lines. Additionally, each operation relies on entities defined in the definitions object, so we needed to ensure that when splitting the OpenAPI specification, the entities required by the endpoints were included. We also wrote a quick script to fill the example parameter data with actual data from the testing environment, such as project IDs.\n\nWhile it was possible to run these scripts locally, then push the split jobs and OpenAPI specifications to the repository, this created a large number of changes every time we updated the original OpenAPI specification. Instead, we adapted the workflow to use dynamically generated child pipelines that would split the OpenAPI document in a CI job, then generate a child pipeline with jobs for each split document. This made iterating a lot easier and more agile. We have uploaded [the scripts and pipeline configuration](https://gitlab.com/eugene_lim/api-fuzzing-dogfooding) for reference.\n\nBy tweaking the number of parallel jobs and fuzzing profile, we were eventually able to achieve a reasonably comprehensive fuzzing session in an acceptable time frame. When tuning your own fuzzing workflow, balancing these trade-offs is essential.\n\n## Triaging the API fuzzing findings\n\nWith the fuzzing done, we were now confronted with hundreds of findings. Unlike DAST analyzers that try to detect specific vulnerabilities, Web API Fuzz Testing looks for unexpected behavior and errors that may not necessarily be vulnerabilities. This is why fuzzing faults discovered by the API Fuzzing Analyzer show up as vulnerabilities with a severity of “Unknown.” This requires more involved triaging.\n\nFortunately, the Web API fuzzer also outputs Postman collections as artifacts in the Vulnerability Report page. These collections allow you to quickly repeat requests that triggered a fault during fuzzing. For this stage of the fuzzing workflow, we recommend that you set up a local instance of the application so that you can easily check logs and debug specific faults. In this case, we ran the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).\n\nMany of the faults occurred due to a lack of error handling for unexpected inputs. We created issues from the Vulnerability Report page, and if we found that a particular fault had the same root cause as a previously triaged fault, we linked the vulnerability to the original issue instead.\n\n## Lessons learned\n\nThe API fuzzing dogfooding project turned out to be a fruitful exercise that benefited other workstreams at GitLab, such as the API documentation project. In addition, tuning and triaging helped us identify key pain points in the process for improvement. Automated API documentation generation is difficult even with OpenAPI, particularly on a long-lived codebase. GitLab’s existing annotations and tests helped speed up documentation via a distributed, asynchronous workflow across multiple teams. In addition, many GitLab features such as Review Apps, Vulnerability Reports, and dynamically generated child pipelines helped us build a robust fuzzing workflow.\n\nThere are still many improvements that can be made to the workflow. Moving to OpenAPI v3 could improve endpoint coverage. The Secure team also wrote a [HAR Recorder](https://gitlab.com/gitlab-org/security-products/har-recorder) tool that could help generate HAR files on the fly instead of relying on static documentation. For now, due to the high compute cost of fuzzing thousands of operations in GitLab’s API, the workflow is better suited to a scheduled pipeline instead of GitLab’s core pipeline.\n\nFor teams that have already implemented several layers of static and dynamic checks and want to take further steps to increase coverage, we recommend trying a Web API fuzzing exercise as a way to validate assumptions and discover “unknown unknowns” in your code.\n\nWe encourage you to get familiar with API fuzzing and let us know how it works for you. If you face any issues or have any feedback, please file an issue at the [issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/). Use the `~\"Category:API Security\"` label when opening a new issue regarding API fuzzing to ensure it is quickly reviewed by the appropriate team members.\n","engineering",[24,25,24,26,27],"inside GitLab","security","testing","tutorial",{"slug":29,"featured":6,"template":30},"building-gitlab-with-gitlab-api-fuzzing-workflow","BlogPost","content:en-us:blog:building-gitlab-with-gitlab-api-fuzzing-workflow.yml","yaml","Building Gitlab With Gitlab Api Fuzzing Workflow","content","en-us/blog/building-gitlab-with-gitlab-api-fuzzing-workflow.yml","en-us/blog/building-gitlab-with-gitlab-api-fuzzing-workflow","yml",{"_path":39,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"data":41,"_id":451,"_type":32,"title":452,"_source":34,"_file":453,"_stem":454,"_extension":37},"/shared/en-us/main-navigation","en-us",{"logo":42,"freeTrial":47,"sales":52,"login":57,"items":62,"search":392,"minimal":423,"duo":442},{"config":43},{"href":44,"dataGaName":45,"dataGaLocation":46},"/","gitlab logo","header",{"text":48,"config":49},"Get free trial",{"href":50,"dataGaName":51,"dataGaLocation":46},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":53,"config":54},"Talk to sales",{"href":55,"dataGaName":56,"dataGaLocation":46},"/sales/","sales",{"text":58,"config":59},"Sign in",{"href":60,"dataGaName":61,"dataGaLocation":46},"https://gitlab.com/users/sign_in/","sign in",[63,107,203,208,313,373],{"text":64,"config":65,"cards":67,"footer":90},"Platform",{"dataNavLevelOne":66},"platform",[68,74,82],{"title":64,"description":69,"link":70},"The most comprehensive AI-powered DevSecOps Platform",{"text":71,"config":72},"Explore our Platform",{"href":73,"dataGaName":66,"dataGaLocation":46},"/platform/",{"title":75,"description":76,"link":77},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":78,"config":79},"Meet GitLab Duo",{"href":80,"dataGaName":81,"dataGaLocation":46},"/gitlab-duo/","gitlab duo ai",{"title":83,"description":84,"link":85},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":86,"config":87},"Learn more",{"href":88,"dataGaName":89,"dataGaLocation":46},"/why-gitlab/","why gitlab",{"title":91,"items":92},"Get started with",[93,98,103],{"text":94,"config":95},"Platform Engineering",{"href":96,"dataGaName":97,"dataGaLocation":46},"/solutions/platform-engineering/","platform engineering",{"text":99,"config":100},"Developer Experience",{"href":101,"dataGaName":102,"dataGaLocation":46},"/developer-experience/","Developer experience",{"text":104,"config":105},"MLOps",{"href":106,"dataGaName":104,"dataGaLocation":46},"/topics/devops/the-role-of-ai-in-devops/",{"text":108,"left":109,"config":110,"link":112,"lists":116,"footer":185},"Product",true,{"dataNavLevelOne":111},"solutions",{"text":113,"config":114},"View all Solutions",{"href":115,"dataGaName":111,"dataGaLocation":46},"/solutions/",[117,142,164],{"title":118,"description":119,"link":120,"items":125},"Automation","CI/CD and automation to accelerate deployment",{"config":121},{"icon":122,"href":123,"dataGaName":124,"dataGaLocation":46},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[126,130,134,138],{"text":127,"config":128},"CI/CD",{"href":129,"dataGaLocation":46,"dataGaName":127},"/solutions/continuous-integration/",{"text":131,"config":132},"AI-Assisted Development",{"href":80,"dataGaLocation":46,"dataGaName":133},"AI assisted development",{"text":135,"config":136},"Source Code Management",{"href":137,"dataGaLocation":46,"dataGaName":135},"/solutions/source-code-management/",{"text":139,"config":140},"Automated Software Delivery",{"href":123,"dataGaLocation":46,"dataGaName":141},"Automated software delivery",{"title":143,"description":144,"link":145,"items":150},"Security","Deliver code faster without compromising security",{"config":146},{"href":147,"dataGaName":148,"dataGaLocation":46,"icon":149},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[151,154,159],{"text":152,"config":153},"Security & Compliance",{"href":147,"dataGaLocation":46,"dataGaName":152},{"text":155,"config":156},"Software Supply Chain Security",{"href":157,"dataGaLocation":46,"dataGaName":158},"/solutions/supply-chain/","Software supply chain security",{"text":160,"config":161},"Compliance & Governance",{"href":162,"dataGaLocation":46,"dataGaName":163},"/solutions/continuous-software-compliance/","Compliance and governance",{"title":165,"link":166,"items":171},"Measurement",{"config":167},{"icon":168,"href":169,"dataGaName":170,"dataGaLocation":46},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[172,176,180],{"text":173,"config":174},"Visibility & Measurement",{"href":169,"dataGaLocation":46,"dataGaName":175},"Visibility and Measurement",{"text":177,"config":178},"Value Stream Management",{"href":179,"dataGaLocation":46,"dataGaName":177},"/solutions/value-stream-management/",{"text":181,"config":182},"Analytics & Insights",{"href":183,"dataGaLocation":46,"dataGaName":184},"/solutions/analytics-and-insights/","Analytics and insights",{"title":186,"items":187},"GitLab for",[188,193,198],{"text":189,"config":190},"Enterprise",{"href":191,"dataGaLocation":46,"dataGaName":192},"/enterprise/","enterprise",{"text":194,"config":195},"Small Business",{"href":196,"dataGaLocation":46,"dataGaName":197},"/small-business/","small business",{"text":199,"config":200},"Public Sector",{"href":201,"dataGaLocation":46,"dataGaName":202},"/solutions/public-sector/","public sector",{"text":204,"config":205},"Pricing",{"href":206,"dataGaName":207,"dataGaLocation":46,"dataNavLevelOne":207},"/pricing/","pricing",{"text":209,"config":210,"link":212,"lists":216,"feature":300},"Resources",{"dataNavLevelOne":211},"resources",{"text":213,"config":214},"View all resources",{"href":215,"dataGaName":211,"dataGaLocation":46},"/resources/",[217,250,272],{"title":218,"items":219},"Getting started",[220,225,230,235,240,245],{"text":221,"config":222},"Install",{"href":223,"dataGaName":224,"dataGaLocation":46},"/install/","install",{"text":226,"config":227},"Quick start guides",{"href":228,"dataGaName":229,"dataGaLocation":46},"/get-started/","quick setup checklists",{"text":231,"config":232},"Learn",{"href":233,"dataGaLocation":46,"dataGaName":234},"https://university.gitlab.com/","learn",{"text":236,"config":237},"Product documentation",{"href":238,"dataGaName":239,"dataGaLocation":46},"https://docs.gitlab.com/","product documentation",{"text":241,"config":242},"Best practice videos",{"href":243,"dataGaName":244,"dataGaLocation":46},"/getting-started-videos/","best practice videos",{"text":246,"config":247},"Integrations",{"href":248,"dataGaName":249,"dataGaLocation":46},"/integrations/","integrations",{"title":251,"items":252},"Discover",[253,258,262,267],{"text":254,"config":255},"Customer success stories",{"href":256,"dataGaName":257,"dataGaLocation":46},"/customers/","customer success stories",{"text":259,"config":260},"Blog",{"href":261,"dataGaName":5,"dataGaLocation":46},"/blog/",{"text":263,"config":264},"Remote",{"href":265,"dataGaName":266,"dataGaLocation":46},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":268,"config":269},"TeamOps",{"href":270,"dataGaName":271,"dataGaLocation":46},"/teamops/","teamops",{"title":273,"items":274},"Connect",[275,280,285,290,295],{"text":276,"config":277},"GitLab Services",{"href":278,"dataGaName":279,"dataGaLocation":46},"/services/","services",{"text":281,"config":282},"Community",{"href":283,"dataGaName":284,"dataGaLocation":46},"/community/","community",{"text":286,"config":287},"Forum",{"href":288,"dataGaName":289,"dataGaLocation":46},"https://forum.gitlab.com/","forum",{"text":291,"config":292},"Events",{"href":293,"dataGaName":294,"dataGaLocation":46},"/events/","events",{"text":296,"config":297},"Partners",{"href":298,"dataGaName":299,"dataGaLocation":46},"/partners/","partners",{"backgroundColor":301,"textColor":302,"text":303,"image":304,"link":308},"#2f2a6b","#fff","Insights for the future of software development",{"altText":305,"config":306},"the source promo card",{"src":307},"/images/navigation/the-source-promo-card.svg",{"text":309,"config":310},"Read the latest",{"href":311,"dataGaName":312,"dataGaLocation":46},"/the-source/","the source",{"text":314,"config":315,"lists":317},"Company",{"dataNavLevelOne":316},"company",[318],{"items":319},[320,325,331,333,338,343,348,353,358,363,368],{"text":321,"config":322},"About",{"href":323,"dataGaName":324,"dataGaLocation":46},"/company/","about",{"text":326,"config":327,"footerGa":330},"Jobs",{"href":328,"dataGaName":329,"dataGaLocation":46},"/jobs/","jobs",{"dataGaName":329},{"text":291,"config":332},{"href":293,"dataGaName":294,"dataGaLocation":46},{"text":334,"config":335},"Leadership",{"href":336,"dataGaName":337,"dataGaLocation":46},"/company/team/e-group/","leadership",{"text":339,"config":340},"Team",{"href":341,"dataGaName":342,"dataGaLocation":46},"/company/team/","team",{"text":344,"config":345},"Handbook",{"href":346,"dataGaName":347,"dataGaLocation":46},"https://handbook.gitlab.com/","handbook",{"text":349,"config":350},"Investor relations",{"href":351,"dataGaName":352,"dataGaLocation":46},"https://ir.gitlab.com/","investor relations",{"text":354,"config":355},"Trust Center",{"href":356,"dataGaName":357,"dataGaLocation":46},"/security/","trust center",{"text":359,"config":360},"AI Transparency Center",{"href":361,"dataGaName":362,"dataGaLocation":46},"/ai-transparency-center/","ai transparency center",{"text":364,"config":365},"Newsletter",{"href":366,"dataGaName":367,"dataGaLocation":46},"/company/contact/","newsletter",{"text":369,"config":370},"Press",{"href":371,"dataGaName":372,"dataGaLocation":46},"/press/","press",{"text":374,"config":375,"lists":376},"Contact us",{"dataNavLevelOne":316},[377],{"items":378},[379,382,387],{"text":53,"config":380},{"href":55,"dataGaName":381,"dataGaLocation":46},"talk to sales",{"text":383,"config":384},"Get help",{"href":385,"dataGaName":386,"dataGaLocation":46},"/support/","get help",{"text":388,"config":389},"Customer portal",{"href":390,"dataGaName":391,"dataGaLocation":46},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":393,"login":394,"suggestions":401},"Close",{"text":395,"link":396},"To search repositories and projects, login to",{"text":397,"config":398},"gitlab.com",{"href":60,"dataGaName":399,"dataGaLocation":400},"search login","search",{"text":402,"default":403},"Suggestions",[404,406,410,412,416,420],{"text":75,"config":405},{"href":80,"dataGaName":75,"dataGaLocation":400},{"text":407,"config":408},"Code Suggestions (AI)",{"href":409,"dataGaName":407,"dataGaLocation":400},"/solutions/code-suggestions/",{"text":127,"config":411},{"href":129,"dataGaName":127,"dataGaLocation":400},{"text":413,"config":414},"GitLab on AWS",{"href":415,"dataGaName":413,"dataGaLocation":400},"/partners/technology-partners/aws/",{"text":417,"config":418},"GitLab on Google Cloud",{"href":419,"dataGaName":417,"dataGaLocation":400},"/partners/technology-partners/google-cloud-platform/",{"text":421,"config":422},"Why GitLab?",{"href":88,"dataGaName":421,"dataGaLocation":400},{"freeTrial":424,"mobileIcon":429,"desktopIcon":434,"secondaryButton":437},{"text":425,"config":426},"Start free trial",{"href":427,"dataGaName":51,"dataGaLocation":428},"https://gitlab.com/-/trials/new/","nav",{"altText":430,"config":431},"Gitlab Icon",{"src":432,"dataGaName":433,"dataGaLocation":428},"/images/brand/gitlab-logo-tanuki.svg","gitlab icon",{"altText":430,"config":435},{"src":436,"dataGaName":433,"dataGaLocation":428},"/images/brand/gitlab-logo-type.svg",{"text":438,"config":439},"Get Started",{"href":440,"dataGaName":441,"dataGaLocation":428},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":443,"mobileIcon":447,"desktopIcon":449},{"text":444,"config":445},"Learn more about GitLab Duo",{"href":80,"dataGaName":446,"dataGaLocation":428},"gitlab duo",{"altText":430,"config":448},{"src":432,"dataGaName":433,"dataGaLocation":428},{"altText":430,"config":450},{"src":436,"dataGaName":433,"dataGaLocation":428},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":456,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"title":457,"button":458,"image":462,"config":465,"_id":467,"_type":32,"_source":34,"_file":468,"_stem":469,"_extension":37},"/shared/en-us/banner","is now in public beta!",{"text":86,"config":459},{"href":460,"dataGaName":461,"dataGaLocation":46},"/gitlab-duo/agent-platform/","duo banner",{"config":463},{"src":464},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":466},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":471,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"data":472,"_id":677,"_type":32,"title":678,"_source":34,"_file":679,"_stem":680,"_extension":37},"/shared/en-us/main-footer",{"text":473,"source":474,"edit":480,"contribute":485,"config":490,"items":495,"minimal":669},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":475,"config":476},"View page source",{"href":477,"dataGaName":478,"dataGaLocation":479},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":481,"config":482},"Edit this page",{"href":483,"dataGaName":484,"dataGaLocation":479},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":486,"config":487},"Please contribute",{"href":488,"dataGaName":489,"dataGaLocation":479},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":491,"facebook":492,"youtube":493,"linkedin":494},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[496,519,576,605,639],{"title":64,"links":497,"subMenu":502},[498],{"text":499,"config":500},"DevSecOps platform",{"href":73,"dataGaName":501,"dataGaLocation":479},"devsecops platform",[503],{"title":204,"links":504},[505,509,514],{"text":506,"config":507},"View plans",{"href":206,"dataGaName":508,"dataGaLocation":479},"view plans",{"text":510,"config":511},"Why Premium?",{"href":512,"dataGaName":513,"dataGaLocation":479},"/pricing/premium/","why premium",{"text":515,"config":516},"Why Ultimate?",{"href":517,"dataGaName":518,"dataGaLocation":479},"/pricing/ultimate/","why ultimate",{"title":520,"links":521},"Solutions",[522,527,530,532,537,542,546,549,553,558,560,563,566,571],{"text":523,"config":524},"Digital transformation",{"href":525,"dataGaName":526,"dataGaLocation":479},"/topics/digital-transformation/","digital transformation",{"text":152,"config":528},{"href":147,"dataGaName":529,"dataGaLocation":479},"security & compliance",{"text":141,"config":531},{"href":123,"dataGaName":124,"dataGaLocation":479},{"text":533,"config":534},"Agile development",{"href":535,"dataGaName":536,"dataGaLocation":479},"/solutions/agile-delivery/","agile delivery",{"text":538,"config":539},"Cloud transformation",{"href":540,"dataGaName":541,"dataGaLocation":479},"/topics/cloud-native/","cloud transformation",{"text":543,"config":544},"SCM",{"href":137,"dataGaName":545,"dataGaLocation":479},"source code management",{"text":127,"config":547},{"href":129,"dataGaName":548,"dataGaLocation":479},"continuous integration & delivery",{"text":550,"config":551},"Value stream management",{"href":179,"dataGaName":552,"dataGaLocation":479},"value stream management",{"text":554,"config":555},"GitOps",{"href":556,"dataGaName":557,"dataGaLocation":479},"/solutions/gitops/","gitops",{"text":189,"config":559},{"href":191,"dataGaName":192,"dataGaLocation":479},{"text":561,"config":562},"Small business",{"href":196,"dataGaName":197,"dataGaLocation":479},{"text":564,"config":565},"Public sector",{"href":201,"dataGaName":202,"dataGaLocation":479},{"text":567,"config":568},"Education",{"href":569,"dataGaName":570,"dataGaLocation":479},"/solutions/education/","education",{"text":572,"config":573},"Financial services",{"href":574,"dataGaName":575,"dataGaLocation":479},"/solutions/finance/","financial services",{"title":209,"links":577},[578,580,582,584,587,589,591,593,595,597,599,601,603],{"text":221,"config":579},{"href":223,"dataGaName":224,"dataGaLocation":479},{"text":226,"config":581},{"href":228,"dataGaName":229,"dataGaLocation":479},{"text":231,"config":583},{"href":233,"dataGaName":234,"dataGaLocation":479},{"text":236,"config":585},{"href":238,"dataGaName":586,"dataGaLocation":479},"docs",{"text":259,"config":588},{"href":261,"dataGaName":5,"dataGaLocation":479},{"text":254,"config":590},{"href":256,"dataGaName":257,"dataGaLocation":479},{"text":263,"config":592},{"href":265,"dataGaName":266,"dataGaLocation":479},{"text":276,"config":594},{"href":278,"dataGaName":279,"dataGaLocation":479},{"text":268,"config":596},{"href":270,"dataGaName":271,"dataGaLocation":479},{"text":281,"config":598},{"href":283,"dataGaName":284,"dataGaLocation":479},{"text":286,"config":600},{"href":288,"dataGaName":289,"dataGaLocation":479},{"text":291,"config":602},{"href":293,"dataGaName":294,"dataGaLocation":479},{"text":296,"config":604},{"href":298,"dataGaName":299,"dataGaLocation":479},{"title":314,"links":606},[607,609,611,613,615,617,619,623,628,630,632,634],{"text":321,"config":608},{"href":323,"dataGaName":316,"dataGaLocation":479},{"text":326,"config":610},{"href":328,"dataGaName":329,"dataGaLocation":479},{"text":334,"config":612},{"href":336,"dataGaName":337,"dataGaLocation":479},{"text":339,"config":614},{"href":341,"dataGaName":342,"dataGaLocation":479},{"text":344,"config":616},{"href":346,"dataGaName":347,"dataGaLocation":479},{"text":349,"config":618},{"href":351,"dataGaName":352,"dataGaLocation":479},{"text":620,"config":621},"Sustainability",{"href":622,"dataGaName":620,"dataGaLocation":479},"/sustainability/",{"text":624,"config":625},"Diversity, inclusion and belonging (DIB)",{"href":626,"dataGaName":627,"dataGaLocation":479},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":354,"config":629},{"href":356,"dataGaName":357,"dataGaLocation":479},{"text":364,"config":631},{"href":366,"dataGaName":367,"dataGaLocation":479},{"text":369,"config":633},{"href":371,"dataGaName":372,"dataGaLocation":479},{"text":635,"config":636},"Modern Slavery Transparency Statement",{"href":637,"dataGaName":638,"dataGaLocation":479},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":640,"links":641},"Contact Us",[642,645,647,649,654,659,664],{"text":643,"config":644},"Contact an expert",{"href":55,"dataGaName":56,"dataGaLocation":479},{"text":383,"config":646},{"href":385,"dataGaName":386,"dataGaLocation":479},{"text":388,"config":648},{"href":390,"dataGaName":391,"dataGaLocation":479},{"text":650,"config":651},"Status",{"href":652,"dataGaName":653,"dataGaLocation":479},"https://status.gitlab.com/","status",{"text":655,"config":656},"Terms of use",{"href":657,"dataGaName":658,"dataGaLocation":479},"/terms/","terms of use",{"text":660,"config":661},"Privacy statement",{"href":662,"dataGaName":663,"dataGaLocation":479},"/privacy/","privacy statement",{"text":665,"config":666},"Cookie preferences",{"dataGaName":667,"dataGaLocation":479,"id":668,"isOneTrustButton":109},"cookie preferences","ot-sdk-btn",{"items":670},[671,673,675],{"text":655,"config":672},{"href":657,"dataGaName":658,"dataGaLocation":479},{"text":660,"config":674},{"href":662,"dataGaName":663,"dataGaLocation":479},{"text":665,"config":676},{"dataGaName":667,"dataGaLocation":479,"id":668,"isOneTrustButton":109},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[682,694],{"_path":683,"_dir":684,"_draft":6,"_partial":6,"_locale":7,"content":685,"config":689,"_id":691,"_type":32,"title":18,"_source":34,"_file":692,"_stem":693,"_extension":37},"/en-us/blog/authors/mike-eddington","authors",{"name":18,"config":686},{"headshot":687,"ctfId":688},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","q5tK0TgB1ZovSwShKSvOJ",{"template":690},"BlogAuthor","content:en-us:blog:authors:mike-eddington.yml","en-us/blog/authors/mike-eddington.yml","en-us/blog/authors/mike-eddington",{"_path":695,"_dir":684,"_draft":6,"_partial":6,"_locale":7,"content":696,"config":699,"_id":700,"_type":32,"title":19,"_source":34,"_file":701,"_stem":702,"_extension":37},"/en-us/blog/authors/eugene-lim",{"name":19,"config":697},{"headshot":687,"ctfId":698},"6KHdIdghkUfSTzV2MzxNcj",{"template":690},"content:en-us:blog:authors:eugene-lim.yml","en-us/blog/authors/eugene-lim.yml","en-us/blog/authors/eugene-lim",{"_path":704,"_dir":40,"_draft":6,"_partial":6,"_locale":7,"header":705,"eyebrow":706,"blurb":707,"button":708,"secondaryButton":712,"_id":714,"_type":32,"title":715,"_source":34,"_file":716,"_stem":717,"_extension":37},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":48,"config":709},{"href":710,"dataGaName":51,"dataGaLocation":711},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":53,"config":713},{"href":55,"dataGaName":56,"dataGaLocation":711},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1753981620425]