[{"data":1,"prerenderedAt":801},["ShallowReactive",2],{"/en-us/blog/automating-cybersecurity-threat-detections-with-gitlab-ci-cd":3,"navigation-en-us":42,"banner-en-us":451,"footer-en-us":461,"blog-post-authors-en-us-Mitra Jozenazemian":699,"blog-related-posts-en-us-automating-cybersecurity-threat-detections-with-gitlab-ci-cd":713,"assessment-promotions-en-us":753,"next-steps-en-us":791},{"id":4,"title":5,"authorSlugs":6,"body":8,"categorySlug":9,"config":10,"content":14,"description":8,"extension":28,"isFeatured":12,"meta":29,"navigation":30,"path":31,"publishedDate":20,"seo":32,"stem":36,"tagSlugs":37,"__hash__":41},"blogPosts/en-us/blog/automating-cybersecurity-threat-detections-with-gitlab-ci-cd.yml","Automating Cybersecurity Threat Detections With Gitlab Ci Cd",[7],"mitra-jozenazemian",null,"security-labs",{"slug":11,"featured":12,"template":13},"automating-cybersecurity-threat-detections-with-gitlab-ci-cd",false,"BlogPost",{"title":15,"description":16,"authors":17,"heroImage":19,"date":20,"body":21,"category":9,"tags":22},"Automating cybersecurity threat detections with GitLab CI/CD","Discover how GUARD automates cybersecurity threat detections through the use\nof GitLab CI/CD and how it ensures high-quality detections.",[18],"Mitra Jozenazemian","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663239/Blog/Hero%20Images/AdobeStock_1023776629.jpg","2025-01-29","*This blog post is the second post in a series about [GitLab Universal\nAutomated Response and Detection (GUARD)](https://about.gitlab.com/blog/unveiling-the-guard-framework-to-automate-security-detections-at-gitlab/).*\n\nWriting and deploying security threat detections in an organization’s security information event management platform (SIEM) is a critical component of a successful cybersecurity program. Moving from manual detection engineering to a fully automated process by implementing\nDetections as Code (DaC) ensures detection consistency, quality, auditing, and automated testing. At GitLab, we’ve embedded DaC capabilities into\nGUARD, our fully automated detection and response framework. \n\n## The problem: Source control and automated tests\n\nThe [Signals\nEngineering](https://handbook.gitlab.com/handbook/security/security-operations/signals-engineering/)\nand [SIRT](https://handbook.gitlab.com/handbook/security/security-operations/sirt/)\nteam at GitLab share the responsibility to create, update, and decommission threat detections in our SIEM. Maintaining a single source of truth for detections is critical to ensure detection consistency and quality standards are met. Our teams made the conscious decision to abstract the detection creation process from our SIEM, improving our issue tracking, consistency, roll-back process, and metrics. Additionally, conducting pre-commit detection tests outside of our SIEM ensured that newly created detections didn’t introduce overly false positive heavy alerts, which would require tuning or disablement while the alert was fixed. \n\n## The Solution: Leverage GitLab CI/CD for detection testing and validation\n\nTo address these challenges, we developed an efficient workflow using GitLab [CI/CD](https://about.gitlab.com/topics/ci-cd/), resulting in a streamlined and secure SIEM detection deployment process.\n\n### Key components of the GUARD DaC pipeline \n\n__1. Detections stored in JSON format in a GitLab project__\n\nGitLab uses the JSON format for our threat detections. The template includes essential information such as SIEM query logic, detection title, and description along with runbook page link, MITRE tactic and technique related to the detection, and other necessary details.\n\n__2. Initiating merge requests__\n\nWhen a GitLab team member intends to create a new threat detection, update an existing one, or delete a current detection, they initiate the process by submitting a merge request (MR) in the DaC project containing the detection\nJSON template. Creating the MR automatically triggers a CI/CD pipeline.\n\n__3. Automated validation with CI/CD jobs__\n\nEach MR contains a number of automated checks via GitLab CI/CD:   \n\n* Query format validation queries SIEM API to ensure detection query is\nvalid  \n\n* JSON Detection fields validation validates all required fields are\npresent, and are in the correct format   \n\n* New detections and detection modification trigger a number of SIEM API\ncalls to ensure the detection does not have any errors and that no issues will be introduced into our production detection rules   \n\n* Detection deletion MRs trigger the pipeline to issue a SIEM API query to\nensure the detection to be deleted is still active and can be deleted \n\n__4. Peer review and approval__\n\nWhen a detection MR job completes successfully, a peer review is required to review and confirm the MR meets required quality and content standards before the detection MR can be merged. [Merge request approval rules](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/rules.html)\nare used to trigger the peer review process. \n\n__5. Merge and final deployment__\n\nAfter the MR is approved, it is merged into the main branch. As part of the\nCI/CD pipeline, an automated job executes a SIEM API command in order to perform two tasks:   \n\n* Create the new detection or update/delete the existing detection if\nneeded.   \n\n* Extract the MITRE ATT&CK tactic and technique information related to the\nalert from the JSON files and transmit these details to a lookup table within the SIEM. This lookup table plays an important role in mapping our alerts to MITRE tactics and techniques, helping us improve our threat analysis and identify gaps in our detection capabilities in alignment with the MITRE framework.\n\n**Note:** The necessary credentials for these actions are securely stored in [CI/CD variables](https://docs.gitlab.com/ee/ci/variables/) to ensure the process remains confidential and secure.\n\nBelow is a template GitLab CI/CD `gitlab-ci.yml` configuration file for a\nDaC pipeline: \n\n```text\n\n\n#\n---------------------------------------------------------------------------\n#\n\n# GitLab CI/CD Pipeline for SIEM Detection Management\n\n#\n---------------------------------------------------------------------------\n#\n\n\nimage: python:3.12\n\n\n#\n---------------------------------------------------------------------------\n#\n\n# Global Configuration\n\n#\n---------------------------------------------------------------------------\n#\n\n\nbefore_script:\n  - apt-get update && apt-get install -y jq\n  - pip install --upgrade pip\n  - pip install -r requirements.txt\n\n#\n---------------------------------------------------------------------------\n#\n\n\nstages:\n  - fetch\n  - test\n  - process\n  - upload\n\n#\n---------------------------------------------------------------------------\n#\n\n# Fetch Stage\n\n#\n---------------------------------------------------------------------------\n#\n\n\nfetch_changed_files:\n  stage: fetch\n  Script:\n    - echo \"Fetching changed files...\"\n    - git branch\n    - git fetch origin $CI_DEFAULT_BRANCH:$CI_DEFAULT_BRANCH --depth 2000\n    - |\n      if [[ \"$CI_COMMIT_BRANCH\" == \"$CI_DEFAULT_BRANCH\" ]]; then\n        git diff --name-status HEAD^1...HEAD > changed-files-temp.txt\n      else\n        git fetch origin $CI_COMMIT_BRANCH:$CI_COMMIT_BRANCH --depth 2000\n        git diff --name-status ${CI_DEFAULT_BRANCH}...${CI_COMMIT_SHA} > changed-files-temp.txt\n      fi\n    - grep -E '\\.json$' changed-files-temp.txt > changed-files.txt || true\n    - flake8 .\n    - pytest\n  artifacts:\n    paths:\n      - changed-files.txt\n    expose_as: 'changed_files'\n\n#\n---------------------------------------------------------------------------\n#\n\n# Test Stage\n\n#\n---------------------------------------------------------------------------\n#\n\n\nflake8:\n  stage: test\n  script:\n    - echo \"Running Flake8 for linting...\"\n    - flake8 .\n\npytest:\n  stage: test\n  script:\n    - echo \"Running Pytest for unit tests...\"\n    - pytest\n  artifacts:\n    when: always\n    reports:\n      junit: report.xml\n\n#\n---------------------------------------------------------------------------\n#\n\n# Process Stage\n\n#\n---------------------------------------------------------------------------\n#\n\n\nprocess_files:\n  stage: process\n  script:\n    - echo \"Processing changed files...\"\n    - git clone --depth 2000 --branch $CI_DEFAULT_BRANCH $CI_REPOSITORY_URL\n    - mkdir -p modified_rules delete_file new_file\n    - python3 move-files.py -x changed-files.txt\n    - python3 check-alerts-format.py\n  artifacts:\n    paths:\n      - modified_rules\n      - delete_file\n      - new_file\n#\n---------------------------------------------------------------------------\n#\n\n# Upload Stage\n\n#\n---------------------------------------------------------------------------\n#\n\n\nupdate_rules:\n  stage: upload\n  script:\n    - echo \"Uploading updated rules and lookup tables...\"\n    - git fetch origin $CI_DEFAULT_BRANCH:$CI_DEFAULT_BRANCH --depth 2000\n    - git clone --depth 2000 --branch $CI_DEFAULT_BRANCH $CI_REPOSITORY_URL \n    - python3 update-rules.py\n    - python3 update-exceptions.py\n    - python3 create_ttps_layers.py\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\" && $CI_PIPELINE_SOURCE != \"schedule\"\n      changes:\n        - detections/**/*\n        - exceptions/**/*\n```\n\nThe diagram below illustrates the workflow of the CI/CD process described above.\n\n```mermaid\n\ngraph TD;\n    fetch[Fetch Stage: Identify Changed Files] --> test[Test Stage: Run Linting and Tests];\n    test --> process[Process Stage: Categorize Files];\n    process --> upload[Upload Stage: Update Rules and Lookup Tables];\n    fetch --> fetch_details[Details: Filter JSON files, Output 'changed-files.txt'];\n    test --> test_details[Details: Run Flake8 for linting, Pytest for testing];\n    process --> process_details[Details: Categorize into 'modified', 'new', 'deleted', Prepare for upload];\n    upload --> upload_details[Details: Update repo, Update detections in SIEM and SIEM lookup table];\n```\n\n## Benefits and outcomes\n\nAutomating our detections lifecycle through a DaC CI/CD-powered workflow introduces numerous benefits to our threat detection deployment process:\n\n* Automation: Automating the creation and validation of SIEM detections\nreduces manual errors and saves time.\n\n* Enhanced security: The CI-driven workflow enforces a \"least privilege\"\npolicy, ensuring consistency, peer reviews, and quality standards for creating, updating, or deleting threat detections. \n\n* Efficiency: The standardized JSON detection format and automated creation\nexpedite the deployment process.\n\n* Collaboration: The MR and review process fosters collaboration and\nknowledge sharing among GitLab team members.\n\n* Version control: Treating threat detection as code abstracts the\ndetections from the SIEM platform they are ultimately stored in. This abstraction provides a historical record of changes, facilitates collaboration, and enables rollbacks to previous configurations if issues arise.\n\n## Get started with DaC\n\nUsing GitLab CI/CD and a \"least privilege\" policy has made our SIEM detection and alert management easier and more secure. Automation has improved efficiency and reduced risks, providing a helpful example for others wanting to improve their security and compliance. You can try this tutorial by signing up for a [free trial of GitLab\nUltimate](https://about.gitlab.com/free-trial/).",[23,24,25,26,27],"security","tutorial","DevSecOps","DevSecOps platform","CI/CD","yml",{},true,"/en-us/blog/automating-cybersecurity-threat-detections-with-gitlab-ci-cd",{"ogTitle":15,"ogImage":19,"ogDescription":16,"ogSiteName":33,"noIndex":12,"ogType":34,"ogUrl":35,"title":15,"canonicalUrls":35,"description":16},"https://about.gitlab.com","article","https://about.gitlab.com/blog/automating-cybersecurity-threat-detections-with-gitlab-ci-cd","en-us/blog/automating-cybersecurity-threat-detections-with-gitlab-ci-cd",[23,24,38,39,40],"devsecops","devsecops-platform","cicd","hD_UAq4wQhvKXoKw-_nxoXdhVq-ru5lr1x6Tqje6HCQ",{"data":43},{"logo":44,"freeTrial":49,"sales":54,"login":59,"items":64,"search":371,"minimal":402,"duo":421,"switchNav":430,"pricingDeployment":441},{"config":45},{"href":46,"dataGaName":47,"dataGaLocation":48},"/","gitlab logo","header",{"text":50,"config":51},"Get free trial",{"href":52,"dataGaName":53,"dataGaLocation":48},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":55,"config":56},"Talk to sales",{"href":57,"dataGaName":58,"dataGaLocation":48},"/sales/","sales",{"text":60,"config":61},"Sign in",{"href":62,"dataGaName":63,"dataGaLocation":48},"https://gitlab.com/users/sign_in/","sign in",[65,92,186,191,292,352],{"text":66,"config":67,"cards":69},"Platform",{"dataNavLevelOne":68},"platform",[70,76,84],{"title":66,"description":71,"link":72},"The intelligent orchestration platform for DevSecOps",{"text":73,"config":74},"Explore our Platform",{"href":75,"dataGaName":68,"dataGaLocation":48},"/platform/",{"title":77,"description":78,"link":79},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":80,"config":81},"Meet GitLab Duo",{"href":82,"dataGaName":83,"dataGaLocation":48},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":85,"description":86,"link":87},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":88,"config":89},"Learn more",{"href":90,"dataGaName":91,"dataGaLocation":48},"/why-gitlab/","why gitlab",{"text":93,"left":30,"config":94,"link":96,"lists":100,"footer":168},"Product",{"dataNavLevelOne":95},"solutions",{"text":97,"config":98},"View all Solutions",{"href":99,"dataGaName":95,"dataGaLocation":48},"/solutions/",[101,124,147],{"title":102,"description":103,"link":104,"items":109},"Automation","CI/CD and automation to accelerate deployment",{"config":105},{"icon":106,"href":107,"dataGaName":108,"dataGaLocation":48},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[110,113,116,120],{"text":27,"config":111},{"href":112,"dataGaLocation":48,"dataGaName":27},"/solutions/continuous-integration/",{"text":77,"config":114},{"href":82,"dataGaLocation":48,"dataGaName":115},"gitlab duo agent platform - product menu",{"text":117,"config":118},"Source Code Management",{"href":119,"dataGaLocation":48,"dataGaName":117},"/solutions/source-code-management/",{"text":121,"config":122},"Automated Software Delivery",{"href":107,"dataGaLocation":48,"dataGaName":123},"Automated software delivery",{"title":125,"description":126,"link":127,"items":132},"Security","Deliver code faster without compromising security",{"config":128},{"href":129,"dataGaName":130,"dataGaLocation":48,"icon":131},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[133,137,142],{"text":134,"config":135},"Application Security Testing",{"href":129,"dataGaName":136,"dataGaLocation":48},"Application security testing",{"text":138,"config":139},"Software Supply Chain Security",{"href":140,"dataGaLocation":48,"dataGaName":141},"/solutions/supply-chain/","Software supply chain security",{"text":143,"config":144},"Software Compliance",{"href":145,"dataGaName":146,"dataGaLocation":48},"/solutions/software-compliance/","software compliance",{"title":148,"link":149,"items":154},"Measurement",{"config":150},{"icon":151,"href":152,"dataGaName":153,"dataGaLocation":48},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[155,159,163],{"text":156,"config":157},"Visibility & Measurement",{"href":152,"dataGaLocation":48,"dataGaName":158},"Visibility and Measurement",{"text":160,"config":161},"Value Stream Management",{"href":162,"dataGaLocation":48,"dataGaName":160},"/solutions/value-stream-management/",{"text":164,"config":165},"Analytics & Insights",{"href":166,"dataGaLocation":48,"dataGaName":167},"/solutions/analytics-and-insights/","Analytics and insights",{"title":169,"items":170},"GitLab for",[171,176,181],{"text":172,"config":173},"Enterprise",{"href":174,"dataGaLocation":48,"dataGaName":175},"/enterprise/","enterprise",{"text":177,"config":178},"Small Business",{"href":179,"dataGaLocation":48,"dataGaName":180},"/small-business/","small business",{"text":182,"config":183},"Public Sector",{"href":184,"dataGaLocation":48,"dataGaName":185},"/solutions/public-sector/","public sector",{"text":187,"config":188},"Pricing",{"href":189,"dataGaName":190,"dataGaLocation":48,"dataNavLevelOne":190},"/pricing/","pricing",{"text":192,"config":193,"link":195,"lists":199,"feature":279},"Resources",{"dataNavLevelOne":194},"resources",{"text":196,"config":197},"View all resources",{"href":198,"dataGaName":194,"dataGaLocation":48},"/resources/",[200,233,251],{"title":201,"items":202},"Getting started",[203,208,213,218,223,228],{"text":204,"config":205},"Install",{"href":206,"dataGaName":207,"dataGaLocation":48},"/install/","install",{"text":209,"config":210},"Quick start guides",{"href":211,"dataGaName":212,"dataGaLocation":48},"/get-started/","quick setup checklists",{"text":214,"config":215},"Learn",{"href":216,"dataGaLocation":48,"dataGaName":217},"https://university.gitlab.com/","learn",{"text":219,"config":220},"Product documentation",{"href":221,"dataGaName":222,"dataGaLocation":48},"https://docs.gitlab.com/","product documentation",{"text":224,"config":225},"Best practice videos",{"href":226,"dataGaName":227,"dataGaLocation":48},"/getting-started-videos/","best practice videos",{"text":229,"config":230},"Integrations",{"href":231,"dataGaName":232,"dataGaLocation":48},"/integrations/","integrations",{"title":234,"items":235},"Discover",[236,241,246],{"text":237,"config":238},"Customer success stories",{"href":239,"dataGaName":240,"dataGaLocation":48},"/customers/","customer success stories",{"text":242,"config":243},"Blog",{"href":244,"dataGaName":245,"dataGaLocation":48},"/blog/","blog",{"text":247,"config":248},"Remote",{"href":249,"dataGaName":250,"dataGaLocation":48},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":252,"items":253},"Connect",[254,259,264,269,274],{"text":255,"config":256},"GitLab Services",{"href":257,"dataGaName":258,"dataGaLocation":48},"/services/","services",{"text":260,"config":261},"Community",{"href":262,"dataGaName":263,"dataGaLocation":48},"/community/","community",{"text":265,"config":266},"Forum",{"href":267,"dataGaName":268,"dataGaLocation":48},"https://forum.gitlab.com/","forum",{"text":270,"config":271},"Events",{"href":272,"dataGaName":273,"dataGaLocation":48},"/events/","events",{"text":275,"config":276},"Partners",{"href":277,"dataGaName":278,"dataGaLocation":48},"/partners/","partners",{"backgroundColor":280,"textColor":281,"text":282,"image":283,"link":287},"#2f2a6b","#fff","Insights for the future of software development",{"altText":284,"config":285},"the source promo card",{"src":286},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":288,"config":289},"Read the latest",{"href":290,"dataGaName":291,"dataGaLocation":48},"/the-source/","the source",{"text":293,"config":294,"lists":296},"Company",{"dataNavLevelOne":295},"company",[297],{"items":298},[299,304,310,312,317,322,327,332,337,342,347],{"text":300,"config":301},"About",{"href":302,"dataGaName":303,"dataGaLocation":48},"/company/","about",{"text":305,"config":306,"footerGa":309},"Jobs",{"href":307,"dataGaName":308,"dataGaLocation":48},"/jobs/","jobs",{"dataGaName":308},{"text":270,"config":311},{"href":272,"dataGaName":273,"dataGaLocation":48},{"text":313,"config":314},"Leadership",{"href":315,"dataGaName":316,"dataGaLocation":48},"/company/team/e-group/","leadership",{"text":318,"config":319},"Team",{"href":320,"dataGaName":321,"dataGaLocation":48},"/company/team/","team",{"text":323,"config":324},"Handbook",{"href":325,"dataGaName":326,"dataGaLocation":48},"https://handbook.gitlab.com/","handbook",{"text":328,"config":329},"Investor relations",{"href":330,"dataGaName":331,"dataGaLocation":48},"https://ir.gitlab.com/","investor relations",{"text":333,"config":334},"Trust Center",{"href":335,"dataGaName":336,"dataGaLocation":48},"/security/","trust center",{"text":338,"config":339},"AI Transparency Center",{"href":340,"dataGaName":341,"dataGaLocation":48},"/ai-transparency-center/","ai transparency center",{"text":343,"config":344},"Newsletter",{"href":345,"dataGaName":346,"dataGaLocation":48},"/company/contact/#contact-forms","newsletter",{"text":348,"config":349},"Press",{"href":350,"dataGaName":351,"dataGaLocation":48},"/press/","press",{"text":353,"config":354,"lists":355},"Contact us",{"dataNavLevelOne":295},[356],{"items":357},[358,361,366],{"text":55,"config":359},{"href":57,"dataGaName":360,"dataGaLocation":48},"talk to sales",{"text":362,"config":363},"Support portal",{"href":364,"dataGaName":365,"dataGaLocation":48},"https://support.gitlab.com","support portal",{"text":367,"config":368},"Customer portal",{"href":369,"dataGaName":370,"dataGaLocation":48},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":372,"login":373,"suggestions":380},"Close",{"text":374,"link":375},"To search repositories and projects, login to",{"text":376,"config":377},"gitlab.com",{"href":62,"dataGaName":378,"dataGaLocation":379},"search login","search",{"text":381,"default":382},"Suggestions",[383,385,389,391,395,399],{"text":77,"config":384},{"href":82,"dataGaName":77,"dataGaLocation":379},{"text":386,"config":387},"Code Suggestions (AI)",{"href":388,"dataGaName":386,"dataGaLocation":379},"/solutions/code-suggestions/",{"text":27,"config":390},{"href":112,"dataGaName":27,"dataGaLocation":379},{"text":392,"config":393},"GitLab on AWS",{"href":394,"dataGaName":392,"dataGaLocation":379},"/partners/technology-partners/aws/",{"text":396,"config":397},"GitLab on Google Cloud",{"href":398,"dataGaName":396,"dataGaLocation":379},"/partners/technology-partners/google-cloud-platform/",{"text":400,"config":401},"Why GitLab?",{"href":90,"dataGaName":400,"dataGaLocation":379},{"freeTrial":403,"mobileIcon":408,"desktopIcon":413,"secondaryButton":416},{"text":404,"config":405},"Start free trial",{"href":406,"dataGaName":53,"dataGaLocation":407},"https://gitlab.com/-/trials/new/","nav",{"altText":409,"config":410},"Gitlab Icon",{"src":411,"dataGaName":412,"dataGaLocation":407},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":409,"config":414},{"src":415,"dataGaName":412,"dataGaLocation":407},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":417,"config":418},"Get Started",{"href":419,"dataGaName":420,"dataGaLocation":407},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":422,"mobileIcon":426,"desktopIcon":428},{"text":423,"config":424},"Learn more about GitLab Duo",{"href":82,"dataGaName":425,"dataGaLocation":407},"gitlab duo",{"altText":409,"config":427},{"src":411,"dataGaName":412,"dataGaLocation":407},{"altText":409,"config":429},{"src":415,"dataGaName":412,"dataGaLocation":407},{"button":431,"mobileIcon":436,"desktopIcon":438},{"text":432,"config":433},"/switch",{"href":434,"dataGaName":435,"dataGaLocation":407},"#contact","switch",{"altText":409,"config":437},{"src":411,"dataGaName":412,"dataGaLocation":407},{"altText":409,"config":439},{"src":440,"dataGaName":412,"dataGaLocation":407},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":442,"mobileIcon":447,"desktopIcon":449},{"text":443,"config":444},"Back to pricing",{"href":189,"dataGaName":445,"dataGaLocation":407,"icon":446},"back to pricing","GoBack",{"altText":409,"config":448},{"src":411,"dataGaName":412,"dataGaLocation":407},{"altText":409,"config":450},{"src":415,"dataGaName":412,"dataGaLocation":407},{"title":452,"button":453,"config":458},"See how agentic AI transforms software delivery",{"text":454,"config":455},"Watch GitLab Transcend now",{"href":456,"dataGaName":457,"dataGaLocation":48},"/events/transcend/virtual/","transcend event",{"layout":459,"icon":460,"disabled":30},"release","AiStar",{"data":462},{"text":463,"source":464,"edit":470,"contribute":475,"config":480,"items":485,"minimal":688},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":465,"config":466},"View page source",{"href":467,"dataGaName":468,"dataGaLocation":469},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":471,"config":472},"Edit this page",{"href":473,"dataGaName":474,"dataGaLocation":469},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":476,"config":477},"Please contribute",{"href":478,"dataGaName":479,"dataGaLocation":469},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":481,"facebook":482,"youtube":483,"linkedin":484},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[486,533,583,627,654],{"title":187,"links":487,"subMenu":502},[488,492,497],{"text":489,"config":490},"View plans",{"href":189,"dataGaName":491,"dataGaLocation":469},"view plans",{"text":493,"config":494},"Why Premium?",{"href":495,"dataGaName":496,"dataGaLocation":469},"/pricing/premium/","why premium",{"text":498,"config":499},"Why Ultimate?",{"href":500,"dataGaName":501,"dataGaLocation":469},"/pricing/ultimate/","why ultimate",[503],{"title":504,"links":505},"Contact Us",[506,509,511,513,518,523,528],{"text":507,"config":508},"Contact sales",{"href":57,"dataGaName":58,"dataGaLocation":469},{"text":362,"config":510},{"href":364,"dataGaName":365,"dataGaLocation":469},{"text":367,"config":512},{"href":369,"dataGaName":370,"dataGaLocation":469},{"text":514,"config":515},"Status",{"href":516,"dataGaName":517,"dataGaLocation":469},"https://status.gitlab.com/","status",{"text":519,"config":520},"Terms of use",{"href":521,"dataGaName":522,"dataGaLocation":469},"/terms/","terms of use",{"text":524,"config":525},"Privacy statement",{"href":526,"dataGaName":527,"dataGaLocation":469},"/privacy/","privacy statement",{"text":529,"config":530},"Cookie preferences",{"dataGaName":531,"dataGaLocation":469,"id":532,"isOneTrustButton":30},"cookie preferences","ot-sdk-btn",{"title":93,"links":534,"subMenu":542},[535,538],{"text":26,"config":536},{"href":75,"dataGaName":537,"dataGaLocation":469},"devsecops platform",{"text":539,"config":540},"AI-Assisted Development",{"href":82,"dataGaName":541,"dataGaLocation":469},"ai-assisted development",[543],{"title":544,"links":545},"Topics",[546,550,555,560,565,568,573,578],{"text":547,"config":548},"CICD",{"href":549,"dataGaName":40,"dataGaLocation":469},"/topics/ci-cd/",{"text":551,"config":552},"GitOps",{"href":553,"dataGaName":554,"dataGaLocation":469},"/topics/gitops/","gitops",{"text":556,"config":557},"DevOps",{"href":558,"dataGaName":559,"dataGaLocation":469},"/topics/devops/","devops",{"text":561,"config":562},"Version Control",{"href":563,"dataGaName":564,"dataGaLocation":469},"/topics/version-control/","version control",{"text":25,"config":566},{"href":567,"dataGaName":38,"dataGaLocation":469},"/topics/devsecops/",{"text":569,"config":570},"Cloud Native",{"href":571,"dataGaName":572,"dataGaLocation":469},"/topics/cloud-native/","cloud native",{"text":574,"config":575},"AI for Coding",{"href":576,"dataGaName":577,"dataGaLocation":469},"/topics/devops/ai-for-coding/","ai for coding",{"text":579,"config":580},"Agentic AI",{"href":581,"dataGaName":582,"dataGaLocation":469},"/topics/agentic-ai/","agentic ai",{"title":584,"links":585},"Solutions",[586,588,590,595,599,602,606,609,611,614,617,622],{"text":134,"config":587},{"href":129,"dataGaName":134,"dataGaLocation":469},{"text":123,"config":589},{"href":107,"dataGaName":108,"dataGaLocation":469},{"text":591,"config":592},"Agile development",{"href":593,"dataGaName":594,"dataGaLocation":469},"/solutions/agile-delivery/","agile delivery",{"text":596,"config":597},"SCM",{"href":119,"dataGaName":598,"dataGaLocation":469},"source code management",{"text":547,"config":600},{"href":112,"dataGaName":601,"dataGaLocation":469},"continuous integration & delivery",{"text":603,"config":604},"Value stream management",{"href":162,"dataGaName":605,"dataGaLocation":469},"value stream management",{"text":551,"config":607},{"href":608,"dataGaName":554,"dataGaLocation":469},"/solutions/gitops/",{"text":172,"config":610},{"href":174,"dataGaName":175,"dataGaLocation":469},{"text":612,"config":613},"Small business",{"href":179,"dataGaName":180,"dataGaLocation":469},{"text":615,"config":616},"Public sector",{"href":184,"dataGaName":185,"dataGaLocation":469},{"text":618,"config":619},"Education",{"href":620,"dataGaName":621,"dataGaLocation":469},"/solutions/education/","education",{"text":623,"config":624},"Financial services",{"href":625,"dataGaName":626,"dataGaLocation":469},"/solutions/finance/","financial services",{"title":192,"links":628},[629,631,633,635,638,640,642,644,646,648,650,652],{"text":204,"config":630},{"href":206,"dataGaName":207,"dataGaLocation":469},{"text":209,"config":632},{"href":211,"dataGaName":212,"dataGaLocation":469},{"text":214,"config":634},{"href":216,"dataGaName":217,"dataGaLocation":469},{"text":219,"config":636},{"href":221,"dataGaName":637,"dataGaLocation":469},"docs",{"text":242,"config":639},{"href":244,"dataGaName":245,"dataGaLocation":469},{"text":237,"config":641},{"href":239,"dataGaName":240,"dataGaLocation":469},{"text":247,"config":643},{"href":249,"dataGaName":250,"dataGaLocation":469},{"text":255,"config":645},{"href":257,"dataGaName":258,"dataGaLocation":469},{"text":260,"config":647},{"href":262,"dataGaName":263,"dataGaLocation":469},{"text":265,"config":649},{"href":267,"dataGaName":268,"dataGaLocation":469},{"text":270,"config":651},{"href":272,"dataGaName":273,"dataGaLocation":469},{"text":275,"config":653},{"href":277,"dataGaName":278,"dataGaLocation":469},{"title":293,"links":655},[656,658,660,662,664,666,668,672,677,679,681,683],{"text":300,"config":657},{"href":302,"dataGaName":295,"dataGaLocation":469},{"text":305,"config":659},{"href":307,"dataGaName":308,"dataGaLocation":469},{"text":313,"config":661},{"href":315,"dataGaName":316,"dataGaLocation":469},{"text":318,"config":663},{"href":320,"dataGaName":321,"dataGaLocation":469},{"text":323,"config":665},{"href":325,"dataGaName":326,"dataGaLocation":469},{"text":328,"config":667},{"href":330,"dataGaName":331,"dataGaLocation":469},{"text":669,"config":670},"Sustainability",{"href":671,"dataGaName":669,"dataGaLocation":469},"/sustainability/",{"text":673,"config":674},"Diversity, inclusion and belonging (DIB)",{"href":675,"dataGaName":676,"dataGaLocation":469},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":333,"config":678},{"href":335,"dataGaName":336,"dataGaLocation":469},{"text":343,"config":680},{"href":345,"dataGaName":346,"dataGaLocation":469},{"text":348,"config":682},{"href":350,"dataGaName":351,"dataGaLocation":469},{"text":684,"config":685},"Modern Slavery Transparency Statement",{"href":686,"dataGaName":687,"dataGaLocation":469},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":689},[690,693,696],{"text":691,"config":692},"Terms",{"href":521,"dataGaName":522,"dataGaLocation":469},{"text":694,"config":695},"Cookies",{"dataGaName":531,"dataGaLocation":469,"id":532,"isOneTrustButton":30},{"text":697,"config":698},"Privacy",{"href":526,"dataGaName":527,"dataGaLocation":469},[700],{"id":701,"title":18,"body":8,"config":702,"content":704,"description":8,"extension":28,"meta":708,"navigation":30,"path":709,"seo":710,"stem":711,"__hash__":712},"blogAuthors/en-us/blog/authors/mitra-jozenazemian.yml",{"template":703},"BlogAuthor",{"name":18,"config":705},{"headshot":706,"ctfId":707},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749662373/Blog/Author%20Headshots/Screenshot_2024-10-25_at_8.23.56_AM.png","4suqsutT8w5ZmkIvSVrmWQ",{},"/en-us/blog/authors/mitra-jozenazemian",{},"en-us/blog/authors/mitra-jozenazemian","DzsXAQdHz5kyKwnJ-xGAs_Wr_lt8XWi_J9LQijwLiVY",[714,728,742],{"content":715,"config":726},{"body":716,"title":717,"description":718,"category":9,"tags":719,"authors":722,"heroImage":724,"date":725},"\n***Note: The GitLab product did not use any of the compromised package versions mentioned in this post.***\n\nIn the span of 12 days, four separate supply chain attacks revealed that continuous integration and continuous delivery (CI/CD) pipelines have become a high-value target for sophisticated threat actors.\n\nBetween March 19 and March 31, 2026, threat actors compromised:\n\n* an open-source security scanner (Trivy)\n* an infrastructure-as-code (IaC) security scanner (Checkmarx KICS)\n* an AI model gateway (LiteLLM)\n* a JavaScript HTTP client (axios)\n\nEach attack shared the same surface: the build pipeline.\nThis article shows [what happened](#trusted-by-millions-compromised-in-minutes), [why pipelines can be uniquely vulnerable](#the-patterns-behind-these-attacks), and how centralized policy enforcement with GitLab — using policies defined below — can [block, detect, and contain these classes of attack](#how-gitlab-pipeline-execution-policies-address-each-attack-pattern) before they reach production.\n\n\n## Trusted by millions, compromised in minutes\n\nHere is the timeline of the supply chain attacks:\n\n### March 19: Trivy security scanner becomes an attack vector\n\n[Trivy](https://github.com/aquasecurity/trivy) is one of the most widely used open-source vulnerability scanners in the world. It is the tool teams run *inside their pipelines* to find vulnerabilities.\n\nOn March 19, a threat actor group known as [TeamPCP used compromised credentials](https://www.aquasec.com/blog/trivy-supply-chain-attack-what-you-need-to-know/) to force-push malicious code into 76 of 77 version tags of the `aquasecurity/trivy-action` GitHub Action and all 7 tags of `aquasecurity/setup-trivy`. Simultaneously, they published a trojanized Trivy binary (v0.69.4) to official distribution channels. The payload was credential-stealing malware that harvested environment variables, cloud tokens, SSH keys, and CI/CD secrets from every pipeline that ran a Trivy scan.\n\nThe incident was assigned [CVE-2026-33634](https://nvd.nist.gov/vuln/detail/CVE-2026-33634) with a CVSS score of 9.4. The Cybersecurity and Infrastructure Security Agency (CISA) added it to the Known Exploited Vulnerabilities catalog within days.\n\n### March 23: Checkmarx KICS falls next\nUsing stolen credentials, TeamPCP pivoted to Checkmarx’s open-source KICS (Keeping Infrastructure as Code Secure) project. They compromised the `ast-github-action` and `kics-github-action` GitHub Actions, [injecting the same credential-stealing malware](https://thehackernews.com/2026/03/teampcp-hacks-checkmarx-github-actions.html). Between 12:58 and 16:50 UTC on March 23, any CI/CD pipeline referencing these actions was silently exfiltrating sensitive data, such as API keys, database passwords, cloud access tokens, SSH keys, and service account credentials.\n\n### March 24: LiteLLM compromised via stolen Trivy credentials\n\nLiteLLM, an LLM API proxy with 95 million monthly downloads, was the next target. TeamPCP [published backdoored versions](https://thehackernews.com/2026/03/teampcp-backdoors-litellm-versions.html) (1.82.7 and 1.82.8) to PyPI using credentials harvested from LiteLLM’s own CI/CD pipeline, which used Trivy for scanning.\n\nThe malware targeting Version 1.82.7 used a base64-encoded payload injected directly into `litellm/proxy/proxy_server.py` that executed at import time. The version targeting 1.82.8 used a `.pth` file, a Python mechanism that executes automatically during interpreter startup. Simply installing LiteLLM was enough to trigger the payload. Attackers encrypted the stolen data (SSH keys, cloud tokens, .env files, cryptocurrency wallets) and exfiltrated it to `models.litellm.cloud`, a lookalike domain.\n\n### March 31: Source code for AI coding assistant leaked via simple packaging mistake\nWhile the TeamPCP campaign was still unfolding, a software company shipped an npm package containing a 59.8 MB source map file — one that referenced its AI coding assistant's complete, unminified TypeScript source code, hosted in the company's own Cloudflare R2 bucket.\n\nThe leak exposed 1,900 TypeScript files, 512,000+ lines of code, 44 hidden feature flags, unreleased model codenames, and the full system prompt for anyone who knew where to look. As engineer [Gabriel Anhaia explained](https://dev.to/gabrielanhaia/claude-codes-entire-source-code-was-just-leaked-via-npm-source-maps-heres-whats-inside-cjo), “A single misconfigured .npmignore or files field in package.json can expose everything.”\n### March 31: axios and another trojan in the supply chain\nThat same day, a sophisticated campaign [targeted the axios npm package](https://thehackernews.com/2026/03/axios-supply-chain-attack-pushes-cross.html), a JavaScript HTTP client with over 100 million weekly downloads.\n\nA compromised maintainer account published backdoored versions (1.14.1 and 0.30.4). It injected a malicious dependency (`plain-crypto-js@4.2.1`) that deployed a Remote Access Trojan capable of running on macOS, Windows, and Linux. Both release branches were hit within 39 minutes, with the malware designed to self-destruct after execution.\n\n## The patterns behind these attacks\n\nAcross these five incidents, three distinct attack patterns emerge, and all of them exploit the implicit trust that CI/CD pipelines place in their inputs.\n\n### Pattern 1: Poisoned tools and actions\n\nThe TeamPCP campaign exploited a fundamental assumption: that the security tools running *inside* your pipeline are themselves trustworthy. When a GitHub Action tag or a PyPI package version resolves to malicious code, the pipeline executes it with full access to environment secrets, cloud credentials, and deployment tokens. There is no verification step because the pipeline trusts the tag.\n\n**A recommended pipeline-level control:** Pin tools and actions to immutable references (commit SHAs or image digests) rather than mutable version tags. Where pinning is not practical, verify the integrity of tools and dependencies against known-good checksums or signatures. Block execution if verification fails.\n\n### Pattern 2: Packaging misconfigurations that leak IP\n\nA misconfigured build pipeline shipped debugging artifacts straight into the production package. A misconfigured `.npmignore` or files field in package.json is all it takes. A pre-publish validation step should catch this every time.\n\n**A recommended pipeline-level control:** Before any package is published, run automated checks that validate the package contents against an allowlist, flag unexpected files (source maps, internal configs, .env files), and block the publish step if the checks fail.\n\n### Pattern 3: Vulnerabilities in transitive dependencies\n\nThe axios attack targeted not just direct users of axios, but anyone whose dependency tree resolved to the compromised version. A single poisoned dependency in a lockfile can thus propagate through an entire organization’s build infrastructure.\n\n**A recommended pipeline-level control:** Compare dependency checksums against known-good lockfile state. Detect unexpected new dependencies or version changes. Block builds that introduce unverified packages.\n\n## How GitLab Pipeline Execution Policies address each attack pattern\n\nGitLab Pipeline Execution Policies ([PEPs](https://docs.gitlab.com/user/application_security/policies/pipeline_execution_policies/)) enable security and platform teams to inject mandatory CI/CD jobs into every pipeline across an organization, regardless of what a developer defines in their `.gitlab-ci.yml`. Jobs defined in PEPs cannot be skipped, even with `[skip ci]` or `[no_pipeline]` directives. Jobs can be executed in *reserved* stages (`.pipeline-policy-pre` and `.pipeline-policy-post`) that bookend the developer’s pipeline.\n\nWe have published ready-to-use pipeline execution policies for all three patterns as an open-source project: [Supply Chain Policies](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies). These policies are independently deployable, and each one ships with violation samples that you can use to test them. Here is how each one works.\n\n### Use case 1: Prevent accidental exposure in package publishing\n\n**Problem:** A source map file ended up in the npm package of an AI coding tool after the build pipeline skipped publish-time validation.\n\n**PEP approach:** We built an open-source Pipeline Execution Policy for exactly this class of error: [Artifact Hygiene](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies/-/blob/main/artifact-hygiene.gitlab-ci.yml?ref_type=heads).\n\nThe policy injects `.pipeline-policy-pre` jobs that auto-detect the artifact type (npm package, Docker image, or Helm chart) and inspect the contents before any publish step runs. For npm packages, it performs three checks:\n\n1. **File pattern blocklist.** Scans npm pack output for source maps (.map), test directories, build configs, IDE settings, and src/ directories.\n\n2. **Package size gate.** Blocks packages exceeding 50 MB, like the 59.8 MB package that leaked the AI tool.\n\n3. **sourceMappingURL scan.** Detects external URLs (the R2 bucket pattern that exposed a major AI company’s source), inline data: URIs, and local file references embedded in JavaScript bundles.\n\nWhen violations are found, the pipeline fails with a clear report in the failed CI job logs:\n```text\n=============================================\nFAILED: 3 violation(s) found\n=============================================\nBLOCKED: dist/index.js.map (matched: \\.map$)\nBLOCKED: dist/index.js contains external sourceMappingURL\nBLOCKED: dist/utils.js contains inline sourceMappingURL\n\nThis check is enforced by a Pipeline Execution Policy. If this is a false positive, contact the security team to update the policy project or exclude this project.\n```\nThe policy has no user-configurable CI variables. Developers cannot disable or bypass it. Exceptions are managed by the security team at the policy level, ensuring a deliberate process and a clean audit trail.\n\nThe repository includes a test project with intentional violations (examples/leaky-npm-package/) so you can see the policy in action before deploying it to your organization. The [README](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies/-/blob/main/README.md) includes a complete quick-start guide for setup and deployment.\n\n**What this catches:** Any one of these controls would likely have prevented the AI company's source code leak:\n\n* The source map file triggers the file pattern blocklist.\n* Its 59.8 MB size triggers the size gate.\n* The sourceMappingURL pointing to an external R2 bucket triggers the URL scan.\n\n### Use case 2: Detect dependency tampering and lockfile manipulation\n\n**Problem:** The axios attack introduced a malicious transitive dependency (`plain-crypto-js`) that executed a RAT on install. Anyone who ran npm install during the compromise window pulled in the trojan.\n\n**PEP approach:** The [Dependency Integrity policy](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies/-/blob/main/dependency-integrity.gitlab-ci.yml) injects .pipeline-policy-pre jobs that auto-detect the package ecosystem (npm or Python) and perform three checks:\n\n**For npm projects** (triggered by `package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`):\n\n1. **Lockfile integrity.** Runs `npm ci --ignore-scripts`, which fails if `node_modules` would differ from what the lockfile specifies. This catches cases where package.json was updated but the lockfile was not regenerated, and also verifies SRI integrity hashes.\n2. **Blocked package scan.** Cross-references the lockfile’s full dependency tree against `blocked-packages.yml`, a GitLab-maintained list of known-compromised package versions. The shipped blocklist includes `axios@1.14.1`, `axios@0.30.4`, and `plain-crypto-js@4.2.1`.\n3. **Undeclared dependency detection.** After install, compares the contents of node_modules against the lockfile. Any package present on disk but absent from the lockfile indicates tampering (e.g., a compromised postinstall script that fetches additional packages).\n\n**For Python projects** (triggered by `requirements.txt`, `Pipfile.lock`, `poetry.lock`, or `uv.lock`):\n\n1. **Lockfile integrity.** Installs in an isolated virtual environment and verifies that the install succeeds from the lockfile.\n2. **Blocked package scan.** Same blocklist approach. The shipped list includes `litellm==1.82.7` and `litellm==1.82.8`.\n3. **.pth file detection.** Scans site-packages for `.pth` files containing executable code patterns (`import os`, `exec(`, `eval(`, `__import__`, `subprocess`, `socket`). This is the exact mechanism the LiteLLM backdoor used.\n\nWhen a violation is found:\n\n```text\n=============================================\nFAILED: 1 violation(s) found\n=============================================\nBLOCKED: axios@1.14.1 is a known-compromised package\n\nThis check is enforced by a Pipeline Execution Policy.\n```\n\nThe policy runs in *strict mode*: any dependency not present in the committed lockfile blocks the pipeline. If a developer needs to add a dependency, they commit the updated lockfile. The policy verifies that the installed version matches the committed version. If something appears that was not committed (e.g., a transitive dependency injected via a compromised upstream package), the pipeline blocks.\n\n**What this catches:** The introduction of `plain-crypto-js` as a new, previously unseen dependency would be flagged by the undeclared dependency check. The `axios@1.14.1` version would be caught by the blocked package scan. The LiteLLM `.pth` file would be caught by the `.pth` detection check. Each attack has at least one, and often two, independent detection signals.\n\n### Use case 3: Detect and block compromised tools before execution\n\n**Problem:** TeamPCP replaced trusted Trivy and Checkmarx GitHub Action tags with malicious versions. Any pipeline referencing those tags executed credential-stealing malware.\n\n**PEP approach:** The [Tool Integrity policy](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies/-/blob/main/tool-integrity.gitlab-ci.yml) injects a `.pipeline-policy-pre` job that queries the GitLab CI Lint API (or falls back to evaluate the `.gitlab-ci.yml`), extracts the container image references, and compares it against an approved images allowlist maintained by the security team.\n\nThe allowlist (`approved-images.yml`) supports three controls per image:\n\n**Approved repositories:** Only images from repositories on the list are permitted. An unknown repository blocks the pipeline.\n\n**Allowed tags:** Only specific tags are permitted within an approved repository. This prevents drift to untested versions.\n\n**Blocked tags:** Known-compromised versions can be explicitly blocked even if the repository is approved. The shipped allowlist blocks `aquasec/trivy:0.69.4` through `0.69.6`, the exact versions TeamPCP trojanized.\n\nWhen a violation is found, the pipeline fails before any other job runs:\n\n```text\n=============================================\nFAILED: 1 violation(s) found\n=============================================\nBLOCKED: aquasec/trivy:0.69.4 (job: trivy-scan)\n\n - tag '0.69.4' is known-compromised\n\nThis check is enforced by a Pipeline Execution Policy.\n```\n\nThe allowlist is maintained via MRs against the policy project. To add a new approved image, the security team opens an MR. To respond to a new compromise, they add a blocked tag. No code changes required, just YAML.\n\n**What this catches:** When images with unapproved tags are detected, the policy compares the image repository names and tags to an allowlist. A failed match blocks the pipeline before any scanner executes, preventing credential exfiltration.\n\n*Note: By extending the sample above, PEPs can be used to force pinning to digests over tags, which is immune to force pushes. This sample demonstrates a more basic tag-based enforcement pattern.*\n\n## Beyond PEPs: GitLab’s supply chain defenses\n\nPipeline Execution Policies are the enforcement layer, but they work best as part of a broader defense-in-depth strategy. GitLab provides several capabilities that complement PEPs for supply chain protection:\n\n### Secret detection\n\n[GitLab secret detection](https://docs.gitlab.com/user/application_security/secret_detection/) prevents credentials from landing in the repository in the first place, significantly reducing what a compromised pipeline tool can harvest. In the context of the March 2026 attacks:\n\n* Credentials stored in repositories are both easier for attackers to discover and slower to rotate. The Trivy incident showed that even the rotation process can be exploited: Aqua Security's rotation was not atomic, and the attacker captured newly issued tokens before the old ones were fully revoked. GitLab Secret Detection includes automatic revocation for leaked GitLab tokens and a partner API that notifies third-party providers to revoke their credentials, accelerating response when a breach does occur.\n\n* Secret detection combined with proper secret management (short-lived tokens, vault-backed credentials, minimal pipeline secret exposure) limits what an attacker can reach even when a trusted tool turns hostile.\n\n### Dependency scanning via software composition analysis (SCA)\n\nGitLab [dependency scanning](https://docs.gitlab.com/user/application_security/dependency_scanning/) identifies known vulnerabilities in project dependencies by analyzing lockfiles and manifests. In the context of the March 2026 attacks:\n\n* For LiteLLM, the compromised versions (1.82.7, 1.82.8) are tracked in GitLab's advisory database, flagging affected Python projects automatically.\n\n* For axios, dependency scanning identifies the compromised versions (1.14.1, 0.30.4) across every project in the organization, giving security teams a single view for assessing blast radius and prioritizing credential rotation.\n\n* Similarly, all npm packages compromised by TeamPCP's CanisterWorm propagation are also flagged if used.\n\n[GitLab Container Scanning](https://docs.gitlab.com/user/application_security/container_scanning/) detects vulnerable container images used in your deployments. For the Trivy compromise, Container Scanning flags the trojanized Trivy Docker images (0.69.4 through 0.69.6) when they appear in your container registry or deployment manifests.\n\n### Merge request approval policies\n\n[Merge request approval policies](https://docs.gitlab.com/user/application_security/policies/merge_request_approval_policies/) can require security team approval before changes to dependency lockfiles or CI/CD configurations are merged. This ensures a human checkpoint for the types of changes that supply chain attacks typically introduce.\n\n### Coming soon: Dependency Firewall, Artifact Registry, and SLSA Level 3 Attestation & Verification\n\nUpcoming GitLab supply chain security capabilities harden policy enforcement at two critical control points: the registry and the pipeline. The Dependency Firewall and Artifact Registry will block non-conforming packages, while SLSA Level 3 attestation will provide cryptographic proof that artifacts were produced by approved pipelines and remain unmodified. Together, they will give security teams verifiable control over what enters and exits the software supply chain.\n\n## What this means for your organization\n\nAmidst rising AI-assisted threats, attacks on CI/CD pipelines are becoming commonplace. The TeamPCP campaign shows how a single compromised credential can cascade across an ecosystem of trusted tools.\n\nIf your organization used any of the affected components, operate with the assumption that all of your pipeline secrets were exposed: rotate them immediately and audit systems for persisted backdoors. Either way, regularly rotating credentials and using short-lived tokens limits the blast radius of any future compromise.\n\nHere is what we recommend:\n\n1. **Pin dependencies to checksums, when possible.** Mutable version tags (like the ones TeamPCP hijacked) are not a security boundary. Use SHA-pinned references for all [CI/CD components](https://docs.gitlab.com/ci/components/#manage-dependencies) or actions and container images.\n\n2. **Run pre-execution integrity checks.** Use Pipeline Execution Policies to verify tool and dependency integrity *before* any pipeline job runs. This is the `.pipeline-policy-pre` stage.\n\n3. **Audit what you publish.** Every package publish step should include automated validation of the artifact contents. Source maps, environment files, and internal configuration should never leave your build environment. The [Supply Chain Policy](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies) project provides a ready-to-deploy starting point for npm, Docker, and Helm artifacts.\n\n4. **Detect dependency drift.** Compare dependency resolutions against committed lockfiles on every pipeline run. Monitor for unexpected new dependencies.\n\n5. **Centralize policy management.** Do not rely on developers remembering to include security checks. Enforce them at the group or instance level through policies that developers cannot remove or skip.\n\n6. **Assume your security tools are targets.** If your vulnerability scanner, static application security testing (SAST) tool, or AI gateway can be compromised, it will be. Limit each tool to its least necessary privileges and verify that it can't reach anything else.\n\n## Protect your pipelines with GitLab\n\nOver two weeks, attackers compromised production pipelines at organizations running some of the most widely adopted tools in the software development ecosystem.\n\nThe lesson is clear: Build pipelines need the same degree of centralized, policy-driven protection that we apply to networks and cloud infrastructure.\n\nGitLab Pipeline Execution Policies provide that enforcement layer. They ensure that security checks run on every pipeline, in every project regardless of individual project configurations. Combined with dependency scanning, secret detection, and merge request approval policies, they can block, detect, and contain the class of attacks we saw in March 2026.\n\nThe [Supply Chain Policies](https://gitlab.com/gitlab-org/security-risk-management/security-policies/projects/supply-chain-policies) project provides a working Pipeline Execution Policy that catches the exact class of error behind the major AI company’s leak, with coverage for npm packages, Docker images, and Helm charts. Clone it, deploy it to your group, and ensure that all of your pipelines are ready for the supply chain attacks to come.\n\nTo get started with centralized pipeline policies, sign up for a [free trial of GitLab Ultimate](https://about.gitlab.com/free-trial/devsecops/).\n\n\n*This blog post contains \"forward-looking statements\" within the meaning of Section 27A of the Securities Act of 1933, as amended, and Section 21E of the Securities Exchange Act of 1934. Although we believe that the expectations reflected in these statements are reasonable, they are subject to known and unknown risks, uncertainties, assumptions and other factors that may cause actual results or outcomes to differ materially. Further information on these risks and other factors is included under the caption \"Risk Factors\" in our filings with the SEC. We do not undertake any obligation to update or revise these statements after the date of this blog post, except as required by law.*","Pipeline security lessons from March supply chain incidents","Learn how centralized pipeline policies can detect and block the patterns behind a series of recent attacks.",[23,720,24,721],"product","features",[723],"Grant Hickman","https://res.cloudinary.com/about-gitlab-com/image/upload/v1772630163/akp8ly2mrsfrhsb0liyb.png","2026-04-07",{"featured":12,"template":13,"slug":727},"pipeline-security-lessons-from-march-supply-chain-incidents",{"content":729,"config":740},{"body":730,"category":9,"date":731,"tags":732,"title":735,"description":736,"authors":737,"heroImage":739},"After an incident wraps up, every incident response or security operations center faces the same uncomfortable question: What did we miss, and why? Answering that question well takes real work — someone has to read through the incident timeline, map the attacker's actions to detection opportunities, identify the alerts that should have fired but didn't, and translate those findings into concrete improvements. Done manually, it's time-consuming, inconsistent, and easy to deprioritize when the next incident is already knocking.\n\nAt GitLab, our Signals Engineering team is responsible for building and maintaining the detections that protect the platform and the company. We deal with the same detection gap problem that every security team does so we’ve automated detection gap analysis with [GitLab Duo Agent Platform](https://about.gitlab.com/gitlab-duo-agent-platform/) to improve our assessment of those gaps and how we can close them.\n\nIn this article, you'll learn our strategy, which includes two AI agents you can use in your environment: the built-in Security Analyst Agent and a custom agent we built and named the Detection Engineering Assistant.\n\n\n## The detection gap problem\n\nA detection gap is exactly what it sounds like: an attacker took an action, and your detections didn't catch it. Gap analysis is the process of systematically reviewing security incidents to identify those missed opportunities and determine what new or improved detections would close them.\n\nThe challenge isn't that gap analysis is conceptually hard. It's that it requires careful, methodical reading of incident data and mapping those events to your detection coverage. For a single incident, a skilled analyst can do it well. But across a steady stream of incidents, with multiple engineers contributing, it's difficult to maintain consistency and easy to let the review become shallow.\n\nWe wanted a process that was repeatable, thorough, and embedded directly in the workflow where our security incidents already live: GitLab issues.\n\n## What is GitLab Duo Agent Platform?\n\n[GitLab Duo Agent Platform](https://about.gitlab.com/blog/gitlab-duo-agent-platform-is-generally-available/) is GitLab's framework for building and deploying agentic AI agents that can reason, take actions, and integrate natively with GitLab resources like issues, merge requests, and code. Unlike a simple chat interface, agents in Duo Agent Platform can be given specific roles, domain knowledge, and access to tools, making them effective for domain-specific workflows like security operations.\n\nGitLab Duo Agent Platform gives you two practical paths:\n\n1. **Use a pre-built agent** — GitLab ships several out-of-the-box agents, including a Security Analyst Agent designed for security-related tasks.  \n2. **Build your own agent** — You can create a custom agent in just a few minutes by giving it a name, a description, and a system prompt. The system prompt is where the real power lies.\n\nBoth paths are viable for detection gap analysis. Let's look at each.\n\n## 1. Security Analyst Agent\n\nThe easiest way to get started is with [Security Analyst Agent](https://docs.gitlab.com/user/duo_agent_platform/agents/foundational_agents/security_analyst_agent/), which comes pre-configured with security domain knowledge and can be invoked directly from a GitLab issue.\n\nTo use the agent for gap analysis, we navigate to a closed incident issue and ask the agent to review the incident description, timeline, tasks, and comments to identify where detections were absent or insufficient. The agent reads the issue content — including comments, linked artifacts, and timeline details — and reasons over it to surface potential gaps. It can identify undetected tactics, techniques, and procedures (TTPs) mapped to MITRE ATT&CK and suggest areas where new detection rules could improve coverage.\n\nThis works well for a quick first pass, especially if your incident issues are well-documented. Security Analyst Agent is knowledgeable about general security concepts, common attacker behaviors, and detection principles. For teams just getting started with AI-assisted operations, it provides immediate value with no configuration required.\n\nThat said, the pre-built agent doesn't know your specific environment, including your SIEM, your log sources, your detection stack, or your team's detection engineering standards. For us, that meant the recommendations, while valid in general, sometimes missed the specific context we needed to translate them into actionable detections. That's what led us to build our own agent.\n\n## 2. Building the Detection Engineering Assistant\n\n[Creating a custom agent in GitLab Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/agents/custom/) is surprisingly straightforward. From the Duo Agent Platform interface, you give the agent a name (we called ours the **Detection Engineering Assistant**), a brief description, and a system prompt. That's it. The agent is ready to use.\n\nThe system prompt is the most important part. It's the agent's knowledge base: everything it knows about your team, your environment, your standards, and how it should reason about its work. A thin, vague system prompt produces thin, vague output. A verbose, carefully crafted system prompt produces an agent that behaves like a knowledgeable member of your team.\n\nHere's the approach we took when writing our system prompt for the Detection Engineering Assistant:\n\n### Define the agent's role and scope clearly\n\nWe opened the system prompt by telling the agent exactly what it is and what it's responsible for. Not just \"you are a security analyst.\" We specifically prompted: \"You are a detection engineering assistant for GitLab's Signals Engineering team, responsible for analyzing security incidents and identifying gaps in our detection coverage.\" This framing anchors every response it produces.\n\n### Encode your detection philosophy\n\nWe wrote out what \"a good detection\" means to us: low false positive rates, high signal fidelity, and actionable alerts that provide responders with the context they need. We explained our preference for behavioral detections over IOC-based detections where possible, and described how we think about the tradeoff between coverage breadth and alert fatigue.\n\n### Give it context on your tech stack and log sources\n\nAn agent can only recommend what you can actually build. We told the agent which log sources we ingest, what our SIEM looks like, and what data is and isn't available to us. This means when it recommends a new detection, it does so in terms of what we can actually implement, not hypothetical telemetry we don't have.\n\n### Ground it in MITRE ATT&CK\n\nWe told the agent to organize its gap findings using ATT&CK tactics and techniques. This gives us consistent, structured output that maps directly to how we track coverage internally, and makes it easy to prioritize which gaps to address first.\n\n### Set expectations for output format\n\nWe specified exactly what we want the agent to produce: a structured list of detection gaps, each with the relevant ATT&CK technique, a description of what was missed, the log source or data that could support a detection, and a recommended approach. A consistent output format makes the findings easier to triage and turn into engineering work.\n\n### Example system prompt excerpt\n\n*Note: Our full Detection Engineering Assistant system prompt is 1,870 words and 337 lines. The example below is just a small example of what a full custom system prompt can be.* \n\n\n```text\nYou are the Detection Engineering Assistant for GitLab's Security Operations team. Your role is to analyze closed security incidents and identify gaps in our detection capabilities.\n\nWhen reviewing an incident, you should:\n1. Identify each distinct attacker action or technique described in the incident timeline\n2. For each action, assess whether our existing detections would have caught it\n3. For any action that would not have been detected, document it as a detection gap\n\nFor each gap, provide:\n- MITRE ATT&CK Technique ID and name (e.g., T1078 - Valid Accounts)\n- A plain-language description of what happened and why it wasn't detected\n- The log source or telemetry that could support a detection (e.g., authentication logs, process execution events, network flow data)\n- A recommended detection approach, written in terms our SIEM can implement\n\nOur SIEM ingests [log sources]. Our detection standards prioritize behavioral patterns over static IOCs. Avoid recommending detections that would generate significant false positives without a high-confidence tuning path...\n```\n\nA system prompt this specific produces dramatically more useful output than a generic one. The agent stops giving you general security advice and starts giving you detection engineering recommendations.\n\n## Running gap analysis on incidents\n\nWith the Detection Engineering Assistant configured, the workflow is simple. At the close of an incident, we open the incident issue in GitLab and invoke the assistant. It reads the full issue — the incident summary, timeline, investigative notes, and any linked resources — and returns a structured gap analysis.\n\nA typical output looks like this:\n\n**Gap: Lateral movement via valid credentials not detected**\n\n* **ATT&CK:** T1078.004 — Valid Accounts: Cloud Accounts  \n* **What happened:** An attacker used a valid access token to authenticate to an auxiliary GitLab instance. No alert fired because we lacked authentication baseline detections for that instance.  \n* **Log source:** Authentication logs from `example.gitlab.com`  \n* **Recommended approach:** Create a detection that alerts on first-time authentication from a user account to `example.gitlab.com` within a 90-day rolling window, with suppression for accounts with established access patterns.\n\nThis kind of structured output goes directly into our engineering backlog. We treat the agent's analysis as a high-quality first draft. It gets reviewed by a human engineer who validates the findings, checks whether gaps are already covered by detections we haven't documented, and adds context before it becomes an engineering issue. But the hard work of reading the incident and generating the initial findings is automated.\n\n## What we've learned\n\nA few things stand out from building and iterating on this workflow:\n\n**The system prompt is a living document** — Every time the agent produces an output that misses something obvious or gets the framing wrong, we update the prompt. The agent's quality is a direct reflection of how well we've encoded our domain knowledge into it.\n\n**Incident documentation quality matters** — An agent can only reason over what's written down. Incidents with detailed, structured timelines produce much better gap analysis than sparse or informal ones. Building the gap analysis workflow created an unexpected second benefit: it gave us a concrete reason to improve our incident documentation standards.\n\n**This is a force multiplier, not a replacement** — The Detection Engineering Assistant doesn't replace a skilled detection engineer, but it does amplify one. The engineer still reviews the findings, validates the recommendations, and makes the final call on what goes into the backlog. But the time spent on the initial analysis drops significantly, and the consistency across incidents improves.\n\n## Get started\n\nIf you want to build your own detection gap analysis agent, here's where to start:\n\n1. Review your last three to five closed incidents and note what a good gap analysis would have surfaced for each.  \n2. Use those observations to draft a system prompt that encodes your environment, standards, and preferred output format.  \n3. Create a [custom agent](https://docs.gitlab.com/user/duo_agent_platform/agents/custom/) in GitLab Duo Agent Platform with your prompt.  \n4. Run it against one of your incidents and iterate on the prompt based on the output.\n\nThe detection gap problem isn't going away. But with GitLab Duo Agent Platform, you can make the analysis repeatable, consistent, and embedded directly in the place where your security work already happens. \n\n> Start [a free trial of GitLab Duo Agent Platform](https://about.gitlab.com/gitlab-duo-agent-platform/) today!\n","2026-03-10",[23,733,24,734,721,720,26],"security research","AI/ML","Automating detection gap analysis with GitLab Duo Agent Platform","Learn how GitLab's Signals Engineering team uses our AI platform to automatically surface detection gaps from security incidents — no manual review required.",[738],"Matt Coons","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773147991/op5xyroonltdwqix0x3u.png",{"featured":30,"template":13,"slug":741},"automating-detection-gap-analysis-with-gitlab-duo-agent-platform",{"content":743,"config":751},{"title":744,"description":745,"authors":746,"heroImage":724,"date":748,"body":749,"category":9,"tags":750},"How GitLab built a security control framework from scratch","GitLab's Security Compliance team created a custom control framework to scale across multiple certifications and products — here's why and how you can, too.\n",[747],"Davoud Tu","2026-03-04","GitLab's Security Compliance team discovered that existing security control frameworks lacked the customization to fit the platform's multi-product, cloud-native environment.\n\nSo we built our own.\n\nHere's what we learned and why creating your own custom security control framework might be the right move for your compliance program.\n\n## The journey through frameworks\n\nWhen I joined GitLab's Security Compliance team in November 2022, we were using the [Secure Controls Framework](https://securecontrolsframework.com/) to manage controls across our external certifications and internal compliance needs. But as our requirements grew, we realized we needed something more comprehensive. \n\nWith FedRAMP authorization on our roadmap, we chose to adopt [NIST SP 800-53](https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final) next. NIST SP 800-53 includes more than 1,000 controls, but its comprehensiveness isn’t perfectly suited to GitLab’s environment.\n\nWe didn't need to implement every NIST control, only those applicable to our specific requirements. Our focus was on the quality of controls rather than quantity. Implementing unnecessary controls doesn't improve security; in fact, too many can make an environment less secure as individuals find ways to circumvent overly restrictive or irrelevant controls. \n\nSome controls also lacked the necessary granularity for our needs. For example, NIST’s AC-2 “Account Management” control covers account creation and provisioning, account modification and disabling, account removal and termination, shared and group account management, and account monitoring and reviews.\n\nIn practice, these are _at least_ six distinct controls with different owners, testing procedures, and risks. For attestations like SOC 2, each activity is tested as a separate control because they have different evidence requirements and operational contexts. NIST's all-encompassing AC-2 didn't match how we actually operate controls or how auditors actually assess us, and we needed controls granular enough to reflect our operational environment.  \n\nWe found ourselves constantly customizing, adding, and adapting NIST controls to fit our environment. At some point, we realized we weren't really using NIST SP 800-53 anymore, we were building our own framework on top of it. We decided a custom control framework, one tailored to GitLab’s environment, would best accommodate our multi-product offering and each product’s unique compliance needs.\n\n## Building the GitLab Control Framework\n\nThrough five methodical steps, we built our own common controls framework: the GitLab Control Framework (GCF).\n\n### 1. Analyze what we need\n\nWe reviewed our existing controls and mapped every requirement from external certifications we already maintained, certifications on our roadmap, and our internal compliance program: \n\n**External certifications:**\n\n* SOC 2 Type II  \n* ISO 27001, ISO 27017, ISO 27018, ISO 42001  \n* PCI DSS  \n* TISAX  \n* Cyber Essentials  \n* FedRAMP\n\n**Internal compliance needs:**\n\n* Controls for mission-critical systems that are not in-scope for external certifications   \n* Controls for systems with access to sensitive data\n\nThis gave us the baseline: what controls must exist to meet our compliance obligations.\n\n### 2. Learn from industry frameworks\n\nNext, we compared our requirements against industry-recognized frameworks:\n\n* NIST SP 800-53  \n* NIST Cybersecurity Framework (CSF)  \n* Secure Controls Framework (SCF)  \n* Adobe and Cisco Common Controls Framework (CCF)\n\nHaving adopted frameworks in the past, we wanted to learn from their structure and ensure we weren't missing critical security domains, controls, or best practices.\n\n### 3. Create custom control domains\n\nThrough this analysis, we created 18 custom control domains tailored to GitLab's environment:\n\n\n| Abbreviation | Domain | Scope of controls |\n| :---- | :---- | :---- |\n| AAM | Audit & Accountability Management | Logging, monitoring, and maintaining audit trails of system activities |\n| AIM | Artificial Intelligence Management | Specific to AI system development, deployment, and governance |\n| ASM | Asset Management | Identifying, tracking, and managing organizational assets |\n| BCA | Backups, Contingency, and Availability Management | Business continuity, disaster recovery, and system availability |\n| CHM | Change Management | Managing changes to systems, applications, and infrastructure |\n| CSR | Customer Security Relationship Management | Customer communication, transparency, and security commitments |\n| DPM | Data Protection Management | Protecting data confidentiality, integrity, and privacy |\n| EPM | Endpoint Management | Securing end-user devices and workstations |\n| GPM | Governance & Program Management | Security governance, policies, and program oversight |\n| IAM | Identity, Authentication, and Access Management | User identity, authentication mechanisms, and access control |\n| INC | Incident Management | Detecting, responding to, and recovering from security incidents |\n| ISM | Infrastructure Security Management | Network, server, and foundational infrastructure security |\n| PAS | Product and Application Security Management | Security capabilities built into the GitLab product that are dogfooded to secure GitLab's own development, such as branch protection & code security scanning |\n| PSM | People Security Management | Personnel security, training, and awareness |\n| SDL | Software Development & Acquisition Life Cycle Management | Secure SDLC practices and third-party software acquisition |\n| SRM | Security Risk Management | Risk assessment, treatment, and management |\n| TPR | Third Party Risk Management | Managing security risks from vendors and suppliers |\n| TVM | Threat & Vulnerability Management | Identifying and remediating security vulnerabilities |\n\n\u003Cbr>\u003C/br>\n\n\nEach domain groups related controls into logical families that align with how GitLab's security program is actually organized and operated. This structure provides a methodical approach for adding, updating, or removing controls as our needs evolve.\n\n### 4. Add context and data\n\nWith our domains defined, we needed to address two critical challenges: how to represent controls across multiple products without duplicating the framework, and how to capture meaningful implementation context to actually operate and audit at scale. \n\n#### Scaling across multiple products\n\nGitLab provides multiple product offerings: GitLab.com (multi-tenant SaaS on GCP), GitLab Dedicated (single-tenant SaaS on AWS), and GitLab Dedicated for Government (GitLab’s single-tenant FedRAMP offering on AWS). Each offering has different infrastructure, compliance scopes, and audit requirements. We needed to support product-specific audits without creating entirely separate frameworks.\n\nWe designed a control hierarchy where **Level 1 controls are the framework**, defining what should be implemented at the organizational level. **Level 2 controls are the implementation**, capturing the product-specific details of how each requirement is actually fulfilled.\n\n```mermaid\n%%{init: { \"fontFamily\": \"GitLab Sans\" }}%%\ngraph TD\n    accTitle: Control Hierarchy\n    accDescr: Level 1 requirements cascade to Level 2 implementations.\n    \n    L1[\"Level 1: Framework\u003Cbr/>What must be implemented\"];\n    L2A[\"Level 2: GitLab.com\u003Cbr/>How it's implemented\"];\n    L2B[\"Level 2: Dedicated\u003Cbr/>How it's implemented\"];\n    L2C[\"Level 2: Dedicated for Gov\u003Cbr/>How it's implemented\"];\n    L2D[\"Level 2: Entity\u003Cbr/>(inherited by all)\"];\n    \n    L1-->L2A;\n    L1-->L2B;\n    L1-->L2C;\n    L1-->L2D;\n```\n\n\u003Cbr>\u003C/br>\n\nThis separation allows us to maintain one framework with product-specific implementations, rather than managing duplicate frameworks for each offering. Entity controls apply organization-wide and are inherited by GitLab.com, GitLab Dedicated, and GitLab Dedicated for Government.\n\n#### Adding context to controls\n\nTraditional control frameworks track minimal information: a control ID, description, and owner. The GCF takes a different approach and its superpower is the extensive metadata we track for each control. Beyond just stating the control description or implementation statement, we capture:\n\n* Control owner: Who is accountable for the control and its risk?  \n* Environment: Does this apply organization-wide (Entity, inherited by all product offerings), to GitLab.com, or to Dedicated?  \n* Assets: What specific systems does this control cover?  \n* Frequency: How often is the control performed or tested?  \n* Nature: Is it manual, semi-automated, or fully automated?  \n* Classification: Is this for external certifications or internal risk?  \n* Testing details: How do we assess it? What evidence do we collect?\n\nThis context transforms the GCF from a simple control list into an operationalized control inventory.\n\nWith this structure, we can answer questions like: \n\n* Which controls apply to GitLab.com for our SOC 2 audit vs. GitLab Dedicated? → Filter by environment: GitLab.com  \n* What controls does the Infrastructure team own? → Filter by owner   \n* Which controls can we automate? → Filter by nature: Manual \n\n### 5. Iterate, mature, and scale\n\nThe GCF isn't static and was designed to evolve with our business and compliance landscape.\n\n#### Pursuing new certifications\n\nBecause we've operationalized context into the GCF, we can quickly determine the scope and gaps when pursuing new certifications (ISMAP, IRAP, C5, etc.): \n\n1. Determine scope: Which product has the business need (GitLab.com, GitLab Dedicated, or both)?\n2. Map requirements: Do existing controls already cover the new certification requirements?   \n3. Identify gaps: What new controls need to be created?  \n4. Update mappings: Link existing controls to the new certification requirements.\n\n#### Adapting to new regulations\n\nWhen new regulations emerge or existing requirements change: \n\n* Review existing controls: Does an existing control already cover the new requirement?   \n* Update or create: Either update existing control language or create a new control.  \n* Apply the most stringent: When multiple certifications have similar requirements, we implement the most stringent version — secure once, comply with many.\n* Map across certifications: Link the control to all relevant certification requirements.\n\n#### Managing control lifecycle\n\nThe framework adapts to various changes:\n\n* Requirement changes: When certifications update their requirements, we review impacted controls and update descriptions or mappings.\n* Deprecated controls: If a requirement is removed or a control is no longer needed, we mark it as deprecated and remove it from our monitoring schedule.  \n* New risks identified: Risk assessments may identify gaps requiring new internal controls.\n\n## The power of common controls: One control, multiple requirements\n\nSecuring once and complying with many isn't just a principle, it has tangible benefits across how we prepare for audits, support control owners, and pursue new certifications. Here's what that looks like in practice, both qualitatively and in the numbers. \n\n### Qualitative results\n\nSince implementing the GCF, we've seen significant improvements in how we manage compliance: \n\n#### Integrated audit approach\n\nThe GCF enables us to maintain one framework with controls mapped to multiple certification requirements, instead of managing separate control sets for each audit. One control can satisfy SOC 2, ISO 27001, and PCI DSS requirements simultaneously.\n\n#### Faster audit preparation\n\nThrough the GCF, we maintain one consolidated request list instead of separate lists for each audit. Because we've defined controls with specific context, our request lists say \"Okta user list\" instead of generic \"production user list,\" eliminating ambiguity and interpretation. We're not collecting “N/A” evidence or leaving it up to auditors to interpret what \"production\" means in our environment. Everything is already scoped to our actual systems.\n\n#### Reduced stakeholder burden\n\nThis integration directly reduces burden on our stakeholders. Control owners provide evidence once instead of responding to separate requests from SOC 2, ISO, and PCI auditors. When we collect evidence for access controls, it satisfies SOC 2, ISO 27001, and PCI DSS requirements simultaneously. One control, one test, one piece of evidence with multiple certifications and requirements satisfied.\n\n#### Efficient gap assessments\n\nWhen pursuing new certifications or launching new features, the operationalized context enables more efficient gap analysis. We can determine which controls already exist, what's missing, and what implementation is required. \n\n### Quantifiable results\n\n**Control efficiency:**\n\n* Reduced SOC controls by 58% (200 controls → 84\\) for GitLab.com and 55% (181 → 82) for GitLab Dedicated  \n* One framework now supports 8+ certifications \n\n**Audit efficiency:**\n\n* Consolidated 4 audit request lists into 1, reducing requests by 44% (415 → 231)  \n* 95% evidence acceptance rate before fieldwork for recent PCI audits\n\n**Framework scale:**\n\n* 220+ active controls across 18 custom domains  \n* Mapped to 1,300+ certification requirements  \n* Supports multiple product offerings\n\n## The path forward\n\nThe GCF continues to evolve as we add security and AI controls, pursue new certifications, and refine our approach. \n\n**For security compliance practitioners:** Don't be afraid to build your own framework if industry standards don't fit. The upfront investment pays dividends in scalability, efficiency, and controls that actually make sense for your environment. Sometimes the best framework is the one you design yourself.\n\n> If you found this helpful, check out our complete [GitLab Control Framework documentation](https://handbook.gitlab.com/handbook/security/security-assurance/security-compliance/sec-controls/), where we detail our framework methodology, control domains, and field structures.",[23,24],{"featured":30,"template":13,"slug":752},"how-gitlab-built-a-security-control-framework-from-scratch",{"promotions":754},[755,769,780],{"id":756,"categories":757,"header":759,"text":760,"button":761,"image":766},"ai-modernization",[758],"ai-ml","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":762,"config":763},"Get your AI maturity score",{"href":764,"dataGaName":765,"dataGaLocation":245},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":767},{"src":768},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":770,"categories":771,"header":772,"text":760,"button":773,"image":777},"devops-modernization",[720,38],"Are you just managing tools or shipping innovation?",{"text":774,"config":775},"Get your DevOps maturity score",{"href":776,"dataGaName":765,"dataGaLocation":245},"/assessments/devops-modernization-assessment/",{"config":778},{"src":779},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":781,"categories":782,"header":783,"text":760,"button":784,"image":788},"security-modernization",[23],"Are you trading speed for security?",{"text":785,"config":786},"Get your security maturity score",{"href":787,"dataGaName":765,"dataGaLocation":245},"/assessments/security-modernization-assessment/",{"config":789},{"src":790},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"header":792,"blurb":793,"button":794,"secondaryButton":799},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":795,"config":796},"Get your free trial",{"href":797,"dataGaName":53,"dataGaLocation":798},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":507,"config":800},{"href":57,"dataGaName":58,"dataGaLocation":798},1776438098539]