[{"data":1,"prerenderedAt":726},["ShallowReactive",2],{"/en-us/blog/write-vulnerability-detection-rules/":3,"navigation-en-us":37,"banner-en-us":454,"footer-en-us":469,"Ross Fuhrman-Anshuman Singh-Julian Thome":680,"next-steps-en-us":711},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":27,"_id":30,"_type":31,"title":32,"_source":33,"_file":34,"_stem":35,"_extension":36},"/en-us/blog/write-vulnerability-detection-rules","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"How to write and continuously test vulnerability detection rules for SAST","Interns with the Google Summer of Code helped GitLab transition from our old SAST tools to Semgrep.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667819/Blog/Hero%20Images/anomaly-detection-cover.png","https://about.gitlab.com/blog/write-vulnerability-detection-rules","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How to write and continuously test vulnerability detection rules for SAST\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Ross Fuhrman\"},{\"@type\":\"Person\",\"name\":\"Anshuman Singh\"},{\"@type\":\"Person\",\"name\":\"Julian Thome\"}],\n        \"datePublished\": \"2021-09-08\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":21,"body":22,"category":23,"tags":24},[18,19,20],"Ross Fuhrman","Anshuman Singh","Julian Thome","2021-09-08","\nIn summer 2021, the [Vulnerability Research](/handbook/engineering/development/sec/secure/vulnerability-research/) and [Static Analysis](/handbook/engineering/development/sec/secure/static-analysis/)\nteams launched the [Google Summer of Code (GSoC)](https://summerofcode.withgoogle.com/) project: [Write vulnerability detection rules for SAST](https://gitlab.com/gitlab-com/marketing/community-relations/contributor-program/gitlab-gsoc-2021/-/issues/3).\n\nFor this project, we built and implemented a framework to helps transition GitLab away from our current SAST tools over to Semgrep. Semgrep is a language-agnostic SAST tool that is gaining popularity in CI/CD environments.\nBefore replacing an analyzer with the corresponding Semgrep configuration (called rule-sets), we need to ensure that they are equivalent – in that they yield the same set of findings.\n\nFor this purpose, we built a testing framework that helps us assess the quality of a Semgrep rule-set. This framework has been used to guide the replacement of [flawfinder](https://gitlab.com/gitlab-org/security-products/analyzers/flawfinder), a C/C++ analyzer with a corresponding Semgrep rule-set. This new testing framework leverages the power of GitLab CI/CD.\n\n## Preliminaries\n\n### GitLab and the Google Summer Of Code (GSoC)\n\nThe Google Summer of Code (GSoC) is a 10-week program that enlists student interns to work on an open source project in collaboration with open source organizations. For GSoC 2021, GitLab offered [4 GSoC projects to the GSoC interns](/blog/gsoc-at-gitlab/). The [interns completed each of project](https://gitlab.com/gitlab-com/marketing/community-relations/contributor-program/gitlab-gsoc-2021/-/issues) under the guidance of a GitLab team member who serves as their mentor and provides regular feedback and assistance when needed.\n\n**[Read reflections from the Google Summer of Code interns about [what it was like working with GitLab](/blog/gsoc-at-gitlab/)]**\n\n### About Semgrep\n\n[Semgrep](https://semgrep.dev/) is a language-agnostic static-analysis (SAST) tool that is powered by [tree-sitter](https://tree-sitter.github.io/tree-sitter/). Tree-sitter is a robust parser-generator tool that supports parsing a variety of languages.\n\nSemgrep supports a [rule-syntax](https://semgrep.dev/docs/writing-rules/rule-syntax/) which can be used to formulate detection rules in a configuration-as-code YAML format. A Semgrep rule determines the findings that Semgrep is supposed to detect. These rules are combined together to create a rule-set.\n\n### About GitLab SAST\n\nGitLab is a complete DevSecOps platform and integrates a [variety of static analysis tools](https://docs.gitlab.com/ee/user/application_security/sast/analyzers.html) that help developers find vulnerabilities as early as possible in the software development lifecycle (SDLC).\n\nSince all the integrated SAST tools are very different in terms of implementation as well as tech stack they depend on, the SAST tools are all wrapped in Docker images. The wrappers translate the native vulnerability reports to a [generic, common report format](https://docs.gitlab.com/ee/user/application_security/sast/) which is made available by means of the `gl-sast-report.json` artifact. This generic report is GitLab's common interface between analyzers and the GitLab Rails backend.\n\n## Write vulnerability detection rules\n\n### Some background on our SAST tools\n\nOver time, the growing number of integrated SAST tools has become a maintenance burden for GitLab due to two major contributing factors.\n\n1. **Integration cost**: All SAST tools have different release cycles – new releases have to be pulled in immediately so that our users can benefit from them. Given the large amount of integrated SAST tools, the time spent to monitor the SAST tools for new releases, integrating and testing them is expensive in terms of engineering effort/time.\n\n1. **Inflexibility**: Adapting or modifying SAST tools behavior is non-trivial because each tool is based on different technologies. Also, upstream contributions to the original analyzer repositories are not guaranteed to be included by the maintainers. In these cases, they require us to fork a project which is not a scalable solution with regards to maintenance effort.\n\nGitLab is in the process of replacing various SAST tools with a single, language-agnostic SAST tool, called Semgrep, to fix these problems. Semgrep can be configured by means of rules that are used to define what Semgrep is supposed to find. These rules are provided as YAML configuration files so it is fairly easy to modify the behavior of Semgrep to different use cases.\nSemgrep's configuration-as-code approach paired with its language support enables us to replace multiple analyzers, which effectively reduces the maintenance burden.\n\nHowever, the SAST tool replacement itself is a challenging process. For the majority of use cases we have to assume that there is already a large amount of historic vulnerability data recorded and acted upon using [GitLab's vulnerability management features](https://handbook.gitlab.com/handbook/security/threat-management/vulnerability-management/). Users may also have grown accustomed to working with certain analyzers and may even have a certain level of expectation with regards to the findings produced by the analyzer.\n\nA smooth transition from a language-specific analyzer to a corresponding Semgrep rule-set must be guaranteed by meeting a certain level of quality assurance. A rule-set should be at least as good as the results produced by the original analyzers, also known as parity. In turn, parity required we build test-suites to be used to measure the gap (in terms of rule coverage) between the original analyzer and the rule-set that is to replace it. A good quality rule-set is expected to perform at least as well as the SAST tool it aims to replace (zero gap, full parity).\n\nThere are cases where the original SAST tool may falsely report vulnerabilities. In these situations, we aim to improve our rule-set in a controlled manner by explicitly documenting our improvements. However, before improving a rule-set, we want to start from a position of complete parity so that we have a holistic view of the impact incurred by single rule improvements. This documentation of applied improvements is important so we can justify changes with regard to reported findings to the customer.\n\nThere are three challenges we tried to address with this project:\n\n1. **Rule management**: Provide a central rule repository to store, distribute and track changes applied to rules as well as test-cases.\n1. **Rule testing**: Every change applied to a rule in the rule repository triggers an automated gap-analysis that measure the quality of the rules in comparison to the original analyzers.\n1. **Analyzer replacement**: Replace at least one SAST tool (in our case flawfinder) with a corresponding rule-set – use the testing framework to ensure that the rule-set is on par with the original SAST tool.\n\nWe unpack each of these challenges in the next section.\n\n### How we approached these challenges\n\nThe architecture of the rule-testing framework is depicted in the code snippets below. All the Semgrep rules and the corresponding test-cases are stored in a central rule repository. Changes that are applied to the rules trigger the execution of our rule testing framework that uses the rules and test-cases to perform an automated gap analysis.\n\n\u003Cpre class=\"mermaid\">\nflowchart LR\n  crr[GitLab Rule Repository]\n\n  bandit[\"GitLab bandit\"]\n  bx[\"gl-sast-report.json\"]\n  sbx[\"gl-sast-report.json\"]\n  breport[\"bandit gap analysis report\"]\n\n  subgraph bandit_comparison[\"bandit comparison\"]\n    banditsemgrep[\"GitLab Semgrep\"]\n    banditcompare[\"compare\"]\n    bandit --> |run analyzer on test-cases| bx\n    banditsemgrep --> |run analyzer on test-cases| sbx\n    bx --> banditcompare\n    sbx --> banditcompare\n  end\n  crr -->|bandit rules + rule id mappings| banditsemgrep\n  banditcompare --> breport\n\n  fx[\"gl-sast-report.json\"]\n  fbx[\"gl-sast-report.json\"]\n  freport[\"flawfinder gap analysis report\"]\n  flawfinder[\"GitLab flawfinder\"]\n\n  subgraph flawfinder_comparison[\"flawfinder comparison\"]\n    flawfindersemgrep[\"GitLab Semgrep\"]\n    flawfindercompare[\"compare\"]\n    flawfinder --> |run analyzer on test-cases| fx\n    flawfindersemgrep --> |run analyzer on test-cases| fbx\n    fx --> flawfindercompare\n    fbx --> flawfindercompare\n  end\n  crr -->|flawfinder rules + rule id mappings| flawfindersemgrep\n  flawfindercompare --> freport\n\n\u003C/pre>\n\nThe rule testing framework is a compass that guides us through the rule development process by automatically measuring the efficacy of rules that are stored in the central rule (git) repository. This measurement happens during a comparison step that validates the findings reported by the original analyzer against the corresponding Semgrep rule-set. For the comparisons we cross-validate the SAST\nreports ([`gl-sast-report.json`](https://docs.gitlab.com/ee/user/application_security/sast/)) that adhere to the GitLab security report format. Since the main goal is to achieve parity between the original analyzer and our corresponding Semgrep rules, we treat the original analyzer as the baseline. The code snippet above depicts two example comparison steps for bandit and flawfinder.  The gap analysis is explained in more detail in the \"rule testing\" section below.\n\nUsing a central rule git repository allows us to manage and easily track changes that are applied to rules and their corresponding test-cases in a central location. By means of GitLab CI/CD, we have a mechanism to automatically run tests that enforce constraints and guidelines on the rules and test-cases. Upon rule changes, we automatically trigger the rule-testing framework which enables us to spot gaps in our rules instantly. The structure of the central rule repository is detailed in the \"rule management\" section below.\n\n#### How we addressed rule management challenges\n\nThe central rule repository is used to store, keep track of changes applied to `rules/test-cases` for a variety of different languages. By having a separate rule repository we can add CI jobs to test, verify, and enforce syntax guidelines.\n\nThe structure we use for the central rule repository is depicted below and follows the structure: `\u003Clanguage>/\u003Cruleclass>/{rule-\u003Crulename>.yml, test-\u003Crulename>.*}` where language denotes the target programming language, `\u003Cruleclass>` is a descriptive name for the class of issues the rule aims to detect and `\u003Crulename>` is a descriptive name for the actual rule.  We can have multiple test cases per rule (all prefixed with `test-`) and rule files `rule-\u003Crulename>.yml` that are prefixed with `rule-` – a rule file contains a single Semgrep rule.\n\n``` bash\n.\n├── mappings\n│   └── analyzer.yml\n├── c\n│   ├── buffer\n│   │   ├── rule-strcpy.yml\n│   │   ├── test-strcpy.c\n│   │   ├── rule-memcpy.yml\n│   │   └── test-memcpy.c\n│   └── ...\n└── javascript\n│   └── ...\n└── python\n│    ├── assert\n│    │   ├── rule-assert.yml\n│    │   └── test-assert.py\n│    └── exec\n│    │   ├── rule-exec.yml\n│    │   ├── test-exec.yml\n│    │   ├── rule-something.yml\n│    │   └── test-something.yml\n│    └── permission\n│    │   ├── rule-chmod.yml\n│    │   └── test-chmod.py\n│    └── ...\n└── ...\n```\n\nIn addition to the rules, we also store mapping files (in the `mappings` subdirectory). The mappings directory in this repository contains YAML configuration/mapping files that map native analyzer IDs to the corresponding Semgrep rules. An analyzer ID uniquely identifies the type of finding. The information in the mapping files helps us to correlate the finding from the original analyzer with their corresponding Semgrep findings and vice versa.\n\nThe mapping files are digested by the testing framework to perform an automated gap analysis. The goal of this analysis is to check if there is an unexpected deviation between Semgrep (with the rules in this repository) and a given analyzer.\n\nA mapping file groups distinct rules into rule-sets and, thus, can be used to bundle different rules based on a certain domain. An excerpt from a mapping file is depicted below – it maps bandit rules (identified by bandit IDs) to Semgrep rules from the central rule repository.\n\n``` yaml\nbandit:\n  - id: \"B101\"\n    rules:\n      - \"python/assert/rule-assert_used\"\n  - id: \"B102\"\n    rules:\n      - \"python/exec/rule-exec_used\"\n  - id: \"B103\"\n    rules:\n      - \"python/file_permissions/rule-general_bad_permission\"\n  - id: \"B104\"\n    rules:\n      - \"python/bind_all_interfaces/rule-general_bindall_interfaces\"\n```\n\n#### How the rule testing framework works\n\nThe test-oracle/baseline is provided by the original analyzer when executed on the test-files. The rules in the central rule repository are compared and evaluated against this baseline. The execution of the testing framework is triggered by any change applied to the rule repository.\n\nWe run all analyzers (flawfinder, bandit, etc.) and their corresponding Semgrep rule-sets (as defined by the mapping files) on the test-files from the GitLab rule repository. The resulting `gl-sast-reports.json` reports that are produced by the original analyzer and by the Semgrep analyzer are then compared in a pairwise manner. To identify identical findings in both reports, we leverage the information from the mapping files that maps the rule-ids of the baseline analyzer to the corresponding Semgrep rule-ids for the rules stored in the central rule repository.\n\nAs output, we produce a gap analysis report (in markdown format). The gap analysis lists all the findings that have been reported by the original analyzers and groups them into different tables (based on the native rule-ids). The screenshot below shows a single table from the gap analysis report.\n\n![Gap Analysis Report](https://about.gitlab.com/images/blogimages/testing-framework-report.png){: .shadow.center}\nAn example table from the gap analysis report.\n{: .note.text-center}\n\nThe `X` symbols indicate whether the analyzers (in the example, flawfinder and Semgrep) were able to detect a given finding. The concrete findings as well as the rule files are linked in the table. To reach full coverage, flawfinder as well as Semgrep have to cover the same findings for all the rules that are reported by the baseline.\n\n#### The analyzer replacement\n\nTo build a Semgrep rule-set that is on par with the capabilities of the original/baseline analyzer we leveraged the newly created testing framework. Flawfinder, a C/C++ analyzer, was the first analyzer we fully migrated to Semgrep using the testing framework as a compass.\n\nFirst, we checked the flawfinder implementation to identify the implemented rules. Given that flawfinder is a Python script and that the rules are essentially stored in a dictionary/hash data-structure, we were able to semi-automatically extract the rules and generate the corresponding Semgrep rule files. We were also able to source the test-files from the flawfinder source code repository.\n\nAfter the initial import of the first set of rules-files and test-cases, we used the information provided by the testing-framework to see which rules needed refinement.\n\nWe responded to the information provided by our testing framework in the following way:\n\n1. Findings covered by Baseline and covered by our rule-set: Nothing to be done.\n1. Findings covered by Baseline but not covered by our rule-set: This denotes an incomplete ruleset. In this case we extended the rule-file by providing additional `pattern` entries.\n1. Findings not covered by Baseline but covered by our rule-set: This usually denotes that some rules are too vaguely formulated. In this case, we refined our rules by using exclusions, e.g., by using `pattern-not` or by adding more detail to an already existing pattern.\n\nThe rule design was an iterative process where we closed the gaps between our semgrep rule-set and the flawfinder baseline in an iterative manner using the testing framework as an oracle to ultimately achieve 100% parity.\n\n## How the GSoC project helped GitLab\n\nIn this GSoC project we successfully built an automated rule/configuration testing framework that is driven by GitLab CI/CD capabilities and that provided the data we needed to replace flawfinder reliably and quickly with a corresponding Semgrep rule-set.\n\nIf you are interested in finding out more information about this GSoC project, please check out the following repositories:\n\n- [Central Rule Repository](https://gitlab.com/gitlab-org/secure/gsoc-sast-vulnerability-rules/playground/sast-rules)\n- [Testing Framework](https://gitlab.com/gitlab-org/secure/gsoc-sast-vulnerability-rules/rule-testing-framework/rule-testing)\n- [Gap Analysis Computation Tool](https://gitlab.com/gitlab-org/secure/gsoc-sast-vulnerability-rules/rule-testing-framework/report-diff)\n- [Repository to track gap statistics](https://gitlab.com/gitlab-org/secure/gsoc-sast-vulnerability-rules/rule-testing-framework/rule-testing-stats)\n","security",[23,25,26],"AWS","demo",{"slug":28,"featured":6,"template":29},"write-vulnerability-detection-rules","BlogPost","content:en-us:blog:write-vulnerability-detection-rules.yml","yaml","Write Vulnerability Detection Rules","content","en-us/blog/write-vulnerability-detection-rules.yml","en-us/blog/write-vulnerability-detection-rules","yml",{"_path":38,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"data":40,"_id":450,"_type":31,"title":451,"_source":33,"_file":452,"_stem":453,"_extension":36},"/shared/en-us/main-navigation","en-us",{"logo":41,"freeTrial":46,"sales":51,"login":56,"items":61,"search":391,"minimal":422,"duo":441},{"config":42},{"href":43,"dataGaName":44,"dataGaLocation":45},"/","gitlab logo","header",{"text":47,"config":48},"Get free trial",{"href":49,"dataGaName":50,"dataGaLocation":45},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":52,"config":53},"Talk to sales",{"href":54,"dataGaName":55,"dataGaLocation":45},"/sales/","sales",{"text":57,"config":58},"Sign in",{"href":59,"dataGaName":60,"dataGaLocation":45},"https://gitlab.com/users/sign_in/","sign in",[62,106,202,207,312,372],{"text":63,"config":64,"cards":66,"footer":89},"Platform",{"dataNavLevelOne":65},"platform",[67,73,81],{"title":63,"description":68,"link":69},"The most comprehensive AI-powered DevSecOps Platform",{"text":70,"config":71},"Explore our Platform",{"href":72,"dataGaName":65,"dataGaLocation":45},"/platform/",{"title":74,"description":75,"link":76},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":77,"config":78},"Meet GitLab Duo",{"href":79,"dataGaName":80,"dataGaLocation":45},"/gitlab-duo/","gitlab duo ai",{"title":82,"description":83,"link":84},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":85,"config":86},"Learn more",{"href":87,"dataGaName":88,"dataGaLocation":45},"/why-gitlab/","why gitlab",{"title":90,"items":91},"Get started with",[92,97,102],{"text":93,"config":94},"Platform Engineering",{"href":95,"dataGaName":96,"dataGaLocation":45},"/solutions/platform-engineering/","platform engineering",{"text":98,"config":99},"Developer Experience",{"href":100,"dataGaName":101,"dataGaLocation":45},"/developer-experience/","Developer experience",{"text":103,"config":104},"MLOps",{"href":105,"dataGaName":103,"dataGaLocation":45},"/topics/devops/the-role-of-ai-in-devops/",{"text":107,"left":108,"config":109,"link":111,"lists":115,"footer":184},"Product",true,{"dataNavLevelOne":110},"solutions",{"text":112,"config":113},"View all Solutions",{"href":114,"dataGaName":110,"dataGaLocation":45},"/solutions/",[116,141,163],{"title":117,"description":118,"link":119,"items":124},"Automation","CI/CD and automation to accelerate deployment",{"config":120},{"icon":121,"href":122,"dataGaName":123,"dataGaLocation":45},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[125,129,133,137],{"text":126,"config":127},"CI/CD",{"href":128,"dataGaLocation":45,"dataGaName":126},"/solutions/continuous-integration/",{"text":130,"config":131},"AI-Assisted Development",{"href":79,"dataGaLocation":45,"dataGaName":132},"AI assisted development",{"text":134,"config":135},"Source Code Management",{"href":136,"dataGaLocation":45,"dataGaName":134},"/solutions/source-code-management/",{"text":138,"config":139},"Automated Software Delivery",{"href":122,"dataGaLocation":45,"dataGaName":140},"Automated software delivery",{"title":142,"description":143,"link":144,"items":149},"Security","Deliver code faster without compromising security",{"config":145},{"href":146,"dataGaName":147,"dataGaLocation":45,"icon":148},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[150,153,158],{"text":151,"config":152},"Security & Compliance",{"href":146,"dataGaLocation":45,"dataGaName":151},{"text":154,"config":155},"Software Supply Chain Security",{"href":156,"dataGaLocation":45,"dataGaName":157},"/solutions/supply-chain/","Software supply chain security",{"text":159,"config":160},"Compliance & Governance",{"href":161,"dataGaLocation":45,"dataGaName":162},"/solutions/continuous-software-compliance/","Compliance and governance",{"title":164,"link":165,"items":170},"Measurement",{"config":166},{"icon":167,"href":168,"dataGaName":169,"dataGaLocation":45},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[171,175,179],{"text":172,"config":173},"Visibility & Measurement",{"href":168,"dataGaLocation":45,"dataGaName":174},"Visibility and Measurement",{"text":176,"config":177},"Value Stream Management",{"href":178,"dataGaLocation":45,"dataGaName":176},"/solutions/value-stream-management/",{"text":180,"config":181},"Analytics & Insights",{"href":182,"dataGaLocation":45,"dataGaName":183},"/solutions/analytics-and-insights/","Analytics and insights",{"title":185,"items":186},"GitLab for",[187,192,197],{"text":188,"config":189},"Enterprise",{"href":190,"dataGaLocation":45,"dataGaName":191},"/enterprise/","enterprise",{"text":193,"config":194},"Small Business",{"href":195,"dataGaLocation":45,"dataGaName":196},"/small-business/","small business",{"text":198,"config":199},"Public Sector",{"href":200,"dataGaLocation":45,"dataGaName":201},"/solutions/public-sector/","public sector",{"text":203,"config":204},"Pricing",{"href":205,"dataGaName":206,"dataGaLocation":45,"dataNavLevelOne":206},"/pricing/","pricing",{"text":208,"config":209,"link":211,"lists":215,"feature":299},"Resources",{"dataNavLevelOne":210},"resources",{"text":212,"config":213},"View all resources",{"href":214,"dataGaName":210,"dataGaLocation":45},"/resources/",[216,249,271],{"title":217,"items":218},"Getting started",[219,224,229,234,239,244],{"text":220,"config":221},"Install",{"href":222,"dataGaName":223,"dataGaLocation":45},"/install/","install",{"text":225,"config":226},"Quick start guides",{"href":227,"dataGaName":228,"dataGaLocation":45},"/get-started/","quick setup checklists",{"text":230,"config":231},"Learn",{"href":232,"dataGaLocation":45,"dataGaName":233},"https://university.gitlab.com/","learn",{"text":235,"config":236},"Product documentation",{"href":237,"dataGaName":238,"dataGaLocation":45},"https://docs.gitlab.com/","product documentation",{"text":240,"config":241},"Best practice videos",{"href":242,"dataGaName":243,"dataGaLocation":45},"/getting-started-videos/","best practice videos",{"text":245,"config":246},"Integrations",{"href":247,"dataGaName":248,"dataGaLocation":45},"/integrations/","integrations",{"title":250,"items":251},"Discover",[252,257,261,266],{"text":253,"config":254},"Customer success stories",{"href":255,"dataGaName":256,"dataGaLocation":45},"/customers/","customer success stories",{"text":258,"config":259},"Blog",{"href":260,"dataGaName":5,"dataGaLocation":45},"/blog/",{"text":262,"config":263},"Remote",{"href":264,"dataGaName":265,"dataGaLocation":45},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":267,"config":268},"TeamOps",{"href":269,"dataGaName":270,"dataGaLocation":45},"/teamops/","teamops",{"title":272,"items":273},"Connect",[274,279,284,289,294],{"text":275,"config":276},"GitLab Services",{"href":277,"dataGaName":278,"dataGaLocation":45},"/services/","services",{"text":280,"config":281},"Community",{"href":282,"dataGaName":283,"dataGaLocation":45},"/community/","community",{"text":285,"config":286},"Forum",{"href":287,"dataGaName":288,"dataGaLocation":45},"https://forum.gitlab.com/","forum",{"text":290,"config":291},"Events",{"href":292,"dataGaName":293,"dataGaLocation":45},"/events/","events",{"text":295,"config":296},"Partners",{"href":297,"dataGaName":298,"dataGaLocation":45},"/partners/","partners",{"backgroundColor":300,"textColor":301,"text":302,"image":303,"link":307},"#2f2a6b","#fff","Insights for the future of software development",{"altText":304,"config":305},"the source promo card",{"src":306},"/images/navigation/the-source-promo-card.svg",{"text":308,"config":309},"Read the latest",{"href":310,"dataGaName":311,"dataGaLocation":45},"/the-source/","the source",{"text":313,"config":314,"lists":316},"Company",{"dataNavLevelOne":315},"company",[317],{"items":318},[319,324,330,332,337,342,347,352,357,362,367],{"text":320,"config":321},"About",{"href":322,"dataGaName":323,"dataGaLocation":45},"/company/","about",{"text":325,"config":326,"footerGa":329},"Jobs",{"href":327,"dataGaName":328,"dataGaLocation":45},"/jobs/","jobs",{"dataGaName":328},{"text":290,"config":331},{"href":292,"dataGaName":293,"dataGaLocation":45},{"text":333,"config":334},"Leadership",{"href":335,"dataGaName":336,"dataGaLocation":45},"/company/team/e-group/","leadership",{"text":338,"config":339},"Team",{"href":340,"dataGaName":341,"dataGaLocation":45},"/company/team/","team",{"text":343,"config":344},"Handbook",{"href":345,"dataGaName":346,"dataGaLocation":45},"https://handbook.gitlab.com/","handbook",{"text":348,"config":349},"Investor relations",{"href":350,"dataGaName":351,"dataGaLocation":45},"https://ir.gitlab.com/","investor relations",{"text":353,"config":354},"Trust Center",{"href":355,"dataGaName":356,"dataGaLocation":45},"/security/","trust center",{"text":358,"config":359},"AI Transparency Center",{"href":360,"dataGaName":361,"dataGaLocation":45},"/ai-transparency-center/","ai transparency center",{"text":363,"config":364},"Newsletter",{"href":365,"dataGaName":366,"dataGaLocation":45},"/company/contact/","newsletter",{"text":368,"config":369},"Press",{"href":370,"dataGaName":371,"dataGaLocation":45},"/press/","press",{"text":373,"config":374,"lists":375},"Contact us",{"dataNavLevelOne":315},[376],{"items":377},[378,381,386],{"text":52,"config":379},{"href":54,"dataGaName":380,"dataGaLocation":45},"talk to sales",{"text":382,"config":383},"Get help",{"href":384,"dataGaName":385,"dataGaLocation":45},"/support/","get help",{"text":387,"config":388},"Customer portal",{"href":389,"dataGaName":390,"dataGaLocation":45},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":392,"login":393,"suggestions":400},"Close",{"text":394,"link":395},"To search repositories and projects, login to",{"text":396,"config":397},"gitlab.com",{"href":59,"dataGaName":398,"dataGaLocation":399},"search login","search",{"text":401,"default":402},"Suggestions",[403,405,409,411,415,419],{"text":74,"config":404},{"href":79,"dataGaName":74,"dataGaLocation":399},{"text":406,"config":407},"Code Suggestions (AI)",{"href":408,"dataGaName":406,"dataGaLocation":399},"/solutions/code-suggestions/",{"text":126,"config":410},{"href":128,"dataGaName":126,"dataGaLocation":399},{"text":412,"config":413},"GitLab on AWS",{"href":414,"dataGaName":412,"dataGaLocation":399},"/partners/technology-partners/aws/",{"text":416,"config":417},"GitLab on Google Cloud",{"href":418,"dataGaName":416,"dataGaLocation":399},"/partners/technology-partners/google-cloud-platform/",{"text":420,"config":421},"Why GitLab?",{"href":87,"dataGaName":420,"dataGaLocation":399},{"freeTrial":423,"mobileIcon":428,"desktopIcon":433,"secondaryButton":436},{"text":424,"config":425},"Start free trial",{"href":426,"dataGaName":50,"dataGaLocation":427},"https://gitlab.com/-/trials/new/","nav",{"altText":429,"config":430},"Gitlab Icon",{"src":431,"dataGaName":432,"dataGaLocation":427},"/images/brand/gitlab-logo-tanuki.svg","gitlab icon",{"altText":429,"config":434},{"src":435,"dataGaName":432,"dataGaLocation":427},"/images/brand/gitlab-logo-type.svg",{"text":437,"config":438},"Get Started",{"href":439,"dataGaName":440,"dataGaLocation":427},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":442,"mobileIcon":446,"desktopIcon":448},{"text":443,"config":444},"Learn more about GitLab Duo",{"href":79,"dataGaName":445,"dataGaLocation":427},"gitlab duo",{"altText":429,"config":447},{"src":431,"dataGaName":432,"dataGaLocation":427},{"altText":429,"config":449},{"src":435,"dataGaName":432,"dataGaLocation":427},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":455,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"title":456,"button":457,"image":461,"config":464,"_id":466,"_type":31,"_source":33,"_file":467,"_stem":468,"_extension":36},"/shared/en-us/banner","is now in public beta!",{"text":85,"config":458},{"href":459,"dataGaName":460,"dataGaLocation":45},"/gitlab-duo/agent-platform/","duo banner",{"config":462},{"src":463},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":465},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":470,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"data":471,"_id":676,"_type":31,"title":677,"_source":33,"_file":678,"_stem":679,"_extension":36},"/shared/en-us/main-footer",{"text":472,"source":473,"edit":479,"contribute":484,"config":489,"items":494,"minimal":668},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":474,"config":475},"View page source",{"href":476,"dataGaName":477,"dataGaLocation":478},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":480,"config":481},"Edit this page",{"href":482,"dataGaName":483,"dataGaLocation":478},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":485,"config":486},"Please contribute",{"href":487,"dataGaName":488,"dataGaLocation":478},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":490,"facebook":491,"youtube":492,"linkedin":493},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[495,518,575,604,638],{"title":63,"links":496,"subMenu":501},[497],{"text":498,"config":499},"DevSecOps platform",{"href":72,"dataGaName":500,"dataGaLocation":478},"devsecops platform",[502],{"title":203,"links":503},[504,508,513],{"text":505,"config":506},"View plans",{"href":205,"dataGaName":507,"dataGaLocation":478},"view plans",{"text":509,"config":510},"Why Premium?",{"href":511,"dataGaName":512,"dataGaLocation":478},"/pricing/premium/","why premium",{"text":514,"config":515},"Why Ultimate?",{"href":516,"dataGaName":517,"dataGaLocation":478},"/pricing/ultimate/","why ultimate",{"title":519,"links":520},"Solutions",[521,526,529,531,536,541,545,548,552,557,559,562,565,570],{"text":522,"config":523},"Digital transformation",{"href":524,"dataGaName":525,"dataGaLocation":478},"/topics/digital-transformation/","digital transformation",{"text":151,"config":527},{"href":146,"dataGaName":528,"dataGaLocation":478},"security & compliance",{"text":140,"config":530},{"href":122,"dataGaName":123,"dataGaLocation":478},{"text":532,"config":533},"Agile development",{"href":534,"dataGaName":535,"dataGaLocation":478},"/solutions/agile-delivery/","agile delivery",{"text":537,"config":538},"Cloud transformation",{"href":539,"dataGaName":540,"dataGaLocation":478},"/topics/cloud-native/","cloud transformation",{"text":542,"config":543},"SCM",{"href":136,"dataGaName":544,"dataGaLocation":478},"source code management",{"text":126,"config":546},{"href":128,"dataGaName":547,"dataGaLocation":478},"continuous integration & delivery",{"text":549,"config":550},"Value stream management",{"href":178,"dataGaName":551,"dataGaLocation":478},"value stream management",{"text":553,"config":554},"GitOps",{"href":555,"dataGaName":556,"dataGaLocation":478},"/solutions/gitops/","gitops",{"text":188,"config":558},{"href":190,"dataGaName":191,"dataGaLocation":478},{"text":560,"config":561},"Small business",{"href":195,"dataGaName":196,"dataGaLocation":478},{"text":563,"config":564},"Public sector",{"href":200,"dataGaName":201,"dataGaLocation":478},{"text":566,"config":567},"Education",{"href":568,"dataGaName":569,"dataGaLocation":478},"/solutions/education/","education",{"text":571,"config":572},"Financial services",{"href":573,"dataGaName":574,"dataGaLocation":478},"/solutions/finance/","financial services",{"title":208,"links":576},[577,579,581,583,586,588,590,592,594,596,598,600,602],{"text":220,"config":578},{"href":222,"dataGaName":223,"dataGaLocation":478},{"text":225,"config":580},{"href":227,"dataGaName":228,"dataGaLocation":478},{"text":230,"config":582},{"href":232,"dataGaName":233,"dataGaLocation":478},{"text":235,"config":584},{"href":237,"dataGaName":585,"dataGaLocation":478},"docs",{"text":258,"config":587},{"href":260,"dataGaName":5,"dataGaLocation":478},{"text":253,"config":589},{"href":255,"dataGaName":256,"dataGaLocation":478},{"text":262,"config":591},{"href":264,"dataGaName":265,"dataGaLocation":478},{"text":275,"config":593},{"href":277,"dataGaName":278,"dataGaLocation":478},{"text":267,"config":595},{"href":269,"dataGaName":270,"dataGaLocation":478},{"text":280,"config":597},{"href":282,"dataGaName":283,"dataGaLocation":478},{"text":285,"config":599},{"href":287,"dataGaName":288,"dataGaLocation":478},{"text":290,"config":601},{"href":292,"dataGaName":293,"dataGaLocation":478},{"text":295,"config":603},{"href":297,"dataGaName":298,"dataGaLocation":478},{"title":313,"links":605},[606,608,610,612,614,616,618,622,627,629,631,633],{"text":320,"config":607},{"href":322,"dataGaName":315,"dataGaLocation":478},{"text":325,"config":609},{"href":327,"dataGaName":328,"dataGaLocation":478},{"text":333,"config":611},{"href":335,"dataGaName":336,"dataGaLocation":478},{"text":338,"config":613},{"href":340,"dataGaName":341,"dataGaLocation":478},{"text":343,"config":615},{"href":345,"dataGaName":346,"dataGaLocation":478},{"text":348,"config":617},{"href":350,"dataGaName":351,"dataGaLocation":478},{"text":619,"config":620},"Sustainability",{"href":621,"dataGaName":619,"dataGaLocation":478},"/sustainability/",{"text":623,"config":624},"Diversity, inclusion and belonging (DIB)",{"href":625,"dataGaName":626,"dataGaLocation":478},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":353,"config":628},{"href":355,"dataGaName":356,"dataGaLocation":478},{"text":363,"config":630},{"href":365,"dataGaName":366,"dataGaLocation":478},{"text":368,"config":632},{"href":370,"dataGaName":371,"dataGaLocation":478},{"text":634,"config":635},"Modern Slavery Transparency Statement",{"href":636,"dataGaName":637,"dataGaLocation":478},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":639,"links":640},"Contact Us",[641,644,646,648,653,658,663],{"text":642,"config":643},"Contact an expert",{"href":54,"dataGaName":55,"dataGaLocation":478},{"text":382,"config":645},{"href":384,"dataGaName":385,"dataGaLocation":478},{"text":387,"config":647},{"href":389,"dataGaName":390,"dataGaLocation":478},{"text":649,"config":650},"Status",{"href":651,"dataGaName":652,"dataGaLocation":478},"https://status.gitlab.com/","status",{"text":654,"config":655},"Terms of use",{"href":656,"dataGaName":657,"dataGaLocation":478},"/terms/","terms of use",{"text":659,"config":660},"Privacy statement",{"href":661,"dataGaName":662,"dataGaLocation":478},"/privacy/","privacy statement",{"text":664,"config":665},"Cookie preferences",{"dataGaName":666,"dataGaLocation":478,"id":667,"isOneTrustButton":108},"cookie preferences","ot-sdk-btn",{"items":669},[670,672,674],{"text":654,"config":671},{"href":656,"dataGaName":657,"dataGaLocation":478},{"text":659,"config":673},{"href":661,"dataGaName":662,"dataGaLocation":478},{"text":664,"config":675},{"dataGaName":666,"dataGaLocation":478,"id":667,"isOneTrustButton":108},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[681,693,702],{"_path":682,"_dir":683,"_draft":6,"_partial":6,"_locale":7,"content":684,"config":688,"_id":690,"_type":31,"title":18,"_source":33,"_file":691,"_stem":692,"_extension":36},"/en-us/blog/authors/ross-fuhrman","authors",{"name":18,"config":685},{"headshot":686,"ctfId":687},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","7dkuWBvIc0AQanUclt3pOk",{"template":689},"BlogAuthor","content:en-us:blog:authors:ross-fuhrman.yml","en-us/blog/authors/ross-fuhrman.yml","en-us/blog/authors/ross-fuhrman",{"_path":694,"_dir":683,"_draft":6,"_partial":6,"_locale":7,"content":695,"config":698,"_id":699,"_type":31,"title":19,"_source":33,"_file":700,"_stem":701,"_extension":36},"/en-us/blog/authors/anshuman-singh",{"name":19,"config":696},{"headshot":686,"ctfId":697},"4xzrY67JSkxp4j7hlK1DWA",{"template":689},"content:en-us:blog:authors:anshuman-singh.yml","en-us/blog/authors/anshuman-singh.yml","en-us/blog/authors/anshuman-singh",{"_path":703,"_dir":683,"_draft":6,"_partial":6,"_locale":7,"content":704,"config":707,"_id":708,"_type":31,"title":20,"_source":33,"_file":709,"_stem":710,"_extension":36},"/en-us/blog/authors/julian-thome",{"name":20,"config":705},{"headshot":7,"ctfId":706},"jthome",{"template":689},"content:en-us:blog:authors:julian-thome.yml","en-us/blog/authors/julian-thome.yml","en-us/blog/authors/julian-thome",{"_path":712,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"header":713,"eyebrow":714,"blurb":715,"button":716,"secondaryButton":720,"_id":722,"_type":31,"title":723,"_source":33,"_file":724,"_stem":725,"_extension":36},"/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":47,"config":717},{"href":718,"dataGaName":50,"dataGaLocation":719},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":52,"config":721},{"href":54,"dataGaName":55,"dataGaLocation":719},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1754424511701]