[{"data":1,"prerenderedAt":759},["ShallowReactive",2],{"/en-us/blog/how-to-detecting-secrets-in-video":3,"navigation-en-us":36,"banner-en-us":463,"footer-en-us":480,"Dennis Appelt":725,"next-steps-en-us":738,"footer-source-/en-us/blog/how-to-detecting-secrets-in-video/":753},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":25,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},"/en-us/blog/how-to-detecting-secrets-in-video","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"How-to: Detecting secrets in video content ","GitLab’s Security team identifies and mitigates security risks in video content by searching for API keys or other sensitive tokens. Here's how we do it (with an assist from AI) and how you can, too.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099421/Blog/Hero%20Images/Blog/Hero%20Images/security-checklist_security-checklist.png_1750099421443.png","https://about.gitlab.com/blog/how-to-detecting-secrets-in-video-content","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How-to: Detecting secrets in video content \",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Dennis Appelt\"}],\n        \"datePublished\": \"2024-02-29\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Dennis Appelt","2024-02-29","Today, we are open-sourcing our [solution for detecting secrets in video content]( https://gitlab.com/gitlab-com/gl-security/security-research/video-scanner/youtube-video-scanner). We use it internally to search videos published on our [GitLab Unfiltered YouTube channel](https://www.youtube.com/@GitLabUnfiltered) for [secrets](https://docs.gitlab.com/ee/security/token_overview.html) such as API keys and other sensitive tokens.\n\nWhile there are existing tools for secret detection, we did not find a tool that quite fit the bill for our use case, so we decided to implement a custom scanner. In this blog post, we'll walk through our general approach, some of the challenges we encountered, and our solution. We'll also discuss how GitLab’s new AI assistant, [GitLab Duo Chat](https://about.gitlab.com/gitlab-duo/), helped with the implementation of the scanner.\n\n## Scanning videos, one frame at a time\n\nOur general approach to secret detection in videos is quite simple: Split the video into frames, run optical character recognition (OCR) over each frame, and match the resulting text against known [secret patterns](https://docs.gitlab.com/ee/security/token_overview.html). If a secret is found, a [security incident](https://handbook.gitlab.com/handbook/security/security-operations/sirt/engaging-security-on-call/#engage-the-security-engineer-on-call) is kicked off to investigate the leak and revoke exposed secrets.\n\nTo implement this approach, we first experimented using [FFmpeg](https://ffmpeg.org//) for splitting the video into frames and feeding the frames to [Tesseract](https://github.com/tesseract-ocr/tesseract), an open-source engine for OCR. This worked quite well and gave us confidence that the general approach was feasible. However, we decided to switch to [Google Cloud Platform's Video Intelligence API](https://cloud.google.com/video-intelligence/docs/) for the frame splitting and OCR for the simple reason of not having to scale and maintain our own implementation.\n\nFFmpeg and Tesseract are good options if third-party APIs cannot be used or if more control over the process is required. For example, if the secrets are only exposed for a brief moment in the video, using FFmpeg allows you to increase the frame sampling rate to analyze more frames per second and increases the chances of catching the frame that exposes the secret. The Video Intelligence API does not provide a comparable level of control. \n\nThe choice between the Video Intelligence API and FFmpeg + Tesseract also depends on the data set that has to be analyzed. The Video Intelligence API works well on our data set, which makes the additional complexity of a custom implementation based on FFmpeg + Tesseract hard to justify. After settling for the Video Intelligence API, it was a natural choice to host the rest of the scanner on GCP as well. The below diagram gives an overview of the design:\n\n![video content scanners - image 1](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099431/Blog/Content%20Images/Blog/Content%20Images/image1_aHR0cHM6_1750099431138.png)\n\nThe scanner is implemented as a collection of cloud functions running on GCP. The cloud function `WebSub API` implements the WebSub [spec](https://www.w3.org/TR/websub/), which is used by YouTube to deliver notifications. Notifications of new videos are published to a PubSub topic, which the cloud function `Video Fetcher` is subscribed to. If a message is received, the video is downloaded and submitted for OCR to the Video Intelligence API. The resulting text extract is checked for secrets by the `Secret Matcher` and alerts are created in case a secret is found.\n\n## Accounting for inaccuracies in OCR\n\nThe described approach sounds simple enough, but as with most things, the devil is in the details. When comparing the video scanner to other secret scanning methods, a notable difference is how the video scanner determines if a given string literal is a secret. Secret detection tools usually determine if the given text contains a secret by matching the text against a list of regular expressions, each defining the format of a secret. If there is a match, a secret is detected.\n\n![video content scanners - image 2](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099431/Blog/Content%20Images/Blog/Content%20Images/image2_aHR0cHM6_1750099431139.png)\n\n\u003Ccenter>\u003Ci>A video frame showing a GitLab access token\u003C/i>\u003C/center>\n\n\u003Cp>\u003C/p>\n\nWhen it comes to video scanning, this approach has limited effectiveness due to the OCR step. In some instances, the recognized text does not quite match the text displayed in the video. For example, the above video frame shows the access token `glpat-HseyLLLE92Ubso2vyVeD` and OCR extracted the text `glpat-HseyLLLE92Ubso2vyVe\\`. The last character of the secret is `D`, but OCR extracted a backslash ( `\\`). This error causes the extracted text to no longer match the format of GitLab personal access tokens; therefore, simply matching the text against a regular expression conforming to the token format would have not detected the leaked access token.\n\nTo account for the inaccuracies that are introduced by the OCR step, the video scanner uses approximate regular expression matching where a string is not required to match a regular expression exactly, but small deviations in the strings are allowed. These deviations are expressed as string edit distance and define how many characters in the string need to be inserted, deleted, or substituted to make the string match a given regular expression. For example, the string edit distance for the previous example is 1 because the erroneously detected backslash has to be substituted with an alphanumeric character or a minus sign to make the string match the GitLab personal access token format.\n\nFinding the right value for the maximum edit distance for a string to still be considered to match a regular expression depends on the data set and requires some experimentation. If the value is too low, secrets might be missed and if the value is too high, strings that are not secrets will be matched. To find the right balance for our use case, we collected a data set of videos leaking secrets and ran them through our scanner pipeline. We measured precision (how many matches are not secrets) and recall (how many matches are relevant/true secrets) for different maximum edit distances. We collected this data for the two most relevant types of secrets: GitLab API access tokens and runner registration tokens. The below diagrams show the results.\n\nFor both types of secrets, the optimal maximum cost is 2, at which point a high precision and recall are achieved (97% precision and 91% recall for access tokens; 100% precision and 86% recall for runner registration tokens). Decreasing the maximum distance results in worse recall, while increasing the maximum cost leads to either no improvement (runner registration token) or adversely affects precision (access tokens).\n\n![video content scanner - image 3](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099431/Blog/Content%20Images/Blog/Content%20Images/image3_aHR0cHM6_1750099431141.png)\n\n\u003Cp>\u003C/p>\n\n![video content scanner - image 4](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099431/Blog/Content%20Images/Blog/Content%20Images/image2_aHR0cHM6_1750099431143.png)\n\n## Implementation made easy using GitLab Duo Chat\n\nAfter some initial experimentation, it was clear to us that approximate regular expression matching can increase the detection accuracy of our video scanner. However, the implementation turned out not to be trivial. While the [algorithms for approximate regular expression matching](https://www.tgries.de/agrep/) have existed since the '90s, there is no widespread library support. [TRE](https://laurikari.net/tre/) is a solid choice but is written in C and thus requires bindings to be called from the scanner’s runtime environment (Node.js).\n\nTo reduce the development effort, we turned to [GitLab Duo Chat](https://about.gitlab.com/gitlab-duo/). After discussing our options with Duo Chat for calling C from Node.js, we settled on [implementing a C++ add-on](https://nodejs.org/docs/latest-v20.x/api/addons.html). We quickly iterated towards a working implementation, which would have undoubtedly taken considerably more time without the help of GitLab Duo Chat. Below is an excerpt from the chat session.\n\n> Me: Are you familiar with the C library TRE? Can you help me write a Node.js addon that is a wrapper for TRE?\n\n> Duo: Here is the template for wrapping the TRE library in a Node.js addon using N-API:\n>\n>![video content scanner - image 5](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099431/Blog/Content%20Images/Blog/Content%20Images/Screenshot_2024-02-28_at_9.51.14_AM_aHR0cHM6_1750099431144.png)\n>\n> The key steps are:\n> - Include TRE header\n> - Get tre regex_t from parameters\n> - Use tre_regcomp() to compile pattern\n> - Execute regexec()\n> - Handle TRE match results\n> - Return results back to JavaScript\n>\n> Let me know if this helps explain how to integrate TRE regex usage in a Node.js add-on!\n\n> Me: Ok, now let's add code for the tasks that you outlined in the template. I want to call this C++ from JavaScript like this:\n>\n>![video content scanners - image 6](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099431/Blog/Content%20Images/Blog/Content%20Images/Screenshot_2024-02-28_at_9.53.05_AM_aHR0cHM6_1750099431145.png)\n>\n> In this example, the first parameter is the regular expression that should be used to create the parameter regex_t *preg, the second parameter is the string that should be matched against the regular expression, and the third parameter is an integer that represents TRE’s max_cost.\n\n>Duo: Here is example C++ code implementing TRE regex usage to match the provided JavaScript code snippet.\n>\n>![video content scanners - image 7](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099431/Blog/Content%20Images/Blog/Content%20Images/Screenshot_2024-02-28_at_9.54.08_AM_aHR0cHM6_1750099431146.png)\n>\n> Let me know if you need any clarification on this implementation!\n\n> Me: Thank you for the code. Can you change the call tre_regexec to tre_regaexec?\n\n> Duo: Here is the code updated to use tre_regaexec instead of tre_regexec:\n>\n>![code updated to use tre_regaexec instead of tre_regexec](https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099431/Blog/Content%20Images/Blog/Content%20Images/Screenshot_2024-02-28_at_9.56.29_AM_aHR0cHM6_1750099431146.png)\n>\n> The key changes are:\n> - Using `tre_regaexec` instead of `tre_regexec`\n> - Initializing a `regamatch_t` instead of `regmatch_t`\n> - Passing a regaparams_t struct to configure match parameters\n> \n> Let me know if you need any clarification or have additional questions!\n\nWhile the code generation capabilities are very impressive and save a lot of time, the generated code needs review and sometimes correction. For example, in our session with Duo Chat it is prompted to call [tre_regaexec](https://laurikari.net/tre/documentation/regaexec/), but the generated code is calling the similarly named, but distinct function [tre_regexec](https://laurikari.net/tre/documentation/regexec/). Typically it is enough to make Duo Chat aware of its mistake and it will correct the code in question. In this respect, working with Duo Chat feels like pair programming rather than fully outsourcing a programming task.\n\nThe full add-on code is available [here](https://gitlab.com/gitlab-com/gl-security/security-research/video-scanner/tre-node-bindings/).\n\n## Try our open-source implementation\n\nWe are [making the implementation of the scanner open source](https://gitlab.com/gitlab-com/gl-security/security-research/video-scanner/youtube-video-scanner) under the MIT license. We hope this solution can help you with detecting secrets in your own video content. Please [share your feedback in an issue](https://gitlab.com/gitlab-com/gl-security/security-research/video-scanner/youtube-video-scanner/-/issues/new).","security",[23,24,21],"tutorial","open source",{"slug":26,"featured":27,"template":28},"how-to-detecting-secrets-in-video",true,"BlogPost","content:en-us:blog:how-to-detecting-secrets-in-video.yml","yaml","How To Detecting Secrets In Video","content","en-us/blog/how-to-detecting-secrets-in-video.yml","en-us/blog/how-to-detecting-secrets-in-video","yml",{"_path":37,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":39,"_id":459,"_type":30,"title":460,"_source":32,"_file":461,"_stem":462,"_extension":35},"/shared/en-us/main-navigation","en-us",{"logo":40,"freeTrial":45,"sales":50,"login":55,"items":60,"search":390,"minimal":421,"duo":440,"pricingDeployment":449},{"config":41},{"href":42,"dataGaName":43,"dataGaLocation":44},"/","gitlab logo","header",{"text":46,"config":47},"Get free trial",{"href":48,"dataGaName":49,"dataGaLocation":44},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":51,"config":52},"Talk to sales",{"href":53,"dataGaName":54,"dataGaLocation":44},"/sales/","sales",{"text":56,"config":57},"Sign in",{"href":58,"dataGaName":59,"dataGaLocation":44},"https://gitlab.com/users/sign_in/","sign in",[61,105,201,206,311,371],{"text":62,"config":63,"cards":65,"footer":88},"Platform",{"dataNavLevelOne":64},"platform",[66,72,80],{"title":62,"description":67,"link":68},"The most comprehensive AI-powered DevSecOps Platform",{"text":69,"config":70},"Explore our Platform",{"href":71,"dataGaName":64,"dataGaLocation":44},"/platform/",{"title":73,"description":74,"link":75},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":76,"config":77},"Meet GitLab Duo",{"href":78,"dataGaName":79,"dataGaLocation":44},"/gitlab-duo/","gitlab duo ai",{"title":81,"description":82,"link":83},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":84,"config":85},"Learn more",{"href":86,"dataGaName":87,"dataGaLocation":44},"/why-gitlab/","why gitlab",{"title":89,"items":90},"Get started with",[91,96,101],{"text":92,"config":93},"Platform Engineering",{"href":94,"dataGaName":95,"dataGaLocation":44},"/solutions/platform-engineering/","platform engineering",{"text":97,"config":98},"Developer Experience",{"href":99,"dataGaName":100,"dataGaLocation":44},"/developer-experience/","Developer experience",{"text":102,"config":103},"MLOps",{"href":104,"dataGaName":102,"dataGaLocation":44},"/topics/devops/the-role-of-ai-in-devops/",{"text":106,"left":27,"config":107,"link":109,"lists":113,"footer":183},"Product",{"dataNavLevelOne":108},"solutions",{"text":110,"config":111},"View all Solutions",{"href":112,"dataGaName":108,"dataGaLocation":44},"/solutions/",[114,139,162],{"title":115,"description":116,"link":117,"items":122},"Automation","CI/CD and automation to accelerate deployment",{"config":118},{"icon":119,"href":120,"dataGaName":121,"dataGaLocation":44},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[123,127,131,135],{"text":124,"config":125},"CI/CD",{"href":126,"dataGaLocation":44,"dataGaName":124},"/solutions/continuous-integration/",{"text":128,"config":129},"AI-Assisted Development",{"href":78,"dataGaLocation":44,"dataGaName":130},"AI assisted development",{"text":132,"config":133},"Source Code Management",{"href":134,"dataGaLocation":44,"dataGaName":132},"/solutions/source-code-management/",{"text":136,"config":137},"Automated Software Delivery",{"href":120,"dataGaLocation":44,"dataGaName":138},"Automated software delivery",{"title":140,"description":141,"link":142,"items":147},"Security","Deliver code faster without compromising security",{"config":143},{"href":144,"dataGaName":145,"dataGaLocation":44,"icon":146},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[148,152,157],{"text":149,"config":150},"Application Security Testing",{"href":144,"dataGaName":151,"dataGaLocation":44},"Application security testing",{"text":153,"config":154},"Software Supply Chain Security",{"href":155,"dataGaLocation":44,"dataGaName":156},"/solutions/supply-chain/","Software supply chain security",{"text":158,"config":159},"Software Compliance",{"href":160,"dataGaName":161,"dataGaLocation":44},"/solutions/software-compliance/","software compliance",{"title":163,"link":164,"items":169},"Measurement",{"config":165},{"icon":166,"href":167,"dataGaName":168,"dataGaLocation":44},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[170,174,178],{"text":171,"config":172},"Visibility & Measurement",{"href":167,"dataGaLocation":44,"dataGaName":173},"Visibility and Measurement",{"text":175,"config":176},"Value Stream Management",{"href":177,"dataGaLocation":44,"dataGaName":175},"/solutions/value-stream-management/",{"text":179,"config":180},"Analytics & Insights",{"href":181,"dataGaLocation":44,"dataGaName":182},"/solutions/analytics-and-insights/","Analytics and insights",{"title":184,"items":185},"GitLab for",[186,191,196],{"text":187,"config":188},"Enterprise",{"href":189,"dataGaLocation":44,"dataGaName":190},"/enterprise/","enterprise",{"text":192,"config":193},"Small Business",{"href":194,"dataGaLocation":44,"dataGaName":195},"/small-business/","small business",{"text":197,"config":198},"Public Sector",{"href":199,"dataGaLocation":44,"dataGaName":200},"/solutions/public-sector/","public sector",{"text":202,"config":203},"Pricing",{"href":204,"dataGaName":205,"dataGaLocation":44,"dataNavLevelOne":205},"/pricing/","pricing",{"text":207,"config":208,"link":210,"lists":214,"feature":298},"Resources",{"dataNavLevelOne":209},"resources",{"text":211,"config":212},"View all resources",{"href":213,"dataGaName":209,"dataGaLocation":44},"/resources/",[215,248,270],{"title":216,"items":217},"Getting started",[218,223,228,233,238,243],{"text":219,"config":220},"Install",{"href":221,"dataGaName":222,"dataGaLocation":44},"/install/","install",{"text":224,"config":225},"Quick start guides",{"href":226,"dataGaName":227,"dataGaLocation":44},"/get-started/","quick setup checklists",{"text":229,"config":230},"Learn",{"href":231,"dataGaLocation":44,"dataGaName":232},"https://university.gitlab.com/","learn",{"text":234,"config":235},"Product documentation",{"href":236,"dataGaName":237,"dataGaLocation":44},"https://docs.gitlab.com/","product documentation",{"text":239,"config":240},"Best practice videos",{"href":241,"dataGaName":242,"dataGaLocation":44},"/getting-started-videos/","best practice videos",{"text":244,"config":245},"Integrations",{"href":246,"dataGaName":247,"dataGaLocation":44},"/integrations/","integrations",{"title":249,"items":250},"Discover",[251,256,260,265],{"text":252,"config":253},"Customer success stories",{"href":254,"dataGaName":255,"dataGaLocation":44},"/customers/","customer success stories",{"text":257,"config":258},"Blog",{"href":259,"dataGaName":5,"dataGaLocation":44},"/blog/",{"text":261,"config":262},"Remote",{"href":263,"dataGaName":264,"dataGaLocation":44},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":266,"config":267},"TeamOps",{"href":268,"dataGaName":269,"dataGaLocation":44},"/teamops/","teamops",{"title":271,"items":272},"Connect",[273,278,283,288,293],{"text":274,"config":275},"GitLab Services",{"href":276,"dataGaName":277,"dataGaLocation":44},"/services/","services",{"text":279,"config":280},"Community",{"href":281,"dataGaName":282,"dataGaLocation":44},"/community/","community",{"text":284,"config":285},"Forum",{"href":286,"dataGaName":287,"dataGaLocation":44},"https://forum.gitlab.com/","forum",{"text":289,"config":290},"Events",{"href":291,"dataGaName":292,"dataGaLocation":44},"/events/","events",{"text":294,"config":295},"Partners",{"href":296,"dataGaName":297,"dataGaLocation":44},"/partners/","partners",{"backgroundColor":299,"textColor":300,"text":301,"image":302,"link":306},"#2f2a6b","#fff","Insights for the future of software development",{"altText":303,"config":304},"the source promo card",{"src":305},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":307,"config":308},"Read the latest",{"href":309,"dataGaName":310,"dataGaLocation":44},"/the-source/","the source",{"text":312,"config":313,"lists":315},"Company",{"dataNavLevelOne":314},"company",[316],{"items":317},[318,323,329,331,336,341,346,351,356,361,366],{"text":319,"config":320},"About",{"href":321,"dataGaName":322,"dataGaLocation":44},"/company/","about",{"text":324,"config":325,"footerGa":328},"Jobs",{"href":326,"dataGaName":327,"dataGaLocation":44},"/jobs/","jobs",{"dataGaName":327},{"text":289,"config":330},{"href":291,"dataGaName":292,"dataGaLocation":44},{"text":332,"config":333},"Leadership",{"href":334,"dataGaName":335,"dataGaLocation":44},"/company/team/e-group/","leadership",{"text":337,"config":338},"Team",{"href":339,"dataGaName":340,"dataGaLocation":44},"/company/team/","team",{"text":342,"config":343},"Handbook",{"href":344,"dataGaName":345,"dataGaLocation":44},"https://handbook.gitlab.com/","handbook",{"text":347,"config":348},"Investor relations",{"href":349,"dataGaName":350,"dataGaLocation":44},"https://ir.gitlab.com/","investor relations",{"text":352,"config":353},"Trust Center",{"href":354,"dataGaName":355,"dataGaLocation":44},"/security/","trust center",{"text":357,"config":358},"AI Transparency Center",{"href":359,"dataGaName":360,"dataGaLocation":44},"/ai-transparency-center/","ai transparency center",{"text":362,"config":363},"Newsletter",{"href":364,"dataGaName":365,"dataGaLocation":44},"/company/contact/","newsletter",{"text":367,"config":368},"Press",{"href":369,"dataGaName":370,"dataGaLocation":44},"/press/","press",{"text":372,"config":373,"lists":374},"Contact us",{"dataNavLevelOne":314},[375],{"items":376},[377,380,385],{"text":51,"config":378},{"href":53,"dataGaName":379,"dataGaLocation":44},"talk to sales",{"text":381,"config":382},"Support portal",{"href":383,"dataGaName":384,"dataGaLocation":44},"https://support.gitlab.com","support portal",{"text":386,"config":387},"Customer portal",{"href":388,"dataGaName":389,"dataGaLocation":44},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":391,"login":392,"suggestions":399},"Close",{"text":393,"link":394},"To search repositories and projects, login to",{"text":395,"config":396},"gitlab.com",{"href":58,"dataGaName":397,"dataGaLocation":398},"search login","search",{"text":400,"default":401},"Suggestions",[402,404,408,410,414,418],{"text":73,"config":403},{"href":78,"dataGaName":73,"dataGaLocation":398},{"text":405,"config":406},"Code Suggestions (AI)",{"href":407,"dataGaName":405,"dataGaLocation":398},"/solutions/code-suggestions/",{"text":124,"config":409},{"href":126,"dataGaName":124,"dataGaLocation":398},{"text":411,"config":412},"GitLab on AWS",{"href":413,"dataGaName":411,"dataGaLocation":398},"/partners/technology-partners/aws/",{"text":415,"config":416},"GitLab on Google Cloud",{"href":417,"dataGaName":415,"dataGaLocation":398},"/partners/technology-partners/google-cloud-platform/",{"text":419,"config":420},"Why GitLab?",{"href":86,"dataGaName":419,"dataGaLocation":398},{"freeTrial":422,"mobileIcon":427,"desktopIcon":432,"secondaryButton":435},{"text":423,"config":424},"Start free trial",{"href":425,"dataGaName":49,"dataGaLocation":426},"https://gitlab.com/-/trials/new/","nav",{"altText":428,"config":429},"Gitlab Icon",{"src":430,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":428,"config":433},{"src":434,"dataGaName":431,"dataGaLocation":426},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":436,"config":437},"Get Started",{"href":438,"dataGaName":439,"dataGaLocation":426},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":441,"mobileIcon":445,"desktopIcon":447},{"text":442,"config":443},"Learn more about GitLab Duo",{"href":78,"dataGaName":444,"dataGaLocation":426},"gitlab duo",{"altText":428,"config":446},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":448},{"src":434,"dataGaName":431,"dataGaLocation":426},{"freeTrial":450,"mobileIcon":455,"desktopIcon":457},{"text":451,"config":452},"Back to pricing",{"href":204,"dataGaName":453,"dataGaLocation":426,"icon":454},"back to pricing","GoBack",{"altText":428,"config":456},{"src":430,"dataGaName":431,"dataGaLocation":426},{"altText":428,"config":458},{"src":434,"dataGaName":431,"dataGaLocation":426},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":464,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"title":465,"button":466,"image":471,"config":475,"_id":477,"_type":30,"_source":32,"_file":478,"_stem":479,"_extension":35},"/shared/en-us/banner","is now in public beta!",{"text":467,"config":468},"Try the Beta",{"href":469,"dataGaName":470,"dataGaLocation":44},"/gitlab-duo/agent-platform/","duo banner",{"altText":472,"config":473},"GitLab Duo Agent Platform",{"src":474},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":476},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":481,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":482,"_id":721,"_type":30,"title":722,"_source":32,"_file":723,"_stem":724,"_extension":35},"/shared/en-us/main-footer",{"text":483,"source":484,"edit":490,"contribute":495,"config":500,"items":505,"minimal":713},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":485,"config":486},"View page source",{"href":487,"dataGaName":488,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":491,"config":492},"Edit this page",{"href":493,"dataGaName":494,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":496,"config":497},"Please contribute",{"href":498,"dataGaName":499,"dataGaLocation":489},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":501,"facebook":502,"youtube":503,"linkedin":504},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[506,553,606,650,679],{"title":202,"links":507,"subMenu":522},[508,512,517],{"text":509,"config":510},"View plans",{"href":204,"dataGaName":511,"dataGaLocation":489},"view plans",{"text":513,"config":514},"Why Premium?",{"href":515,"dataGaName":516,"dataGaLocation":489},"/pricing/premium/","why premium",{"text":518,"config":519},"Why Ultimate?",{"href":520,"dataGaName":521,"dataGaLocation":489},"/pricing/ultimate/","why ultimate",[523],{"title":524,"links":525},"Contact Us",[526,529,531,533,538,543,548],{"text":527,"config":528},"Contact sales",{"href":53,"dataGaName":54,"dataGaLocation":489},{"text":381,"config":530},{"href":383,"dataGaName":384,"dataGaLocation":489},{"text":386,"config":532},{"href":388,"dataGaName":389,"dataGaLocation":489},{"text":534,"config":535},"Status",{"href":536,"dataGaName":537,"dataGaLocation":489},"https://status.gitlab.com/","status",{"text":539,"config":540},"Terms of use",{"href":541,"dataGaName":542,"dataGaLocation":489},"/terms/","terms of use",{"text":544,"config":545},"Privacy statement",{"href":546,"dataGaName":547,"dataGaLocation":489},"/privacy/","privacy statement",{"text":549,"config":550},"Cookie preferences",{"dataGaName":551,"dataGaLocation":489,"id":552,"isOneTrustButton":27},"cookie preferences","ot-sdk-btn",{"title":106,"links":554,"subMenu":562},[555,559],{"text":556,"config":557},"DevSecOps platform",{"href":71,"dataGaName":558,"dataGaLocation":489},"devsecops platform",{"text":128,"config":560},{"href":78,"dataGaName":561,"dataGaLocation":489},"ai-assisted development",[563],{"title":564,"links":565},"Topics",[566,571,576,581,586,591,596,601],{"text":567,"config":568},"CICD",{"href":569,"dataGaName":570,"dataGaLocation":489},"/topics/ci-cd/","cicd",{"text":572,"config":573},"GitOps",{"href":574,"dataGaName":575,"dataGaLocation":489},"/topics/gitops/","gitops",{"text":577,"config":578},"DevOps",{"href":579,"dataGaName":580,"dataGaLocation":489},"/topics/devops/","devops",{"text":582,"config":583},"Version Control",{"href":584,"dataGaName":585,"dataGaLocation":489},"/topics/version-control/","version control",{"text":587,"config":588},"DevSecOps",{"href":589,"dataGaName":590,"dataGaLocation":489},"/topics/devsecops/","devsecops",{"text":592,"config":593},"Cloud Native",{"href":594,"dataGaName":595,"dataGaLocation":489},"/topics/cloud-native/","cloud native",{"text":597,"config":598},"AI for Coding",{"href":599,"dataGaName":600,"dataGaLocation":489},"/topics/devops/ai-for-coding/","ai for coding",{"text":602,"config":603},"Agentic AI",{"href":604,"dataGaName":605,"dataGaLocation":489},"/topics/agentic-ai/","agentic ai",{"title":607,"links":608},"Solutions",[609,611,613,618,622,625,629,632,634,637,640,645],{"text":149,"config":610},{"href":144,"dataGaName":149,"dataGaLocation":489},{"text":138,"config":612},{"href":120,"dataGaName":121,"dataGaLocation":489},{"text":614,"config":615},"Agile development",{"href":616,"dataGaName":617,"dataGaLocation":489},"/solutions/agile-delivery/","agile delivery",{"text":619,"config":620},"SCM",{"href":134,"dataGaName":621,"dataGaLocation":489},"source code management",{"text":567,"config":623},{"href":126,"dataGaName":624,"dataGaLocation":489},"continuous integration & delivery",{"text":626,"config":627},"Value stream management",{"href":177,"dataGaName":628,"dataGaLocation":489},"value stream management",{"text":572,"config":630},{"href":631,"dataGaName":575,"dataGaLocation":489},"/solutions/gitops/",{"text":187,"config":633},{"href":189,"dataGaName":190,"dataGaLocation":489},{"text":635,"config":636},"Small business",{"href":194,"dataGaName":195,"dataGaLocation":489},{"text":638,"config":639},"Public sector",{"href":199,"dataGaName":200,"dataGaLocation":489},{"text":641,"config":642},"Education",{"href":643,"dataGaName":644,"dataGaLocation":489},"/solutions/education/","education",{"text":646,"config":647},"Financial services",{"href":648,"dataGaName":649,"dataGaLocation":489},"/solutions/finance/","financial services",{"title":207,"links":651},[652,654,656,658,661,663,665,667,669,671,673,675,677],{"text":219,"config":653},{"href":221,"dataGaName":222,"dataGaLocation":489},{"text":224,"config":655},{"href":226,"dataGaName":227,"dataGaLocation":489},{"text":229,"config":657},{"href":231,"dataGaName":232,"dataGaLocation":489},{"text":234,"config":659},{"href":236,"dataGaName":660,"dataGaLocation":489},"docs",{"text":257,"config":662},{"href":259,"dataGaName":5,"dataGaLocation":489},{"text":252,"config":664},{"href":254,"dataGaName":255,"dataGaLocation":489},{"text":261,"config":666},{"href":263,"dataGaName":264,"dataGaLocation":489},{"text":274,"config":668},{"href":276,"dataGaName":277,"dataGaLocation":489},{"text":266,"config":670},{"href":268,"dataGaName":269,"dataGaLocation":489},{"text":279,"config":672},{"href":281,"dataGaName":282,"dataGaLocation":489},{"text":284,"config":674},{"href":286,"dataGaName":287,"dataGaLocation":489},{"text":289,"config":676},{"href":291,"dataGaName":292,"dataGaLocation":489},{"text":294,"config":678},{"href":296,"dataGaName":297,"dataGaLocation":489},{"title":312,"links":680},[681,683,685,687,689,691,693,697,702,704,706,708],{"text":319,"config":682},{"href":321,"dataGaName":314,"dataGaLocation":489},{"text":324,"config":684},{"href":326,"dataGaName":327,"dataGaLocation":489},{"text":332,"config":686},{"href":334,"dataGaName":335,"dataGaLocation":489},{"text":337,"config":688},{"href":339,"dataGaName":340,"dataGaLocation":489},{"text":342,"config":690},{"href":344,"dataGaName":345,"dataGaLocation":489},{"text":347,"config":692},{"href":349,"dataGaName":350,"dataGaLocation":489},{"text":694,"config":695},"Sustainability",{"href":696,"dataGaName":694,"dataGaLocation":489},"/sustainability/",{"text":698,"config":699},"Diversity, inclusion and belonging (DIB)",{"href":700,"dataGaName":701,"dataGaLocation":489},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":352,"config":703},{"href":354,"dataGaName":355,"dataGaLocation":489},{"text":362,"config":705},{"href":364,"dataGaName":365,"dataGaLocation":489},{"text":367,"config":707},{"href":369,"dataGaName":370,"dataGaLocation":489},{"text":709,"config":710},"Modern Slavery Transparency Statement",{"href":711,"dataGaName":712,"dataGaLocation":489},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":714},[715,717,719],{"text":539,"config":716},{"href":541,"dataGaName":542,"dataGaLocation":489},{"text":544,"config":718},{"href":546,"dataGaName":547,"dataGaLocation":489},{"text":549,"config":720},{"dataGaName":551,"dataGaLocation":489,"id":552,"isOneTrustButton":27},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[726],{"_path":727,"_dir":728,"_draft":6,"_partial":6,"_locale":7,"content":729,"config":733,"_id":735,"_type":30,"title":18,"_source":32,"_file":736,"_stem":737,"_extension":35},"/en-us/blog/authors/dennis-appelt","authors",{"name":18,"config":730},{"headshot":731,"ctfId":732},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749672032/Blog/Author%20Headshots/dappelt-headshot.jpg","dappelt",{"template":734},"BlogAuthor","content:en-us:blog:authors:dennis-appelt.yml","en-us/blog/authors/dennis-appelt.yml","en-us/blog/authors/dennis-appelt",{"_path":739,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"header":740,"eyebrow":741,"blurb":742,"button":743,"secondaryButton":747,"_id":749,"_type":30,"title":750,"_source":32,"_file":751,"_stem":752,"_extension":35},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":46,"config":744},{"href":745,"dataGaName":49,"dataGaLocation":746},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":51,"config":748},{"href":53,"dataGaName":54,"dataGaLocation":746},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":754,"content":755,"config":758,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},{"title":9,"description":10,"authors":756,"heroImage":11,"date":19,"body":20,"category":21,"tags":757},[18],[23,24,21],{"slug":26,"featured":27,"template":28},1761814427036]