[{"data":1,"prerenderedAt":707},["ShallowReactive",2],{"/en-us/blog/fantastic-infrastructure-as-code-security-attacks-and-how-to-find-them/":3,"navigation-en-us":36,"banner-en-us":453,"footer-en-us":468,"Michael Friedrich":679,"next-steps-en-us":692},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":26,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},"/en-us/blog/fantastic-infrastructure-as-code-security-attacks-and-how-to-find-them","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"Fantastic Infrastructure as Code security attacks and how to find them","Learn about possible attack scenarios in Infrastructure as Code and GitOps environments, evaluate tools and scanners with Terraform, Kubernetes, etc., and more.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667482/Blog/Hero%20Images/cover-image-unsplash.jpg","https://about.gitlab.com/blog/fantastic-infrastructure-as-code-security-attacks-and-how-to-find-them","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Fantastic Infrastructure as Code security attacks and how to find them\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Michael Friedrich\"}],\n        \"datePublished\": \"2022-02-17\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Michael Friedrich","2022-02-17","\n[Infrastructure as Code](/topics/gitops/infrastructure-as-code/)(IaC) has eaten the world. It helps manage and provision computer resources automatically and avoids manual work or UI form workflows. Lifecycle management with IaC started with declarative and idempotent configuration, package, and tool installation. In the era of cloud providers, IaC tools additionally help abstract cloud provisioning. They can create defined resources automatically (network, storage, databases, etc.) and apply the configuration (DNS entries, firewall rules, etc.).\n\nLike everything else, it has its flaws. IaC workflows have shifted left in the development lifecycle, making it more efficient. Developers and DevOps engineers need to learn new tools and best practices. Mistakes may result in leaked credentials or supply chain attacks. Existing security assessment tools might not be able to detect these new vulnerabilities.\n\nIn this post, we will dive into these specific risks and focus on IaC management tools such as Terraform, cloud providers, and deployment platforms involving containers and Kubernetes.\n\nFor each scenario, we will look into threats, tools, integrations, and best practices to reduce risk.\n\nYou can read the blog post top-down or navigate into the chapters individually.\n\n- [Scan your own infrastructure - know what's important](#scan-your-infrastructure---know-what-is-important)\n    - [Thinking like an attacker](#thinking-like-an-attacker)\n- [Tools to detect Terraform vulnerabilities](#tools-to-detect-terraform-vulnerabilities)\n- [Develop more IaC scenarios](#develop-more-iac-scenarios)\n    - [Terraform Module Dependency Scans](#terraform-module-dependency-scans)\n    - [IaC Security Scanning for Containers](#iac-security-scanning-for-containers)\n    - [IaC Security Scanning with Kubernetes](#iac-security-scanning-with-kubernetes)\n- [Integrations into CI/CD and Merge Requests for Review](#integrations-into-cicd-and-merge-requests-for-review)\n    - [Reports in MRs as comment](#reports-in-mrs-as-comment)\n    - [MR Comments using GitLab IaC SAST reports as source](#mr-comments-using-gitlab-iac-sast-reports-as-source)\n- [What is the best integration strategy?](#what-is-the-best-integration-strategy)\n\n## Scan your infrastructure - know what is important\n\nStart with identifying the project/group responsible for managing the IAC tasks. An inventory search for specific IaC tools, file suffixes (Terraform uses `.tf`, for example), and languages can be helpful. The security scan tools discussed in this blog post will discover all supported types automatically. Once you have identified the projects, you can use one of the tools to run a scan and identify the detected possible vulnerabilities.\n\nThere might not be any scan results because your infrastructure is secure at this time. Though, your processes may require you to create documentation, runbooks, and action items for eventually discovered vulnerabilities in the future. Creating a forecast on possible scenarios to defend is hard, so let us change roles from the defender to the attacker for a moment. Which security vulnerabilities are out there to exploit as a malicious attacker? Maybe it is possible to create vulnerable scenarios and simulate the attacker role by running a security scan.\n\n### Thinking like an attacker\n\nThere can be noticeable potential vulnerabilities like plaintext passwords in the configuration. Other scenarios involve cases you would never think of or a chain of items causing a security issue.\n\nLet us create a scenario for an attacker by provisioning an S3 bucket in AWS with Terraform. We intend to store logs, database dumps, or credential vaults in this S3 bucket.\n\nThe following example creates the `aws_s3_bucket` resource in Terraform using the AWS provider.\n\n```hcl\n# Create the bucket\nresource \"aws_s3_bucket\" \"demobucket\" {\n  bucket = \"terraformdemobucket\"\n  acl = \"private\"\n}\n```\n\nAfter provisioning the S3 bucket for the first time, someone decided to make the S3 bucket accessible by default. The example below grants public access to the bucket using `aws_s3_bucket_public_access_block`. `block_public_acls` and `block_public_policy` are set to `false` to allow any public access.\n\n```\n# Grant bucket access: public\nresource \"aws_s3_bucket_public_access_block\" \"publicaccess\" {\n  bucket = aws_s3_bucket.demobucket.id\n  block_public_acls = false\n  block_public_policy = false\n}\n```\n\nThe S3 bucket is now publicly readable, and anyone who knows the URL or scans network ranges for open ports may find the S3 bucket and its data. Malicious actors can not only capture credentials but also may learn about your infrastructure, IP addresses, internal server FQDNs, etc. from the logs, backups, and database dumps being stored in the S3 bucket.\n\nWe need ways to mitigate and detect this security problem. The following sections describe the different tools you can use. The full Terraform code is located in [this project](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/tree/main/terraform/aws) and allows you to test all tools described in this blog post.\n\n## Tools to detect Terraform vulnerabilities\n\nIn the \"not worst case\" scenario, the Terraform code to manage your infrastructure is persisted at a central Git server and not hidden somewhere on a host or local desktop. Maybe you are using `terraform init, plan, apply` jobs in CI/CD pipelines already. Let us look into methods and tools that help detect the public S3 bucket vulnerability. Later, we will discuss CI/CD integrations and automating IaC security scanning.\n\nBefore we dive into the tools, make sure to clone the demo project locally to follow the examples yourself.\n\n```shell\n$ cd /tmp\n$ git clone https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning.git && cd  infrastructure-as-code-scanning/\n```\n\nThe tool installation steps in this blog post are illustrated with [Homebrew on macOS](https://brew.sh/). Please refer to the tools documentation for alternative installation methods and supported platforms.\n\nYou can follow the tools for Terraform security scanning by reading top-down, or navigate into the tools sections directly:\n\n- [tfsec](#tfsec)\n- [kics](#kics)\n- [terrascan](#terrascan)\n- [semgrep](#semgrep)\n- [tflint](#tflint)\n\n### tfsec\n\n[tfsec](https://github.com/aquasecurity/tfsec) from Aqua Security can help detect Terraform vulnerabilities. There are [Docker images available](https://github.com/aquasecurity/tfsec#use-with-docker) to quickly test the scanner on the CLI, or binaries to [install tfsec](https://aquasecurity.github.io/tfsec/v1.1.4/getting-started/installation/). Run `tfsec` on the local project path `terraform/aws/` to get a list of vulnerabilities.\n\n```shell\n$ brew install tfsec\n$ tfsec terraform/aws/\n```\n\nThe default scan provides a table overview on the CLI, which may need additional filters. Inspect `tfsec –help` to get a list of all available [parameters](https://aquasecurity.github.io/tfsec/v1.1.4/getting-started/usage/) and try generating JSON and JUnit output files to process further.\n\n```shell\n$ tfsec terraform/aws --format json --out tfsec-report.json\n1 file(s) written: tfsec-report.json\n$ tfsec terraform/aws --format junit --out tfsec-junit.xml\n1 file(s) written: tfsec-junit.xml\n```\n\nThe full example is located in the [terraform/aws directory in this project](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/tree/main/terraform/aws).\n\n#### Parse tfsec JSON reports with jq\n\nIn an earlier blog post, we shared [how to detect the JSON data structures and filter with chained jq commands](/blog/devops-workflows-json-format-jq-ci-cd-lint/). The tfsec report is a good practice: Extract the `results` key, iterate through all array list items and filtered by `rule_service` being `s3`, and only print `severity`, `description` and `location.filename`.\n\n```shell\n$ jq \u003C tfsec-report.json | jq -c '.[\"results\"]' | jq -c '.[] | select (.rule_service == \"s3\") | [.severity, .description, .location.filename]'\n```\n\n![tfsec parser output example](https://about.gitlab.com/images/blogimages/iac-security-scanning/tfsec-json-jq-parser.png){: .shadow}\n\n### kics\n\n[kics](https://kics.io/) is another IaC scanner, providing support for many different tools (Ansible, Terraform, Kubernetes, Dockerfile, and cloud configuration APIs such as AWS CloudFormation, Azure Resource Manager, and Google Deployment Manager).\n\nLet's try it: [Install kics](https://docs.kics.io/latest/getting-started/) and run it on the vulnerable project. `--report-formats`, `--output-path` and `--output-name` allow you to create a JSON report which can be automatically parsed with additional tooling.\n\n```shell\n$ kics scan --path .\n$ kics scan --path . --report-formats json --output-path kics --output-name kics-report.json\n```\n\nParsing the JSON report from `kics` with jq works the same way as the tfsec example above. Inspect the data structure and nested object, and filter by AWS as `cloud_provider`. The `files` entry is an array of dictionaries, which turned out to be a little tricky to extract with an additional `(.files[] | .file_name )` to add:\n\n```\n$ jq \u003C kics/kics-report.json | jq -c '.[\"queries\"]' | jq -c '.[] | select (.cloud_provider == \"AWS\") | [.severity, .description, (.files[] | .file_name ) ]'\n```\n\n![kics json jq parser](https://about.gitlab.com/images/blogimages/iac-security-scanning/kics-json-jq-parser.png){: .shadow}\n\n`kics` returns different [exit codes](https://docs.kics.io/latest/results/#exit_status_code) based on the number of different severities found. `50` indicates `HIGH` severities and causes your CI/CD pipeline to fail.\n\n### checkov\n\n[Checkov](https://checkov.io) supports Terraform (for AWS, GCP, Azure and OCI), CloudFormation, ARM, Severless framework, Helm charts, Kubernetes, and Docker.\n\n```shell\n$ brew install checkov\n$ checkov --directory .\n```\n\n### terrascan\n\n[Terrascan](https://runterrascan.io/docs/getting-started/) supports Terraform, and more [policies](https://runterrascan.io/docs/policies/) for cloud providers, Docker, and Kubernetes.\n\n```shell\n$ brew install terrascan\n$ terrascan scan .\n```\n\n### semgrep\n\nSemgrep is working on [Terraform support](https://semgrep.dev/docs/language-support/), currently in Beta. It also detects Dockerfile errors - for example invalid port ranges and multiple ranges, similar to kics.\n\n```shell\n$ brew install semgrep\n$ semgrep --config auto .\n```\n\n### tflint\n\n[tflint](https://github.com/terraform-linters/tflint) also is an alternative scanner.\n\n## Develop more IaC scenarios\n\nWhile testing IaC Security Scanners for the first time, I was looking for demo projects and examples. The [kics queries list for Terraform](https://docs.kics.io/latest/queries/terraform-queries/) provides an exhaustive list of all vulnerabilities and the documentation linked. From there, you can build and create potential attack vectors for demos and showcases without leaking your company code and workflows.\n\n[Terragoat](https://github.com/bridgecrewio/terragoat) also is a great learning resource to test various scanners and see real-life examples for vulnerabilities.\n\n```shell\n$ cd /tmp && git clone https://github.com/bridgecrewio/terragoat.git && cd terragoat\n\n$ tfsec .\n$ kics scan --path .\n$ checkov --directory .\n$ semgrep --config auto .\n$ terrascan scan .\n```\n\nIt is also important to verify the reported vulnerabilities and create documentation for required actions for your teams. Not all detected vulnerabilities are necessarily equally critical in your environment. With the rapid development of IaC, [GitOps}(https://about.gitlab.com/topics/gitops/), and cloud-native environments, it can also be a good idea to use 2+ scanners to see if there are missing vulnerabilities on one or the other.\n\nThe following sections discuss more scenarios in detail.\n\n- [Terraform Module Dependency Scans](#terraform-module-dependency-scans)\n- [IaC Security Scanning for Containers](#iac-security-scanning-for-containers)\n- [IaC Security Scanning with Kubernetes](#iac-security-scanning-with-kubernetes)\n\n### Terraform Module Dependency Scans\n\nRe-usable IaC workflows also can introduce security vulnerabilities you are not aware of. [This project](https://gitlab.com/gitlab-de/use-cases/iac-tf-vuln-module) provides the module files and package in the registry, which can be consumed by `main.tf` in the demo project.\n\n```hcl\nmodule \"my_module_name\" {\n  source = \"gitlab.com/gitlab-de/iac-tf-vuln-module/aws\"\n  version = \"1.0.0\"\n}\n```\n\nkics has [limited support for the official Terraform module registry](https://docs.kics.io/latest/platforms/#terraform_modules), `checkov` failed to download private modules, `terrascan` and `tfsec` work when `terraform init` is run before the scan. Depending on your requirements, running `kics` for everything and `tfsec` for module dependency checks can be a solution, suggestion added [here](https://gitlab.com/groups/gitlab-org/-/epics/6653#note_840447132).\n\n### IaC Security Scanning for Containers\n\nSecurity problems in containers can lead to application deployment vulnerabilities. The [kics query database](https://docs.kics.io/latest/queries/dockerfile-queries/) helps to reverse engineer more vulnerable examples: Using the latest tag, privilege escalations with invoking sudo in a container, ports out of range, and multiple entrypoints are just a few bad practices.\n\nThe following [Dockerfile](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/blob/main/Dockerfile) implements example vulnerabilities for the scanners to detect:\n\n```\n# Create vulnerabilities based on kics queries in https://docs.kics.io/latest/queries/dockerfile-queries/\nFROM debian:latest\n\n# kics: Run Using Sudo\n# kics: Run Using apt\nRUN sudo apt install git\n\n# kics: UNIX Ports Out Of Range\nEXPOSE 99999\n\n# kics: Multiple ENTRYPOINT Instructions Listed\nENTRYPOINT [\"ex1\"]\nENTRYPOINT [\"ex2\"]\n```\n\nKics, tfsec, and terrascan can detect `Dockerfile` vulnerabilities, similar to semgrep and checkov. As an example scanner, terrascan can detect the vulnerabilities using the `--iac-type docker` parameter that allows to filter the scan type.\n\n```shell\n$ terrascan scan --iac-type docker\n```\n\n![terrascan Docker IaC type scan result](https://about.gitlab.com/images/blogimages/iac-security-scanning/terrascan-docker-iac.png){: .shadow}\n\nYou can run kics and tfsec as an exercise to verify the results.\n\n### IaC Security Scanning with Kubernetes\n\nSecuring a Kubernetes cluster can be a challenging task. Open Policy Agent, Kyverno, RBAC, etc., and many different YAML configuration attributes require reviews and automated checks before the production deployments. [Cluster image scanning](https://docs.gitlab.com/ee/user/clusters/agent/vulnerabilities.html) is one way to mitigate security threats, next to [Container scanning](https://docs.gitlab.com/ee/user/application_security/container_scanning/) for the applications being deployed. A suggested read is the book [“Hacking Kubernetes” book](https://www.oreilly.com/library/view/hacking-kubernetes/9781492081722/) by Andrew Martin and Michael Hausenblas if you want to dive deeper into Kubernetes security and attack vectors.\n\nIt's possible to make mistakes when, for example, copying YAML example configuration and continue using it. I've created a deployment and service for a [Kubernetes monitoring workshop](/handbook/marketing/developer-relations/developer-evangelism/projects/#practical-kubernetes-monitoring-with-prometheus), which provides a practical example to learn but also uses some not so good practices.\n\nThe following configuration in [ecc-demo-service.yml](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/blob/main/kubernetes/ecc-demo-service.yml) introduces vulnerabilities and potential production problems:\n\n```yaml\n---\n# A deployment for the ECC Prometheus demo service with 3 replicas.\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: ecc-demo-service\n  labels:\n    app: ecc-demo-service\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: ecc-demo-service\n  template:\n    metadata:\n      labels:\n        app: ecc-demo-service\n    spec:\n      containers:\n      - name: ecc-demo-service\n        image: registry.gitlab.com/everyonecancontribute/observability/prometheus_demo_service:latest\n        imagePullPolicy: IfNotPresent\n        args:\n        - -listen-address=:80\n        ports:\n        - containerPort: 80\n---\n# A service that references the demo service deployment.\napiVersion: v1\nkind: Service\nmetadata:\n  name: ecc-demo-service\n  labels:\n    app: ecc-demo-service\nspec:\n  ports:\n  - port: 80\n    name: web\n  selector:\n    app: ecc-demo-service\n```\n\nLet's scan the Kubernetes manifest with kics and parse the results again with jq. A list of kics queries for Kubernetes can be found in the [kics documentation](https://docs.kics.io/latest/queries/kubernetes-queries/).\n\n```shell\n$ kics scan --path kubernetes --report-formats json --output-path kics --output-name kics-report.json\n\n$ jq \u003C kics/kics-report.json | jq -c '.[\"queries\"]' | jq -c '.[] | select (.platform == \"Kubernetes\") | [.severity, .description, (.files[] | .file_name ) ]'\n```\n\n![Kubernetes manifest scans and jq parser results with kics](https://about.gitlab.com/images/blogimages/iac-security-scanning/kics-kubernetes-jq-parser.png){: .shadow}\n\n[Checkov](https://www.checkov.io/) detects similar vulnerabilities with Kubernetes.\n\n```\n$ checkov --directory kubernetes/\n$ checkov --directory kubernetes -o json > checkov-report.json\n```\n\n[kube-linter](https://docs.kubelinter.io/#/?id=installing-kubelinter) analyzes Kubernetes YAML files and Helm charts for production readiness and security.\n\n```shell\n$ brew install kube-linter\n$ kube-linter lint kubernetes/ecc-demo-service.yml --format json > kube-linter-report.json\n```\n\n[kubesec](https://kubesec.io/) provides security risk analysis for Kubernetes resources. `kubesec` is also integrated into the [GitLab SAST scanners](https://docs.gitlab.com/ee/user/application_security/sast/#enabling-kubesec-analyzer).\n\n```shell\n$ docker run -i kubesec/kubesec:512c5e0 scan /dev/stdin \u003C kubernetes/ecc-demo-service.yml\n```\n\n## Integrations into CI/CD and Merge Requests for Review\n\nThere are many scanners out there, and most of them return the results in JSON which can be parsed and integrated into your CI/CD pipelines. You can learn more about the evaluation of GitLab IaC scanners in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/39695). The table in the issue includes licenses, languages, outputs, and examples.\n\n`checkov` and `tfsec` provide JUnit XML reports as output format, which can be parsed and integrated into CI/CD. Vulnerability reports will need a different format though to not confuse them with unit test results for example. Integrating a SAST scanner in GitLab requires you to provide [artifacts:reports:sast](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportssast) as a specified output format and API. [This report](https://docs.gitlab.com/ee/user/application_security/iac_scanning/#reports-json-format) can then be consumed by GitLab integrations such as MR widgets and vulnerability dashboards, available in the Ultimate tier. The following screenshot shows adding a Kubernetes deployment and service with potential vulnerabilities in [this MR](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/merge_requests/3).\n\n![MR widget showing IaC vulnerabilities with Kubernetes](https://about.gitlab.com/images/blogimages/iac-security-scanning/gitlab-iac-mr-widget-kubernetes.png){: .shadow}\n\n### Reports in MRs as comment\n\nThere are different ways to collect the JSON reports in your CI/CD pipelines or scheduled runs. One of the ideas can be creating a merge request comment with a Markdown table. It needs a bit more work with parsing the reports, formatting the comment text, and interacting with the GitLab REST API, shown in the following steps in a Python script. You can follow the implementation steps to re-create them in your preferred language for the scanner type and use [GitLab API clients](/partners/technology-partners/#api-clients).\n\nFirst, read the report in JSON format, and inspect whether `kics_version` is set to continue. Then extract the `queries` key, and prepare the `comment_body` with the markdown table header columns.\n\n```python\nFILE=\"kics/kics-report.json\"\n\nf = open(FILE)\nreport = json.load(f)\n\n# Parse the report: kics\nif \"kics_version\" in report:\n    print(\"Found kics '%s' in '%s'\" % (report[\"kics_version\"], FILE))\n    queries = report[\"queries\"]\nelse:\n    raise Exception(\"Unsupported report format\")\n\ncomment_body = \"\"\"### kics vulnerabilities report\n\n| Severity | Description | Platform | Filename |\n|----------|-------------|----------|----------|\n\"\"\"\n```\n\nNext, we need to parse all queries in a loop, and collect all column values. They are collected into a new list, which then gets joined with the `|` character. The `files` key needs a nested collection, as this is a list of dictionaries where only the `file_name` is of interest for the demo.\n\n```python\n# Example query to parse: {'query_name': 'Service Does Not Target Pod', 'query_id': '3ca03a61-3249-4c16-8427-6f8e47dda729', 'query_url': 'https://kubernetes.io/docs/concepts/services-networking/service/', 'severity': 'LOW', 'platform': 'Kubernetes', 'category': 'Insecure Configurations', 'description': 'Service should Target a Pod', 'description_id': 'e7c26645', 'files': [{'file_name': 'kubernetes/ecc-demo-service.yml', 'similarity_id': '9da6166956ad0fcfb1dd533df17852342dcbcca02ac559becaf51f6efdc015e8', 'line': 38, 'issue_type': 'IncorrectValue', 'search_key': 'metadata.name={{ecc-demo-service}}.spec.ports.name={{web}}.targetPort', 'search_line': 0, 'search_value': '', 'expected_value': 'metadata.name={{ecc-demo-service}}.spec.ports={{web}}.targetPort has a Pod Port', 'actual_value': 'metadata.name={{ecc-demo-service}}.spec.ports={{web}}.targetPort does not have a Pod Port'}]}\n\nfor q in queries:\n    #print(q) # DEBUG\n    l = []\n    l.append(q[\"severity\"])\n    l.append(q[\"description\"])\n    l.append(q[\"platform\"])\n\n    if \"files\" in q:\n        l.append(\",\".join((f[\"file_name\"] for f in q[\"files\"])))\n\n    comment_body += \"| \" + \" | \".join(l) + \" |\\n\"\n\nf.close()\n```\n\nThe markdown table has been prepared, so now it is time to communicate with the GitLab API. [python-gitlab](https://python-gitlab.readthedocs.io/en/stable/api-usage.html) provides a great abstraction layer with programmatic interfaces.\n\nThe GitLab API needs a project/group access token with API permissions. The `CI_JOB_TOKEN` is not sufficient.\n\n![Set the Project Access Token as CI/CD variable, not protected](https://about.gitlab.com/images/blogimages/iac-security-scanning/gitlab-cicd-variable-project-access-token.png){: .shadow}\n\nRead the `GITLAB_TOKEN` from the environment, and instantiate a new `Gitlab` object.\n\n```python\nGITLAB_URL='https://gitlab.com'\n\nif 'GITLAB_TOKEN' in os.environ:\n    gl = gitlab.Gitlab(GITLAB_URL, private_token=os.environ['GITLAB_TOKEN'])\nelse:\n    raise Exception('GITLAB_TOKEN variable not set. Please provide an API token to update the MR!')\n```\n\nNext, use the `CI_PROJECT_ID` CI/CD variable from the environment to select the [project object](https://python-gitlab.readthedocs.io/en/stable/gl_objects/projects.html) which contains the merge request we want to target.\n\n```python\nproject = gl.projects.get(os.environ['CI_PROJECT_ID'])\n```\n\nThe tricky part is to fetch the [merge request](https://python-gitlab.readthedocs.io/en/stable/gl_objects/merge_requests.html) ID from the CI/CD pipeline, it is not always available. A workaround can be to read the `CI_COMMIT_REF_NAME` variable and match it against all MRs in the project, looking if the `source_branch` matches.\n\n```python\nreal_mr = None\n\nif 'CI_MERGE_REQUEST_ID' in os.environ:\n    mr_id = os.environ['CI_MERGE_REQUEST_ID']\n    real_mr = project.mergerequests.get(mr_id)\n\n# Note: This workaround can be very expensive in projects with many MRs\nif 'CI_COMMIT_REF_NAME' in os.environ:\n    commit_ref_name = os.environ['CI_COMMIT_REF_NAME']\n\n    mrs = project.mergerequests.list()\n\n    for mr in mrs:\n        if mr.source_branch in commit_ref_name:\n            real_mr = mr\n            # found the MR for this source branch\n            # print(mr) # DEBUG\n\nif not real_mr:\n    print(\"Pipeline not run in a merge request, no reports sent\")\n    sys.exit(0)\n```\n\nLast but not least, use the MR object to [create a new note](https://python-gitlab.readthedocs.io/en/stable/gl_objects/notes.html) with the `comment_body` including the Markdown table created before.\n\n```python\nmr_note = real_mr.notes.create({'body': comment_body})\n```\n\nThis workflow creates a new MR comment every time a new commit is pushed. Consider evaluating the script and refining the update frequency by yourself. The script can be integrated into CI/CD with running kics before generating the reports shown in the following example configuration for `.gitlab-ci.yml`:\n\n```yaml\n# Full RAW example for kics reports and scans\nkics-scan:\n  image: python:3.10.2-slim-bullseye\n  variables:\n    # Visit for new releases\n    # https://github.com/Checkmarx/kics/releases\n    KICS_VERSION: \"1.5.1\"\n  script:\n    - echo $CI_PIPELINE_SOURCE\n    - echo $CI_COMMIT_REF_NAME\n    - echo $CI_MERGE_REQUEST_ID\n    - echo $CI_MERGE_REQUEST_IID\n    - apt-get update && apt-get install wget tar --no-install-recommends\n    - set -ex; wget -q -c \"https://github.com/Checkmarx/kics/releases/download/v${KICS_VERSION}/kics_${KICS_VERSION}_linux_x64.tar.gz\" -O - | tar -xz --directory /usr/bin &>/dev/null\n    # local requirements\n    - pip install -r requirements.txt\n    - kics scan --no-progress -q /usr/bin/assets/queries -p $(pwd) -o $(pwd) --report-formats json --output-path kics --output-name kics-report.json || true\n    - python ./integrations/kics-scan-report-mr-update.py\n```\n\nYou can find the [.gitlab-ci.yml configuration](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/blob/main/.gitlab-ci.yml) and the full script, including more inline comments and debug output [in this project](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning). You can see the implementation MR testing itself in [this comment](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/merge_requests/4#note_840472146).\n\n![MR comment with the kics report as Markdown table](https://about.gitlab.com/images/blogimages/iac-security-scanning/kics-python-gitlab-mr-update-table.png){: .shadow}\n\n### MR comments using GitLab IaC SAST reports as source\n\nThe steps in the previous section show the raw `kics` command execution, including JSON report parsing that requires you to create your own parsing logic. Alternatively, you can rely on the [IaC scanner in GitLab](https://docs.gitlab.com/ee/user/application_security/iac_scanning/#making-iac-analyzers-available-to-all-gitlab-tiers) and parse the SAST JSON report as [a standardized format](https://docs.gitlab.com/ee/user/application_security/iac_scanning/#reports-json-format). This is available for all GitLab tiers.\n\nDownload the [gl-sast-report.json example](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/blob/main/example-reports/gl-sast-report-kics-iac.json), save it as `gl-sast-report.json` in the same directory as the script, and parse the report in a similar way shown above.\n\n```python\nFILE=\"gl-sast-report.json\"\n\nf = open(FILE)\nreport = json.load(f)\n\n# Parse the report: kics\nif \"scan\" in report:\n    print(\"Found scanner '%s' in '%s'\" % (report[\"scan\"][\"scanner\"][\"name\"], FILE))\n    queries = report[\"vulnerabilities\"]\nelse:\n    raise Exception(\"Unsupported report format\")\n```\n\nThe parameters in the vulnerability report also include the CVE number. The `location` is using a nested dictionary and thus easier to parse.\n\n```python\ncomment_body = \"\"\"### IaC SAST vulnerabilities report\n\n| Severity | Description | Category | Location | CVE |\n|----------|-------------|----------|----------|-----|\n\"\"\"\n\nfor q in queries:\n    #print(q) # DEBUG\n    l = []\n    l.append(q[\"severity\"])\n    l.append(q[\"description\"])\n    l.append(q[\"category\"])\n    l.append(q[\"location\"][\"file\"])\n    l.append(q[\"cve\"])\n\n    comment_body += \"| \" + \" | \".join(l) + \" |\\n\"\n\nf.close()\n```\n\nThe `comment_body` contains the Markdown table, and can use the same code to update the MR with a comment using the GitLab API Python bindings. An example run is shown in [this MR comment](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/merge_requests/8#note_841940319).\n\nYou can integrate the script into your CI/CD workflows using the following steps: 1) Override the `kics-iac-sast` job `artifacts` created by the `Security/SAST-IaC.latest.gitlab-ci.yml` template and 2) Add a job `iac-sast-parse` which parses the JSON report and calls the script to send a MR comment.\n\n```yaml\n# GitLab integration with SAST reports spec\ninclude:\n- template: Security/SAST-IaC.latest.gitlab-ci.yml\n\n# Override the SAST report artifacts\nkics-iac-sast:\n  artifacts:\n    name: sast\n    paths:\n      - gl-sast-report.json\n    reports:\n      sast: gl-sast-report.json\n\niac-sast-parse:\n  image: python:3.10.2-slim-bullseye\n  needs: ['kics-iac-sast']\n  script:\n    - echo \"Parsing gl-sast-report.json\"\n    - pip install -r requirements.txt\n    - python ./integrations/sast-iac-report-mr-update.py\n  artifacts:\n      paths:\n      - gl-sast-report.json\n```\n\nThe CI/CD pipeline testing itself can be found in [this MR comment](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/merge_requests/9#note_841976761). Please review the [sast-iac-report-mr-update.py](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/blob/main/integrations/sast-iac-report-mr-update.py) script and evaluate whether it is useful for your workflows.\n\n## What is the best integration strategy?\n\nOne way to evaluate the scanners is to look at their extensibility. For example, [kics](https://docs.kics.io/latest/creating-queries/) calls them `queries`, [semgrep](https://semgrep.dev/docs/writing-rules/overview/) uses `rules`, [checkov](https://www.checkov.io/3.Custom%20Policies/Custom%20Policies%20Overview.html) says `policies`, [tfsec](https://aquasecurity.github.io/tfsec/v1.1.5/getting-started/configuration/custom-checks/) goes for `custom checks` as a name. These specifications allow you to create and contribute your own detection methods with extensive tutorial guides.\n\nMany of the shown scanners provide container images to use, or CI/CD integration documentation. Make sure to include this requirement in your evaluation. For a fully integrated and tested solution, use the [IaC Security Scanning feature in GitLab](https://docs.gitlab.com/ee/user/application_security/iac_scanning/), currently based on the `kics` scanner. If you already have experience with other scanners, or prefer your own custom integration, evaluate the alternatives for your solution. All scanners discussed in this blog post provide JSON as output format, which helps with programmatic parsing and automation.\n\nMaybe you'd like to [contribute a new IaC scanner](https://docs.gitlab.com/ee/user/application_security/iac_scanning/#contribute-your-scanner) or help improve the detection rules and functionality from the open source scanners :-)\n\nCover image by [Sawyer Bengtson](https://unsplash.com/photos/tnv84LOjes4) on [Unsplash](https://unsplash.com)\n{: .note}\n","insights",[23,24,25],"security","kubernetes","DevOps",{"slug":27,"featured":6,"template":28},"fantastic-infrastructure-as-code-security-attacks-and-how-to-find-them","BlogPost","content:en-us:blog:fantastic-infrastructure-as-code-security-attacks-and-how-to-find-them.yml","yaml","Fantastic Infrastructure As Code Security Attacks And How To Find Them","content","en-us/blog/fantastic-infrastructure-as-code-security-attacks-and-how-to-find-them.yml","en-us/blog/fantastic-infrastructure-as-code-security-attacks-and-how-to-find-them","yml",{"_path":37,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":39,"_id":449,"_type":30,"title":450,"_source":32,"_file":451,"_stem":452,"_extension":35},"/shared/en-us/main-navigation","en-us",{"logo":40,"freeTrial":45,"sales":50,"login":55,"items":60,"search":390,"minimal":421,"duo":440},{"config":41},{"href":42,"dataGaName":43,"dataGaLocation":44},"/","gitlab logo","header",{"text":46,"config":47},"Get free trial",{"href":48,"dataGaName":49,"dataGaLocation":44},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":51,"config":52},"Talk to sales",{"href":53,"dataGaName":54,"dataGaLocation":44},"/sales/","sales",{"text":56,"config":57},"Sign in",{"href":58,"dataGaName":59,"dataGaLocation":44},"https://gitlab.com/users/sign_in/","sign in",[61,105,201,206,311,371],{"text":62,"config":63,"cards":65,"footer":88},"Platform",{"dataNavLevelOne":64},"platform",[66,72,80],{"title":62,"description":67,"link":68},"The most comprehensive AI-powered DevSecOps Platform",{"text":69,"config":70},"Explore our Platform",{"href":71,"dataGaName":64,"dataGaLocation":44},"/platform/",{"title":73,"description":74,"link":75},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":76,"config":77},"Meet GitLab Duo",{"href":78,"dataGaName":79,"dataGaLocation":44},"/gitlab-duo/","gitlab duo ai",{"title":81,"description":82,"link":83},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":84,"config":85},"Learn more",{"href":86,"dataGaName":87,"dataGaLocation":44},"/why-gitlab/","why gitlab",{"title":89,"items":90},"Get started with",[91,96,101],{"text":92,"config":93},"Platform Engineering",{"href":94,"dataGaName":95,"dataGaLocation":44},"/solutions/platform-engineering/","platform engineering",{"text":97,"config":98},"Developer Experience",{"href":99,"dataGaName":100,"dataGaLocation":44},"/developer-experience/","Developer experience",{"text":102,"config":103},"MLOps",{"href":104,"dataGaName":102,"dataGaLocation":44},"/topics/devops/the-role-of-ai-in-devops/",{"text":106,"left":107,"config":108,"link":110,"lists":114,"footer":183},"Product",true,{"dataNavLevelOne":109},"solutions",{"text":111,"config":112},"View all Solutions",{"href":113,"dataGaName":109,"dataGaLocation":44},"/solutions/",[115,140,162],{"title":116,"description":117,"link":118,"items":123},"Automation","CI/CD and automation to accelerate deployment",{"config":119},{"icon":120,"href":121,"dataGaName":122,"dataGaLocation":44},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[124,128,132,136],{"text":125,"config":126},"CI/CD",{"href":127,"dataGaLocation":44,"dataGaName":125},"/solutions/continuous-integration/",{"text":129,"config":130},"AI-Assisted Development",{"href":78,"dataGaLocation":44,"dataGaName":131},"AI assisted development",{"text":133,"config":134},"Source Code Management",{"href":135,"dataGaLocation":44,"dataGaName":133},"/solutions/source-code-management/",{"text":137,"config":138},"Automated Software Delivery",{"href":121,"dataGaLocation":44,"dataGaName":139},"Automated software delivery",{"title":141,"description":142,"link":143,"items":148},"Security","Deliver code faster without compromising security",{"config":144},{"href":145,"dataGaName":146,"dataGaLocation":44,"icon":147},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[149,152,157],{"text":150,"config":151},"Security & Compliance",{"href":145,"dataGaLocation":44,"dataGaName":150},{"text":153,"config":154},"Software Supply Chain Security",{"href":155,"dataGaLocation":44,"dataGaName":156},"/solutions/supply-chain/","Software supply chain security",{"text":158,"config":159},"Compliance & Governance",{"href":160,"dataGaLocation":44,"dataGaName":161},"/solutions/continuous-software-compliance/","Compliance and governance",{"title":163,"link":164,"items":169},"Measurement",{"config":165},{"icon":166,"href":167,"dataGaName":168,"dataGaLocation":44},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[170,174,178],{"text":171,"config":172},"Visibility & Measurement",{"href":167,"dataGaLocation":44,"dataGaName":173},"Visibility and Measurement",{"text":175,"config":176},"Value Stream Management",{"href":177,"dataGaLocation":44,"dataGaName":175},"/solutions/value-stream-management/",{"text":179,"config":180},"Analytics & Insights",{"href":181,"dataGaLocation":44,"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":44,"dataGaName":190},"/enterprise/","enterprise",{"text":192,"config":193},"Small Business",{"href":194,"dataGaLocation":44,"dataGaName":195},"/small-business/","small business",{"text":197,"config":198},"Public Sector",{"href":199,"dataGaLocation":44,"dataGaName":200},"/solutions/public-sector/","public sector",{"text":202,"config":203},"Pricing",{"href":204,"dataGaName":205,"dataGaLocation":44,"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":44},"/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":44},"/install/","install",{"text":224,"config":225},"Quick start guides",{"href":226,"dataGaName":227,"dataGaLocation":44},"/get-started/","quick setup checklists",{"text":229,"config":230},"Learn",{"href":231,"dataGaLocation":44,"dataGaName":232},"https://university.gitlab.com/","learn",{"text":234,"config":235},"Product documentation",{"href":236,"dataGaName":237,"dataGaLocation":44},"https://docs.gitlab.com/","product documentation",{"text":239,"config":240},"Best practice videos",{"href":241,"dataGaName":242,"dataGaLocation":44},"/getting-started-videos/","best practice videos",{"text":244,"config":245},"Integrations",{"href":246,"dataGaName":247,"dataGaLocation":44},"/integrations/","integrations",{"title":249,"items":250},"Discover",[251,256,260,265],{"text":252,"config":253},"Customer success stories",{"href":254,"dataGaName":255,"dataGaLocation":44},"/customers/","customer success stories",{"text":257,"config":258},"Blog",{"href":259,"dataGaName":5,"dataGaLocation":44},"/blog/",{"text":261,"config":262},"Remote",{"href":263,"dataGaName":264,"dataGaLocation":44},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":266,"config":267},"TeamOps",{"href":268,"dataGaName":269,"dataGaLocation":44},"/teamops/","teamops",{"title":271,"items":272},"Connect",[273,278,283,288,293],{"text":274,"config":275},"GitLab Services",{"href":276,"dataGaName":277,"dataGaLocation":44},"/services/","services",{"text":279,"config":280},"Community",{"href":281,"dataGaName":282,"dataGaLocation":44},"/community/","community",{"text":284,"config":285},"Forum",{"href":286,"dataGaName":287,"dataGaLocation":44},"https://forum.gitlab.com/","forum",{"text":289,"config":290},"Events",{"href":291,"dataGaName":292,"dataGaLocation":44},"/events/","events",{"text":294,"config":295},"Partners",{"href":296,"dataGaName":297,"dataGaLocation":44},"/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},"/images/navigation/the-source-promo-card.svg",{"text":307,"config":308},"Read the latest",{"href":309,"dataGaName":310,"dataGaLocation":44},"/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":44},"/company/","about",{"text":324,"config":325,"footerGa":328},"Jobs",{"href":326,"dataGaName":327,"dataGaLocation":44},"/jobs/","jobs",{"dataGaName":327},{"text":289,"config":330},{"href":291,"dataGaName":292,"dataGaLocation":44},{"text":332,"config":333},"Leadership",{"href":334,"dataGaName":335,"dataGaLocation":44},"/company/team/e-group/","leadership",{"text":337,"config":338},"Team",{"href":339,"dataGaName":340,"dataGaLocation":44},"/company/team/","team",{"text":342,"config":343},"Handbook",{"href":344,"dataGaName":345,"dataGaLocation":44},"https://handbook.gitlab.com/","handbook",{"text":347,"config":348},"Investor relations",{"href":349,"dataGaName":350,"dataGaLocation":44},"https://ir.gitlab.com/","investor relations",{"text":352,"config":353},"Trust Center",{"href":354,"dataGaName":355,"dataGaLocation":44},"/security/","trust center",{"text":357,"config":358},"AI Transparency Center",{"href":359,"dataGaName":360,"dataGaLocation":44},"/ai-transparency-center/","ai transparency center",{"text":362,"config":363},"Newsletter",{"href":364,"dataGaName":365,"dataGaLocation":44},"/company/contact/","newsletter",{"text":367,"config":368},"Press",{"href":369,"dataGaName":370,"dataGaLocation":44},"/press/","press",{"text":372,"config":373,"lists":374},"Contact us",{"dataNavLevelOne":314},[375],{"items":376},[377,380,385],{"text":51,"config":378},{"href":53,"dataGaName":379,"dataGaLocation":44},"talk to sales",{"text":381,"config":382},"Get help",{"href":383,"dataGaName":384,"dataGaLocation":44},"/support/","get help",{"text":386,"config":387},"Customer portal",{"href":388,"dataGaName":389,"dataGaLocation":44},"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":58,"dataGaName":397,"dataGaLocation":398},"search login","search",{"text":400,"default":401},"Suggestions",[402,404,408,410,414,418],{"text":73,"config":403},{"href":78,"dataGaName":73,"dataGaLocation":398},{"text":405,"config":406},"Code Suggestions (AI)",{"href":407,"dataGaName":405,"dataGaLocation":398},"/solutions/code-suggestions/",{"text":125,"config":409},{"href":127,"dataGaName":125,"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":86,"dataGaName":419,"dataGaLocation":398},{"freeTrial":422,"mobileIcon":427,"desktopIcon":432,"secondaryButton":435},{"text":423,"config":424},"Start free trial",{"href":425,"dataGaName":49,"dataGaLocation":426},"https://gitlab.com/-/trials/new/","nav",{"altText":428,"config":429},"Gitlab Icon",{"src":430,"dataGaName":431,"dataGaLocation":426},"/images/brand/gitlab-logo-tanuki.svg","gitlab icon",{"altText":428,"config":433},{"src":434,"dataGaName":431,"dataGaLocation":426},"/images/brand/gitlab-logo-type.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":78,"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},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":454,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"title":455,"button":456,"image":460,"config":463,"_id":465,"_type":30,"_source":32,"_file":466,"_stem":467,"_extension":35},"/shared/en-us/banner","is now in public beta!",{"text":84,"config":457},{"href":458,"dataGaName":459,"dataGaLocation":44},"/gitlab-duo/agent-platform/","duo banner",{"config":461},{"src":462},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":464},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":469,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":470,"_id":675,"_type":30,"title":676,"_source":32,"_file":677,"_stem":678,"_extension":35},"/shared/en-us/main-footer",{"text":471,"source":472,"edit":478,"contribute":483,"config":488,"items":493,"minimal":667},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":473,"config":474},"View page source",{"href":475,"dataGaName":476,"dataGaLocation":477},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":479,"config":480},"Edit this page",{"href":481,"dataGaName":482,"dataGaLocation":477},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":484,"config":485},"Please contribute",{"href":486,"dataGaName":487,"dataGaLocation":477},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":489,"facebook":490,"youtube":491,"linkedin":492},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[494,517,574,603,637],{"title":62,"links":495,"subMenu":500},[496],{"text":497,"config":498},"DevSecOps platform",{"href":71,"dataGaName":499,"dataGaLocation":477},"devsecops platform",[501],{"title":202,"links":502},[503,507,512],{"text":504,"config":505},"View plans",{"href":204,"dataGaName":506,"dataGaLocation":477},"view plans",{"text":508,"config":509},"Why Premium?",{"href":510,"dataGaName":511,"dataGaLocation":477},"/pricing/premium/","why premium",{"text":513,"config":514},"Why Ultimate?",{"href":515,"dataGaName":516,"dataGaLocation":477},"/pricing/ultimate/","why ultimate",{"title":518,"links":519},"Solutions",[520,525,528,530,535,540,544,547,551,556,558,561,564,569],{"text":521,"config":522},"Digital transformation",{"href":523,"dataGaName":524,"dataGaLocation":477},"/topics/digital-transformation/","digital transformation",{"text":150,"config":526},{"href":145,"dataGaName":527,"dataGaLocation":477},"security & compliance",{"text":139,"config":529},{"href":121,"dataGaName":122,"dataGaLocation":477},{"text":531,"config":532},"Agile development",{"href":533,"dataGaName":534,"dataGaLocation":477},"/solutions/agile-delivery/","agile delivery",{"text":536,"config":537},"Cloud transformation",{"href":538,"dataGaName":539,"dataGaLocation":477},"/topics/cloud-native/","cloud transformation",{"text":541,"config":542},"SCM",{"href":135,"dataGaName":543,"dataGaLocation":477},"source code management",{"text":125,"config":545},{"href":127,"dataGaName":546,"dataGaLocation":477},"continuous integration & delivery",{"text":548,"config":549},"Value stream management",{"href":177,"dataGaName":550,"dataGaLocation":477},"value stream management",{"text":552,"config":553},"GitOps",{"href":554,"dataGaName":555,"dataGaLocation":477},"/solutions/gitops/","gitops",{"text":187,"config":557},{"href":189,"dataGaName":190,"dataGaLocation":477},{"text":559,"config":560},"Small business",{"href":194,"dataGaName":195,"dataGaLocation":477},{"text":562,"config":563},"Public sector",{"href":199,"dataGaName":200,"dataGaLocation":477},{"text":565,"config":566},"Education",{"href":567,"dataGaName":568,"dataGaLocation":477},"/solutions/education/","education",{"text":570,"config":571},"Financial services",{"href":572,"dataGaName":573,"dataGaLocation":477},"/solutions/finance/","financial services",{"title":207,"links":575},[576,578,580,582,585,587,589,591,593,595,597,599,601],{"text":219,"config":577},{"href":221,"dataGaName":222,"dataGaLocation":477},{"text":224,"config":579},{"href":226,"dataGaName":227,"dataGaLocation":477},{"text":229,"config":581},{"href":231,"dataGaName":232,"dataGaLocation":477},{"text":234,"config":583},{"href":236,"dataGaName":584,"dataGaLocation":477},"docs",{"text":257,"config":586},{"href":259,"dataGaName":5,"dataGaLocation":477},{"text":252,"config":588},{"href":254,"dataGaName":255,"dataGaLocation":477},{"text":261,"config":590},{"href":263,"dataGaName":264,"dataGaLocation":477},{"text":274,"config":592},{"href":276,"dataGaName":277,"dataGaLocation":477},{"text":266,"config":594},{"href":268,"dataGaName":269,"dataGaLocation":477},{"text":279,"config":596},{"href":281,"dataGaName":282,"dataGaLocation":477},{"text":284,"config":598},{"href":286,"dataGaName":287,"dataGaLocation":477},{"text":289,"config":600},{"href":291,"dataGaName":292,"dataGaLocation":477},{"text":294,"config":602},{"href":296,"dataGaName":297,"dataGaLocation":477},{"title":312,"links":604},[605,607,609,611,613,615,617,621,626,628,630,632],{"text":319,"config":606},{"href":321,"dataGaName":314,"dataGaLocation":477},{"text":324,"config":608},{"href":326,"dataGaName":327,"dataGaLocation":477},{"text":332,"config":610},{"href":334,"dataGaName":335,"dataGaLocation":477},{"text":337,"config":612},{"href":339,"dataGaName":340,"dataGaLocation":477},{"text":342,"config":614},{"href":344,"dataGaName":345,"dataGaLocation":477},{"text":347,"config":616},{"href":349,"dataGaName":350,"dataGaLocation":477},{"text":618,"config":619},"Sustainability",{"href":620,"dataGaName":618,"dataGaLocation":477},"/sustainability/",{"text":622,"config":623},"Diversity, inclusion and belonging (DIB)",{"href":624,"dataGaName":625,"dataGaLocation":477},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":352,"config":627},{"href":354,"dataGaName":355,"dataGaLocation":477},{"text":362,"config":629},{"href":364,"dataGaName":365,"dataGaLocation":477},{"text":367,"config":631},{"href":369,"dataGaName":370,"dataGaLocation":477},{"text":633,"config":634},"Modern Slavery Transparency Statement",{"href":635,"dataGaName":636,"dataGaLocation":477},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":638,"links":639},"Contact Us",[640,643,645,647,652,657,662],{"text":641,"config":642},"Contact an expert",{"href":53,"dataGaName":54,"dataGaLocation":477},{"text":381,"config":644},{"href":383,"dataGaName":384,"dataGaLocation":477},{"text":386,"config":646},{"href":388,"dataGaName":389,"dataGaLocation":477},{"text":648,"config":649},"Status",{"href":650,"dataGaName":651,"dataGaLocation":477},"https://status.gitlab.com/","status",{"text":653,"config":654},"Terms of use",{"href":655,"dataGaName":656,"dataGaLocation":477},"/terms/","terms of use",{"text":658,"config":659},"Privacy statement",{"href":660,"dataGaName":661,"dataGaLocation":477},"/privacy/","privacy statement",{"text":663,"config":664},"Cookie preferences",{"dataGaName":665,"dataGaLocation":477,"id":666,"isOneTrustButton":107},"cookie preferences","ot-sdk-btn",{"items":668},[669,671,673],{"text":653,"config":670},{"href":655,"dataGaName":656,"dataGaLocation":477},{"text":658,"config":672},{"href":660,"dataGaName":661,"dataGaLocation":477},{"text":663,"config":674},{"dataGaName":665,"dataGaLocation":477,"id":666,"isOneTrustButton":107},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[680],{"_path":681,"_dir":682,"_draft":6,"_partial":6,"_locale":7,"content":683,"config":687,"_id":689,"_type":30,"title":18,"_source":32,"_file":690,"_stem":691,"_extension":35},"/en-us/blog/authors/michael-friedrich","authors",{"name":18,"config":684},{"headshot":685,"ctfId":686},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659879/Blog/Author%20Headshots/dnsmichi-headshot.jpg","dnsmichi",{"template":688},"BlogAuthor","content:en-us:blog:authors:michael-friedrich.yml","en-us/blog/authors/michael-friedrich.yml","en-us/blog/authors/michael-friedrich",{"_path":693,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"header":694,"eyebrow":695,"blurb":696,"button":697,"secondaryButton":701,"_id":703,"_type":30,"title":704,"_source":32,"_file":705,"_stem":706,"_extension":35},"/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":46,"config":698},{"href":699,"dataGaName":49,"dataGaLocation":700},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":51,"config":702},{"href":53,"dataGaName":54,"dataGaLocation":700},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1753981624354]