[{"data":1,"prerenderedAt":739},["ShallowReactive",2],{"/en-us/blog/write-vulnerability-detection-rules/":3,"navigation-en-us":37,"banner-en-us":466,"footer-en-us":483,"Ross Fuhrman-Anshuman Singh-Julian Thome":693,"next-steps-en-us":724},{"_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","In summer 2021, the [Vulnerability\nResearch](/handbook/engineering/development/sec/secure/vulnerability-research/)\nand [Static\nAnalysis](/handbook/engineering/development/sec/secure/static-analysis/)\n\nteams launched the [Google Summer of Code\n(GSoC)](https://summerofcode.withgoogle.com/) project: [Write vulnerability\ndetection rules for\nSAST](https://gitlab.com/gitlab-com/marketing/community-relations/contributor-program/gitlab-gsoc-2021/-/issues/3).\n\n\nFor this project, we built and implemented a framework to helps transition\nGitLab away from our current SAST tools over to Semgrep. Semgrep is a\nlanguage-agnostic SAST tool that is gaining popularity in CI/CD\nenvironments.\n\nBefore replacing an analyzer with the corresponding Semgrep configuration\n(called rule-sets), we need to ensure that they are equivalent – in that\nthey yield the same set of findings.\n\n\nFor this purpose, we built a testing framework that helps us assess the\nquality of a Semgrep rule-set. This framework has been used to guide the\nreplacement of\n[flawfinder](https://gitlab.com/gitlab-org/security-products/analyzers/flawfinder),\na C/C++ analyzer with a corresponding Semgrep rule-set. This new testing\nframework leverages the power of GitLab CI/CD.\n\n\n## Preliminaries\n\n\n### GitLab and the Google Summer Of Code (GSoC)\n\n\nThe Google Summer of Code (GSoC) is a 10-week program that enlists student\ninterns to work on an open source project in collaboration with open source\norganizations. For GSoC 2021, GitLab offered [4 GSoC projects to the GSoC\ninterns](/blog/gsoc-at-gitlab/). The [interns completed each of\nproject](https://gitlab.com/gitlab-com/marketing/community-relations/contributor-program/gitlab-gsoc-2021/-/issues)\nunder the guidance of a GitLab team member who serves as their mentor and\nprovides regular feedback and assistance when needed.\n\n\n**[Read reflections from the Google Summer of Code interns about [what it\nwas like working with GitLab](/blog/gsoc-at-gitlab/)]**\n\n\n### About Semgrep\n\n\n[Semgrep](https://semgrep.dev/) is a language-agnostic static-analysis\n(SAST) tool that is powered by\n[tree-sitter](https://tree-sitter.github.io/tree-sitter/). Tree-sitter is a\nrobust parser-generator tool that supports parsing a variety of languages.\n\n\nSemgrep supports a\n[rule-syntax](https://semgrep.dev/docs/writing-rules/rule-syntax/) which can\nbe used to formulate detection rules in a configuration-as-code YAML format.\nA Semgrep rule determines the findings that Semgrep is supposed to detect.\nThese rules are combined together to create a rule-set.\n\n\n### About GitLab SAST\n\n\nGitLab is a complete DevSecOps platform and integrates a [variety of static\nanalysis\ntools](https://docs.gitlab.com/ee/user/application_security/sast/analyzers.html)\nthat help developers find vulnerabilities as early as possible in the\nsoftware development lifecycle (SDLC).\n\n\nSince all the integrated SAST tools are very different in terms of\nimplementation as well as tech stack they depend on, the SAST tools are all\nwrapped in Docker images. The wrappers translate the native vulnerability\nreports to a [generic, common report\nformat](https://docs.gitlab.com/ee/user/application_security/sast/) which is\nmade available by means of the `gl-sast-report.json` artifact. This generic\nreport is GitLab's common interface between analyzers and the GitLab Rails\nbackend.\n\n\n## Write vulnerability detection rules\n\n\n### Some background on our SAST tools\n\n\nOver time, the growing number of integrated SAST tools has become a\nmaintenance burden for GitLab due to two major contributing factors.\n\n\n1. **Integration cost**: All SAST tools have different release cycles – new\nreleases have to be pulled in immediately so that our users can benefit from\nthem. Given the large amount of integrated SAST tools, the time spent to\nmonitor the SAST tools for new releases, integrating and testing them is\nexpensive in terms of engineering effort/time.\n\n\n1. **Inflexibility**: Adapting or modifying SAST tools behavior is\nnon-trivial because each tool is based on different technologies. Also,\nupstream contributions to the original analyzer repositories are not\nguaranteed to be included by the maintainers. In these cases, they require\nus to fork a project which is not a scalable solution with regards to\nmaintenance effort.\n\n\nGitLab is in the process of replacing various SAST tools with a single,\nlanguage-agnostic SAST tool, called Semgrep, to fix these problems. Semgrep\ncan be configured by means of rules that are used to define what Semgrep is\nsupposed to find. These rules are provided as YAML configuration files so it\nis fairly easy to modify the behavior of Semgrep to different use cases.\n\nSemgrep's configuration-as-code approach paired with its language support\nenables us to replace multiple analyzers, which effectively reduces the\nmaintenance burden.\n\n\nHowever, the SAST tool replacement itself is a challenging process. For the\nmajority of use cases we have to assume that there is already a large amount\nof historic vulnerability data recorded and acted upon using [GitLab's\nvulnerability management\nfeatures](https://handbook.gitlab.com/handbook/security/threat-management/vulnerability-management/).\nUsers may also have grown accustomed to working with certain analyzers and\nmay even have a certain level of expectation with regards to the findings\nproduced by the analyzer.\n\n\nA smooth transition from a language-specific analyzer to a corresponding\nSemgrep rule-set must be guaranteed by meeting a certain level of quality\nassurance. A rule-set should be at least as good as the results produced by\nthe original analyzers, also known as parity. In turn, parity required we\nbuild test-suites to be used to measure the gap (in terms of rule coverage)\nbetween the original analyzer and the rule-set that is to replace it. A good\nquality rule-set is expected to perform at least as well as the SAST tool it\naims to replace (zero gap, full parity).\n\n\nThere are cases where the original SAST tool may falsely report\nvulnerabilities. In these situations, we aim to improve our rule-set in a\ncontrolled manner by explicitly documenting our improvements. However,\nbefore improving a rule-set, we want to start from a position of complete\nparity so that we have a holistic view of the impact incurred by single rule\nimprovements. This documentation of applied improvements is important so we\ncan justify changes with regard to reported findings to the customer.\n\n\nThere are three challenges we tried to address with this project:\n\n\n1. **Rule management**: Provide a central rule repository to store,\ndistribute and track changes applied to rules as well as test-cases.\n\n1. **Rule testing**: Every change applied to a rule in the rule repository\ntriggers an automated gap-analysis that measure the quality of the rules in\ncomparison to the original analyzers.\n\n1. **Analyzer replacement**: Replace at least one SAST tool (in our case\nflawfinder) with a corresponding rule-set – use the testing framework to\nensure that the rule-set is on par with the original SAST tool.\n\n\nWe unpack each of these challenges in the next section.\n\n\n### How we approached these challenges\n\n\nThe architecture of the rule-testing framework is depicted in the code\nsnippets below. All the Semgrep rules and the corresponding test-cases are\nstored in a central rule repository. Changes that are applied to the rules\ntrigger the execution of our rule testing framework that uses the rules and\ntest-cases to perform an automated gap analysis.\n\n\n\u003Cpre class=\"mermaid\">\n\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\n\nThe rule testing framework is a compass that guides us through the rule\ndevelopment process by automatically measuring the efficacy of rules that\nare stored in the central rule (git) repository. This measurement happens\nduring a comparison step that validates the findings reported by the\noriginal analyzer against the corresponding Semgrep rule-set. For the\ncomparisons we cross-validate the SAST\n\nreports\n([`gl-sast-report.json`](https://docs.gitlab.com/ee/user/application_security/sast/))\nthat adhere to the GitLab security report format. Since the main goal is to\nachieve parity between the original analyzer and our corresponding Semgrep\nrules, we treat the original analyzer as the baseline. The code snippet\nabove depicts two example comparison steps for bandit and flawfinder.  The\ngap analysis is explained in more detail in the \"rule testing\" section\nbelow.\n\n\nUsing a central rule git repository allows us to manage and easily track\nchanges that are applied to rules and their corresponding test-cases in a\ncentral location. By means of GitLab CI/CD, we have a mechanism to\nautomatically run tests that enforce constraints and guidelines on the rules\nand test-cases. Upon rule changes, we automatically trigger the rule-testing\nframework which enables us to spot gaps in our rules instantly. The\nstructure of the central rule repository is detailed in the \"rule\nmanagement\" section below.\n\n\n#### How we addressed rule management challenges\n\n\nThe central rule repository is used to store, keep track of changes applied\nto `rules/test-cases` for a variety of different languages. By having a\nseparate rule repository we can add CI jobs to test, verify, and enforce\nsyntax guidelines.\n\n\nThe structure we use for the central rule repository is depicted below and\nfollows the structure: `\u003Clanguage>/\u003Cruleclass>/{rule-\u003Crulename>.yml,\ntest-\u003Crulename>.*}` where language denotes the target programming language,\n`\u003Cruleclass>` is a descriptive name for the class of issues the rule aims to\ndetect and `\u003Crulename>` is a descriptive name for the actual rule.  We can\nhave multiple test cases per rule (all prefixed with `test-`) and rule files\n`rule-\u003Crulename>.yml` that are prefixed with `rule-` – a rule file contains\na single Semgrep rule.\n\n\n``` bash\n\n.\n\n├── mappings\n\n│   └── analyzer.yml\n\n├── c\n\n│   ├── buffer\n\n│   │   ├── rule-strcpy.yml\n\n│   │   ├── test-strcpy.c\n\n│   │   ├── rule-memcpy.yml\n\n│   │   └── test-memcpy.c\n\n│   └── ...\n\n└── javascript\n\n│   └── ...\n\n└── python\n\n│    ├── assert\n\n│    │   ├── rule-assert.yml\n\n│    │   └── test-assert.py\n\n│    └── exec\n\n│    │   ├── rule-exec.yml\n\n│    │   ├── test-exec.yml\n\n│    │   ├── rule-something.yml\n\n│    │   └── test-something.yml\n\n│    └── permission\n\n│    │   ├── rule-chmod.yml\n\n│    │   └── test-chmod.py\n\n│    └── ...\n\n└── ...\n\n```\n\n\nIn addition to the rules, we also store mapping files (in the `mappings`\nsubdirectory). The mappings directory in this repository contains YAML\nconfiguration/mapping files that map native analyzer IDs to the\ncorresponding Semgrep rules. An analyzer ID uniquely identifies the type of\nfinding. The information in the mapping files helps us to correlate the\nfinding from the original analyzer with their corresponding Semgrep findings\nand vice versa.\n\n\nThe mapping files are digested by the testing framework to perform an\nautomated gap analysis. The goal of this analysis is to check if there is an\nunexpected deviation between Semgrep (with the rules in this repository) and\na given analyzer.\n\n\nA mapping file groups distinct rules into rule-sets and, thus, can be used\nto bundle different rules based on a certain domain. An excerpt from a\nmapping file is depicted below – it maps bandit rules (identified by bandit\nIDs) to Semgrep rules from the central rule repository.\n\n\n``` yaml\n\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\n#### How the rule testing framework works\n\n\nThe test-oracle/baseline is provided by the original analyzer when executed\non the test-files. The rules in the central rule repository are compared and\nevaluated against this baseline. The execution of the testing framework is\ntriggered by any change applied to the rule repository.\n\n\nWe run all analyzers (flawfinder, bandit, etc.) and their corresponding\nSemgrep rule-sets (as defined by the mapping files) on the test-files from\nthe GitLab rule repository. The resulting `gl-sast-reports.json` reports\nthat are produced by the original analyzer and by the Semgrep analyzer are\nthen compared in a pairwise manner. To identify identical findings in both\nreports, we leverage the information from the mapping files that maps the\nrule-ids of the baseline analyzer to the corresponding Semgrep rule-ids for\nthe rules stored in the central rule repository.\n\n\nAs output, we produce a gap analysis report (in markdown format). The gap\nanalysis lists all the findings that have been reported by the original\nanalyzers and groups them into different tables (based on the native\nrule-ids). The screenshot below shows a single table from the gap analysis\nreport.\n\n\n![Gap Analysis\nReport](https://about.gitlab.com/images/blogimages/testing-framework-report.png){:\n.shadow.center}\n\nAn example table from the gap analysis report.\n\n{: .note.text-center}\n\n\nThe `X` symbols indicate whether the analyzers (in the example, flawfinder\nand Semgrep) were able to detect a given finding. The concrete findings as\nwell as the rule files are linked in the table. To reach full coverage,\nflawfinder as well as Semgrep have to cover the same findings for all the\nrules that are reported by the baseline.\n\n\n#### The analyzer replacement\n\n\nTo build a Semgrep rule-set that is on par with the capabilities of the\noriginal/baseline analyzer we leveraged the newly created testing framework.\nFlawfinder, a C/C++ analyzer, was the first analyzer we fully migrated to\nSemgrep using the testing framework as a compass.\n\n\nFirst, we checked the flawfinder implementation to identify the implemented\nrules. Given that flawfinder is a Python script and that the rules are\nessentially stored in a dictionary/hash data-structure, we were able to\nsemi-automatically extract the rules and generate the corresponding Semgrep\nrule files. We were also able to source the test-files from the flawfinder\nsource code repository.\n\n\nAfter the initial import of the first set of rules-files and test-cases, we\nused the information provided by the testing-framework to see which rules\nneeded refinement.\n\n\nWe responded to the information provided by our testing framework in the\nfollowing way:\n\n\n1. Findings covered by Baseline and covered by our rule-set: Nothing to be\ndone.\n\n1. Findings covered by Baseline but not covered by our rule-set: This\ndenotes an incomplete ruleset. In this case we extended the rule-file by\nproviding additional `pattern` entries.\n\n1. Findings not covered by Baseline but covered by our rule-set: This\nusually denotes that some rules are too vaguely formulated. In this case, we\nrefined our rules by using exclusions, e.g., by using `pattern-not` or by\nadding more detail to an already existing pattern.\n\n\nThe rule design was an iterative process where we closed the gaps between\nour semgrep rule-set and the flawfinder baseline in an iterative manner\nusing the testing framework as an oracle to ultimately achieve 100% parity.\n\n\n## How the GSoC project helped GitLab\n\n\nIn this GSoC project we successfully built an automated rule/configuration\ntesting framework that is driven by GitLab CI/CD capabilities and that\nprovided the data we needed to replace flawfinder reliably and quickly with\na corresponding Semgrep rule-set.\n\n\nIf you are interested in finding out more information about this GSoC\nproject, please check out the following repositories:\n\n\n- [Central Rule\nRepository](https://gitlab.com/gitlab-org/secure/gsoc-sast-vulnerability-rules/playground/sast-rules)\n\n- [Testing\nFramework](https://gitlab.com/gitlab-org/secure/gsoc-sast-vulnerability-rules/rule-testing-framework/rule-testing)\n\n- [Gap Analysis Computation\nTool](https://gitlab.com/gitlab-org/secure/gsoc-sast-vulnerability-rules/rule-testing-framework/report-diff)\n\n- [Repository to track gap\nstatistics](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":462,"_type":31,"title":463,"_source":33,"_file":464,"_stem":465,"_extension":36},"/shared/en-us/main-navigation","en-us",{"logo":41,"freeTrial":46,"sales":51,"login":56,"items":61,"search":393,"minimal":424,"duo":443,"pricingDeployment":452},{"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,204,209,314,374],{"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":186},"Product",true,{"dataNavLevelOne":110},"solutions",{"text":112,"config":113},"View all Solutions",{"href":114,"dataGaName":110,"dataGaLocation":45},"/solutions/",[116,141,165],{"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,155,160],{"text":151,"config":152},"Application Security Testing",{"href":153,"dataGaName":154,"dataGaLocation":45},"/solutions/application-security-testing/","Application security testing",{"text":156,"config":157},"Software Supply Chain Security",{"href":158,"dataGaLocation":45,"dataGaName":159},"/solutions/supply-chain/","Software supply chain security",{"text":161,"config":162},"Software Compliance",{"href":163,"dataGaName":164,"dataGaLocation":45},"/solutions/software-compliance/","software compliance",{"title":166,"link":167,"items":172},"Measurement",{"config":168},{"icon":169,"href":170,"dataGaName":171,"dataGaLocation":45},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[173,177,181],{"text":174,"config":175},"Visibility & Measurement",{"href":170,"dataGaLocation":45,"dataGaName":176},"Visibility and Measurement",{"text":178,"config":179},"Value Stream Management",{"href":180,"dataGaLocation":45,"dataGaName":178},"/solutions/value-stream-management/",{"text":182,"config":183},"Analytics & Insights",{"href":184,"dataGaLocation":45,"dataGaName":185},"/solutions/analytics-and-insights/","Analytics and insights",{"title":187,"items":188},"GitLab for",[189,194,199],{"text":190,"config":191},"Enterprise",{"href":192,"dataGaLocation":45,"dataGaName":193},"/enterprise/","enterprise",{"text":195,"config":196},"Small Business",{"href":197,"dataGaLocation":45,"dataGaName":198},"/small-business/","small business",{"text":200,"config":201},"Public Sector",{"href":202,"dataGaLocation":45,"dataGaName":203},"/solutions/public-sector/","public sector",{"text":205,"config":206},"Pricing",{"href":207,"dataGaName":208,"dataGaLocation":45,"dataNavLevelOne":208},"/pricing/","pricing",{"text":210,"config":211,"link":213,"lists":217,"feature":301},"Resources",{"dataNavLevelOne":212},"resources",{"text":214,"config":215},"View all resources",{"href":216,"dataGaName":212,"dataGaLocation":45},"/resources/",[218,251,273],{"title":219,"items":220},"Getting started",[221,226,231,236,241,246],{"text":222,"config":223},"Install",{"href":224,"dataGaName":225,"dataGaLocation":45},"/install/","install",{"text":227,"config":228},"Quick start guides",{"href":229,"dataGaName":230,"dataGaLocation":45},"/get-started/","quick setup checklists",{"text":232,"config":233},"Learn",{"href":234,"dataGaLocation":45,"dataGaName":235},"https://university.gitlab.com/","learn",{"text":237,"config":238},"Product documentation",{"href":239,"dataGaName":240,"dataGaLocation":45},"https://docs.gitlab.com/","product documentation",{"text":242,"config":243},"Best practice videos",{"href":244,"dataGaName":245,"dataGaLocation":45},"/getting-started-videos/","best practice videos",{"text":247,"config":248},"Integrations",{"href":249,"dataGaName":250,"dataGaLocation":45},"/integrations/","integrations",{"title":252,"items":253},"Discover",[254,259,263,268],{"text":255,"config":256},"Customer success stories",{"href":257,"dataGaName":258,"dataGaLocation":45},"/customers/","customer success stories",{"text":260,"config":261},"Blog",{"href":262,"dataGaName":5,"dataGaLocation":45},"/blog/",{"text":264,"config":265},"Remote",{"href":266,"dataGaName":267,"dataGaLocation":45},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":269,"config":270},"TeamOps",{"href":271,"dataGaName":272,"dataGaLocation":45},"/teamops/","teamops",{"title":274,"items":275},"Connect",[276,281,286,291,296],{"text":277,"config":278},"GitLab Services",{"href":279,"dataGaName":280,"dataGaLocation":45},"/services/","services",{"text":282,"config":283},"Community",{"href":284,"dataGaName":285,"dataGaLocation":45},"/community/","community",{"text":287,"config":288},"Forum",{"href":289,"dataGaName":290,"dataGaLocation":45},"https://forum.gitlab.com/","forum",{"text":292,"config":293},"Events",{"href":294,"dataGaName":295,"dataGaLocation":45},"/events/","events",{"text":297,"config":298},"Partners",{"href":299,"dataGaName":300,"dataGaLocation":45},"/partners/","partners",{"backgroundColor":302,"textColor":303,"text":304,"image":305,"link":309},"#2f2a6b","#fff","Insights for the future of software development",{"altText":306,"config":307},"the source promo card",{"src":308},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":310,"config":311},"Read the latest",{"href":312,"dataGaName":313,"dataGaLocation":45},"/the-source/","the source",{"text":315,"config":316,"lists":318},"Company",{"dataNavLevelOne":317},"company",[319],{"items":320},[321,326,332,334,339,344,349,354,359,364,369],{"text":322,"config":323},"About",{"href":324,"dataGaName":325,"dataGaLocation":45},"/company/","about",{"text":327,"config":328,"footerGa":331},"Jobs",{"href":329,"dataGaName":330,"dataGaLocation":45},"/jobs/","jobs",{"dataGaName":330},{"text":292,"config":333},{"href":294,"dataGaName":295,"dataGaLocation":45},{"text":335,"config":336},"Leadership",{"href":337,"dataGaName":338,"dataGaLocation":45},"/company/team/e-group/","leadership",{"text":340,"config":341},"Team",{"href":342,"dataGaName":343,"dataGaLocation":45},"/company/team/","team",{"text":345,"config":346},"Handbook",{"href":347,"dataGaName":348,"dataGaLocation":45},"https://handbook.gitlab.com/","handbook",{"text":350,"config":351},"Investor relations",{"href":352,"dataGaName":353,"dataGaLocation":45},"https://ir.gitlab.com/","investor relations",{"text":355,"config":356},"Trust Center",{"href":357,"dataGaName":358,"dataGaLocation":45},"/security/","trust center",{"text":360,"config":361},"AI Transparency Center",{"href":362,"dataGaName":363,"dataGaLocation":45},"/ai-transparency-center/","ai transparency center",{"text":365,"config":366},"Newsletter",{"href":367,"dataGaName":368,"dataGaLocation":45},"/company/contact/","newsletter",{"text":370,"config":371},"Press",{"href":372,"dataGaName":373,"dataGaLocation":45},"/press/","press",{"text":375,"config":376,"lists":377},"Contact us",{"dataNavLevelOne":317},[378],{"items":379},[380,383,388],{"text":52,"config":381},{"href":54,"dataGaName":382,"dataGaLocation":45},"talk to sales",{"text":384,"config":385},"Get help",{"href":386,"dataGaName":387,"dataGaLocation":45},"/support/","get help",{"text":389,"config":390},"Customer portal",{"href":391,"dataGaName":392,"dataGaLocation":45},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":394,"login":395,"suggestions":402},"Close",{"text":396,"link":397},"To search repositories and projects, login to",{"text":398,"config":399},"gitlab.com",{"href":59,"dataGaName":400,"dataGaLocation":401},"search login","search",{"text":403,"default":404},"Suggestions",[405,407,411,413,417,421],{"text":74,"config":406},{"href":79,"dataGaName":74,"dataGaLocation":401},{"text":408,"config":409},"Code Suggestions (AI)",{"href":410,"dataGaName":408,"dataGaLocation":401},"/solutions/code-suggestions/",{"text":126,"config":412},{"href":128,"dataGaName":126,"dataGaLocation":401},{"text":414,"config":415},"GitLab on AWS",{"href":416,"dataGaName":414,"dataGaLocation":401},"/partners/technology-partners/aws/",{"text":418,"config":419},"GitLab on Google Cloud",{"href":420,"dataGaName":418,"dataGaLocation":401},"/partners/technology-partners/google-cloud-platform/",{"text":422,"config":423},"Why GitLab?",{"href":87,"dataGaName":422,"dataGaLocation":401},{"freeTrial":425,"mobileIcon":430,"desktopIcon":435,"secondaryButton":438},{"text":426,"config":427},"Start free trial",{"href":428,"dataGaName":50,"dataGaLocation":429},"https://gitlab.com/-/trials/new/","nav",{"altText":431,"config":432},"Gitlab Icon",{"src":433,"dataGaName":434,"dataGaLocation":429},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":431,"config":436},{"src":437,"dataGaName":434,"dataGaLocation":429},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":439,"config":440},"Get Started",{"href":441,"dataGaName":442,"dataGaLocation":429},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":444,"mobileIcon":448,"desktopIcon":450},{"text":445,"config":446},"Learn more about GitLab Duo",{"href":79,"dataGaName":447,"dataGaLocation":429},"gitlab duo",{"altText":431,"config":449},{"src":433,"dataGaName":434,"dataGaLocation":429},{"altText":431,"config":451},{"src":437,"dataGaName":434,"dataGaLocation":429},{"freeTrial":453,"mobileIcon":458,"desktopIcon":460},{"text":454,"config":455},"Back to pricing",{"href":207,"dataGaName":456,"dataGaLocation":429,"icon":457},"back to pricing","GoBack",{"altText":431,"config":459},{"src":433,"dataGaName":434,"dataGaLocation":429},{"altText":431,"config":461},{"src":437,"dataGaName":434,"dataGaLocation":429},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":467,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"title":468,"button":469,"image":474,"config":478,"_id":480,"_type":31,"_source":33,"_file":481,"_stem":482,"_extension":36},"/shared/en-us/banner","is now in public beta!",{"text":470,"config":471},"Try the Beta",{"href":472,"dataGaName":473,"dataGaLocation":45},"/gitlab-duo/agent-platform/","duo banner",{"altText":475,"config":476},"GitLab Duo Agent Platform",{"src":477},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":479},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":484,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"data":485,"_id":689,"_type":31,"title":690,"_source":33,"_file":691,"_stem":692,"_extension":36},"/shared/en-us/main-footer",{"text":486,"source":487,"edit":493,"contribute":498,"config":503,"items":508,"minimal":681},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":488,"config":489},"View page source",{"href":490,"dataGaName":491,"dataGaLocation":492},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":494,"config":495},"Edit this page",{"href":496,"dataGaName":497,"dataGaLocation":492},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":499,"config":500},"Please contribute",{"href":501,"dataGaName":502,"dataGaLocation":492},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":504,"facebook":505,"youtube":506,"linkedin":507},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[509,532,588,617,651],{"title":63,"links":510,"subMenu":515},[511],{"text":512,"config":513},"DevSecOps platform",{"href":72,"dataGaName":514,"dataGaLocation":492},"devsecops platform",[516],{"title":205,"links":517},[518,522,527],{"text":519,"config":520},"View plans",{"href":207,"dataGaName":521,"dataGaLocation":492},"view plans",{"text":523,"config":524},"Why Premium?",{"href":525,"dataGaName":526,"dataGaLocation":492},"/pricing/premium/","why premium",{"text":528,"config":529},"Why Ultimate?",{"href":530,"dataGaName":531,"dataGaLocation":492},"/pricing/ultimate/","why ultimate",{"title":533,"links":534},"Solutions",[535,540,542,544,549,554,558,561,565,570,572,575,578,583],{"text":536,"config":537},"Digital transformation",{"href":538,"dataGaName":539,"dataGaLocation":492},"/topics/digital-transformation/","digital transformation",{"text":151,"config":541},{"href":153,"dataGaName":151,"dataGaLocation":492},{"text":140,"config":543},{"href":122,"dataGaName":123,"dataGaLocation":492},{"text":545,"config":546},"Agile development",{"href":547,"dataGaName":548,"dataGaLocation":492},"/solutions/agile-delivery/","agile delivery",{"text":550,"config":551},"Cloud transformation",{"href":552,"dataGaName":553,"dataGaLocation":492},"/topics/cloud-native/","cloud transformation",{"text":555,"config":556},"SCM",{"href":136,"dataGaName":557,"dataGaLocation":492},"source code management",{"text":126,"config":559},{"href":128,"dataGaName":560,"dataGaLocation":492},"continuous integration & delivery",{"text":562,"config":563},"Value stream management",{"href":180,"dataGaName":564,"dataGaLocation":492},"value stream management",{"text":566,"config":567},"GitOps",{"href":568,"dataGaName":569,"dataGaLocation":492},"/solutions/gitops/","gitops",{"text":190,"config":571},{"href":192,"dataGaName":193,"dataGaLocation":492},{"text":573,"config":574},"Small business",{"href":197,"dataGaName":198,"dataGaLocation":492},{"text":576,"config":577},"Public sector",{"href":202,"dataGaName":203,"dataGaLocation":492},{"text":579,"config":580},"Education",{"href":581,"dataGaName":582,"dataGaLocation":492},"/solutions/education/","education",{"text":584,"config":585},"Financial services",{"href":586,"dataGaName":587,"dataGaLocation":492},"/solutions/finance/","financial services",{"title":210,"links":589},[590,592,594,596,599,601,603,605,607,609,611,613,615],{"text":222,"config":591},{"href":224,"dataGaName":225,"dataGaLocation":492},{"text":227,"config":593},{"href":229,"dataGaName":230,"dataGaLocation":492},{"text":232,"config":595},{"href":234,"dataGaName":235,"dataGaLocation":492},{"text":237,"config":597},{"href":239,"dataGaName":598,"dataGaLocation":492},"docs",{"text":260,"config":600},{"href":262,"dataGaName":5,"dataGaLocation":492},{"text":255,"config":602},{"href":257,"dataGaName":258,"dataGaLocation":492},{"text":264,"config":604},{"href":266,"dataGaName":267,"dataGaLocation":492},{"text":277,"config":606},{"href":279,"dataGaName":280,"dataGaLocation":492},{"text":269,"config":608},{"href":271,"dataGaName":272,"dataGaLocation":492},{"text":282,"config":610},{"href":284,"dataGaName":285,"dataGaLocation":492},{"text":287,"config":612},{"href":289,"dataGaName":290,"dataGaLocation":492},{"text":292,"config":614},{"href":294,"dataGaName":295,"dataGaLocation":492},{"text":297,"config":616},{"href":299,"dataGaName":300,"dataGaLocation":492},{"title":315,"links":618},[619,621,623,625,627,629,631,635,640,642,644,646],{"text":322,"config":620},{"href":324,"dataGaName":317,"dataGaLocation":492},{"text":327,"config":622},{"href":329,"dataGaName":330,"dataGaLocation":492},{"text":335,"config":624},{"href":337,"dataGaName":338,"dataGaLocation":492},{"text":340,"config":626},{"href":342,"dataGaName":343,"dataGaLocation":492},{"text":345,"config":628},{"href":347,"dataGaName":348,"dataGaLocation":492},{"text":350,"config":630},{"href":352,"dataGaName":353,"dataGaLocation":492},{"text":632,"config":633},"Sustainability",{"href":634,"dataGaName":632,"dataGaLocation":492},"/sustainability/",{"text":636,"config":637},"Diversity, inclusion and belonging (DIB)",{"href":638,"dataGaName":639,"dataGaLocation":492},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":355,"config":641},{"href":357,"dataGaName":358,"dataGaLocation":492},{"text":365,"config":643},{"href":367,"dataGaName":368,"dataGaLocation":492},{"text":370,"config":645},{"href":372,"dataGaName":373,"dataGaLocation":492},{"text":647,"config":648},"Modern Slavery Transparency Statement",{"href":649,"dataGaName":650,"dataGaLocation":492},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":652,"links":653},"Contact Us",[654,657,659,661,666,671,676],{"text":655,"config":656},"Contact an expert",{"href":54,"dataGaName":55,"dataGaLocation":492},{"text":384,"config":658},{"href":386,"dataGaName":387,"dataGaLocation":492},{"text":389,"config":660},{"href":391,"dataGaName":392,"dataGaLocation":492},{"text":662,"config":663},"Status",{"href":664,"dataGaName":665,"dataGaLocation":492},"https://status.gitlab.com/","status",{"text":667,"config":668},"Terms of use",{"href":669,"dataGaName":670,"dataGaLocation":492},"/terms/","terms of use",{"text":672,"config":673},"Privacy statement",{"href":674,"dataGaName":675,"dataGaLocation":492},"/privacy/","privacy statement",{"text":677,"config":678},"Cookie preferences",{"dataGaName":679,"dataGaLocation":492,"id":680,"isOneTrustButton":108},"cookie preferences","ot-sdk-btn",{"items":682},[683,685,687],{"text":667,"config":684},{"href":669,"dataGaName":670,"dataGaLocation":492},{"text":672,"config":686},{"href":674,"dataGaName":675,"dataGaLocation":492},{"text":677,"config":688},{"dataGaName":679,"dataGaLocation":492,"id":680,"isOneTrustButton":108},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[694,706,715],{"_path":695,"_dir":696,"_draft":6,"_partial":6,"_locale":7,"content":697,"config":701,"_id":703,"_type":31,"title":18,"_source":33,"_file":704,"_stem":705,"_extension":36},"/en-us/blog/authors/ross-fuhrman","authors",{"name":18,"config":698},{"headshot":699,"ctfId":700},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","7dkuWBvIc0AQanUclt3pOk",{"template":702},"BlogAuthor","content:en-us:blog:authors:ross-fuhrman.yml","en-us/blog/authors/ross-fuhrman.yml","en-us/blog/authors/ross-fuhrman",{"_path":707,"_dir":696,"_draft":6,"_partial":6,"_locale":7,"content":708,"config":711,"_id":712,"_type":31,"title":19,"_source":33,"_file":713,"_stem":714,"_extension":36},"/en-us/blog/authors/anshuman-singh",{"name":19,"config":709},{"headshot":699,"ctfId":710},"4xzrY67JSkxp4j7hlK1DWA",{"template":702},"content:en-us:blog:authors:anshuman-singh.yml","en-us/blog/authors/anshuman-singh.yml","en-us/blog/authors/anshuman-singh",{"_path":716,"_dir":696,"_draft":6,"_partial":6,"_locale":7,"content":717,"config":720,"_id":721,"_type":31,"title":20,"_source":33,"_file":722,"_stem":723,"_extension":36},"/en-us/blog/authors/julian-thome",{"name":20,"config":718},{"headshot":7,"ctfId":719},"jthome",{"template":702},"content:en-us:blog:authors:julian-thome.yml","en-us/blog/authors/julian-thome.yml","en-us/blog/authors/julian-thome",{"_path":725,"_dir":39,"_draft":6,"_partial":6,"_locale":7,"header":726,"eyebrow":727,"blurb":728,"button":729,"secondaryButton":733,"_id":735,"_type":31,"title":736,"_source":33,"_file":737,"_stem":738,"_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":730},{"href":731,"dataGaName":50,"dataGaLocation":732},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":52,"config":734},{"href":54,"dataGaName":55,"dataGaLocation":732},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1758326248966]