[{"data":1,"prerenderedAt":1172},["ShallowReactive",2],{"/blog/how-to-detecting-secrets-in-video":3,"navigation-en-us":386,"banner-en-us":814,"footer-en-us":824,"blog-post-authors-en-us-Dennis Appelt":1069,"blog-related-posts-en-us-how-to-detecting-secrets-in-video":1084,"blog-promotions-en-us":1109,"next-steps-en-us":1162},{"id":4,"title":5,"authors":6,"body":8,"category":365,"date":366,"description":367,"extension":368,"externalUrl":369,"faq":369,"featured":370,"heroImage":371,"meta":372,"navigation":370,"path":373,"seo":374,"slug":379,"stem":380,"tags":381,"template":384,"updatedDate":369,"__hash__":385},"blogPosts/en-us/blog/how-to-detecting-secrets-in-video.md","How-to: Detecting secrets in video content ",[7],"Dennis Appelt",{"type":9,"value":10,"toc":357},"minimark",[11,28,37,42,51,72,75,78,85,107,111,114,120,127,146,149,152,155,161,167,171,186,199,205,244,258,272,277,318,331,340,344],[12,13,14,15,21,22,27],"p",{},"Today, we are open-sourcing our ",[16,17,20],"a",{"href":18,"rel":19},"https://gitlab.com/gitlab-com/gl-security/security-research/video-scanner/youtube-video-scanner",[],"solution for detecting secrets in video content",". We use it internally to search videos published on our ",[16,23,26],{"href":24,"rel":25},"https://www.youtube.com/@GitLabUnfiltered",[],"GitLab Unfiltered YouTube channel"," for secrets such as API keys and other sensitive tokens.",[12,29,30,31,36],{},"While 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, ",[16,32,35],{"href":33,"rel":34},"https://about.gitlab.com/gitlab-duo-agent-platform/",[],"GitLab Duo Chat",", helped with the implementation of the scanner.",[38,39,41],"h2",{"id":40},"scanning-videos-one-frame-at-a-time","Scanning videos, one frame at a time",[12,43,44,45,50],{},"Our 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. If a secret is found, a ",[16,46,49],{"href":47,"rel":48},"https://handbook.gitlab.com/handbook/security/security-operations/sirt/engaging-security-on-call/#engage-the-security-engineer-on-call",[],"security incident"," is kicked off to investigate the leak and revoke exposed secrets.",[12,52,53,54,59,60,65,66,71],{},"To implement this approach, we first experimented using ",[16,55,58],{"href":56,"rel":57},"https://ffmpeg.org//",[],"FFmpeg"," for splitting the video into frames and feeding the frames to ",[16,61,64],{"href":62,"rel":63},"https://github.com/tesseract-ocr/tesseract",[],"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 ",[16,67,70],{"href":68,"rel":69},"https://cloud.google.com/video-intelligence/docs/",[],"Google Cloud Platform's Video Intelligence API"," for the frame splitting and OCR for the simple reason of not having to scale and maintain our own implementation.",[12,73,74],{},"FFmpeg 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.",[12,76,77],{},"The 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:",[12,79,80],{},[81,82],"img",{"alt":83,"src":84},"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",[12,86,87,88,92,93,98,99,102,103,106],{},"The scanner is implemented as a collection of cloud functions running on GCP. The cloud function ",[89,90,91],"code",{},"WebSub API"," implements the WebSub ",[16,94,97],{"href":95,"rel":96},"https://www.w3.org/TR/websub/",[],"spec",", which is used by YouTube to deliver notifications. Notifications of new videos are published to a PubSub topic, which the cloud function ",[89,100,101],{},"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 ",[89,104,105],{},"Secret Matcher"," and alerts are created in case a secret is found.",[38,108,110],{"id":109},"accounting-for-inaccuracies-in-ocr","Accounting for inaccuracies in OCR",[12,112,113],{},"The 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.",[12,115,116],{},[81,117],{"alt":118,"src":119},"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",[121,122,123],"center",{},[124,125,126],"em",{},"A video frame showing a GitLab access token",[12,128,129,130,133,134,137,138,141,142,145],{},"When 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 ",[89,131,132],{},"glpat-HseyLLLE92Ubso2vyVeD"," and OCR extracted the text ",[89,135,136],{},"glpat-HseyLLLE92Ubso2vyVe\\",". The last character of the secret is ",[89,139,140],{},"D",", but OCR extracted a backslash ( ",[89,143,144],{},"\\","). 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.",[12,147,148],{},"To 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.",[12,150,151],{},"Finding 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.",[12,153,154],{},"For 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).",[12,156,157],{},[81,158],{"alt":159,"src":160},"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",[12,162,163],{},[81,164],{"alt":165,"src":166},"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",[38,168,170],{"id":169},"implementation-made-easy-using-gitlab-duo-chat","Implementation made easy using GitLab Duo Chat",[12,172,173,174,179,180,185],{},"After 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 ",[16,175,178],{"href":176,"rel":177},"https://www.tgries.de/agrep/",[],"algorithms for approximate regular expression matching"," have existed since the '90s, there is no widespread library support. ",[16,181,184],{"href":182,"rel":183},"https://laurikari.net/tre/",[],"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).",[12,187,188,189,192,193,198],{},"To reduce the development effort, we turned to ",[16,190,35],{"href":33,"rel":191},[],". After discussing our options with Duo Chat for calling C from Node.js, we settled on ",[16,194,197],{"href":195,"rel":196},"https://nodejs.org/docs/latest-v20.x/api/addons.html",[],"implementing a C++ add-on",". 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.",[200,201,202],"blockquote",{},[12,203,204],{},"Me: Are you familiar with the C library TRE? Can you help me write a Node.js addon that is a wrapper for TRE?",[200,206,207,210,216,219,241],{},[12,208,209],{},"Duo: Here is the template for wrapping the TRE library in a Node.js addon using N-API:",[12,211,212],{},[81,213],{"alt":214,"src":215},"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",[12,217,218],{},"The key steps are:",[220,221,222,226,229,232,235,238],"ul",{},[223,224,225],"li",{},"Include TRE header",[223,227,228],{},"Get tre regex_t from parameters",[223,230,231],{},"Use tre_regcomp() to compile pattern",[223,233,234],{},"Execute regexec()",[223,236,237],{},"Handle TRE match results",[223,239,240],{},"Return results back to JavaScript",[12,242,243],{},"Let me know if this helps explain how to integrate TRE regex usage in a Node.js add-on!",[200,245,246,249,255],{},[12,247,248],{},"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:",[12,250,251],{},[81,252],{"alt":253,"src":254},"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",[12,256,257],{},"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.",[200,259,260,263,269],{},[12,261,262],{},"Duo: Here is example C++ code implementing TRE regex usage to match the provided JavaScript code snippet.",[12,264,265],{},[81,266],{"alt":267,"src":268},"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",[12,270,271],{},"Let me know if you need any clarification on this implementation!",[200,273,274],{},[12,275,276],{},"Me: Thank you for the code. Can you change the call tre_regexec to tre_regaexec?",[200,278,279,282,288,291,315],{},[12,280,281],{},"Duo: Here is the code updated to use tre_regaexec instead of tre_regexec:",[12,283,284],{},[81,285],{"alt":286,"src":287},"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",[12,289,290],{},"The key changes are:",[220,292,293,303,312],{},[223,294,295,296,299,300],{},"Using ",[89,297,298],{},"tre_regaexec"," instead of ",[89,301,302],{},"tre_regexec",[223,304,305,306,299,309],{},"Initializing a ",[89,307,308],{},"regamatch_t",[89,310,311],{},"regmatch_t",[223,313,314],{},"Passing a regaparams_t struct to configure match parameters",[12,316,317],{},"Let me know if you need any clarification or have additional questions!",[12,319,320,321,325,326,330],{},"While 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 ",[16,322,298],{"href":323,"rel":324},"https://laurikari.net/tre/documentation/regaexec/",[],", but the generated code is calling the similarly named, but distinct function ",[16,327,302],{"href":328,"rel":329},"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.",[12,332,333,334,339],{},"The full add-on code is available ",[16,335,338],{"href":336,"rel":337},"https://gitlab.com/gitlab-com/gl-security/security-research/video-scanner/tre-node-bindings/",[],"here",".",[38,341,343],{"id":342},"try-our-open-source-implementation","Try our open-source implementation",[12,345,346,347,351,352,339],{},"We are ",[16,348,350],{"href":18,"rel":349},[],"making the implementation of the scanner open source"," under the MIT license. We hope this solution can help you with detecting secrets in your own video content. Please ",[16,353,356],{"href":354,"rel":355},"https://gitlab.com/gitlab-com/gl-security/security-research/video-scanner/youtube-video-scanner/-/issues/new",[],"share your feedback in an issue",{"title":358,"searchDepth":359,"depth":359,"links":360},"",2,[361,362,363,364],{"id":40,"depth":359,"text":41},{"id":109,"depth":359,"text":110},{"id":169,"depth":359,"text":170},{"id":342,"depth":359,"text":343},"security","2024-02-29","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.","md",null,true,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1750099421/Blog/Hero%20Images/Blog/Hero%20Images/security-checklist_security-checklist.png_1750099421443.png",{},"/en-us/blog/how-to-detecting-secrets-in-video",{"title":5,"description":367,"ogTitle":5,"ogDescription":367,"noIndex":375,"ogImage":371,"ogUrl":376,"ogSiteName":377,"ogType":378,"canonicalUrls":376},false,"https://about.gitlab.com/blog/how-to-detecting-secrets-in-video-content","https://about.gitlab.com","article","how-to-detecting-secrets-in-video","en-us/blog/how-to-detecting-secrets-in-video",[382,383,365],"tutorial","open source","BlogPost","kvM_m18PGCVvI4JblgEyl3KbChqbw9Lwr1ORB1dudKQ",{"logo":387,"freeTrial":392,"sales":397,"login":402,"items":407,"search":734,"minimal":765,"duo":784,"switchNav":793,"pricingDeployment":804},{"config":388},{"href":389,"dataGaName":390,"dataGaLocation":391},"/","gitlab logo","header",{"text":393,"config":394},"Get free trial",{"href":395,"dataGaName":396,"dataGaLocation":391},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":398,"config":399},"Request a demo",{"href":400,"dataGaName":401,"dataGaLocation":391},"/sales/?contact-topic=request-demo","sales",{"text":403,"config":404},"Sign in",{"href":405,"dataGaName":406,"dataGaLocation":391},"https://gitlab.com/users/sign_in/","sign in",[408,437,537,542,656,712],{"text":409,"config":410,"menu":412},"Platform",{"dataNavLevelOne":411},"platform",{"type":413,"columns":414},"cards",[415,421,429],{"title":409,"description":416,"link":417},"The intelligent orchestration platform for DevSecOps",{"text":418,"config":419},"Explore our Platform",{"href":420,"dataGaName":411,"dataGaLocation":391},"/platform/",{"title":422,"description":423,"link":424},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":425,"config":426},"Meet GitLab Duo",{"href":427,"dataGaName":428,"dataGaLocation":391},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":430,"description":431,"link":432},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":433,"config":434},"Learn more",{"href":435,"dataGaName":436,"dataGaLocation":391},"/why-gitlab/","why gitlab",{"text":438,"left":370,"config":439,"menu":441},"Product",{"dataNavLevelOne":440},"solutions",{"type":442,"link":443,"columns":447,"feature":516},"lists",{"text":444,"config":445},"View all Solutions",{"href":446,"dataGaName":440,"dataGaLocation":391},"/solutions/",[448,472,495],{"title":449,"description":450,"link":451,"items":456},"Automation","CI/CD and automation to accelerate deployment",{"config":452},{"icon":453,"href":454,"dataGaName":455,"dataGaLocation":391},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[457,461,464,468],{"text":458,"config":459},"CI/CD",{"href":460,"dataGaLocation":391,"dataGaName":458},"/solutions/continuous-integration/",{"text":422,"config":462},{"href":427,"dataGaLocation":391,"dataGaName":463},"gitlab duo agent platform - product menu",{"text":465,"config":466},"Source Code Management",{"href":467,"dataGaLocation":391,"dataGaName":465},"/solutions/source-code-management/",{"text":469,"config":470},"Automated Software Delivery",{"href":454,"dataGaLocation":391,"dataGaName":471},"Automated software delivery",{"title":473,"description":474,"link":475,"items":480},"Security","Deliver code faster without compromising security",{"config":476},{"href":477,"dataGaName":478,"dataGaLocation":391,"icon":479},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[481,485,490],{"text":482,"config":483},"Application Security Testing",{"href":477,"dataGaName":484,"dataGaLocation":391},"Application security testing",{"text":486,"config":487},"Software Supply Chain Security",{"href":488,"dataGaLocation":391,"dataGaName":489},"/solutions/supply-chain/","Software supply chain security",{"text":491,"config":492},"Software Compliance",{"href":493,"dataGaName":494,"dataGaLocation":391},"/solutions/software-compliance/","software compliance",{"title":496,"link":497,"items":502},"Measurement",{"config":498},{"icon":499,"href":500,"dataGaName":501,"dataGaLocation":391},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[503,507,511],{"text":504,"config":505},"Visibility & Measurement",{"href":500,"dataGaLocation":391,"dataGaName":506},"Visibility and Measurement",{"text":508,"config":509},"Value Stream Management",{"href":510,"dataGaLocation":391,"dataGaName":508},"/solutions/value-stream-management/",{"text":512,"config":513},"Analytics & Insights",{"href":514,"dataGaLocation":391,"dataGaName":515},"/solutions/analytics-and-insights/","Analytics and insights",{"title":517,"type":442,"items":518},"GitLab for",[519,525,531],{"text":520,"config":521},"Enterprise",{"icon":522,"href":523,"dataGaLocation":391,"dataGaName":524},"Building","/enterprise/","enterprise",{"text":526,"config":527},"Small Business",{"icon":528,"href":529,"dataGaLocation":391,"dataGaName":530},"Work","/small-business/","small business",{"text":532,"config":533},"Public Sector",{"icon":534,"href":535,"dataGaLocation":391,"dataGaName":536},"Organization","/solutions/public-sector/","public sector",{"text":538,"config":539},"Pricing",{"href":540,"dataGaName":541,"dataGaLocation":391,"dataNavLevelOne":541},"/pricing/","pricing",{"text":543,"config":544,"menu":546},"Resources",{"dataNavLevelOne":545},"resources",{"type":442,"link":547,"columns":551,"feature":645},{"text":548,"config":549},"View all resources",{"href":550,"dataGaName":545,"dataGaLocation":391},"/resources/",[552,585,612],{"title":553,"items":554},"Getting started",[555,560,565,570,575,580],{"text":556,"config":557},"Install",{"href":558,"dataGaName":559,"dataGaLocation":391},"/install/","install",{"text":561,"config":562},"Quick start guides",{"href":563,"dataGaName":564,"dataGaLocation":391},"/get-started/","quick setup checklists",{"text":566,"config":567},"Learn",{"href":568,"dataGaLocation":391,"dataGaName":569},"https://university.gitlab.com/","learn",{"text":571,"config":572},"Product documentation",{"href":573,"dataGaName":574,"dataGaLocation":391},"https://docs.gitlab.com/","product documentation",{"text":576,"config":577},"Best practice videos",{"href":578,"dataGaName":579,"dataGaLocation":391},"/getting-started-videos/","best practice videos",{"text":581,"config":582},"Integrations",{"href":583,"dataGaName":584,"dataGaLocation":391},"/integrations/","integrations",{"title":586,"items":587},"Discover",[588,593,598,603,607],{"text":589,"config":590},"Customer success stories",{"href":591,"dataGaName":592,"dataGaLocation":391},"/customers/","customer success stories",{"text":594,"config":595},"Blog",{"href":596,"dataGaName":597,"dataGaLocation":391},"/blog/","blog",{"text":599,"config":600},"Demo Hub",{"href":601,"dataGaName":602,"dataGaLocation":391},"/demo-hub/","demo hub",{"text":604,"config":605},"The Source",{"href":606,"dataGaName":597,"dataGaLocation":391},"/the-source/",{"text":608,"config":609},"Remote",{"href":610,"dataGaName":611,"dataGaLocation":391},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":613,"items":614},"Connect",[615,620,625,630,635,640],{"text":616,"config":617},"GitLab Services",{"href":618,"dataGaName":619,"dataGaLocation":391},"/services/","services",{"text":621,"config":622},"Contribute",{"href":623,"dataGaName":624,"dataGaLocation":391},"https://contributors.gitlab.com","contribute",{"text":626,"config":627},"Community",{"href":628,"dataGaName":629,"dataGaLocation":391},"/community/","community",{"text":631,"config":632},"Forum",{"href":633,"dataGaName":634,"dataGaLocation":391},"https://forum.gitlab.com/","forum",{"text":636,"config":637},"Events",{"href":638,"dataGaName":639,"dataGaLocation":391},"/events/","events",{"text":641,"config":642},"Partners",{"href":643,"dataGaName":644,"dataGaLocation":391},"/partners/","partners",{"config":646,"title":649,"text":650,"link":651},{"background":647,"textColor":648},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":652,"config":653},"Read the latest",{"href":654,"dataGaName":655,"dataGaLocation":391},"/whats-new/","whats new",{"text":657,"config":658,"menu":660},"Company",{"dataNavLevelOne":659},"company",{"type":442,"columns":661},[662],{"items":663},[664,669,675,677,682,687,692,697,702,707],{"text":665,"config":666},"About",{"href":667,"dataGaName":668,"dataGaLocation":391},"/company/","about",{"text":670,"config":671,"footerGa":674},"Jobs",{"href":672,"dataGaName":673,"dataGaLocation":391},"/jobs/","jobs",{"dataGaName":673},{"text":636,"config":676},{"href":638,"dataGaName":639,"dataGaLocation":391},{"text":678,"config":679},"Leadership",{"href":680,"dataGaName":681,"dataGaLocation":391},"/company/team/e-group/","leadership",{"text":683,"config":684},"Handbook",{"href":685,"dataGaName":686,"dataGaLocation":391},"https://handbook.gitlab.com/","handbook",{"text":688,"config":689},"Investor relations",{"href":690,"dataGaName":691,"dataGaLocation":391},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":693,"config":694},"Trust Center",{"href":695,"dataGaName":696,"dataGaLocation":391},"/security/","trust center",{"text":698,"config":699},"AI Transparency Center",{"href":700,"dataGaName":701,"dataGaLocation":391},"/ai-transparency-center/","ai transparency center",{"text":703,"config":704},"Newsletter",{"href":705,"dataGaName":706,"dataGaLocation":391},"/company/contact/#contact-forms","newsletter",{"text":708,"config":709},"Press",{"href":710,"dataGaName":711,"dataGaLocation":391},"/press/","press",{"text":713,"config":714,"menu":715},"Contact us",{"dataNavLevelOne":659},{"type":442,"columns":716},[717],{"items":718},[719,724,729],{"text":720,"config":721},"Talk to sales",{"href":722,"dataGaName":723,"dataGaLocation":391},"/sales/","talk to sales",{"text":725,"config":726},"Support portal",{"href":727,"dataGaName":728,"dataGaLocation":391},"https://support.gitlab.com/hc/en-us","support portal",{"text":730,"config":731},"Customer portal",{"href":732,"dataGaName":733,"dataGaLocation":391},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":735,"login":736,"suggestions":743},"Close",{"text":737,"link":738},"To search repositories and projects, login to",{"text":739,"config":740},"gitlab.com",{"href":405,"dataGaName":741,"dataGaLocation":742},"search login","search",{"text":744,"default":745},"Suggestions",[746,748,752,754,758,762],{"text":422,"config":747},{"href":427,"dataGaName":422,"dataGaLocation":742},{"text":749,"config":750},"Code Suggestions (AI)",{"href":751,"dataGaName":749,"dataGaLocation":742},"/solutions/code-suggestions/",{"text":458,"config":753},{"href":460,"dataGaName":458,"dataGaLocation":742},{"text":755,"config":756},"GitLab on AWS",{"href":757,"dataGaName":755,"dataGaLocation":742},"/partners/technology-partners/aws/",{"text":759,"config":760},"GitLab on Google Cloud",{"href":761,"dataGaName":759,"dataGaLocation":742},"/partners/technology-partners/google-cloud-platform/",{"text":763,"config":764},"Why GitLab?",{"href":435,"dataGaName":763,"dataGaLocation":742},{"freeTrial":766,"mobileIcon":771,"desktopIcon":776,"secondaryButton":779},{"text":767,"config":768},"Start free trial",{"href":769,"dataGaName":396,"dataGaLocation":770},"https://gitlab.com/-/trials/new/","nav",{"altText":772,"config":773},"Gitlab Icon",{"src":774,"dataGaName":775,"dataGaLocation":770},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":772,"config":777},{"src":778,"dataGaName":775,"dataGaLocation":770},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":780,"config":781},"Get Started",{"href":782,"dataGaName":783,"dataGaLocation":770},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":785,"mobileIcon":789,"desktopIcon":791},{"text":786,"config":787},"Learn more about GitLab Duo",{"href":427,"dataGaName":788,"dataGaLocation":770},"gitlab duo",{"altText":772,"config":790},{"src":774,"dataGaName":775,"dataGaLocation":770},{"altText":772,"config":792},{"src":778,"dataGaName":775,"dataGaLocation":770},{"button":794,"mobileIcon":799,"desktopIcon":801},{"text":795,"config":796},"/switch",{"href":797,"dataGaName":798,"dataGaLocation":770},"#contact","switch",{"altText":772,"config":800},{"src":774,"dataGaName":775,"dataGaLocation":770},{"altText":772,"config":802},{"src":803,"dataGaName":775,"dataGaLocation":770},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":805,"mobileIcon":810,"desktopIcon":812},{"text":806,"config":807},"Back to pricing",{"href":540,"dataGaName":808,"dataGaLocation":770,"icon":809},"back to pricing","GoBack",{"altText":772,"config":811},{"src":774,"dataGaName":775,"dataGaLocation":770},{"altText":772,"config":813},{"src":778,"dataGaName":775,"dataGaLocation":770},{"title":815,"titleMobile":816,"button":817,"config":822},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":433,"config":818},{"href":819,"dataGaName":820,"dataGaLocation":821},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":823,"disabled":375},"release",{"data":825},{"text":826,"source":827,"edit":833,"contribute":838,"config":843,"items":848,"minimal":1058},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":828,"config":829},"View page source",{"href":830,"dataGaName":831,"dataGaLocation":832},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":834,"config":835},"Edit this page",{"href":836,"dataGaName":837,"dataGaLocation":832},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":839,"config":840},"Please contribute",{"href":841,"dataGaName":842,"dataGaLocation":832},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":844,"facebook":845,"youtube":846,"linkedin":847},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[849,896,950,994,1026],{"title":538,"links":850,"subMenu":865},[851,855,860],{"text":852,"config":853},"View plans",{"href":540,"dataGaName":854,"dataGaLocation":832},"view plans",{"text":856,"config":857},"Why Premium?",{"href":858,"dataGaName":859,"dataGaLocation":832},"/pricing/premium/","why premium",{"text":861,"config":862},"Why Ultimate?",{"href":863,"dataGaName":864,"dataGaLocation":832},"/pricing/ultimate/","why ultimate",[866],{"title":867,"links":868},"Contact Us",[869,872,874,876,881,886,891],{"text":870,"config":871},"Contact sales",{"href":722,"dataGaName":401,"dataGaLocation":832},{"text":725,"config":873},{"href":727,"dataGaName":728,"dataGaLocation":832},{"text":730,"config":875},{"href":732,"dataGaName":733,"dataGaLocation":832},{"text":877,"config":878},"Status",{"href":879,"dataGaName":880,"dataGaLocation":832},"https://status.gitlab.com/","status",{"text":882,"config":883},"Terms of use",{"href":884,"dataGaName":885,"dataGaLocation":832},"/terms/","terms of use",{"text":887,"config":888},"Privacy statement",{"href":889,"dataGaName":890,"dataGaLocation":832},"/privacy/","privacy statement",{"text":892,"config":893},"Cookie preferences",{"dataGaName":894,"dataGaLocation":832,"id":895,"isOneTrustButton":370},"cookie preferences","ot-sdk-btn",{"title":438,"links":897,"subMenu":906},[898,902],{"text":899,"config":900},"DevSecOps platform",{"href":420,"dataGaName":901,"dataGaLocation":832},"devsecops platform",{"text":903,"config":904},"AI-Assisted Development",{"href":427,"dataGaName":905,"dataGaLocation":832},"ai-assisted development",[907],{"title":908,"links":909},"Topics",[910,915,920,925,930,935,940,945],{"text":911,"config":912},"CICD",{"href":913,"dataGaName":914,"dataGaLocation":832},"/topics/ci-cd/","cicd",{"text":916,"config":917},"GitOps",{"href":918,"dataGaName":919,"dataGaLocation":832},"/topics/gitops/","gitops",{"text":921,"config":922},"DevOps",{"href":923,"dataGaName":924,"dataGaLocation":832},"/topics/devops/","devops",{"text":926,"config":927},"Version Control",{"href":928,"dataGaName":929,"dataGaLocation":832},"/topics/version-control/","version control",{"text":931,"config":932},"DevSecOps",{"href":933,"dataGaName":934,"dataGaLocation":832},"/topics/devsecops/","devsecops",{"text":936,"config":937},"Cloud Native",{"href":938,"dataGaName":939,"dataGaLocation":832},"/topics/cloud-native/","cloud native",{"text":941,"config":942},"AI for Coding",{"href":943,"dataGaName":944,"dataGaLocation":832},"/topics/devops/ai-for-coding/","ai for coding",{"text":946,"config":947},"Agentic AI",{"href":948,"dataGaName":949,"dataGaLocation":832},"/topics/agentic-ai/","agentic ai",{"title":951,"links":952},"Solutions",[953,955,957,962,966,969,973,976,978,981,984,989],{"text":482,"config":954},{"href":477,"dataGaName":482,"dataGaLocation":832},{"text":471,"config":956},{"href":454,"dataGaName":455,"dataGaLocation":832},{"text":958,"config":959},"Agile development",{"href":960,"dataGaName":961,"dataGaLocation":832},"/solutions/agile-delivery/","agile delivery",{"text":963,"config":964},"SCM",{"href":467,"dataGaName":965,"dataGaLocation":832},"source code management",{"text":911,"config":967},{"href":460,"dataGaName":968,"dataGaLocation":832},"continuous integration & delivery",{"text":970,"config":971},"Value stream management",{"href":510,"dataGaName":972,"dataGaLocation":832},"value stream management",{"text":916,"config":974},{"href":975,"dataGaName":919,"dataGaLocation":832},"/solutions/gitops/",{"text":520,"config":977},{"href":523,"dataGaName":524,"dataGaLocation":832},{"text":979,"config":980},"Small business",{"href":529,"dataGaName":530,"dataGaLocation":832},{"text":982,"config":983},"Public sector",{"href":535,"dataGaName":536,"dataGaLocation":832},{"text":985,"config":986},"Education",{"href":987,"dataGaName":988,"dataGaLocation":832},"/solutions/education/","education",{"text":990,"config":991},"Financial services",{"href":992,"dataGaName":993,"dataGaLocation":832},"/solutions/finance/","financial services",{"title":543,"links":995},[996,998,1000,1002,1005,1007,1010,1012,1014,1016,1018,1020,1022,1024],{"text":556,"config":997},{"href":558,"dataGaName":559,"dataGaLocation":832},{"text":561,"config":999},{"href":563,"dataGaName":564,"dataGaLocation":832},{"text":566,"config":1001},{"href":568,"dataGaName":569,"dataGaLocation":832},{"text":571,"config":1003},{"href":573,"dataGaName":1004,"dataGaLocation":832},"docs",{"text":594,"config":1006},{"href":596,"dataGaName":597,"dataGaLocation":832},{"text":1008,"config":1009},"What's new",{"href":654,"dataGaName":655,"dataGaLocation":832},{"text":589,"config":1011},{"href":591,"dataGaName":592,"dataGaLocation":832},{"text":608,"config":1013},{"href":610,"dataGaName":611,"dataGaLocation":832},{"text":616,"config":1015},{"href":618,"dataGaName":619,"dataGaLocation":832},{"text":621,"config":1017},{"href":623,"dataGaName":624,"dataGaLocation":832},{"text":626,"config":1019},{"href":628,"dataGaName":629,"dataGaLocation":832},{"text":631,"config":1021},{"href":633,"dataGaName":634,"dataGaLocation":832},{"text":636,"config":1023},{"href":638,"dataGaName":639,"dataGaLocation":832},{"text":641,"config":1025},{"href":643,"dataGaName":644,"dataGaLocation":832},{"title":657,"links":1027},[1028,1030,1032,1034,1036,1038,1042,1047,1049,1051,1053],{"text":665,"config":1029},{"href":667,"dataGaName":659,"dataGaLocation":832},{"text":670,"config":1031},{"href":672,"dataGaName":673,"dataGaLocation":832},{"text":678,"config":1033},{"href":680,"dataGaName":681,"dataGaLocation":832},{"text":683,"config":1035},{"href":685,"dataGaName":686,"dataGaLocation":832},{"text":688,"config":1037},{"href":690,"dataGaName":691,"dataGaLocation":832},{"text":1039,"config":1040},"Sustainability",{"href":1041,"dataGaName":1039,"dataGaLocation":832},"/sustainability/",{"text":1043,"config":1044},"Diversity, inclusion and belonging (DIB)",{"href":1045,"dataGaName":1046,"dataGaLocation":832},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":693,"config":1048},{"href":695,"dataGaName":696,"dataGaLocation":832},{"text":703,"config":1050},{"href":705,"dataGaName":706,"dataGaLocation":832},{"text":708,"config":1052},{"href":710,"dataGaName":711,"dataGaLocation":832},{"text":1054,"config":1055},"Modern Slavery Transparency Statement",{"href":1056,"dataGaName":1057,"dataGaLocation":832},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1059},[1060,1063,1066],{"text":1061,"config":1062},"Terms",{"href":884,"dataGaName":885,"dataGaLocation":832},{"text":1064,"config":1065},"Cookies",{"dataGaName":894,"dataGaLocation":832,"id":895,"isOneTrustButton":370},{"text":1067,"config":1068},"Privacy",{"href":889,"dataGaName":890,"dataGaLocation":832},[1070],{"id":1071,"title":7,"body":369,"config":1072,"content":1074,"description":369,"extension":1078,"meta":1079,"navigation":370,"path":1080,"seo":1081,"stem":1082,"__hash__":1083},"blogAuthors/en-us/blog/authors/dennis-appelt.yml",{"template":1073},"BlogAuthor",{"name":7,"config":1075},{"headshot":1076,"ctfId":1077},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749672032/Blog/Author%20Headshots/dappelt-headshot.jpg","dappelt","yml",{},"/en-us/blog/authors/dennis-appelt",{},"en-us/blog/authors/dennis-appelt","s0eKcsSwHvpcru4Dk7IkXJyRfuBEWrMhfVAJ_Fan-L4",[1085,1093,1101],{"title":1086,"description":1087,"heroImage":1088,"category":365,"date":1089,"authors":1090,"slug":1092,"externalUrl":369},"How GitLab tracks vulnerabilities through refactors and reformatting","Learn how GitLab's improved Scope+Offset fingerprinting keeps vulnerability tracking stable across comments, blank lines, and reformatting.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1759320418/xjmqcozxzt4frx0hori3.png","2026-08-12",[1091],"Julian Thome","improved-scope-offset-fingerprinting",{"title":1094,"description":1095,"heroImage":1088,"category":365,"date":1096,"authors":1097,"slug":1100,"externalUrl":369},"GitLab Secrets Manager adds ESO, Terraform, API support","Simplify credential management across your stack. GitLab Secrets Manager provides secure retrieval in Kubernetes, Terraform, and external workflows.","2026-08-06",[1098,1099],"Erick Bajao","Joe Randazzo","gitlab-secrets-manager-add-eso-terraform-api-support",{"title":1102,"description":1103,"heroImage":1104,"category":365,"date":1105,"authors":1106,"slug":1108,"externalUrl":369},"Secure every commit to production with Claude and GitLab","Claude Security catches vulnerabilities inside a coding session. GitLab picks up from there, scanning, enforcing policy, and producing audit evidence for the software lifecycle. ","https://res.cloudinary.com/about-gitlab-com/image/upload/v1756122536/akivvcnafog9c4dhhzkp.png","2026-08-03",[1107],"Alisa Ho","claude-security-and-gitlab",{"promotions":1110},[1111,1125,1137,1148],{"id":1112,"categories":1113,"header":1115,"text":1116,"button":1117,"image":1122},"ai-modernization",[1114],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1118,"config":1119},"Get your AI maturity score",{"href":1120,"dataGaName":1121,"dataGaLocation":597},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1123},{"src":1124},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1126,"categories":1127,"header":1129,"text":1116,"button":1130,"image":1134},"devops-modernization",[1128,934],"product","Are you just managing tools or shipping innovation?",{"text":1131,"config":1132},"Get your DevOps maturity score",{"href":1133,"dataGaName":1121,"dataGaLocation":597},"/assessments/devops-modernization-assessment/",{"config":1135},{"src":1136},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1138,"categories":1139,"header":1140,"text":1116,"button":1141,"image":1145},"security-modernization",[365],"Are you trading speed for security?",{"text":1142,"config":1143},"Get your security maturity score",{"href":1144,"dataGaName":1121,"dataGaLocation":597},"/assessments/security-modernization-assessment/",{"config":1146},{"src":1147},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1149,"paths":1150,"header":1153,"text":1154,"button":1155,"image":1160},"github-azure-migration",[1151,1152],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":1156,"config":1157},"See how GitLab compares to GitHub",{"href":1158,"dataGaName":1159,"dataGaLocation":597},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1161},{"src":1136},{"header":1163,"blurb":1164,"button":1165,"secondaryButton":1170},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1166,"config":1167},"Get your free trial",{"href":1168,"dataGaName":396,"dataGaLocation":1169},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":870,"config":1171},{"href":722,"dataGaName":401,"dataGaLocation":1169},1786803752024]