[{"data":1,"prerenderedAt":2733},["ShallowReactive",2],{"/blog/how-to-choose-the-right-security-scanning-approach":3,"navigation-en-us":1936,"banner-en-us":2363,"footer-en-us":2373,"blog-post-authors-en-us-Matt Genelin|Mathias Ewald":2618,"blog-related-posts-en-us-how-to-choose-the-right-security-scanning-approach":2645,"blog-promotions-en-us":2670,"next-steps-en-us":2723},{"id":4,"title":5,"authors":6,"body":9,"category":1917,"date":1918,"description":1919,"extension":1920,"externalUrl":1921,"faq":1921,"featured":298,"heroImage":1922,"meta":1923,"navigation":298,"path":1924,"seo":1925,"slug":1930,"stem":1931,"tags":1932,"template":1934,"updatedDate":1921,"__hash__":1935},"blogPosts/en-us/blog/how-to-choose-the-right-security-scanning-approach.md","How to choose the right security scanning approach",[7,8],"Matt Genelin","Mathias Ewald",{"type":10,"value":11,"toc":1897},"minimark",[12,16,26,29,63,67,89,97,100,120,123,135,138,143,151,225,250,259,272,316,325,346,356,412,418,421,425,434,449,465,537,550,553,560,564,569,572,576,579,582,586,593,596,599,608,611,626,629,635,639,642,645,729,732,736,739,742,921,925,928,931,934,937,940,943,947,950,953,956,1013,1016,1068,1075,1078,1080,1137,1139,1160,1169,1220,1238,1241,1244,1253,1335,1338,1341,1348,1355,1362,1366,1373,1376,1382,1469,1477,1481,1484,1487,1490,1493,1496,1499,1521,1533,1545,1548,1597,1601,1604,1614,1618,1628,1631,1637,1644,1647,1653,1803,1806,1812,1817,1820,1824,1840,1843,1846,1849,1852,1867,1871,1893],[13,14,15],"p",{},"Integrating security scans into your CI/CD pipeline is crucial for maintaining robust and secure applications. But who's responsible for those scans? Who is responsible for adding them into every CI/CD pipeline for all projects? And who decides which identified vulnerability may pass or needs fixing? For organizations in regulated industries, these are critical questions.",[13,17,18,19,25],{},"In this article, you'll learn how GitLab ",[20,21,24],"a",{"href":22,"rel":23},"https://about.gitlab.com/topics/ci-cd/",[],"CI/CD"," enables each person in the software development lifecycle to incorporate security scanning. You'll also discover the advantages and disadvantages of the various options available to add scanning to GitLab project pipelines. Code examples will help you kickstart security scanning on the GitLab DevSecOps platform.",[13,27,28],{},"Article contents:",[30,31,32,39,45,51,57],"ul",{},[33,34,35],"li",{},[20,36,38],{"href":37},"#the-basics-of-setting-up-security-scanning","The basics of setting up security scanning",[33,40,41],{},[20,42,44],{"href":43},"#pipeline-includes","Pipeline includes",[33,46,47],{},[20,48,50],{"href":49},"#compliance-frameworks","Compliance frameworks",[33,52,53],{},[20,54,56],{"href":55},"#policies","Policies",[33,58,59],{},[20,60,62],{"href":61},"#get-started-with-security-scanning","Get started with security scanning",[64,65,38],"h2",{"id":66},"the-basics-of-setting-up-security-scanning",[13,68,69,70,75,76,80,81,84,85,88],{},"GitLab uses ",[20,71,74],{"href":72,"rel":73},"https://handbook.gitlab.com/handbook/product/personas/#user-personas",[],"fictional personas"," to describe the individual team member who would typically use a given security feature or approach. By exploring the perspective of a ",[77,78,79],"strong",{},"Software Developer (Sasha)",", ",[77,82,83],{},"Application Security Engineer (Amy)",", or ",[77,86,87],{},"Platform Engineer (Priyanka)",", you can better understand the needs of each role on your team.",[13,90,91,92,96],{},"GitLab follows a \"pipeline-per-project\" principle, stored in the file named ",[93,94,95],"code",{},".gitlab-ci.yml",". This file contains the project's CI/CD pipeline definition and is revision controlled like any other file in the project. You'll learn about these project pipelines, as well as compliance pipelines and policy pipelines. While compliance pipelines and policy pipelines also refer to the YAML files in GitLab projects, they typically have a different file name and serve a different purpose.",[13,98,99],{},"Readers already familiar with security scanning in GitLab will find clarity in the security pipeline choices available in the context of your team/organization. Therefore, we will discuss each of the approaches with respect to the following criteria:",[30,101,102,108,114],{},[33,103,104,107],{},[77,105,106],{},"Ease of use:"," How easy is it to add security scanning to project pipelines? Is it a reasonable task for Sasha, or something that Amy and Priyanka should handle?",[33,109,110,113],{},[77,111,112],{},"Customization:"," How deeply can scanner configurations be customized using that approach? While default configurations that make sense and cover a wide range of customer needs are worth gold, the time often comes when scanner configurations need adjustments.",[33,115,116,119],{},[77,117,118],{},"Enforcement:"," Is this approach suitable to companies operating in regulated industries or that otherwise have global policies in place? Can we ensure each relevant project runs Scanner X with Configuration Y?",[64,121,44],{"id":122},"pipeline-includes",[13,124,125,130,131,134],{},[20,126,129],{"href":127,"rel":128},"https://docs.gitlab.com/ci/yaml/includes/",[],"GitLab project pipeline includes"," are a mechanism that allows the integration of external pipelines into the ",[93,132,133],{},".gitlab-ci.yaml"," project pipeline. This is similar to including a library in many programming languages. This powerful feature enables the seamless incorporation of your own templates, as well as GitLab-provided templates, to be used as building blocks for your pipelines. Includes can be used in project pipelines or other pipeline files. An example of a commonly included external pipeline is including a security scanning pipeline into a GitLab project pipeline.",[13,136,137],{},"Here are the common types of includes, which use the security scanner example.",[139,140,142],"h3",{"id":141},"templates","Templates",[13,144,145,146,150],{},"GitLab offers ready-to-use ",[20,147,141],{"href":148,"rel":149},"https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates/Jobs",[]," that can be included in a project pipeline to make it easier for teams to add in various pre-built elements. The following is example code:",[152,153,158],"pre",{"className":154,"code":155,"language":156,"meta":157,"style":157},"language-yaml shiki shiki-themes github-light","include:\n  - template: Jobs/Secret-Detection.gitlab-ci.yml\n  - template: Jobs/SAST.gitlab-ci.yml\n  - template: Jobs/Dependency-Scanning.gitlab-ci.yml\n  - template: Jobs/Container-Scanning.gitlab-ci.yml\n\n","yaml","",[93,159,160,173,189,201,213],{"__ignoreMap":157},[161,162,165,169],"span",{"class":163,"line":164},"line",1,[161,166,168],{"class":167},"shJU0","include",[161,170,172],{"class":171},"sgsFI",":\n",[161,174,176,179,182,185],{"class":163,"line":175},2,[161,177,178],{"class":171},"  - ",[161,180,181],{"class":167},"template",[161,183,184],{"class":171},": ",[161,186,188],{"class":187},"sYBdl","Jobs/Secret-Detection.gitlab-ci.yml\n",[161,190,192,194,196,198],{"class":163,"line":191},3,[161,193,178],{"class":171},[161,195,181],{"class":167},[161,197,184],{"class":171},[161,199,200],{"class":187},"Jobs/SAST.gitlab-ci.yml\n",[161,202,204,206,208,210],{"class":163,"line":203},4,[161,205,178],{"class":171},[161,207,181],{"class":167},[161,209,184],{"class":171},[161,211,212],{"class":187},"Jobs/Dependency-Scanning.gitlab-ci.yml\n",[161,214,216,218,220,222],{"class":163,"line":215},5,[161,217,178],{"class":171},[161,219,181],{"class":167},[161,221,184],{"class":171},[161,223,224],{"class":187},"Jobs/Container-Scanning.gitlab-ci.yml\n",[13,226,227,228,80,233,80,238,243,244,249],{},"This code includes GitLab's templates for ",[20,229,232],{"href":230,"rel":231},"https://docs.gitlab.com/user/application_security/secret_detection/",[],"Secret Detection",[20,234,237],{"href":235,"rel":236},"https://docs.gitlab.com/user/application_security/sast/",[],"Static Application Security Testing",[20,239,242],{"href":240,"rel":241},"https://docs.gitlab.com/user/application_security/dependency_scanning/",[],"Dependency Scanning",", and ",[20,245,248],{"href":246,"rel":247},"https://docs.gitlab.com/user/application_security/container_scanning/",[],"Container Scanning"," – all in only five lines of code.",[13,251,252,253,258],{},"To modify the behavior of jobs included via templates, you can either use variables or use ",[20,254,257],{"href":255,"rel":256},"https://docs.gitlab.com/ci/yaml/includes/#merge-method-for-include",[],"GitLab's property merging capabilities",".",[13,260,261,262,267,268,271],{},"You will find an example of modifying the GitLab Container Scanning pipeline using variables below. The ",[20,263,266],{"href":264,"rel":265},"https://gitlab.com/gitlab-org/gitlab/-/blob/59f08760feaab1eb0489f694d4f28408af9c2e8d/lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml",[],"template for Container Scanning"," needs to know the location of the image and uses a variable named ",[93,269,270],{},"CS_IMAGE"," for that as is documented in the template code linked above.",[152,273,275],{"className":154,"code":274,"language":156,"meta":157,"style":157},"variables:\n  CS_IMAGE: \"$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA\"\n\ninclude:\n  - template: Jobs/Container-Scanning.gitlab-ci.yml\n\n",[93,276,277,284,294,300,306],{"__ignoreMap":157},[161,278,279,282],{"class":163,"line":164},[161,280,281],{"class":167},"variables",[161,283,172],{"class":171},[161,285,286,289,291],{"class":163,"line":175},[161,287,288],{"class":167},"  CS_IMAGE",[161,290,184],{"class":171},[161,292,293],{"class":187},"\"$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA\"\n",[161,295,296],{"class":163,"line":191},[161,297,299],{"emptyLinePlaceholder":298},true,"\n",[161,301,302,304],{"class":163,"line":203},[161,303,168],{"class":167},[161,305,172],{"class":171},[161,307,308,310,312,314],{"class":163,"line":215},[161,309,178],{"class":171},[161,311,181],{"class":167},[161,313,184],{"class":171},[161,315,224],{"class":187},[13,317,318,319,321,322,324],{},"The project pipeline variables are available to included job templates by defining the ",[93,320,270],{}," variable before the included pipeline template. The Container Scanning template inherits the ",[93,323,270],{}," variable value.",[13,326,327,328,336,337,339,340,345],{},"If we wanted to make changes to the ",[20,329,332,335],{"href":330,"rel":331},"https://gitlab.com/gitlab-org/gitlab/-/blob/59f08760feaab1eb0489f694d4f28408af9c2e8d/lib/gitlab/ci/templates/Jobs/Container-Scanning.gitlab-ci.yml#L38",[],[93,333,334],{},"allow_failure"," property defined here",", we would need to resort to property merging since the job templates employ no variable for the value. (The ",[93,338,334],{}," property is a property generally available on every GitLab pipeline job. Please check the ",[20,341,344],{"href":342,"rel":343},"https://docs.gitlab.com/ci/yaml/#allow_failure",[],"documentation"," for details.)",[13,347,348,349,351,352,355],{},"In this example, ",[93,350,334],{}," is set to ",[93,353,354],{},"false",", meaning the entire pipeline stops on a container scanning failure. This stops any unscanned containers from moving forward in the pipeline.",[152,357,359],{"className":154,"code":358,"language":156,"meta":157,"style":157},"include:\n  # Includes a job called \"container_scanning\"\n  - template: Jobs/Container-Scanning.gitlab-ci.yml\n\n# Define a job with same name for merging\ncontainer_scanning:\n  allow_failure: false\n\n",[93,360,361,367,373,383,387,392,400],{"__ignoreMap":157},[161,362,363,365],{"class":163,"line":164},[161,364,168],{"class":167},[161,366,172],{"class":171},[161,368,369],{"class":163,"line":175},[161,370,372],{"class":371},"sAwPA","  # Includes a job called \"container_scanning\"\n",[161,374,375,377,379,381],{"class":163,"line":191},[161,376,178],{"class":171},[161,378,181],{"class":167},[161,380,184],{"class":171},[161,382,224],{"class":187},[161,384,385],{"class":163,"line":203},[161,386,299],{"emptyLinePlaceholder":298},[161,388,389],{"class":163,"line":215},[161,390,391],{"class":371},"# Define a job with same name for merging\n",[161,393,395,398],{"class":163,"line":394},6,[161,396,397],{"class":167},"container_scanning",[161,399,172],{"class":171},[161,401,403,406,408],{"class":163,"line":402},7,[161,404,405],{"class":167},"  allow_failure",[161,407,184],{"class":171},[161,409,411],{"class":410},"sYu0t","false\n",[13,413,414,415,417],{},"GitLab will load the job template and – as defined in the template code – register a job called ",[93,416,397],{},". As the pipeline definition declares another job with that name, GitLab will merge that specification with the already registered job.",[13,419,420],{},"While this feature offers many possibilities, it also makes it impossible to protect certain properties from being overwritten. We are only at the point of modifying the project pipeline, so there's no control over that anyway. But later on, you will see that this can pose a challenge when security needs to be enforced on a project.",[139,422,424],{"id":423},"components","Components",[13,426,427,428,433],{},"Templates are a great start for sharing repeatable GitLab pipelines. To further abstract reusable code across an entire organization or a GitLab instance, ",[20,429,432],{"href":430,"rel":431},"https://docs.gitlab.com/ci/components/",[],"GitLab introduced components",". Components are the next logical step in GitLab's evolution of pipelines. Components are designed to simplify the creation and use of functional building blocks to use in pipelines, or even to package and ship entire pipelines if needed. They offer a well-defined interface, which accepts \"inputs\" for configuration. Otherwise, the component is completely isolated, which makes them a great candidate to share work within an organization and to be searchable and reusable building blocks.",[13,435,436,437,442,443,448],{},"Developers can use the ",[20,438,441],{"href":439,"rel":440},"https://gitlab.com/explore/catalog",[],"CI/CD Catalog"," to browse and search the collection of publicly available GitLab components, which are components officially built and maintained by GitLab. GitLab uses the CI/CD Catalog ",[20,444,447],{"href":445,"rel":446},"https://gitlab.com/components",[],"to publish our shipped components"," such as security scanners alongside community-provided components.",[13,450,451,452,454,455,460,461,464],{},"Components are consumed similarly to templates via the ",[93,453,168],{}," keyword. In an example above, we showed how the container scanning job requires knowledge of the image location. This \"input\" uses the component for ",[20,456,459],{"href":457,"rel":458},"https://gitlab.com/components/container-scanning/-/blob/19fd5b83bc631cb9890b4fadb08d31b3150853ce/templates/container-scanning.yml",[],"container scanning"," is called ",[93,462,463],{},"cs_image",". The configuration equivalent to the previous example looks like this:",[152,466,468],{"className":154,"code":467,"language":156,"meta":157,"style":157},"include:\n  - component: $CI_SERVER_FQDN/components/sast/sast@2.0.2\n  - component: $CI_SERVER_FQDN/components/dependency-scanning/cargo@0.2.0\n  - component: $CI_SERVER_FQDN/components/secret-detection/secret-detection@1.1.2\n  - component: $CI_SERVER_FQDN/components/container-scanning/container-scanning@4.1.0\n    inputs:\n      cs_image: \"$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA\"\n\n",[93,469,470,476,488,499,510,521,528],{"__ignoreMap":157},[161,471,472,474],{"class":163,"line":164},[161,473,168],{"class":167},[161,475,172],{"class":171},[161,477,478,480,483,485],{"class":163,"line":175},[161,479,178],{"class":171},[161,481,482],{"class":167},"component",[161,484,184],{"class":171},[161,486,487],{"class":187},"$CI_SERVER_FQDN/components/sast/sast@2.0.2\n",[161,489,490,492,494,496],{"class":163,"line":191},[161,491,178],{"class":171},[161,493,482],{"class":167},[161,495,184],{"class":171},[161,497,498],{"class":187},"$CI_SERVER_FQDN/components/dependency-scanning/cargo@0.2.0\n",[161,500,501,503,505,507],{"class":163,"line":203},[161,502,178],{"class":171},[161,504,482],{"class":167},[161,506,184],{"class":171},[161,508,509],{"class":187},"$CI_SERVER_FQDN/components/secret-detection/secret-detection@1.1.2\n",[161,511,512,514,516,518],{"class":163,"line":215},[161,513,178],{"class":171},[161,515,482],{"class":167},[161,517,184],{"class":171},[161,519,520],{"class":187},"$CI_SERVER_FQDN/components/container-scanning/container-scanning@4.1.0\n",[161,522,523,526],{"class":163,"line":394},[161,524,525],{"class":167},"    inputs",[161,527,172],{"class":171},[161,529,530,533,535],{"class":163,"line":402},[161,531,532],{"class":167},"      cs_image",[161,534,184],{"class":171},[161,536,293],{"class":187},[13,538,539,540,543,544,549],{},"In this example, the SAST component is pinned at Version 2.0.2, the Dependency Scanning component at Version 0.2.0, the Secret Detection component at Version 1.1.2, and the Container Scanning component at Version 4.1.0. ",[93,541,542],{},"~latest"," ",[20,545,548],{"href":546,"rel":547},"https://docs.gitlab.com/ci/components/#component-versions",[],"and more tags are available"," for bleeding-edge component usage and other development needs.",[13,551,552],{},"Whether you use templates or components, your pipeline might look like the image below. The top four jobs in the test stage are the result of the four include statements in the code above.",[13,554,555],{},[556,557],"img",{"alt":558,"src":559},"An example pipeline","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097984/Blog/Content%20Images/Blog/Content%20Images/image1_aHR0cHM6_1750097983863.png",[139,561,563],{"id":562},"advantages-and-disadvantages-of-using-pipeline-includes","Advantages and disadvantages of using pipeline includes",[565,566,568],"h4",{"id":567},"ease-of-use","Ease of use",[13,570,571],{},"One of the benefits of using pipeline includes in GitLab is their ease of use. We have seen how, with essentially six lines of code, we included four commonly used security scanners. All the complex logic and setup are handled within the templates or components, saving Sacha time and effort by providing a ready-to-use solution.",[565,573,575],{"id":574},"customization","Customization",[13,577,578],{},"While templates offer the highest flexibility (variables and merging), it's important to remember that with \"great power comes great responsibility.\" The flexibility of templates supports extensive customization, but requires careful management and oversight to avoid unexpected results.",[13,580,581],{},"In contrast, components provide a more structured mechanism for authoring, sharing, and maintaining building blocks for a broader audience. Components, while not as customizable, enhance stability and reliability, and are a valuable, reusable, and repeatable feature.",[565,583,585],{"id":584},"enforcement","Enforcement",[13,587,588,589,592],{},"As the name ",[590,591,168],"em",{}," suggests, it is the GitLab project pipeline that needs to include templates or components. While scanner templates are straightforward to use, Amy and Priyanka cannot be sure Sacha has included them properly, or even at all. Enforcement of scanner usage is needed.",[13,594,595],{},"For regulated industries, managing security in project pipelines is not an approach that provides the necessary audit trail or enforcement.",[64,597,50],{"id":598},"compliance-frameworks",[13,600,601,602,607],{},"GitLab identified the gap between the ability to enforce security scans on project pipelines and the need to ",[20,603,606],{"href":604,"rel":605},"https://about.gitlab.com/blog/meet-regulatory-standards-with-gitlab/",[],"adhere to regulatory compliance frameworks"," such as PCI DSS, NIST, and many more. The introduction of compliance frameworks as functionality caters to precisely this challenge.",[13,609,610],{},"At first glance, a compliance framework in GitLab is merely a label attached to a project, which would typically be named after the regulatory framework it is supposed to implement. The magic is added with the link between that label and a compliance pipeline YAML file, which is responsible for implementing the necessary steps to ensure compliance.",[13,612,613,614,619,620,625],{},"The mechanism is straightforward: Every time the project pipeline is triggered, GitLab executes the compliance pipeline instead. The compliance pipeline runs with both the ",[20,615,618],{"href":616,"rel":617},"https://docs.gitlab.com/ci/variables/",[],"CI/CD variables"," and ",[20,621,624],{"href":622,"rel":623},"https://docs.gitlab.com/ci/variables/predefined_variables/",[],"predefined CI/CD variables"," of the project pipeline.",[13,627,628],{},"This allows for two main design patterns: a \"wrapping pipeline,\" where the compliance pipeline includes the project pipeline, and an \"overriding pipeline,\" where it does not.",[13,630,631,634],{},[77,632,633],{},"Note:"," Compliance pipelines have been deprecated in GitLab Version 17.3 and are scheduled for removal in Version 19.0. At this point, we cannot recommend implementing this approach for new development platforms. However, you might already be using them, making it worth reading this section.",[139,636,638],{"id":637},"wrapping-pipelines","Wrapping pipelines",[13,640,641],{},"In the wrapping approach, the compliance pipeline defines its own jobs according to specific compliance needs. It includes the project pipeline in the same way we have seen templates included in the previous section. This setup is possible because the predefined CI/CD variables originate from the project pipeline, allowing the system to identify the pipeline definition's location for inclusion.",[13,643,644],{},"Here is an example of what a simple compliance pipeline might look like.",[152,646,648],{"className":154,"code":647,"language":156,"meta":157,"style":157},"include:\n  - component: $CI_SERVER_FQDN/components/sast/sast@2.0.2\n  - component: $CI_SERVER_FQDN/components/dependency-scanning/cargo@0.2.0\n  - component: $CI_SERVER_FQDN/components/secret-detection/secret-detection@1.1.2\n  - component: $CI_SERVER_FQDN/components/container-scanning/container-scanning@4.1.0\n  - project: '$CI_PROJECT_PATH'\n    file: '$CI_CONFIG_PATH'\n    ref: '$CI_COMMIT_SHA'\n\n",[93,649,650,656,666,676,686,696,708,718],{"__ignoreMap":157},[161,651,652,654],{"class":163,"line":164},[161,653,168],{"class":167},[161,655,172],{"class":171},[161,657,658,660,662,664],{"class":163,"line":175},[161,659,178],{"class":171},[161,661,482],{"class":167},[161,663,184],{"class":171},[161,665,487],{"class":187},[161,667,668,670,672,674],{"class":163,"line":191},[161,669,178],{"class":171},[161,671,482],{"class":167},[161,673,184],{"class":171},[161,675,498],{"class":187},[161,677,678,680,682,684],{"class":163,"line":203},[161,679,178],{"class":171},[161,681,482],{"class":167},[161,683,184],{"class":171},[161,685,509],{"class":187},[161,687,688,690,692,694],{"class":163,"line":215},[161,689,178],{"class":171},[161,691,482],{"class":167},[161,693,184],{"class":171},[161,695,520],{"class":187},[161,697,698,700,703,705],{"class":163,"line":394},[161,699,178],{"class":171},[161,701,702],{"class":167},"project",[161,704,184],{"class":171},[161,706,707],{"class":187},"'$CI_PROJECT_PATH'\n",[161,709,710,713,715],{"class":163,"line":402},[161,711,712],{"class":167},"    file",[161,714,184],{"class":171},[161,716,717],{"class":187},"'$CI_CONFIG_PATH'\n",[161,719,721,724,726],{"class":163,"line":720},8,[161,722,723],{"class":167},"    ref",[161,725,184],{"class":171},[161,727,728],{"class":187},"'$CI_COMMIT_SHA'\n",[13,730,731],{},"The last three lines include the project pipeline based on available variables.",[139,733,735],{"id":734},"overriding-pipelines","Overriding pipelines",[13,737,738],{},"Unlike wrapping pipelines, which include the project pipeline, overriding pipelines ignore it entirely and run only their own jobs. This type of pipeline defines each step, encompassing all necessary jobs to build, test, and deploy the application.",[13,740,741],{},"Below we see a mock compliance pipeline that illustrates this approach.",[152,743,745],{"className":154,"code":744,"language":156,"meta":157,"style":157},"stages: [\"build\", \"test\", \"deploy\"]\n\ninclude:\n  - component: $CI_SERVER_FQDN/components/sast/sast@2.0.2\n  - component: $CI_SERVER_FQDN/components/dependency-scanning/cargo@0.2.0\n  - component: $CI_SERVER_FQDN/components/secret-detection/secret-detection@1.1.2\n  - component: $CI_SERVER_FQDN/components/container-scanning/container-scanning@4.1.0\n\nbuild-job:\n  stage: build\n  script: echo \"Building the container image\"\n\ntest-job:\n  stage: test\n  script: echo \"Running unit tests\"\n\ndeploy-job:\n  stage: deploy\n  script: echo \"Deploying app\"\n\n",[93,746,747,771,775,781,791,801,811,821,825,833,844,855,860,868,878,888,893,901,911],{"__ignoreMap":157},[161,748,749,752,755,758,760,763,765,768],{"class":163,"line":164},[161,750,751],{"class":167},"stages",[161,753,754],{"class":171},": [",[161,756,757],{"class":187},"\"build\"",[161,759,80],{"class":171},[161,761,762],{"class":187},"\"test\"",[161,764,80],{"class":171},[161,766,767],{"class":187},"\"deploy\"",[161,769,770],{"class":171},"]\n",[161,772,773],{"class":163,"line":175},[161,774,299],{"emptyLinePlaceholder":298},[161,776,777,779],{"class":163,"line":191},[161,778,168],{"class":167},[161,780,172],{"class":171},[161,782,783,785,787,789],{"class":163,"line":203},[161,784,178],{"class":171},[161,786,482],{"class":167},[161,788,184],{"class":171},[161,790,487],{"class":187},[161,792,793,795,797,799],{"class":163,"line":215},[161,794,178],{"class":171},[161,796,482],{"class":167},[161,798,184],{"class":171},[161,800,498],{"class":187},[161,802,803,805,807,809],{"class":163,"line":394},[161,804,178],{"class":171},[161,806,482],{"class":167},[161,808,184],{"class":171},[161,810,509],{"class":187},[161,812,813,815,817,819],{"class":163,"line":402},[161,814,178],{"class":171},[161,816,482],{"class":167},[161,818,184],{"class":171},[161,820,520],{"class":187},[161,822,823],{"class":163,"line":720},[161,824,299],{"emptyLinePlaceholder":298},[161,826,828,831],{"class":163,"line":827},9,[161,829,830],{"class":167},"build-job",[161,832,172],{"class":171},[161,834,836,839,841],{"class":163,"line":835},10,[161,837,838],{"class":167},"  stage",[161,840,184],{"class":171},[161,842,843],{"class":187},"build\n",[161,845,847,850,852],{"class":163,"line":846},11,[161,848,849],{"class":167},"  script",[161,851,184],{"class":171},[161,853,854],{"class":187},"echo \"Building the container image\"\n",[161,856,858],{"class":163,"line":857},12,[161,859,299],{"emptyLinePlaceholder":298},[161,861,863,866],{"class":163,"line":862},13,[161,864,865],{"class":167},"test-job",[161,867,172],{"class":171},[161,869,871,873,875],{"class":163,"line":870},14,[161,872,838],{"class":167},[161,874,184],{"class":171},[161,876,877],{"class":187},"test\n",[161,879,881,883,885],{"class":163,"line":880},15,[161,882,849],{"class":167},[161,884,184],{"class":171},[161,886,887],{"class":187},"echo \"Running unit tests\"\n",[161,889,891],{"class":163,"line":890},16,[161,892,299],{"emptyLinePlaceholder":298},[161,894,896,899],{"class":163,"line":895},17,[161,897,898],{"class":167},"deploy-job",[161,900,172],{"class":171},[161,902,904,906,908],{"class":163,"line":903},18,[161,905,838],{"class":167},[161,907,184],{"class":171},[161,909,910],{"class":187},"deploy\n",[161,912,914,916,918],{"class":163,"line":913},19,[161,915,849],{"class":167},[161,917,184],{"class":171},[161,919,920],{"class":187},"echo \"Deploying app\"\n",[139,922,924],{"id":923},"advantages-and-disadvantages-of-compliance-frameworks","Advantages and disadvantages of compliance frameworks",[565,926,568],{"id":927},"ease-of-use-1",[13,929,930],{},"While compliance frameworks aren't terribly complicated, they aren't as straightforward and simple as pipeline includes. They're meant to be written and assigned to projects by Amy and Priyanka, who now need to interact with pipeline YAML code. A framework needs to be declared in the top-level namespace and compliance pipelines need to be created and maintained, and compliance frameworks need to be attached to the right projects.",[565,932,575],{"id":933},"customization-1",[13,935,936],{},"Amy and Priyanka are the authors of compliance pipelines. Like Sacha in the previous section on includes, they have full control over what they include and how they include it, giving them maximum customizability of compliance jobs such as security scanners.",[565,938,585],{"id":939},"enforcement-1",[13,941,942],{},"This aspect of enforcing pipelines questions whether developers can tamper with security jobs? In an environment with a strong separation of duties, this nuance requires some extra attention. To answer this, we need to look at each pattern separately:",[944,945,638],"h5",{"id":946},"wrapping-pipelines-1",[13,948,949],{},"As seen before, project pipelines are included in compliance pipelines. In addition to group- or project-level CI/CD variables, every element of that project pipeline must be considered a potential threat to the compliance pipeline. Obviously, variables and jobs stick out as primary candidates. And, in fact, they can and will influence security job behavior if used maliciously.",[13,951,952],{},"Here is a simple example to illustrate the issue.",[13,954,955],{},"Compliance pipeline:",[152,957,959],{"className":154,"code":958,"language":156,"meta":157,"style":157},"include:\n  - template: Jobs/SAST.gitlab-ci.yml\n  - template: Jobs/Secret-Detection.gitlab-ci.yml\n  - project: '$CI_PROJECT_PATH'\n    file: '$CI_CONFIG_PATH'\n    ref: '$CI_COMMIT_SHA'\n\n",[93,960,961,967,977,987,997,1005],{"__ignoreMap":157},[161,962,963,965],{"class":163,"line":164},[161,964,168],{"class":167},[161,966,172],{"class":171},[161,968,969,971,973,975],{"class":163,"line":175},[161,970,178],{"class":171},[161,972,181],{"class":167},[161,974,184],{"class":171},[161,976,200],{"class":187},[161,978,979,981,983,985],{"class":163,"line":191},[161,980,178],{"class":171},[161,982,181],{"class":167},[161,984,184],{"class":171},[161,986,188],{"class":187},[161,988,989,991,993,995],{"class":163,"line":203},[161,990,178],{"class":171},[161,992,702],{"class":167},[161,994,184],{"class":171},[161,996,707],{"class":187},[161,998,999,1001,1003],{"class":163,"line":215},[161,1000,712],{"class":167},[161,1002,184],{"class":171},[161,1004,717],{"class":187},[161,1006,1007,1009,1011],{"class":163,"line":394},[161,1008,723],{"class":167},[161,1010,184],{"class":171},[161,1012,728],{"class":187},[13,1014,1015],{},"Project pipeline:",[152,1017,1019],{"className":154,"code":1018,"language":156,"meta":157,"style":157},"variables:\n  SECRET_DETECTION_DISABLED: true\n\nsemgrep-sast:\n  rules:\n    - when: never\n\n",[93,1020,1021,1027,1037,1041,1048,1055],{"__ignoreMap":157},[161,1022,1023,1025],{"class":163,"line":164},[161,1024,281],{"class":167},[161,1026,172],{"class":171},[161,1028,1029,1032,1034],{"class":163,"line":175},[161,1030,1031],{"class":167},"  SECRET_DETECTION_DISABLED",[161,1033,184],{"class":171},[161,1035,1036],{"class":410},"true\n",[161,1038,1039],{"class":163,"line":191},[161,1040,299],{"emptyLinePlaceholder":298},[161,1042,1043,1046],{"class":163,"line":203},[161,1044,1045],{"class":167},"semgrep-sast",[161,1047,172],{"class":171},[161,1049,1050,1053],{"class":163,"line":215},[161,1051,1052],{"class":167},"  rules",[161,1054,172],{"class":171},[161,1056,1057,1060,1063,1065],{"class":163,"line":394},[161,1058,1059],{"class":171},"    - ",[161,1061,1062],{"class":167},"when",[161,1064,184],{"class":171},[161,1066,1067],{"class":187},"never\n",[13,1069,1070,1071,1074],{},"This project pipeline declares a variable ",[93,1072,1073],{},"SECRET_DETECTION_DISABLED"," (this could be done via project or croup-level CI/CD variables, too), which is evaluated in the included secret detection template. Further, the last three lines use the merging mechanism discussed previously, to not execute the job at all. Kind of redundant, we know.",[13,1076,1077],{},"Both overrides could be prevented using components, but you get the idea. Components, too, are receptive to such attacks via their inputs' default values, which often use variables, too! Let's take a look at how this could be taken advantage of.",[13,1079,955],{},[152,1081,1083],{"className":154,"code":1082,"language":156,"meta":157,"style":157},"include:\n  - component: $CI_SERVER_FQDN/components/sast/sast@2.0.2\n  - component: $CI_SERVER_FQDN/components/secret-detection/secret-detection@1.1.2\n  - project: '$CI_PROJECT_PATH'\n    file: '$CI_CONFIG_PATH'\n    ref: '$CI_COMMIT_SHA'\n\n",[93,1084,1085,1091,1101,1111,1121,1129],{"__ignoreMap":157},[161,1086,1087,1089],{"class":163,"line":164},[161,1088,168],{"class":167},[161,1090,172],{"class":171},[161,1092,1093,1095,1097,1099],{"class":163,"line":175},[161,1094,178],{"class":171},[161,1096,482],{"class":167},[161,1098,184],{"class":171},[161,1100,487],{"class":187},[161,1102,1103,1105,1107,1109],{"class":163,"line":191},[161,1104,178],{"class":171},[161,1106,482],{"class":167},[161,1108,184],{"class":171},[161,1110,509],{"class":187},[161,1112,1113,1115,1117,1119],{"class":163,"line":203},[161,1114,178],{"class":171},[161,1116,702],{"class":167},[161,1118,184],{"class":171},[161,1120,707],{"class":187},[161,1122,1123,1125,1127],{"class":163,"line":215},[161,1124,712],{"class":167},[161,1126,184],{"class":171},[161,1128,717],{"class":187},[161,1130,1131,1133,1135],{"class":163,"line":394},[161,1132,723],{"class":167},[161,1134,184],{"class":171},[161,1136,728],{"class":187},[13,1138,1015],{},[152,1140,1142],{"className":154,"code":1141,"language":156,"meta":157,"style":157},"variables:\n  CI_TEMPLATE_REGISTRY_HOST: \"docker.io\"\n\n",[93,1143,1144,1150],{"__ignoreMap":157},[161,1145,1146,1148],{"class":163,"line":164},[161,1147,281],{"class":167},[161,1149,172],{"class":171},[161,1151,1152,1155,1157],{"class":163,"line":175},[161,1153,1154],{"class":167},"  CI_TEMPLATE_REGISTRY_HOST",[161,1156,184],{"class":171},[161,1158,1159],{"class":187},"\"docker.io\"\n",[13,1161,1162,1163,1168],{},"To understand what is happening here, look at the ",[20,1164,1167],{"href":1165,"rel":1166},"https://gitlab.com/components/sast/-/blob/main/templates/sast.yml?ref_type=heads#L6",[],"SAST scanner component's Line 6",":",[152,1170,1172],{"className":154,"code":1171,"language":156,"meta":157,"style":157},"spec:\n  inputs:\n    stage:\n      default: test\n    image_prefix:\n      default: \"$CI_TEMPLATE_REGISTRY_HOST/security-products\"\n\n",[93,1173,1174,1181,1188,1195,1204,1211],{"__ignoreMap":157},[161,1175,1176,1179],{"class":163,"line":164},[161,1177,1178],{"class":167},"spec",[161,1180,172],{"class":171},[161,1182,1183,1186],{"class":163,"line":175},[161,1184,1185],{"class":167},"  inputs",[161,1187,172],{"class":171},[161,1189,1190,1193],{"class":163,"line":191},[161,1191,1192],{"class":167},"    stage",[161,1194,172],{"class":171},[161,1196,1197,1200,1202],{"class":163,"line":203},[161,1198,1199],{"class":167},"      default",[161,1201,184],{"class":171},[161,1203,877],{"class":187},[161,1205,1206,1209],{"class":163,"line":215},[161,1207,1208],{"class":167},"    image_prefix",[161,1210,172],{"class":171},[161,1212,1213,1215,1217],{"class":163,"line":394},[161,1214,1199],{"class":167},[161,1216,184],{"class":171},[161,1218,1219],{"class":187},"\"$CI_TEMPLATE_REGISTRY_HOST/security-products\"\n",[13,1221,1222,1223,1226,1227,1230,1231,1233,1234,1237],{},"The ",[93,1224,1225],{},"image_prefix"," input uses the ",[93,1228,1229],{},"CI_TEMPLATE_REGISTRY_HOST"," to build the default value. By setting this variable to a false value in the same way we set ",[93,1232,1073],{}," to ",[93,1235,1236],{},"true"," before, Sacha may cause the job to load a wrong image and break SAST testing.",[13,1239,1240],{},"To prevent this override ability by the developer role, avoid templates in favor of components. This approach covers many developer-induced loopholes. To be certain of compliance, hardcode values for component inputs.",[944,1242,735],{"id":1243},"overriding-pipelines-1",[13,1245,1246,1247,1249,1250,1252],{},"This type is an entirely different beast. Developers get no chance of injecting actual pipeline code into the compliance pipeline. However, compliance pipelines do run with the project's CI/CD variables. Hence, any variable specified on the group- or project-level might modify the compliance pipeline's behavior. With ",[93,1248,1073],{}," set to ",[93,1251,1236],{}," in the project CI/CD variables, the following compliance pipeline can be modified again:",[152,1254,1256],{"className":154,"code":1255,"language":156,"meta":157,"style":157},"stages: [\"build\", \"test\", \"deploy\"]\n\ninclude:\n  - template: Jobs/SAST.gitlab-ci.yml\n  - template: Jobs/Secret-Detection.gitlab-ci.yml\n\nbuild-job: ...\ntest-job: ...\ndeploy-job: ...\n",[93,1257,1258,1276,1280,1286,1296,1306,1310,1319,1327],{"__ignoreMap":157},[161,1259,1260,1262,1264,1266,1268,1270,1272,1274],{"class":163,"line":164},[161,1261,751],{"class":167},[161,1263,754],{"class":171},[161,1265,757],{"class":187},[161,1267,80],{"class":171},[161,1269,762],{"class":187},[161,1271,80],{"class":171},[161,1273,767],{"class":187},[161,1275,770],{"class":171},[161,1277,1278],{"class":163,"line":175},[161,1279,299],{"emptyLinePlaceholder":298},[161,1281,1282,1284],{"class":163,"line":191},[161,1283,168],{"class":167},[161,1285,172],{"class":171},[161,1287,1288,1290,1292,1294],{"class":163,"line":203},[161,1289,178],{"class":171},[161,1291,181],{"class":167},[161,1293,184],{"class":171},[161,1295,200],{"class":187},[161,1297,1298,1300,1302,1304],{"class":163,"line":215},[161,1299,178],{"class":171},[161,1301,181],{"class":167},[161,1303,184],{"class":171},[161,1305,188],{"class":187},[161,1307,1308],{"class":163,"line":394},[161,1309,299],{"emptyLinePlaceholder":298},[161,1311,1312,1314,1316],{"class":163,"line":402},[161,1313,830],{"class":167},[161,1315,184],{"class":171},[161,1317,1318],{"class":410},"...\n",[161,1320,1321,1323,1325],{"class":163,"line":720},[161,1322,865],{"class":167},[161,1324,184],{"class":171},[161,1326,1318],{"class":410},[161,1328,1329,1331,1333],{"class":163,"line":827},[161,1330,898],{"class":167},[161,1332,184],{"class":171},[161,1334,1318],{"class":410},[13,1336,1337],{},"Components can solve this particular problem, but, as before, component inputs may use CI/CD variables developers can set. Compliance pipeline authors need to identify and take care of these situations.",[64,1339,56],{"id":1340},"policies",[13,1342,1343,1344,258],{},"Compliance pipelines' shortcomings have led to the next step for managing compliance: ",[20,1345,1340],{"href":1346,"rel":1347},"https://docs.gitlab.com/user/application_security/policies/",[],[13,1349,1350,1351,1354],{},"GitLab introduced ",[20,1352,1340],{"href":1346,"rel":1353},[]," as the way forward. Authors store a set of policies in a separate project as YAML files and apply them to projects on the group or project level. This gives Amy and Priyanka the flexibility to target individual projects with specific requirements but also to ensure compliance across the entire organization if needed. Access to the policy project can be controlled within the policy project and audited within GitLab.",[13,1356,1357,1358,258],{},"Policies come in different types for different purposes. The types we are interested in right now are scan execution policies (SEP) and ",[20,1359,1361],{"href":1360},"/blog/tutorial-advanced-use-case-for-gitlab-pipeline-execution-policies/","pipeline execution policies (PEP)",[139,1363,1365],{"id":1364},"scan-execution-policies","Scan execution policies",[13,1367,1368,1369,1372],{},"As the name suggests, SEPs require a particular scan – or set of scans – to be executed as part of the project pipeline and inject the respective scan jobs into the pipelines of associated projects. They include the respective ",[20,1370,181],{"href":148,"rel":1371},[]," in the pipeline according to variables and rules set by Amy and Priyanka.",[13,1374,1375],{},"GitLab supports policy authors with a comprehensive user interface in addition to a YAML-based Git workflow. The following screenshot and code snippet illustrate a very basic example of a SEP:",[13,1377,1378],{},[556,1379],{"alt":1380,"src":1381},"Scan execution policy example","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097984/Blog/Content%20Images/Blog/Content%20Images/image2_aHR0cHM6_1750097983864.png",[152,1383,1385],{"className":154,"code":1384,"language":156,"meta":157,"style":157},"name: Secret Scanner\ndescription: ''\nenabled: true\nactions:\n- scan: secret_detection\nrules:\n- type: pipeline\n  branches:\n  - \"*\"\n\n",[93,1386,1387,1397,1407,1416,1423,1436,1443,1455,1462],{"__ignoreMap":157},[161,1388,1389,1392,1394],{"class":163,"line":164},[161,1390,1391],{"class":167},"name",[161,1393,184],{"class":171},[161,1395,1396],{"class":187},"Secret Scanner\n",[161,1398,1399,1402,1404],{"class":163,"line":175},[161,1400,1401],{"class":167},"description",[161,1403,184],{"class":171},[161,1405,1406],{"class":187},"''\n",[161,1408,1409,1412,1414],{"class":163,"line":191},[161,1410,1411],{"class":167},"enabled",[161,1413,184],{"class":171},[161,1415,1036],{"class":410},[161,1417,1418,1421],{"class":163,"line":203},[161,1419,1420],{"class":167},"actions",[161,1422,172],{"class":171},[161,1424,1425,1428,1431,1433],{"class":163,"line":215},[161,1426,1427],{"class":171},"- ",[161,1429,1430],{"class":167},"scan",[161,1432,184],{"class":171},[161,1434,1435],{"class":187},"secret_detection\n",[161,1437,1438,1441],{"class":163,"line":394},[161,1439,1440],{"class":167},"rules",[161,1442,172],{"class":171},[161,1444,1445,1447,1450,1452],{"class":163,"line":402},[161,1446,1427],{"class":171},[161,1448,1449],{"class":167},"type",[161,1451,184],{"class":171},[161,1453,1454],{"class":187},"pipeline\n",[161,1456,1457,1460],{"class":163,"line":720},[161,1458,1459],{"class":167},"  branches",[161,1461,172],{"class":171},[161,1463,1464,1466],{"class":163,"line":827},[161,1465,178],{"class":171},[161,1467,1468],{"class":187},"\"*\"\n",[13,1470,1471,1472,258],{},"For more details on SEP settings in the UI and YAML, please refer to the ",[20,1473,1476],{"href":1474,"rel":1475},"https://docs.gitlab.com/user/application_security/policies/scan_execution_policies/",[],"policy documentation",[565,1478,1480],{"id":1479},"advantages-and-disadvantages-of-scan-execution-policies","Advantages and disadvantages of scan execution policies",[944,1482,568],{"id":1483},"ease-of-use-2",[13,1485,1486],{},"SEPs provide a lightweight, easy-to-use mechanism that enforces security on existing and new CI/CD pipelines across the organization or on a granular level. The UI support makes them a viable tool for all relevant personas.",[944,1488,575],{"id":1489},"customization-2",[13,1491,1492],{},"SEPs are restricted to predefined scanner jobs, and there is no option to extend this list with custom jobs at this point. This limitation can be restrictive for teams with unique scanning requirements that fall outside the standard options.",[944,1494,585],{"id":1495},"enforcement-2",[13,1497,1498],{},"Once an SEP is applied to a project (directly or indirectly), Sacha has no way to get rid of that scan job. Though, there may be ways to – intentionally or not – manipulate the scan job's behavior.",[13,1500,1501,1502,1507,1508,1513,1514,1517,1518,258],{},"Jobs injected via SEPs generally are receptive to CI/CD variables and adhere to the general rules of ",[20,1503,1506],{"href":1504,"rel":1505},"https://docs.gitlab.com/ci/variables/#cicd-variable-precedence",[],"variable precedence",". For this injection, Policies incorporate logic that denies changing some predefined variables as described ",[20,1509,1512],{"href":1510,"rel":1511},"https://docs.gitlab.com/user/application_security/policies/scan_execution_policies/#cicd-variables",[],"here"," and generally deny the configuration of variables that follow certain patterns such as ",[93,1515,1516],{},"_DISABLED"," or  ",[93,1519,1520],{},"_EXCLUDED_PATHS",[13,1522,1523,1524,1527,1528,1532],{},"Despite these security measures, inconsiderate use of policies may still open opportunities for tampering: In my test, I was able to set a project-level CI/CD variable ",[93,1525,1526],{},"SECURE_ANALYZERS_PREFIX"," to a bad value (a non-existing location) and as you can see ",[20,1529,1512],{"href":1530,"rel":1531},"https://gitlab.com/gitlab-org/gitlab/-/blob/a2d4b8df0095c1363a105a1fa212daf227eca063/lib/gitlab/ci/templates/Jobs/Secret-Detection.gitlab-ci.yml",[],", the secret detection template uses that to build the location of the scanner image.",[13,1534,1535,1536,1544],{},"While the scan job does get included in the pipeline run, it crashes very early and, therefore, provides no scan results. Due to the ",[20,1537,1540,1543],{"href":1538,"rel":1539},"https://gitlab.com/gitlab-org/gitlab/-/blob/a2d4b8df0095c1363a105a1fa212daf227eca063/lib/gitlab/ci/templates/Jobs/Secret-Detection.gitlab-ci.yml#L18",[],[93,1541,1542],{},"allow_failure: true"," configuration",", the pipeline will continue to run and eventually execute a deploy job.",[13,1546,1547],{},"Because SEP variables take the highest variable precedence, there is an easy fix to reduce the attack surface of the policy: Simply hardcode the correct value in your policy YAML or via the UI:",[152,1549,1551],{"className":154,"code":1550,"language":156,"meta":157,"style":157},"- name: Secret Scanner\n  actions:\n  - scan: secret_detection\n    variables:\n      SECURE_ANALYZERS_PREFIX: registry.gitlab.com/security-products\n\n",[93,1552,1553,1563,1570,1580,1587],{"__ignoreMap":157},[161,1554,1555,1557,1559,1561],{"class":163,"line":164},[161,1556,1427],{"class":171},[161,1558,1391],{"class":167},[161,1560,184],{"class":171},[161,1562,1396],{"class":187},[161,1564,1565,1568],{"class":163,"line":175},[161,1566,1567],{"class":167},"  actions",[161,1569,172],{"class":171},[161,1571,1572,1574,1576,1578],{"class":163,"line":191},[161,1573,178],{"class":171},[161,1575,1430],{"class":167},[161,1577,184],{"class":171},[161,1579,1435],{"class":187},[161,1581,1582,1585],{"class":163,"line":203},[161,1583,1584],{"class":167},"    variables",[161,1586,172],{"class":171},[161,1588,1589,1592,1594],{"class":163,"line":215},[161,1590,1591],{"class":167},"      SECURE_ANALYZERS_PREFIX",[161,1593,184],{"class":171},[161,1595,1596],{"class":187},"registry.gitlab.com/security-products\n",[139,1598,1600],{"id":1599},"pipeline-execution-policies","Pipeline execution policies",[13,1602,1603],{},"SEPs enable the injection of a set of security-related jobs into any project pipeline. In contrast, PEPs apply entire pipeline configurations to projects, offering a lot more flexibility when it comes to customizing security constraints.",[13,1605,1606,1607,619,1610,1613],{},"There are two methods for implementing these policies, known as \"actions\": ",[93,1608,1609],{},"inject",[93,1611,1612],{},"override",". These actions function similarly to the patterns we have seen in the compliance frameworks section and provide flexible ways to enhance and enforce security standards within the development workflow.",[565,1615,1617],{"id":1616},"injecting-pipelines","Injecting pipelines",[13,1619,1620,1621,619,1624,1627],{},"Injecting pipelines involves adding the jobs and other elements defined in the policy pipeline into the project pipeline. Currently, jobs should only be injected into reserved stages, namely ",[93,1622,1623],{},".pipeline-policy-pre",[93,1625,1626],{},".pipeline-policy-post"," to avoid unpredictable results.",[13,1629,1630],{},"GitLab handles name clashes between jobs or variables in policy and project pipelines effectively by building each pipeline in isolation before combining them. This ensures that the integration process is seamless and does not disrupt existing workflows or configurations.",[13,1632,1633],{},[556,1634],{"alt":1635,"src":1636},"security scanning - image 4","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097984/Blog/Content%20Images/Blog/Content%20Images/image4_aHR0cHM6_1750097983865.png",[13,1638,1639,1640,1643],{},"The above screenshot shows an example of an injected policy pipeline. Project pipeline jobs are prefixed with ",[93,1641,1642],{},"prj-"," for easier identification.",[565,1645,735],{"id":1646},"overriding-pipelines-2",[13,1648,1649,1650,1652],{},"In the override approach, the project pipeline is completely replaced by the policy pipeline. This method is similar to compliance pipelines that do not include the project's ",[93,1651,95],{}," file. Despite the override, the pipelines run using the project's CI/CD variables, maintaining consistency with project-specific configurations. The compliance pipeline we used earlier makes a perfectly fine policy pipeline, too:",[152,1654,1655],{"className":154,"code":744,"language":156,"meta":157,"style":157},[93,1656,1657,1675,1679,1685,1695,1705,1715,1725,1729,1735,1743,1751,1755,1761,1769,1777,1781,1787,1795],{"__ignoreMap":157},[161,1658,1659,1661,1663,1665,1667,1669,1671,1673],{"class":163,"line":164},[161,1660,751],{"class":167},[161,1662,754],{"class":171},[161,1664,757],{"class":187},[161,1666,80],{"class":171},[161,1668,762],{"class":187},[161,1670,80],{"class":171},[161,1672,767],{"class":187},[161,1674,770],{"class":171},[161,1676,1677],{"class":163,"line":175},[161,1678,299],{"emptyLinePlaceholder":298},[161,1680,1681,1683],{"class":163,"line":191},[161,1682,168],{"class":167},[161,1684,172],{"class":171},[161,1686,1687,1689,1691,1693],{"class":163,"line":203},[161,1688,178],{"class":171},[161,1690,482],{"class":167},[161,1692,184],{"class":171},[161,1694,487],{"class":187},[161,1696,1697,1699,1701,1703],{"class":163,"line":215},[161,1698,178],{"class":171},[161,1700,482],{"class":167},[161,1702,184],{"class":171},[161,1704,498],{"class":187},[161,1706,1707,1709,1711,1713],{"class":163,"line":394},[161,1708,178],{"class":171},[161,1710,482],{"class":167},[161,1712,184],{"class":171},[161,1714,509],{"class":187},[161,1716,1717,1719,1721,1723],{"class":163,"line":402},[161,1718,178],{"class":171},[161,1720,482],{"class":167},[161,1722,184],{"class":171},[161,1724,520],{"class":187},[161,1726,1727],{"class":163,"line":720},[161,1728,299],{"emptyLinePlaceholder":298},[161,1730,1731,1733],{"class":163,"line":827},[161,1732,830],{"class":167},[161,1734,172],{"class":171},[161,1736,1737,1739,1741],{"class":163,"line":835},[161,1738,838],{"class":167},[161,1740,184],{"class":171},[161,1742,843],{"class":187},[161,1744,1745,1747,1749],{"class":163,"line":846},[161,1746,849],{"class":167},[161,1748,184],{"class":171},[161,1750,854],{"class":187},[161,1752,1753],{"class":163,"line":857},[161,1754,299],{"emptyLinePlaceholder":298},[161,1756,1757,1759],{"class":163,"line":862},[161,1758,865],{"class":167},[161,1760,172],{"class":171},[161,1762,1763,1765,1767],{"class":163,"line":870},[161,1764,838],{"class":167},[161,1766,184],{"class":171},[161,1768,877],{"class":187},[161,1770,1771,1773,1775],{"class":163,"line":880},[161,1772,849],{"class":167},[161,1774,184],{"class":171},[161,1776,887],{"class":187},[161,1778,1779],{"class":163,"line":890},[161,1780,299],{"emptyLinePlaceholder":298},[161,1782,1783,1785],{"class":163,"line":895},[161,1784,898],{"class":167},[161,1786,172],{"class":171},[161,1788,1789,1791,1793],{"class":163,"line":903},[161,1790,838],{"class":167},[161,1792,184],{"class":171},[161,1794,910],{"class":187},[161,1796,1797,1799,1801],{"class":163,"line":913},[161,1798,849],{"class":167},[161,1800,184],{"class":171},[161,1802,920],{"class":187},[13,1804,1805],{},"The image below shows a slightly more complete pipeline than the mock pipeline above:",[13,1807,1808],{},[556,1809],{"alt":1810,"src":1811},"More complete pipeline","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097984/Blog/Content%20Images/Blog/Content%20Images/image3_aHR0cHM6_1750097983866.png",[13,1813,1814,1816],{},[77,1815,633],{}," This doesn't currently work with SEPs.",[13,1818,1819],{},"However, the existence of a Dockerfile may not always be a valid indicator, as developers might be building without Dockerfiles using Cloud Native Buildpacks, Heroku Buildpacks, Kaniko, or other tools. Managed pipelines do not encounter this challenge, as they are more controlled and centralized.",[139,1821,1823],{"id":1822},"projects-with-multiple-container-images","Projects with multiple container images",[13,1825,1826,1827,1830,1831,1836,1837,1839],{},"For projects that produce multiple container images, several container scanning jobs would be necessary for proper coverage. This raises similar questions as before: \"How do we know there are multiple?\" and \"Is the source of that information trustworthy?\". If we wanted to rely on the existence of ",[93,1828,1829],{},"Dockerfile","s a ",[20,1832,1835],{"href":1833,"rel":1834},"https://docs.gitlab.com/ci/pipelines/downstream_pipelines/#dynamic-child-pipelines",[],"dynamic approach"," would be necessary that includes a container scanning job for each ",[93,1838,1829],{}," detected.",[64,1841,62],{"id":1842},"get-started-with-security-scanning",[13,1844,1845],{},"In this article, you've learned about a variety of approaches to adding security scanning to CI/CD pipelines with a close look at ease of use, customizability, and the ability to strictly enforce scanning. You've seen that a pipeline author who is held responsible for project compliance needs to keep a few things in mind during the process to avoid surprises down the line. We recommend building a small testing space on your GitLab instance and then run a few tests to reproduce the main points of this article. Put yourself in the shoes of a malicious Sacha (Sachas aren't generally malicious people, but it's a good exercise) and think about how you could fool that annoying Amy and her security scans.",[13,1847,1848],{},"GitLab provides strong support for all sorts of requirements and all approaches are – at least in our eyes – easy to implement due the platform's baked-in functionality. You should find ways to bulletproof your scan jobs and, if not, you should open a ticket with our support.",[13,1850,1851],{},"Happy pipelining!",[1853,1854,1855,1859],"blockquote",{},[565,1856,1858],{"id":1857},"get-started-with-security-scanning-today","Get started with security scanning today!",[13,1860,1861,1866],{},[20,1862,1865],{"href":1863,"rel":1864},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/blog&glm_content=default-saas-trial",[],"Sign up for a free trial of GitLab Ultimate"," to implement security scanning in your software development lifecycle.",[64,1868,1870],{"id":1869},"read-more","Read more",[30,1872,1873,1879,1886],{},[33,1874,1875],{},[20,1876,1878],{"href":604,"rel":1877},[],"Meet regulatory standards with GitLab security and compliance",[33,1880,1881],{},[20,1882,1885],{"href":1883,"rel":1884},"https://about.gitlab.com/blog/how-to-integrate-custom-security-scanners-into-gitlab/",[],"How to integrate custom security scanners into GitLab",[33,1887,1888],{},[20,1889,1892],{"href":1890,"rel":1891},"https://about.gitlab.com/blog/integrate-external-security-scanners-into-your-devsecops-workflow/",[],"Integrate external security scanners into your DevSecOps workflow",[1894,1895,1896],"style",{},"html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}",{"title":157,"searchDepth":175,"depth":175,"links":1898},[1899,1900,1905,1910,1915,1916],{"id":66,"depth":175,"text":38},{"id":122,"depth":175,"text":44,"children":1901},[1902,1903,1904],{"id":141,"depth":191,"text":142},{"id":423,"depth":191,"text":424},{"id":562,"depth":191,"text":563},{"id":598,"depth":175,"text":50,"children":1906},[1907,1908,1909],{"id":637,"depth":191,"text":638},{"id":734,"depth":191,"text":735},{"id":923,"depth":191,"text":924},{"id":1340,"depth":175,"text":56,"children":1911},[1912,1913,1914],{"id":1364,"depth":191,"text":1365},{"id":1599,"depth":191,"text":1600},{"id":1822,"depth":191,"text":1823},{"id":1842,"depth":175,"text":62},{"id":1869,"depth":175,"text":1870},"security","2024-08-26","GitLab offers multiple scanning methods for CI/CD pipelines, including compliance frameworks and scan and pipeline execution policies. Learn the basics, configurations, and advantages/disadvantages.","md",null,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1750097969/Blog/Hero%20Images/Blog/Hero%20Images/AdobeStock_282096522_securitycompliance.jpeg_1750097968823.jpg",{},"/en-us/blog/how-to-choose-the-right-security-scanning-approach",{"title":5,"description":1919,"ogTitle":5,"ogDescription":1919,"noIndex":1926,"ogImage":1922,"ogUrl":1927,"ogSiteName":1928,"ogType":1929,"canonicalUrls":1927},false,"https://about.gitlab.com/blog/how-to-choose-the-right-security-scanning-approach","https://about.gitlab.com","article","how-to-choose-the-right-security-scanning-approach","en-us/blog/how-to-choose-the-right-security-scanning-approach",[1917,1933,24],"tutorial","BlogPost","9TMb5vN2uSF4x7aOr2HeXosDisTXsaDejkri8KeQAmw",{"logo":1937,"freeTrial":1942,"sales":1947,"login":1952,"items":1957,"search":2283,"minimal":2314,"duo":2333,"switchNav":2342,"pricingDeployment":2353},{"config":1938},{"href":1939,"dataGaName":1940,"dataGaLocation":1941},"/","gitlab logo","header",{"text":1943,"config":1944},"Get free trial",{"href":1945,"dataGaName":1946,"dataGaLocation":1941},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":1948,"config":1949},"Request a demo",{"href":1950,"dataGaName":1951,"dataGaLocation":1941},"/sales/?contact-topic=request-demo","sales",{"text":1953,"config":1954},"Sign in",{"href":1955,"dataGaName":1956,"dataGaLocation":1941},"https://gitlab.com/users/sign_in/","sign in",[1958,1987,2086,2091,2205,2261],{"text":1959,"config":1960,"menu":1962},"Platform",{"dataNavLevelOne":1961},"platform",{"type":1963,"columns":1964},"cards",[1965,1971,1979],{"title":1959,"description":1966,"link":1967},"The intelligent orchestration platform for DevSecOps",{"text":1968,"config":1969},"Explore our Platform",{"href":1970,"dataGaName":1961,"dataGaLocation":1941},"/platform/",{"title":1972,"description":1973,"link":1974},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":1975,"config":1976},"Meet GitLab Duo",{"href":1977,"dataGaName":1978,"dataGaLocation":1941},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":1980,"description":1981,"link":1982},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":1983,"config":1984},"Learn more",{"href":1985,"dataGaName":1986,"dataGaLocation":1941},"/why-gitlab/","why gitlab",{"text":1988,"left":298,"config":1989,"menu":1991},"Product",{"dataNavLevelOne":1990},"solutions",{"type":1992,"link":1993,"columns":1997,"feature":2065},"lists",{"text":1994,"config":1995},"View all Solutions",{"href":1996,"dataGaName":1990,"dataGaLocation":1941},"/solutions/",[1998,2021,2044],{"title":1999,"description":2000,"link":2001,"items":2006},"Automation","CI/CD and automation to accelerate deployment",{"config":2002},{"icon":2003,"href":2004,"dataGaName":2005,"dataGaLocation":1941},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[2007,2010,2013,2017],{"text":24,"config":2008},{"href":2009,"dataGaLocation":1941,"dataGaName":24},"/solutions/continuous-integration/",{"text":1972,"config":2011},{"href":1977,"dataGaLocation":1941,"dataGaName":2012},"gitlab duo agent platform - product menu",{"text":2014,"config":2015},"Source Code Management",{"href":2016,"dataGaLocation":1941,"dataGaName":2014},"/solutions/source-code-management/",{"text":2018,"config":2019},"Automated Software Delivery",{"href":2004,"dataGaLocation":1941,"dataGaName":2020},"Automated software delivery",{"title":2022,"description":2023,"link":2024,"items":2029},"Security","Deliver code faster without compromising security",{"config":2025},{"href":2026,"dataGaName":2027,"dataGaLocation":1941,"icon":2028},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[2030,2034,2039],{"text":2031,"config":2032},"Application Security Testing",{"href":2026,"dataGaName":2033,"dataGaLocation":1941},"Application security testing",{"text":2035,"config":2036},"Software Supply Chain Security",{"href":2037,"dataGaLocation":1941,"dataGaName":2038},"/solutions/supply-chain/","Software supply chain security",{"text":2040,"config":2041},"Software Compliance",{"href":2042,"dataGaName":2043,"dataGaLocation":1941},"/solutions/software-compliance/","software compliance",{"title":2045,"link":2046,"items":2051},"Measurement",{"config":2047},{"icon":2048,"href":2049,"dataGaName":2050,"dataGaLocation":1941},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[2052,2056,2060],{"text":2053,"config":2054},"Visibility & Measurement",{"href":2049,"dataGaLocation":1941,"dataGaName":2055},"Visibility and Measurement",{"text":2057,"config":2058},"Value Stream Management",{"href":2059,"dataGaLocation":1941,"dataGaName":2057},"/solutions/value-stream-management/",{"text":2061,"config":2062},"Analytics & Insights",{"href":2063,"dataGaLocation":1941,"dataGaName":2064},"/solutions/analytics-and-insights/","Analytics and insights",{"title":2066,"type":1992,"items":2067},"GitLab for",[2068,2074,2080],{"text":2069,"config":2070},"Enterprise",{"icon":2071,"href":2072,"dataGaLocation":1941,"dataGaName":2073},"Building","/enterprise/","enterprise",{"text":2075,"config":2076},"Small Business",{"icon":2077,"href":2078,"dataGaLocation":1941,"dataGaName":2079},"Work","/small-business/","small business",{"text":2081,"config":2082},"Public Sector",{"icon":2083,"href":2084,"dataGaLocation":1941,"dataGaName":2085},"Organization","/solutions/public-sector/","public sector",{"text":2087,"config":2088},"Pricing",{"href":2089,"dataGaName":2090,"dataGaLocation":1941,"dataNavLevelOne":2090},"/pricing/","pricing",{"text":2092,"config":2093,"menu":2095},"Resources",{"dataNavLevelOne":2094},"resources",{"type":1992,"link":2096,"columns":2100,"feature":2194},{"text":2097,"config":2098},"View all resources",{"href":2099,"dataGaName":2094,"dataGaLocation":1941},"/resources/",[2101,2134,2161],{"title":2102,"items":2103},"Getting started",[2104,2109,2114,2119,2124,2129],{"text":2105,"config":2106},"Install",{"href":2107,"dataGaName":2108,"dataGaLocation":1941},"/install/","install",{"text":2110,"config":2111},"Quick start guides",{"href":2112,"dataGaName":2113,"dataGaLocation":1941},"/get-started/","quick setup checklists",{"text":2115,"config":2116},"Learn",{"href":2117,"dataGaLocation":1941,"dataGaName":2118},"https://university.gitlab.com/","learn",{"text":2120,"config":2121},"Product documentation",{"href":2122,"dataGaName":2123,"dataGaLocation":1941},"https://docs.gitlab.com/","product documentation",{"text":2125,"config":2126},"Best practice videos",{"href":2127,"dataGaName":2128,"dataGaLocation":1941},"/getting-started-videos/","best practice videos",{"text":2130,"config":2131},"Integrations",{"href":2132,"dataGaName":2133,"dataGaLocation":1941},"/integrations/","integrations",{"title":2135,"items":2136},"Discover",[2137,2142,2147,2152,2156],{"text":2138,"config":2139},"Customer success stories",{"href":2140,"dataGaName":2141,"dataGaLocation":1941},"/customers/","customer success stories",{"text":2143,"config":2144},"Blog",{"href":2145,"dataGaName":2146,"dataGaLocation":1941},"/blog/","blog",{"text":2148,"config":2149},"Demo Hub",{"href":2150,"dataGaName":2151,"dataGaLocation":1941},"/demo-hub/","demo hub",{"text":2153,"config":2154},"The Source",{"href":2155,"dataGaName":2146,"dataGaLocation":1941},"/the-source/",{"text":2157,"config":2158},"Remote",{"href":2159,"dataGaName":2160,"dataGaLocation":1941},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":2162,"items":2163},"Connect",[2164,2169,2174,2179,2184,2189],{"text":2165,"config":2166},"GitLab Services",{"href":2167,"dataGaName":2168,"dataGaLocation":1941},"/services/","services",{"text":2170,"config":2171},"Contribute",{"href":2172,"dataGaName":2173,"dataGaLocation":1941},"https://contributors.gitlab.com","contribute",{"text":2175,"config":2176},"Community",{"href":2177,"dataGaName":2178,"dataGaLocation":1941},"/community/","community",{"text":2180,"config":2181},"Forum",{"href":2182,"dataGaName":2183,"dataGaLocation":1941},"https://forum.gitlab.com/","forum",{"text":2185,"config":2186},"Events",{"href":2187,"dataGaName":2188,"dataGaLocation":1941},"/events/","events",{"text":2190,"config":2191},"Partners",{"href":2192,"dataGaName":2193,"dataGaLocation":1941},"/partners/","partners",{"config":2195,"title":2198,"text":2199,"link":2200},{"background":2196,"textColor":2197},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":2201,"config":2202},"Read the latest",{"href":2203,"dataGaName":2204,"dataGaLocation":1941},"/whats-new/","whats new",{"text":2206,"config":2207,"menu":2209},"Company",{"dataNavLevelOne":2208},"company",{"type":1992,"columns":2210},[2211],{"items":2212},[2213,2218,2224,2226,2231,2236,2241,2246,2251,2256],{"text":2214,"config":2215},"About",{"href":2216,"dataGaName":2217,"dataGaLocation":1941},"/company/","about",{"text":2219,"config":2220,"footerGa":2223},"Jobs",{"href":2221,"dataGaName":2222,"dataGaLocation":1941},"/jobs/","jobs",{"dataGaName":2222},{"text":2185,"config":2225},{"href":2187,"dataGaName":2188,"dataGaLocation":1941},{"text":2227,"config":2228},"Leadership",{"href":2229,"dataGaName":2230,"dataGaLocation":1941},"/company/team/e-group/","leadership",{"text":2232,"config":2233},"Handbook",{"href":2234,"dataGaName":2235,"dataGaLocation":1941},"https://handbook.gitlab.com/","handbook",{"text":2237,"config":2238},"Investor relations",{"href":2239,"dataGaName":2240,"dataGaLocation":1941},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":2242,"config":2243},"Trust Center",{"href":2244,"dataGaName":2245,"dataGaLocation":1941},"/security/","trust center",{"text":2247,"config":2248},"AI Transparency Center",{"href":2249,"dataGaName":2250,"dataGaLocation":1941},"/ai-transparency-center/","ai transparency center",{"text":2252,"config":2253},"Newsletter",{"href":2254,"dataGaName":2255,"dataGaLocation":1941},"/company/contact/#contact-forms","newsletter",{"text":2257,"config":2258},"Press",{"href":2259,"dataGaName":2260,"dataGaLocation":1941},"/press/","press",{"text":2262,"config":2263,"menu":2264},"Contact us",{"dataNavLevelOne":2208},{"type":1992,"columns":2265},[2266],{"items":2267},[2268,2273,2278],{"text":2269,"config":2270},"Talk to sales",{"href":2271,"dataGaName":2272,"dataGaLocation":1941},"/sales/","talk to sales",{"text":2274,"config":2275},"Support portal",{"href":2276,"dataGaName":2277,"dataGaLocation":1941},"https://support.gitlab.com/hc/en-us","support portal",{"text":2279,"config":2280},"Customer portal",{"href":2281,"dataGaName":2282,"dataGaLocation":1941},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":2284,"login":2285,"suggestions":2292},"Close",{"text":2286,"link":2287},"To search repositories and projects, login to",{"text":2288,"config":2289},"gitlab.com",{"href":1955,"dataGaName":2290,"dataGaLocation":2291},"search login","search",{"text":2293,"default":2294},"Suggestions",[2295,2297,2301,2303,2307,2311],{"text":1972,"config":2296},{"href":1977,"dataGaName":1972,"dataGaLocation":2291},{"text":2298,"config":2299},"Code Suggestions (AI)",{"href":2300,"dataGaName":2298,"dataGaLocation":2291},"/solutions/code-suggestions/",{"text":24,"config":2302},{"href":2009,"dataGaName":24,"dataGaLocation":2291},{"text":2304,"config":2305},"GitLab on AWS",{"href":2306,"dataGaName":2304,"dataGaLocation":2291},"/partners/technology-partners/aws/",{"text":2308,"config":2309},"GitLab on Google Cloud",{"href":2310,"dataGaName":2308,"dataGaLocation":2291},"/partners/technology-partners/google-cloud-platform/",{"text":2312,"config":2313},"Why GitLab?",{"href":1985,"dataGaName":2312,"dataGaLocation":2291},{"freeTrial":2315,"mobileIcon":2320,"desktopIcon":2325,"secondaryButton":2328},{"text":2316,"config":2317},"Start free trial",{"href":2318,"dataGaName":1946,"dataGaLocation":2319},"https://gitlab.com/-/trials/new/","nav",{"altText":2321,"config":2322},"Gitlab Icon",{"src":2323,"dataGaName":2324,"dataGaLocation":2319},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":2321,"config":2326},{"src":2327,"dataGaName":2324,"dataGaLocation":2319},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":2329,"config":2330},"Get Started",{"href":2331,"dataGaName":2332,"dataGaLocation":2319},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":2334,"mobileIcon":2338,"desktopIcon":2340},{"text":2335,"config":2336},"Learn more about GitLab Duo",{"href":1977,"dataGaName":2337,"dataGaLocation":2319},"gitlab duo",{"altText":2321,"config":2339},{"src":2323,"dataGaName":2324,"dataGaLocation":2319},{"altText":2321,"config":2341},{"src":2327,"dataGaName":2324,"dataGaLocation":2319},{"button":2343,"mobileIcon":2348,"desktopIcon":2350},{"text":2344,"config":2345},"/switch",{"href":2346,"dataGaName":2347,"dataGaLocation":2319},"#contact","switch",{"altText":2321,"config":2349},{"src":2323,"dataGaName":2324,"dataGaLocation":2319},{"altText":2321,"config":2351},{"src":2352,"dataGaName":2324,"dataGaLocation":2319},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":2354,"mobileIcon":2359,"desktopIcon":2361},{"text":2355,"config":2356},"Back to pricing",{"href":2089,"dataGaName":2357,"dataGaLocation":2319,"icon":2358},"back to pricing","GoBack",{"altText":2321,"config":2360},{"src":2323,"dataGaName":2324,"dataGaLocation":2319},{"altText":2321,"config":2362},{"src":2327,"dataGaName":2324,"dataGaLocation":2319},{"title":2364,"titleMobile":2365,"button":2366,"config":2371},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":1983,"config":2367},{"href":2368,"dataGaName":2369,"dataGaLocation":2370},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":2372,"disabled":1926},"release",{"data":2374},{"text":2375,"source":2376,"edit":2382,"contribute":2387,"config":2392,"items":2397,"minimal":2607},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":2377,"config":2378},"View page source",{"href":2379,"dataGaName":2380,"dataGaLocation":2381},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":2383,"config":2384},"Edit this page",{"href":2385,"dataGaName":2386,"dataGaLocation":2381},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":2388,"config":2389},"Please contribute",{"href":2390,"dataGaName":2391,"dataGaLocation":2381},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":2393,"facebook":2394,"youtube":2395,"linkedin":2396},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[2398,2445,2499,2543,2575],{"title":2087,"links":2399,"subMenu":2414},[2400,2404,2409],{"text":2401,"config":2402},"View plans",{"href":2089,"dataGaName":2403,"dataGaLocation":2381},"view plans",{"text":2405,"config":2406},"Why Premium?",{"href":2407,"dataGaName":2408,"dataGaLocation":2381},"/pricing/premium/","why premium",{"text":2410,"config":2411},"Why Ultimate?",{"href":2412,"dataGaName":2413,"dataGaLocation":2381},"/pricing/ultimate/","why ultimate",[2415],{"title":2416,"links":2417},"Contact Us",[2418,2421,2423,2425,2430,2435,2440],{"text":2419,"config":2420},"Contact sales",{"href":2271,"dataGaName":1951,"dataGaLocation":2381},{"text":2274,"config":2422},{"href":2276,"dataGaName":2277,"dataGaLocation":2381},{"text":2279,"config":2424},{"href":2281,"dataGaName":2282,"dataGaLocation":2381},{"text":2426,"config":2427},"Status",{"href":2428,"dataGaName":2429,"dataGaLocation":2381},"https://status.gitlab.com/","status",{"text":2431,"config":2432},"Terms of use",{"href":2433,"dataGaName":2434,"dataGaLocation":2381},"/terms/","terms of use",{"text":2436,"config":2437},"Privacy statement",{"href":2438,"dataGaName":2439,"dataGaLocation":2381},"/privacy/","privacy statement",{"text":2441,"config":2442},"Cookie preferences",{"dataGaName":2443,"dataGaLocation":2381,"id":2444,"isOneTrustButton":298},"cookie preferences","ot-sdk-btn",{"title":1988,"links":2446,"subMenu":2455},[2447,2451],{"text":2448,"config":2449},"DevSecOps platform",{"href":1970,"dataGaName":2450,"dataGaLocation":2381},"devsecops platform",{"text":2452,"config":2453},"AI-Assisted Development",{"href":1977,"dataGaName":2454,"dataGaLocation":2381},"ai-assisted development",[2456],{"title":2457,"links":2458},"Topics",[2459,2464,2469,2474,2479,2484,2489,2494],{"text":2460,"config":2461},"CICD",{"href":2462,"dataGaName":2463,"dataGaLocation":2381},"/topics/ci-cd/","cicd",{"text":2465,"config":2466},"GitOps",{"href":2467,"dataGaName":2468,"dataGaLocation":2381},"/topics/gitops/","gitops",{"text":2470,"config":2471},"DevOps",{"href":2472,"dataGaName":2473,"dataGaLocation":2381},"/topics/devops/","devops",{"text":2475,"config":2476},"Version Control",{"href":2477,"dataGaName":2478,"dataGaLocation":2381},"/topics/version-control/","version control",{"text":2480,"config":2481},"DevSecOps",{"href":2482,"dataGaName":2483,"dataGaLocation":2381},"/topics/devsecops/","devsecops",{"text":2485,"config":2486},"Cloud Native",{"href":2487,"dataGaName":2488,"dataGaLocation":2381},"/topics/cloud-native/","cloud native",{"text":2490,"config":2491},"AI for Coding",{"href":2492,"dataGaName":2493,"dataGaLocation":2381},"/topics/devops/ai-for-coding/","ai for coding",{"text":2495,"config":2496},"Agentic AI",{"href":2497,"dataGaName":2498,"dataGaLocation":2381},"/topics/agentic-ai/","agentic ai",{"title":2500,"links":2501},"Solutions",[2502,2504,2506,2511,2515,2518,2522,2525,2527,2530,2533,2538],{"text":2031,"config":2503},{"href":2026,"dataGaName":2031,"dataGaLocation":2381},{"text":2020,"config":2505},{"href":2004,"dataGaName":2005,"dataGaLocation":2381},{"text":2507,"config":2508},"Agile development",{"href":2509,"dataGaName":2510,"dataGaLocation":2381},"/solutions/agile-delivery/","agile delivery",{"text":2512,"config":2513},"SCM",{"href":2016,"dataGaName":2514,"dataGaLocation":2381},"source code management",{"text":2460,"config":2516},{"href":2009,"dataGaName":2517,"dataGaLocation":2381},"continuous integration & delivery",{"text":2519,"config":2520},"Value stream management",{"href":2059,"dataGaName":2521,"dataGaLocation":2381},"value stream management",{"text":2465,"config":2523},{"href":2524,"dataGaName":2468,"dataGaLocation":2381},"/solutions/gitops/",{"text":2069,"config":2526},{"href":2072,"dataGaName":2073,"dataGaLocation":2381},{"text":2528,"config":2529},"Small business",{"href":2078,"dataGaName":2079,"dataGaLocation":2381},{"text":2531,"config":2532},"Public sector",{"href":2084,"dataGaName":2085,"dataGaLocation":2381},{"text":2534,"config":2535},"Education",{"href":2536,"dataGaName":2537,"dataGaLocation":2381},"/solutions/education/","education",{"text":2539,"config":2540},"Financial services",{"href":2541,"dataGaName":2542,"dataGaLocation":2381},"/solutions/finance/","financial services",{"title":2092,"links":2544},[2545,2547,2549,2551,2554,2556,2559,2561,2563,2565,2567,2569,2571,2573],{"text":2105,"config":2546},{"href":2107,"dataGaName":2108,"dataGaLocation":2381},{"text":2110,"config":2548},{"href":2112,"dataGaName":2113,"dataGaLocation":2381},{"text":2115,"config":2550},{"href":2117,"dataGaName":2118,"dataGaLocation":2381},{"text":2120,"config":2552},{"href":2122,"dataGaName":2553,"dataGaLocation":2381},"docs",{"text":2143,"config":2555},{"href":2145,"dataGaName":2146,"dataGaLocation":2381},{"text":2557,"config":2558},"What's new",{"href":2203,"dataGaName":2204,"dataGaLocation":2381},{"text":2138,"config":2560},{"href":2140,"dataGaName":2141,"dataGaLocation":2381},{"text":2157,"config":2562},{"href":2159,"dataGaName":2160,"dataGaLocation":2381},{"text":2165,"config":2564},{"href":2167,"dataGaName":2168,"dataGaLocation":2381},{"text":2170,"config":2566},{"href":2172,"dataGaName":2173,"dataGaLocation":2381},{"text":2175,"config":2568},{"href":2177,"dataGaName":2178,"dataGaLocation":2381},{"text":2180,"config":2570},{"href":2182,"dataGaName":2183,"dataGaLocation":2381},{"text":2185,"config":2572},{"href":2187,"dataGaName":2188,"dataGaLocation":2381},{"text":2190,"config":2574},{"href":2192,"dataGaName":2193,"dataGaLocation":2381},{"title":2206,"links":2576},[2577,2579,2581,2583,2585,2587,2591,2596,2598,2600,2602],{"text":2214,"config":2578},{"href":2216,"dataGaName":2208,"dataGaLocation":2381},{"text":2219,"config":2580},{"href":2221,"dataGaName":2222,"dataGaLocation":2381},{"text":2227,"config":2582},{"href":2229,"dataGaName":2230,"dataGaLocation":2381},{"text":2232,"config":2584},{"href":2234,"dataGaName":2235,"dataGaLocation":2381},{"text":2237,"config":2586},{"href":2239,"dataGaName":2240,"dataGaLocation":2381},{"text":2588,"config":2589},"Sustainability",{"href":2590,"dataGaName":2588,"dataGaLocation":2381},"/sustainability/",{"text":2592,"config":2593},"Diversity, inclusion and belonging (DIB)",{"href":2594,"dataGaName":2595,"dataGaLocation":2381},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":2242,"config":2597},{"href":2244,"dataGaName":2245,"dataGaLocation":2381},{"text":2252,"config":2599},{"href":2254,"dataGaName":2255,"dataGaLocation":2381},{"text":2257,"config":2601},{"href":2259,"dataGaName":2260,"dataGaLocation":2381},{"text":2603,"config":2604},"Modern Slavery Transparency Statement",{"href":2605,"dataGaName":2606,"dataGaLocation":2381},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":2608},[2609,2612,2615],{"text":2610,"config":2611},"Terms",{"href":2433,"dataGaName":2434,"dataGaLocation":2381},{"text":2613,"config":2614},"Cookies",{"dataGaName":2443,"dataGaLocation":2381,"id":2444,"isOneTrustButton":298},{"text":2616,"config":2617},"Privacy",{"href":2438,"dataGaName":2439,"dataGaLocation":2381},[2619,2633],{"id":2620,"title":7,"body":1921,"config":2621,"content":2623,"description":1921,"extension":2627,"meta":2628,"navigation":298,"path":2629,"seo":2630,"stem":2631,"__hash__":2632},"blogAuthors/en-us/blog/authors/matt-genelin.yml",{"template":2622},"BlogAuthor",{"name":7,"config":2624},{"headshot":2625,"ctfId":2626},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664522/Blog/Author%20Headshots/matty_genelin.png","6x9dTYZik3lSViI8hu6dYQ","yml",{},"/en-us/blog/authors/matt-genelin",{},"en-us/blog/authors/matt-genelin","BNd-jZWck4DOyJIDc9GI-734vQkyzXJgudpPaFgn5pM",{"id":2634,"title":8,"body":1921,"config":2635,"content":2636,"description":1921,"extension":2627,"meta":2640,"navigation":298,"path":2641,"seo":2642,"stem":2643,"__hash__":2644},"blogAuthors/en-us/blog/authors/mathias-ewald.yml",{"template":2622},{"name":8,"config":2637},{"headshot":2638,"ctfId":2639},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664526/Blog/Author%20Headshots/mathias_ewald_headshot.png","7vLTPhU3yvh4xTToXcLpg9",{},"/en-us/blog/authors/mathias-ewald",{},"en-us/blog/authors/mathias-ewald","6h3mh_Isl2z7n-akRG5H9ijs9YS5wqTMLWKfyPjjDyQ",[2646,2654,2662],{"title":2647,"description":2648,"heroImage":2649,"category":1917,"date":2650,"authors":2651,"slug":2653,"externalUrl":1921},"How GitLab tracks vulnerabilities through refactors and reformatting","Learn how GitLab's improved Scope+Offset fingerprinting keeps vulnerability tracking stable across comments, blank lines, and reformatting.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1759320418/xjmqcozxzt4frx0hori3.png","2026-08-12",[2652],"Julian Thome","improved-scope-offset-fingerprinting",{"title":2655,"description":2656,"heroImage":2649,"category":1917,"date":2657,"authors":2658,"slug":2661,"externalUrl":1921},"GitLab Secrets Manager adds ESO, Terraform, API support","Simplify credential management across your stack. GitLab Secrets Manager provides secure retrieval in Kubernetes, Terraform, and external workflows.","2026-08-06",[2659,2660],"Erick Bajao","Joe Randazzo","gitlab-secrets-manager-add-eso-terraform-api-support",{"title":2663,"description":2664,"heroImage":2665,"category":1917,"date":2666,"authors":2667,"slug":2669,"externalUrl":1921},"Secure every commit to production with Claude and GitLab","Claude Security catches vulnerabilities inside a coding session. GitLab picks up from there, scanning, enforcing policy, and producing audit evidence for the software lifecycle. ","https://res.cloudinary.com/about-gitlab-com/image/upload/v1756122536/akivvcnafog9c4dhhzkp.png","2026-08-03",[2668],"Alisa Ho","claude-security-and-gitlab",{"promotions":2671},[2672,2686,2698,2709],{"id":2673,"categories":2674,"header":2676,"text":2677,"button":2678,"image":2683},"ai-modernization",[2675],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":2679,"config":2680},"Get your AI maturity score",{"href":2681,"dataGaName":2682,"dataGaLocation":2146},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":2684},{"src":2685},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":2687,"categories":2688,"header":2690,"text":2677,"button":2691,"image":2695},"devops-modernization",[2689,2483],"product","Are you just managing tools or shipping innovation?",{"text":2692,"config":2693},"Get your DevOps maturity score",{"href":2694,"dataGaName":2682,"dataGaLocation":2146},"/assessments/devops-modernization-assessment/",{"config":2696},{"src":2697},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":2699,"categories":2700,"header":2701,"text":2677,"button":2702,"image":2706},"security-modernization",[1917],"Are you trading speed for security?",{"text":2703,"config":2704},"Get your security maturity score",{"href":2705,"dataGaName":2682,"dataGaLocation":2146},"/assessments/security-modernization-assessment/",{"config":2707},{"src":2708},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":2710,"paths":2711,"header":2714,"text":2715,"button":2716,"image":2721},"github-azure-migration",[2712,2713],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":2717,"config":2718},"See how GitLab compares to GitHub",{"href":2719,"dataGaName":2720,"dataGaLocation":2146},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":2722},{"src":2697},{"header":2724,"blurb":2725,"button":2726,"secondaryButton":2731},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":2727,"config":2728},"Get your free trial",{"href":2729,"dataGaName":1946,"dataGaLocation":2730},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":2419,"config":2732},{"href":2271,"dataGaName":1951,"dataGaLocation":2730},1786803762211]