[{"data":1,"prerenderedAt":720},["ShallowReactive",2],{"/en-us/blog/fantastic-infrastructure-as-code-security-attacks-and-how-to-find-them/":3,"navigation-en-us":36,"banner-en-us":465,"footer-en-us":482,"Michael Friedrich":692,"next-steps-en-us":705},{"_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","[Infrastructure as Code](/topics/gitops/infrastructure-as-code/)(IaC) has\neaten the world. It helps manage and provision computer resources\nautomatically and avoids manual work or UI form workflows. Lifecycle\nmanagement with IaC started with declarative and idempotent configuration,\npackage, and tool installation. In the era of cloud providers, IaC tools\nadditionally help abstract cloud provisioning. They can create defined\nresources automatically (network, storage, databases, etc.) and apply the\nconfiguration (DNS entries, firewall rules, etc.).\n\n\nLike everything else, it has its flaws. IaC workflows have shifted left in\nthe development lifecycle, making it more efficient. Developers and DevOps\nengineers need to learn new tools and best practices. Mistakes may result in\nleaked credentials or supply chain attacks. Existing security assessment\ntools might not be able to detect these new vulnerabilities.\n\n\nIn this post, we will dive into these specific risks and focus on IaC\nmanagement tools such as Terraform, cloud providers, and deployment\nplatforms involving containers and Kubernetes.\n\n\nFor each scenario, we will look into threats, tools, integrations, and best\npractices to reduce risk.\n\n\nYou can read the blog post top-down or navigate into the chapters\nindividually.\n\n\n- [Scan your own infrastructure - know what's\nimportant](#scan-your-infrastructure---know-what-is-important)\n    - [Thinking like an attacker](#thinking-like-an-attacker)\n- [Tools to detect Terraform\nvulnerabilities](#tools-to-detect-terraform-vulnerabilities)\n\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\nReview](#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\nstrategy?](#what-is-the-best-integration-strategy)\n\n\n## Scan your infrastructure - know what is important\n\n\nStart with identifying the project/group responsible for managing the IAC\ntasks. An inventory search for specific IaC tools, file suffixes (Terraform\nuses `.tf`, for example), and languages can be helpful. The security scan\ntools discussed in this blog post will discover all supported types\nautomatically. Once you have identified the projects, you can use one of the\ntools to run a scan and identify the detected possible vulnerabilities.\n\n\nThere might not be any scan results because your infrastructure is secure at\nthis time. Though, your processes may require you to create documentation,\nrunbooks, and action items for eventually discovered vulnerabilities in the\nfuture. Creating a forecast on possible scenarios to defend is hard, so let\nus change roles from the defender to the attacker for a moment. Which\nsecurity vulnerabilities are out there to exploit as a malicious attacker?\nMaybe it is possible to create vulnerable scenarios and simulate the\nattacker role by running a security scan.\n\n\n### Thinking like an attacker\n\n\nThere can be noticeable potential vulnerabilities like plaintext passwords\nin the configuration. Other scenarios involve cases you would never think of\nor a chain of items causing a security issue.\n\n\nLet us create a scenario for an attacker by provisioning an S3 bucket in AWS\nwith Terraform. We intend to store logs, database dumps, or credential\nvaults in this S3 bucket.\n\n\nThe following example creates the `aws_s3_bucket` resource in Terraform\nusing the AWS provider.\n\n\n```hcl\n\n# Create the bucket\n\nresource \"aws_s3_bucket\" \"demobucket\" {\n  bucket = \"terraformdemobucket\"\n  acl = \"private\"\n}\n\n```\n\n\nAfter provisioning the S3 bucket for the first time, someone decided to make\nthe S3 bucket accessible by default. The example below grants public access\nto the bucket using `aws_s3_bucket_public_access_block`. `block_public_acls`\nand `block_public_policy` are set to `false` to allow any public access.\n\n\n```\n\n# Grant bucket access: public\n\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```\n\n\nThe S3 bucket is now publicly readable, and anyone who knows the URL or\nscans network ranges for open ports may find the S3 bucket and its data.\nMalicious actors can not only capture credentials but also may learn about\nyour infrastructure, IP addresses, internal server FQDNs, etc. from the\nlogs, backups, and database dumps being stored in the S3 bucket.\n\n\nWe need ways to mitigate and detect this security problem. The following\nsections describe the different tools you can use. The full Terraform code\nis located in [this\nproject](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/tree/main/terraform/aws)\nand allows you to test all tools described in this blog post.\n\n\n## Tools to detect Terraform vulnerabilities\n\n\nIn the \"not worst case\" scenario, the Terraform code to manage your\ninfrastructure is persisted at a central Git server and not hidden somewhere\non a host or local desktop. Maybe you are using `terraform init, plan,\napply` jobs in CI/CD pipelines already. Let us look into methods and tools\nthat help detect the public S3 bucket vulnerability. Later, we will discuss\nCI/CD integrations and automating IaC security scanning.\n\n\nBefore we dive into the tools, make sure to clone the demo project locally\nto follow the examples yourself.\n\n\n```shell\n\n$ cd /tmp\n\n$ git clone\nhttps://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning.git\n&& cd  infrastructure-as-code-scanning/\n\n```\n\n\nThe tool installation steps in this blog post are illustrated with [Homebrew\non macOS](https://brew.sh/). Please refer to the tools documentation for\nalternative installation methods and supported platforms.\n\n\nYou can follow the tools for Terraform security scanning by reading\ntop-down, or navigate into the tools sections directly:\n\n\n- [tfsec](#tfsec)\n\n- [kics](#kics)\n\n- [terrascan](#terrascan)\n\n- [semgrep](#semgrep)\n\n- [tflint](#tflint)\n\n\n### tfsec\n\n\n[tfsec](https://github.com/aquasecurity/tfsec) from Aqua Security can help\ndetect Terraform vulnerabilities. There are [Docker images\navailable](https://github.com/aquasecurity/tfsec#use-with-docker) to quickly\ntest the scanner on the CLI, or binaries to [install\ntfsec](https://aquasecurity.github.io/tfsec/v1.1.4/getting-started/installation/).\nRun `tfsec` on the local project path `terraform/aws/` to get a list of\nvulnerabilities.\n\n\n```shell\n\n$ brew install tfsec\n\n$ tfsec terraform/aws/\n\n```\n\n\nThe default scan provides a table overview on the CLI, which may need\nadditional filters. Inspect `tfsec –help` to get a list of all available\n[parameters](https://aquasecurity.github.io/tfsec/v1.1.4/getting-started/usage/)\nand try generating JSON and JUnit output files to process further.\n\n\n```shell\n\n$ tfsec terraform/aws --format json --out tfsec-report.json\n\n1 file(s) written: tfsec-report.json\n\n$ tfsec terraform/aws --format junit --out tfsec-junit.xml\n\n1 file(s) written: tfsec-junit.xml\n\n```\n\n\nThe full example is located in the [terraform/aws directory in this\nproject](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/tree/main/terraform/aws).\n\n\n#### Parse tfsec JSON reports with jq\n\n\nIn an earlier blog post, we shared [how to detect the JSON data structures\nand filter with chained jq\ncommands](/blog/devops-workflows-json-format-jq-ci-cd-lint/). The\ntfsec report is a good practice: Extract the `results` key, iterate through\nall array list items and filtered by `rule_service` being `s3`, and only\nprint `severity`, `description` and `location.filename`.\n\n\n```shell\n\n$ jq \u003C tfsec-report.json | jq -c '.[\"results\"]' | jq -c '.[] | select\n(.rule_service == \"s3\") | [.severity, .description, .location.filename]'\n\n```\n\n\n![tfsec parser output\nexample](https://about.gitlab.com/images/blogimages/iac-security-scanning/tfsec-json-jq-parser.png){:\n.shadow}\n\n\n### kics\n\n\n[kics](https://kics.io/) is another IaC scanner, providing support for many\ndifferent tools (Ansible, Terraform, Kubernetes, Dockerfile, and cloud\nconfiguration APIs such as AWS CloudFormation, Azure Resource Manager, and\nGoogle Deployment Manager).\n\n\nLet's try it: [Install kics](https://docs.kics.io/latest/getting-started/)\nand run it on the vulnerable project. `--report-formats`, `--output-path`\nand `--output-name` allow you to create a JSON report which can be\nautomatically parsed with additional tooling.\n\n\n```shell\n\n$ kics scan --path .\n\n$ kics scan --path . --report-formats json --output-path kics --output-name\nkics-report.json\n\n```\n\n\nParsing the JSON report from `kics` with jq works the same way as the tfsec\nexample above. Inspect the data structure and nested object, and filter by\nAWS as `cloud_provider`. The `files` entry is an array of dictionaries,\nwhich turned out to be a little tricky to extract with an additional\n`(.files[] | .file_name )` to add:\n\n\n```\n\n$ jq \u003C kics/kics-report.json | jq -c '.[\"queries\"]' | jq -c '.[] | select\n(.cloud_provider == \"AWS\") | [.severity, .description, (.files[] |\n.file_name ) ]'\n\n```\n\n\n![kics json jq\nparser](https://about.gitlab.com/images/blogimages/iac-security-scanning/kics-json-jq-parser.png){:\n.shadow}\n\n\n`kics` returns different [exit\ncodes](https://docs.kics.io/latest/results/#exit_status_code) based on the\nnumber of different severities found. `50` indicates `HIGH` severities and\ncauses your CI/CD pipeline to fail.\n\n\n### checkov\n\n\n[Checkov](https://checkov.io) supports Terraform (for AWS, GCP, Azure and\nOCI), CloudFormation, ARM, Severless framework, Helm charts, Kubernetes, and\nDocker.\n\n\n```shell\n\n$ brew install checkov\n\n$ checkov --directory .\n\n```\n\n\n### terrascan\n\n\n[Terrascan](https://runterrascan.io/docs/getting-started/) supports\nTerraform, and more [policies](https://runterrascan.io/docs/policies/) for\ncloud providers, Docker, and Kubernetes.\n\n\n```shell\n\n$ brew install terrascan\n\n$ terrascan scan .\n\n```\n\n\n### semgrep\n\n\nSemgrep is working on [Terraform\nsupport](https://semgrep.dev/docs/language-support/), currently in Beta. It\nalso detects Dockerfile errors - for example invalid port ranges and\nmultiple ranges, similar to kics.\n\n\n```shell\n\n$ brew install semgrep\n\n$ semgrep --config auto .\n\n```\n\n\n### tflint\n\n\n[tflint](https://github.com/terraform-linters/tflint) also is an alternative\nscanner.\n\n\n## Develop more IaC scenarios\n\n\nWhile testing IaC Security Scanners for the first time, I was looking for\ndemo projects and examples. The [kics queries list for\nTerraform](https://docs.kics.io/latest/queries/terraform-queries/) provides\nan exhaustive list of all vulnerabilities and the documentation linked. From\nthere, you can build and create potential attack vectors for demos and\nshowcases without leaking your company code and workflows.\n\n\n[Terragoat](https://github.com/bridgecrewio/terragoat) also is a great\nlearning resource to test various scanners and see real-life examples for\nvulnerabilities.\n\n\n```shell\n\n$ cd /tmp && git clone https://github.com/bridgecrewio/terragoat.git && cd\nterragoat\n\n\n$ tfsec .\n\n$ kics scan --path .\n\n$ checkov --directory .\n\n$ semgrep --config auto .\n\n$ terrascan scan .\n\n```\n\n\nIt is also important to verify the reported vulnerabilities and create\ndocumentation for required actions for your teams. Not all detected\nvulnerabilities are necessarily equally critical in your environment. With\nthe rapid development of IaC,\n[GitOps}(https://about.gitlab.com/topics/gitops/), and cloud-native\nenvironments, it can also be a good idea to use 2+ scanners to see if there\nare missing vulnerabilities on one or the other.\n\n\nThe following sections discuss more scenarios in detail.\n\n\n- [Terraform Module Dependency Scans](#terraform-module-dependency-scans)\n\n- [IaC Security Scanning for\nContainers](#iac-security-scanning-for-containers)\n\n- [IaC Security Scanning with\nKubernetes](#iac-security-scanning-with-kubernetes)\n\n\n### Terraform Module Dependency Scans\n\n\nRe-usable IaC workflows also can introduce security vulnerabilities you are\nnot aware of. [This\nproject](https://gitlab.com/gitlab-de/use-cases/iac-tf-vuln-module) provides\nthe module files and package in the registry, which can be consumed by\n`main.tf` in the demo project.\n\n\n```hcl\n\nmodule \"my_module_name\" {\n  source = \"gitlab.com/gitlab-de/iac-tf-vuln-module/aws\"\n  version = \"1.0.0\"\n}\n\n```\n\n\nkics has [limited support for the official Terraform module\nregistry](https://docs.kics.io/latest/platforms/#terraform_modules),\n`checkov` failed to download private modules, `terrascan` and `tfsec` work\nwhen `terraform init` is run before the scan. Depending on your\nrequirements, running `kics` for everything and `tfsec` for module\ndependency checks can be a solution, suggestion added\n[here](https://gitlab.com/groups/gitlab-org/-/epics/6653#note_840447132).\n\n\n### IaC Security Scanning for Containers\n\n\nSecurity problems in containers can lead to application deployment\nvulnerabilities. The [kics query\ndatabase](https://docs.kics.io/latest/queries/dockerfile-queries/) helps to\nreverse engineer more vulnerable examples: Using the latest tag, privilege\nescalations with invoking sudo in a container, ports out of range, and\nmultiple entrypoints are just a few bad practices.\n\n\nThe following\n[Dockerfile](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/blob/main/Dockerfile)\nimplements example vulnerabilities for the scanners to detect:\n\n\n```\n\n# Create vulnerabilities based on kics queries in\nhttps://docs.kics.io/latest/queries/dockerfile-queries/\n\nFROM debian:latest\n\n\n# kics: Run Using Sudo\n\n# kics: Run Using apt\n\nRUN sudo apt install git\n\n\n# kics: UNIX Ports Out Of Range\n\nEXPOSE 99999\n\n\n# kics: Multiple ENTRYPOINT Instructions Listed\n\nENTRYPOINT [\"ex1\"]\n\nENTRYPOINT [\"ex2\"]\n\n```\n\n\nKics, tfsec, and terrascan can detect `Dockerfile` vulnerabilities, similar\nto semgrep and checkov. As an example scanner, terrascan can detect the\nvulnerabilities using the `--iac-type docker` parameter that allows to\nfilter the scan type.\n\n\n```shell\n\n$ terrascan scan --iac-type docker\n\n```\n\n\n![terrascan Docker IaC type scan\nresult](https://about.gitlab.com/images/blogimages/iac-security-scanning/terrascan-docker-iac.png){:\n.shadow}\n\n\nYou can run kics and tfsec as an exercise to verify the results.\n\n\n### IaC Security Scanning with Kubernetes\n\n\nSecuring a Kubernetes cluster can be a challenging task. Open Policy Agent,\nKyverno, RBAC, etc., and many different YAML configuration attributes\nrequire reviews and automated checks before the production deployments.\n[Cluster image\nscanning](https://docs.gitlab.com/ee/user/clusters/agent/vulnerabilities.html)\nis one way to mitigate security threats, next to [Container\nscanning](https://docs.gitlab.com/ee/user/application_security/container_scanning/)\nfor the applications being deployed. A suggested read is the book [“Hacking\nKubernetes”\nbook](https://www.oreilly.com/library/view/hacking-kubernetes/9781492081722/)\nby Andrew Martin and Michael Hausenblas if you want to dive deeper into\nKubernetes security and attack vectors.\n\n\nIt's possible to make mistakes when, for example, copying YAML example\nconfiguration and continue using it. I've created a deployment and service\nfor a [Kubernetes monitoring\nworkshop](/handbook/marketing/developer-relations/developer-evangelism/projects/#practical-kubernetes-monitoring-with-prometheus),\nwhich provides a practical example to learn but also uses some not so good\npractices.\n\n\nThe following configuration in\n[ecc-demo-service.yml](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/blob/main/kubernetes/ecc-demo-service.yml)\nintroduces vulnerabilities and potential production problems:\n\n\n```yaml\n\n---\n\n# A deployment for the ECC Prometheus demo service with 3 replicas.\n\napiVersion: apps/v1\n\nkind: Deployment\n\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\n# A service that references the demo service deployment.\n\napiVersion: v1\n\nkind: Service\n\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\n\nLet's scan the Kubernetes manifest with kics and parse the results again\nwith jq. A list of kics queries for Kubernetes can be found in the [kics\ndocumentation](https://docs.kics.io/latest/queries/kubernetes-queries/).\n\n\n```shell\n\n$ kics scan --path kubernetes --report-formats json --output-path kics\n--output-name kics-report.json\n\n\n$ jq \u003C kics/kics-report.json | jq -c '.[\"queries\"]' | jq -c '.[] | select\n(.platform == \"Kubernetes\") | [.severity, .description, (.files[] |\n.file_name ) ]'\n\n```\n\n\n![Kubernetes manifest scans and jq parser results with\nkics](https://about.gitlab.com/images/blogimages/iac-security-scanning/kics-kubernetes-jq-parser.png){:\n.shadow}\n\n\n[Checkov](https://www.checkov.io/) detects similar vulnerabilities with\nKubernetes.\n\n\n```\n\n$ checkov --directory kubernetes/\n\n$ checkov --directory kubernetes -o json > checkov-report.json\n\n```\n\n\n[kube-linter](https://docs.kubelinter.io/#/?id=installing-kubelinter)\nanalyzes Kubernetes YAML files and Helm charts for production readiness and\nsecurity.\n\n\n```shell\n\n$ brew install kube-linter\n\n$ kube-linter lint kubernetes/ecc-demo-service.yml --format json >\nkube-linter-report.json\n\n```\n\n\n[kubesec](https://kubesec.io/) provides security risk analysis for\nKubernetes resources. `kubesec` is also integrated into the [GitLab SAST\nscanners](https://docs.gitlab.com/ee/user/application_security/sast/#enabling-kubesec-analyzer).\n\n\n```shell\n\n$ docker run -i kubesec/kubesec:512c5e0 scan /dev/stdin \u003C\nkubernetes/ecc-demo-service.yml\n\n```\n\n\n## Integrations into CI/CD and Merge Requests for Review\n\n\nThere are many scanners out there, and most of them return the results in\nJSON which can be parsed and integrated into your CI/CD pipelines. You can\nlearn more about the evaluation of GitLab IaC scanners in [this\nissue](https://gitlab.com/gitlab-org/gitlab/-/issues/39695). The table in\nthe issue includes licenses, languages, outputs, and examples.\n\n\n`checkov` and `tfsec` provide JUnit XML reports as output format, which can\nbe parsed and integrated into CI/CD. Vulnerability reports will need a\ndifferent format though to not confuse them with unit test results for\nexample. Integrating a SAST scanner in GitLab requires you to provide\n[artifacts:reports:sast](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportssast)\nas a specified output format and API. [This\nreport](https://docs.gitlab.com/ee/user/application_security/iac_scanning/#reports-json-format)\ncan then be consumed by GitLab integrations such as MR widgets and\nvulnerability dashboards, available in the Ultimate tier. The following\nscreenshot shows adding a Kubernetes deployment and service with potential\nvulnerabilities in [this\nMR](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/merge_requests/3).\n\n\n![MR widget showing IaC vulnerabilities with\nKubernetes](https://about.gitlab.com/images/blogimages/iac-security-scanning/gitlab-iac-mr-widget-kubernetes.png){:\n.shadow}\n\n\n### Reports in MRs as comment\n\n\nThere are different ways to collect the JSON reports in your CI/CD pipelines\nor scheduled runs. One of the ideas can be creating a merge request comment\nwith a Markdown table. It needs a bit more work with parsing the reports,\nformatting the comment text, and interacting with the GitLab REST API, shown\nin the following steps in a Python script. You can follow the implementation\nsteps to re-create them in your preferred language for the scanner type and\nuse [GitLab API clients](/partners/technology-partners/#api-clients).\n\n\nFirst, read the report in JSON format, and inspect whether `kics_version` is\nset to continue. Then extract the `queries` key, and prepare the\n`comment_body` with the markdown table header columns.\n\n\n```python\n\nFILE=\"kics/kics-report.json\"\n\n\nf = open(FILE)\n\nreport = json.load(f)\n\n\n# Parse the report: kics\n\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\n| Severity | Description | Platform | Filename |\n\n|----------|-------------|----------|----------|\n\n\"\"\"\n\n```\n\n\nNext, we need to parse all queries in a loop, and collect all column values.\nThey are collected into a new list, which then gets joined with the `|`\ncharacter. The `files` key needs a nested collection, as this is a list of\ndictionaries where only the `file_name` is of interest for the demo.\n\n\n```python\n\n# Example query to parse: {'query_name': 'Service Does Not Target Pod',\n'query_id': '3ca03a61-3249-4c16-8427-6f8e47dda729', 'query_url':\n'https://kubernetes.io/docs/concepts/services-networking/service/',\n'severity': 'LOW', 'platform': 'Kubernetes', 'category': 'Insecure\nConfigurations', 'description': 'Service should Target a Pod',\n'description_id': 'e7c26645', 'files': [{'file_name':\n'kubernetes/ecc-demo-service.yml', 'similarity_id':\n'9da6166956ad0fcfb1dd533df17852342dcbcca02ac559becaf51f6efdc015e8', 'line':\n38, 'issue_type': 'IncorrectValue', 'search_key':\n'metadata.name={{ecc-demo-service}}.spec.ports.name={{web}}.targetPort',\n'search_line': 0, 'search_value': '', 'expected_value':\n'metadata.name={{ecc-demo-service}}.spec.ports={{web}}.targetPort has a Pod\nPort', 'actual_value':\n'metadata.name={{ecc-demo-service}}.spec.ports={{web}}.targetPort does not\nhave a Pod Port'}]}\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[\"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```\n\n\nThe markdown table has been prepared, so now it is time to communicate with\nthe GitLab API.\n[python-gitlab](https://python-gitlab.readthedocs.io/en/stable/api-usage.html)\nprovides a great abstraction layer with programmatic interfaces.\n\n\nThe GitLab API needs a project/group access token with API permissions. The\n`CI_JOB_TOKEN` is not sufficient.\n\n\n![Set the Project Access Token as CI/CD variable, not\nprotected](https://about.gitlab.com/images/blogimages/iac-security-scanning/gitlab-cicd-variable-project-access-token.png){:\n.shadow}\n\n\nRead the `GITLAB_TOKEN` from the environment, and instantiate a new `Gitlab`\nobject.\n\n\n```python\n\nGITLAB_URL='https://gitlab.com'\n\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\n\nNext, use the `CI_PROJECT_ID` CI/CD variable from the environment to select\nthe [project\nobject](https://python-gitlab.readthedocs.io/en/stable/gl_objects/projects.html)\nwhich contains the merge request we want to target.\n\n\n```python\n\nproject = gl.projects.get(os.environ['CI_PROJECT_ID'])\n\n```\n\n\nThe tricky part is to fetch the [merge\nrequest](https://python-gitlab.readthedocs.io/en/stable/gl_objects/merge_requests.html)\nID from the CI/CD pipeline, it is not always available. A workaround can be\nto read the `CI_COMMIT_REF_NAME` variable and match it against all MRs in\nthe project, looking if the `source_branch` matches.\n\n\n```python\n\nreal_mr = None\n\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\n\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\n\nLast but not least, use the MR object to [create a new\nnote](https://python-gitlab.readthedocs.io/en/stable/gl_objects/notes.html)\nwith the `comment_body` including the Markdown table created before.\n\n\n```python\n\nmr_note = real_mr.notes.create({'body': comment_body})\n\n```\n\n\nThis workflow creates a new MR comment every time a new commit is pushed.\nConsider evaluating the script and refining the update frequency by\nyourself. The script can be integrated into CI/CD with running kics before\ngenerating the reports shown in the following example configuration for\n`.gitlab-ci.yml`:\n\n\n```yaml\n\n# Full RAW example for kics reports and scans\n\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\n\nYou can find the [.gitlab-ci.yml\nconfiguration](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/blob/main/.gitlab-ci.yml)\nand the full script, including more inline comments and debug output [in\nthis\nproject](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning).\nYou can see the implementation MR testing itself in [this\ncomment](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/merge_requests/4#note_840472146).\n\n\n![MR comment with the kics report as Markdown\ntable](https://about.gitlab.com/images/blogimages/iac-security-scanning/kics-python-gitlab-mr-update-table.png){:\n.shadow}\n\n\n### MR comments using GitLab IaC SAST reports as source\n\n\nThe steps in the previous section show the raw `kics` command execution,\nincluding JSON report parsing that requires you to create your own parsing\nlogic. Alternatively, you can rely on the [IaC scanner in\nGitLab](https://docs.gitlab.com/ee/user/application_security/iac_scanning/#making-iac-analyzers-available-to-all-gitlab-tiers)\nand parse the SAST JSON report as [a standardized\nformat](https://docs.gitlab.com/ee/user/application_security/iac_scanning/#reports-json-format).\nThis is available for all GitLab tiers.\n\n\nDownload the [gl-sast-report.json\nexample](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/blob/main/example-reports/gl-sast-report-kics-iac.json),\nsave it as `gl-sast-report.json` in the same directory as the script, and\nparse the report in a similar way shown above.\n\n\n```python\n\nFILE=\"gl-sast-report.json\"\n\n\nf = open(FILE)\n\nreport = json.load(f)\n\n\n# Parse the report: kics\n\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\n\nThe parameters in the vulnerability report also include the CVE number. The\n`location` is using a nested dictionary and thus easier to parse.\n\n\n```python\n\ncomment_body = \"\"\"### IaC SAST vulnerabilities report\n\n\n| Severity | Description | Category | Location | CVE |\n\n|----------|-------------|----------|----------|-----|\n\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```\n\n\nThe `comment_body` contains the Markdown table, and can use the same code to\nupdate the MR with a comment using the GitLab API Python bindings. An\nexample run is shown in [this MR\ncomment](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/merge_requests/8#note_841940319).\n\n\nYou can integrate the script into your CI/CD workflows using the following\nsteps: 1) Override the `kics-iac-sast` job `artifacts` created by the\n`Security/SAST-IaC.latest.gitlab-ci.yml` template and 2) Add a job\n`iac-sast-parse` which parses the JSON report and calls the script to send a\nMR comment.\n\n\n```yaml\n\n# GitLab integration with SAST reports spec\n\ninclude:\n\n- template: Security/SAST-IaC.latest.gitlab-ci.yml\n\n\n# Override the SAST report artifacts\n\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\n\nThe CI/CD pipeline testing itself can be found in [this MR\ncomment](https://gitlab.com/gitlab-de/use-cases/infrastructure-as-code-scanning/-/merge_requests/9#note_841976761).\nPlease review the\n[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)\nscript and evaluate whether it is useful for your workflows.\n\n\n## What is the best integration strategy?\n\n\nOne way to evaluate the scanners is to look at their extensibility. For\nexample, [kics](https://docs.kics.io/latest/creating-queries/) calls them\n`queries`, [semgrep](https://semgrep.dev/docs/writing-rules/overview/) uses\n`rules`,\n[checkov](https://www.checkov.io/3.Custom%20Policies/Custom%20Policies%20Overview.html)\nsays `policies`,\n[tfsec](https://aquasecurity.github.io/tfsec/v1.1.5/getting-started/configuration/custom-checks/)\ngoes for `custom checks` as a name. These specifications allow you to create\nand contribute your own detection methods with extensive tutorial guides.\n\n\nMany of the shown scanners provide container images to use, or CI/CD\nintegration documentation. Make sure to include this requirement in your\nevaluation. For a fully integrated and tested solution, use the [IaC\nSecurity Scanning feature in\nGitLab](https://docs.gitlab.com/ee/user/application_security/iac_scanning/),\ncurrently based on the `kics` scanner. If you already have experience with\nother scanners, or prefer your own custom integration, evaluate the\nalternatives for your solution. All scanners discussed in this blog post\nprovide JSON as output format, which helps with programmatic parsing and\nautomation.\n\n\nMaybe you'd like to [contribute a new IaC\nscanner](https://docs.gitlab.com/ee/user/application_security/iac_scanning/#contribute-your-scanner)\nor help improve the detection rules and functionality from the open source\nscanners :-)\n\n\nCover image by [Sawyer Bengtson](https://unsplash.com/photos/tnv84LOjes4) on\n[Unsplash](https://unsplash.com)\n\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":461,"_type":30,"title":462,"_source":32,"_file":463,"_stem":464,"_extension":35},"/shared/en-us/main-navigation","en-us",{"logo":40,"freeTrial":45,"sales":50,"login":55,"items":60,"search":392,"minimal":423,"duo":442,"pricingDeployment":451},{"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,203,208,313,373],{"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":185},"Product",true,{"dataNavLevelOne":109},"solutions",{"text":111,"config":112},"View all Solutions",{"href":113,"dataGaName":109,"dataGaLocation":44},"/solutions/",[115,140,164],{"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,154,159],{"text":150,"config":151},"Application Security Testing",{"href":152,"dataGaName":153,"dataGaLocation":44},"/solutions/application-security-testing/","Application security testing",{"text":155,"config":156},"Software Supply Chain Security",{"href":157,"dataGaLocation":44,"dataGaName":158},"/solutions/supply-chain/","Software supply chain security",{"text":160,"config":161},"Software Compliance",{"href":162,"dataGaName":163,"dataGaLocation":44},"/solutions/software-compliance/","software compliance",{"title":165,"link":166,"items":171},"Measurement",{"config":167},{"icon":168,"href":169,"dataGaName":170,"dataGaLocation":44},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[172,176,180],{"text":173,"config":174},"Visibility & Measurement",{"href":169,"dataGaLocation":44,"dataGaName":175},"Visibility and Measurement",{"text":177,"config":178},"Value Stream Management",{"href":179,"dataGaLocation":44,"dataGaName":177},"/solutions/value-stream-management/",{"text":181,"config":182},"Analytics & Insights",{"href":183,"dataGaLocation":44,"dataGaName":184},"/solutions/analytics-and-insights/","Analytics and insights",{"title":186,"items":187},"GitLab for",[188,193,198],{"text":189,"config":190},"Enterprise",{"href":191,"dataGaLocation":44,"dataGaName":192},"/enterprise/","enterprise",{"text":194,"config":195},"Small Business",{"href":196,"dataGaLocation":44,"dataGaName":197},"/small-business/","small business",{"text":199,"config":200},"Public Sector",{"href":201,"dataGaLocation":44,"dataGaName":202},"/solutions/public-sector/","public sector",{"text":204,"config":205},"Pricing",{"href":206,"dataGaName":207,"dataGaLocation":44,"dataNavLevelOne":207},"/pricing/","pricing",{"text":209,"config":210,"link":212,"lists":216,"feature":300},"Resources",{"dataNavLevelOne":211},"resources",{"text":213,"config":214},"View all resources",{"href":215,"dataGaName":211,"dataGaLocation":44},"/resources/",[217,250,272],{"title":218,"items":219},"Getting started",[220,225,230,235,240,245],{"text":221,"config":222},"Install",{"href":223,"dataGaName":224,"dataGaLocation":44},"/install/","install",{"text":226,"config":227},"Quick start guides",{"href":228,"dataGaName":229,"dataGaLocation":44},"/get-started/","quick setup checklists",{"text":231,"config":232},"Learn",{"href":233,"dataGaLocation":44,"dataGaName":234},"https://university.gitlab.com/","learn",{"text":236,"config":237},"Product documentation",{"href":238,"dataGaName":239,"dataGaLocation":44},"https://docs.gitlab.com/","product documentation",{"text":241,"config":242},"Best practice videos",{"href":243,"dataGaName":244,"dataGaLocation":44},"/getting-started-videos/","best practice videos",{"text":246,"config":247},"Integrations",{"href":248,"dataGaName":249,"dataGaLocation":44},"/integrations/","integrations",{"title":251,"items":252},"Discover",[253,258,262,267],{"text":254,"config":255},"Customer success stories",{"href":256,"dataGaName":257,"dataGaLocation":44},"/customers/","customer success stories",{"text":259,"config":260},"Blog",{"href":261,"dataGaName":5,"dataGaLocation":44},"/blog/",{"text":263,"config":264},"Remote",{"href":265,"dataGaName":266,"dataGaLocation":44},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":268,"config":269},"TeamOps",{"href":270,"dataGaName":271,"dataGaLocation":44},"/teamops/","teamops",{"title":273,"items":274},"Connect",[275,280,285,290,295],{"text":276,"config":277},"GitLab Services",{"href":278,"dataGaName":279,"dataGaLocation":44},"/services/","services",{"text":281,"config":282},"Community",{"href":283,"dataGaName":284,"dataGaLocation":44},"/community/","community",{"text":286,"config":287},"Forum",{"href":288,"dataGaName":289,"dataGaLocation":44},"https://forum.gitlab.com/","forum",{"text":291,"config":292},"Events",{"href":293,"dataGaName":294,"dataGaLocation":44},"/events/","events",{"text":296,"config":297},"Partners",{"href":298,"dataGaName":299,"dataGaLocation":44},"/partners/","partners",{"backgroundColor":301,"textColor":302,"text":303,"image":304,"link":308},"#2f2a6b","#fff","Insights for the future of software development",{"altText":305,"config":306},"the source promo card",{"src":307},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":309,"config":310},"Read the latest",{"href":311,"dataGaName":312,"dataGaLocation":44},"/the-source/","the source",{"text":314,"config":315,"lists":317},"Company",{"dataNavLevelOne":316},"company",[318],{"items":319},[320,325,331,333,338,343,348,353,358,363,368],{"text":321,"config":322},"About",{"href":323,"dataGaName":324,"dataGaLocation":44},"/company/","about",{"text":326,"config":327,"footerGa":330},"Jobs",{"href":328,"dataGaName":329,"dataGaLocation":44},"/jobs/","jobs",{"dataGaName":329},{"text":291,"config":332},{"href":293,"dataGaName":294,"dataGaLocation":44},{"text":334,"config":335},"Leadership",{"href":336,"dataGaName":337,"dataGaLocation":44},"/company/team/e-group/","leadership",{"text":339,"config":340},"Team",{"href":341,"dataGaName":342,"dataGaLocation":44},"/company/team/","team",{"text":344,"config":345},"Handbook",{"href":346,"dataGaName":347,"dataGaLocation":44},"https://handbook.gitlab.com/","handbook",{"text":349,"config":350},"Investor relations",{"href":351,"dataGaName":352,"dataGaLocation":44},"https://ir.gitlab.com/","investor relations",{"text":354,"config":355},"Trust Center",{"href":356,"dataGaName":357,"dataGaLocation":44},"/security/","trust center",{"text":359,"config":360},"AI Transparency Center",{"href":361,"dataGaName":362,"dataGaLocation":44},"/ai-transparency-center/","ai transparency center",{"text":364,"config":365},"Newsletter",{"href":366,"dataGaName":367,"dataGaLocation":44},"/company/contact/","newsletter",{"text":369,"config":370},"Press",{"href":371,"dataGaName":372,"dataGaLocation":44},"/press/","press",{"text":374,"config":375,"lists":376},"Contact us",{"dataNavLevelOne":316},[377],{"items":378},[379,382,387],{"text":51,"config":380},{"href":53,"dataGaName":381,"dataGaLocation":44},"talk to sales",{"text":383,"config":384},"Get help",{"href":385,"dataGaName":386,"dataGaLocation":44},"/support/","get help",{"text":388,"config":389},"Customer portal",{"href":390,"dataGaName":391,"dataGaLocation":44},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":393,"login":394,"suggestions":401},"Close",{"text":395,"link":396},"To search repositories and projects, login to",{"text":397,"config":398},"gitlab.com",{"href":58,"dataGaName":399,"dataGaLocation":400},"search login","search",{"text":402,"default":403},"Suggestions",[404,406,410,412,416,420],{"text":73,"config":405},{"href":78,"dataGaName":73,"dataGaLocation":400},{"text":407,"config":408},"Code Suggestions (AI)",{"href":409,"dataGaName":407,"dataGaLocation":400},"/solutions/code-suggestions/",{"text":125,"config":411},{"href":127,"dataGaName":125,"dataGaLocation":400},{"text":413,"config":414},"GitLab on AWS",{"href":415,"dataGaName":413,"dataGaLocation":400},"/partners/technology-partners/aws/",{"text":417,"config":418},"GitLab on Google Cloud",{"href":419,"dataGaName":417,"dataGaLocation":400},"/partners/technology-partners/google-cloud-platform/",{"text":421,"config":422},"Why GitLab?",{"href":86,"dataGaName":421,"dataGaLocation":400},{"freeTrial":424,"mobileIcon":429,"desktopIcon":434,"secondaryButton":437},{"text":425,"config":426},"Start free trial",{"href":427,"dataGaName":49,"dataGaLocation":428},"https://gitlab.com/-/trials/new/","nav",{"altText":430,"config":431},"Gitlab Icon",{"src":432,"dataGaName":433,"dataGaLocation":428},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":430,"config":435},{"src":436,"dataGaName":433,"dataGaLocation":428},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":438,"config":439},"Get Started",{"href":440,"dataGaName":441,"dataGaLocation":428},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":443,"mobileIcon":447,"desktopIcon":449},{"text":444,"config":445},"Learn more about GitLab Duo",{"href":78,"dataGaName":446,"dataGaLocation":428},"gitlab duo",{"altText":430,"config":448},{"src":432,"dataGaName":433,"dataGaLocation":428},{"altText":430,"config":450},{"src":436,"dataGaName":433,"dataGaLocation":428},{"freeTrial":452,"mobileIcon":457,"desktopIcon":459},{"text":453,"config":454},"Back to pricing",{"href":206,"dataGaName":455,"dataGaLocation":428,"icon":456},"back to pricing","GoBack",{"altText":430,"config":458},{"src":432,"dataGaName":433,"dataGaLocation":428},{"altText":430,"config":460},{"src":436,"dataGaName":433,"dataGaLocation":428},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":466,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"title":467,"button":468,"image":473,"config":477,"_id":479,"_type":30,"_source":32,"_file":480,"_stem":481,"_extension":35},"/shared/en-us/banner","is now in public beta!",{"text":469,"config":470},"Try the Beta",{"href":471,"dataGaName":472,"dataGaLocation":44},"/gitlab-duo/agent-platform/","duo banner",{"altText":474,"config":475},"GitLab Duo Agent Platform",{"src":476},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":478},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":483,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":484,"_id":688,"_type":30,"title":689,"_source":32,"_file":690,"_stem":691,"_extension":35},"/shared/en-us/main-footer",{"text":485,"source":486,"edit":492,"contribute":497,"config":502,"items":507,"minimal":680},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":487,"config":488},"View page source",{"href":489,"dataGaName":490,"dataGaLocation":491},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":493,"config":494},"Edit this page",{"href":495,"dataGaName":496,"dataGaLocation":491},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":498,"config":499},"Please contribute",{"href":500,"dataGaName":501,"dataGaLocation":491},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":503,"facebook":504,"youtube":505,"linkedin":506},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[508,531,587,616,650],{"title":62,"links":509,"subMenu":514},[510],{"text":511,"config":512},"DevSecOps platform",{"href":71,"dataGaName":513,"dataGaLocation":491},"devsecops platform",[515],{"title":204,"links":516},[517,521,526],{"text":518,"config":519},"View plans",{"href":206,"dataGaName":520,"dataGaLocation":491},"view plans",{"text":522,"config":523},"Why Premium?",{"href":524,"dataGaName":525,"dataGaLocation":491},"/pricing/premium/","why premium",{"text":527,"config":528},"Why Ultimate?",{"href":529,"dataGaName":530,"dataGaLocation":491},"/pricing/ultimate/","why ultimate",{"title":532,"links":533},"Solutions",[534,539,541,543,548,553,557,560,564,569,571,574,577,582],{"text":535,"config":536},"Digital transformation",{"href":537,"dataGaName":538,"dataGaLocation":491},"/topics/digital-transformation/","digital transformation",{"text":150,"config":540},{"href":152,"dataGaName":150,"dataGaLocation":491},{"text":139,"config":542},{"href":121,"dataGaName":122,"dataGaLocation":491},{"text":544,"config":545},"Agile development",{"href":546,"dataGaName":547,"dataGaLocation":491},"/solutions/agile-delivery/","agile delivery",{"text":549,"config":550},"Cloud transformation",{"href":551,"dataGaName":552,"dataGaLocation":491},"/topics/cloud-native/","cloud transformation",{"text":554,"config":555},"SCM",{"href":135,"dataGaName":556,"dataGaLocation":491},"source code management",{"text":125,"config":558},{"href":127,"dataGaName":559,"dataGaLocation":491},"continuous integration & delivery",{"text":561,"config":562},"Value stream management",{"href":179,"dataGaName":563,"dataGaLocation":491},"value stream management",{"text":565,"config":566},"GitOps",{"href":567,"dataGaName":568,"dataGaLocation":491},"/solutions/gitops/","gitops",{"text":189,"config":570},{"href":191,"dataGaName":192,"dataGaLocation":491},{"text":572,"config":573},"Small business",{"href":196,"dataGaName":197,"dataGaLocation":491},{"text":575,"config":576},"Public sector",{"href":201,"dataGaName":202,"dataGaLocation":491},{"text":578,"config":579},"Education",{"href":580,"dataGaName":581,"dataGaLocation":491},"/solutions/education/","education",{"text":583,"config":584},"Financial services",{"href":585,"dataGaName":586,"dataGaLocation":491},"/solutions/finance/","financial services",{"title":209,"links":588},[589,591,593,595,598,600,602,604,606,608,610,612,614],{"text":221,"config":590},{"href":223,"dataGaName":224,"dataGaLocation":491},{"text":226,"config":592},{"href":228,"dataGaName":229,"dataGaLocation":491},{"text":231,"config":594},{"href":233,"dataGaName":234,"dataGaLocation":491},{"text":236,"config":596},{"href":238,"dataGaName":597,"dataGaLocation":491},"docs",{"text":259,"config":599},{"href":261,"dataGaName":5,"dataGaLocation":491},{"text":254,"config":601},{"href":256,"dataGaName":257,"dataGaLocation":491},{"text":263,"config":603},{"href":265,"dataGaName":266,"dataGaLocation":491},{"text":276,"config":605},{"href":278,"dataGaName":279,"dataGaLocation":491},{"text":268,"config":607},{"href":270,"dataGaName":271,"dataGaLocation":491},{"text":281,"config":609},{"href":283,"dataGaName":284,"dataGaLocation":491},{"text":286,"config":611},{"href":288,"dataGaName":289,"dataGaLocation":491},{"text":291,"config":613},{"href":293,"dataGaName":294,"dataGaLocation":491},{"text":296,"config":615},{"href":298,"dataGaName":299,"dataGaLocation":491},{"title":314,"links":617},[618,620,622,624,626,628,630,634,639,641,643,645],{"text":321,"config":619},{"href":323,"dataGaName":316,"dataGaLocation":491},{"text":326,"config":621},{"href":328,"dataGaName":329,"dataGaLocation":491},{"text":334,"config":623},{"href":336,"dataGaName":337,"dataGaLocation":491},{"text":339,"config":625},{"href":341,"dataGaName":342,"dataGaLocation":491},{"text":344,"config":627},{"href":346,"dataGaName":347,"dataGaLocation":491},{"text":349,"config":629},{"href":351,"dataGaName":352,"dataGaLocation":491},{"text":631,"config":632},"Sustainability",{"href":633,"dataGaName":631,"dataGaLocation":491},"/sustainability/",{"text":635,"config":636},"Diversity, inclusion and belonging (DIB)",{"href":637,"dataGaName":638,"dataGaLocation":491},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":354,"config":640},{"href":356,"dataGaName":357,"dataGaLocation":491},{"text":364,"config":642},{"href":366,"dataGaName":367,"dataGaLocation":491},{"text":369,"config":644},{"href":371,"dataGaName":372,"dataGaLocation":491},{"text":646,"config":647},"Modern Slavery Transparency Statement",{"href":648,"dataGaName":649,"dataGaLocation":491},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":651,"links":652},"Contact Us",[653,656,658,660,665,670,675],{"text":654,"config":655},"Contact an expert",{"href":53,"dataGaName":54,"dataGaLocation":491},{"text":383,"config":657},{"href":385,"dataGaName":386,"dataGaLocation":491},{"text":388,"config":659},{"href":390,"dataGaName":391,"dataGaLocation":491},{"text":661,"config":662},"Status",{"href":663,"dataGaName":664,"dataGaLocation":491},"https://status.gitlab.com/","status",{"text":666,"config":667},"Terms of use",{"href":668,"dataGaName":669,"dataGaLocation":491},"/terms/","terms of use",{"text":671,"config":672},"Privacy statement",{"href":673,"dataGaName":674,"dataGaLocation":491},"/privacy/","privacy statement",{"text":676,"config":677},"Cookie preferences",{"dataGaName":678,"dataGaLocation":491,"id":679,"isOneTrustButton":107},"cookie preferences","ot-sdk-btn",{"items":681},[682,684,686],{"text":666,"config":683},{"href":668,"dataGaName":669,"dataGaLocation":491},{"text":671,"config":685},{"href":673,"dataGaName":674,"dataGaLocation":491},{"text":676,"config":687},{"dataGaName":678,"dataGaLocation":491,"id":679,"isOneTrustButton":107},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[693],{"_path":694,"_dir":695,"_draft":6,"_partial":6,"_locale":7,"content":696,"config":700,"_id":702,"_type":30,"title":18,"_source":32,"_file":703,"_stem":704,"_extension":35},"/en-us/blog/authors/michael-friedrich","authors",{"name":18,"config":697},{"headshot":698,"ctfId":699},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659879/Blog/Author%20Headshots/dnsmichi-headshot.jpg","dnsmichi",{"template":701},"BlogAuthor","content:en-us:blog:authors:michael-friedrich.yml","en-us/blog/authors/michael-friedrich.yml","en-us/blog/authors/michael-friedrich",{"_path":706,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"header":707,"eyebrow":708,"blurb":709,"button":710,"secondaryButton":714,"_id":716,"_type":30,"title":717,"_source":32,"_file":718,"_stem":719,"_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":711},{"href":712,"dataGaName":49,"dataGaLocation":713},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":51,"config":715},{"href":53,"dataGaName":54,"dataGaLocation":713},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1758326218086]