[{"data":1,"prerenderedAt":1790},["ShallowReactive",2],{"/blog/monitor-web-attack-surface-with-gitlab":3,"navigation-en-us":1004,"banner-en-us":1432,"footer-en-us":1442,"blog-post-authors-en-us-Chris Moberly":1687,"blog-related-posts-en-us-monitor-web-attack-surface-with-gitlab":1702,"blog-promotions-en-us":1727,"next-steps-en-us":1780},{"id":4,"title":5,"authors":6,"body":8,"category":985,"date":986,"description":987,"extension":988,"externalUrl":989,"faq":989,"featured":990,"heroImage":991,"meta":992,"navigation":157,"path":993,"seo":994,"slug":998,"stem":999,"tags":1000,"template":1002,"updatedDate":989,"__hash__":1003},"blogPosts/en-us/blog/monitor-web-attack-surface-with-gitlab.md","Monitor your web attack surface with GitLab CI/CD and GitLab Pages",[7],"Chris Moberly",{"type":9,"value":10,"toc":973},"minimark",[11,18,21,24,27,40,45,50,60,67,71,74,107,120,123,130,134,341,344,490,493,565,569,582,802,805,823,830,834,850,857,863,870,874,883,889,892,898,901,907,911,914,917,925,931,935,938,941,947,951,954,963,966,969],[12,13,14],"p",{},[15,16,17],"em",{},"DISCLAIMER: We believe that understanding the tactics and techniques of both attackers and defenders is key to keeping our organization secure. It's important to note that GitLab security blog posts are for informational purposes only, not to provide specific security advice.",[12,19,20],{},"Attackers love insecure web applications. Lucky for them, these applications are everywhere! Test environments, development instances, default installations with hardcoded passwords - you name it, it's out there somewhere waiting to be exploited.",[12,22,23],{},"The easier it becomes to deploy resources in the cloud, the more of these insecure web applications end up exposed to hacking and/or unintended access. You can get ahead of potential threats by proactively identifying and reviewing your own web application attack surface.",[12,25,26],{},"In this tutorial, we'll explain how you can monitor the web applications in your environment by generating a screenshot report using GitLab CI/CD, GitLab Pages, and a handful of free and open source security tools. You'll end up with a fully automated solution that can:",[28,29,30,34,37],"ul",{},[31,32,33],"li",{},"Identify web services on a list of addresses you own.",[31,35,36],{},"Capture screenshots of these web services.",[31,38,39],{},"Build an authenticated web portal for you to visually see each site that was discovered.",[41,42,44],"h2",{"id":43},"building-the-solution","Building the solution",[46,47,49],"h3",{"id":48},"start-with-a-project","Start with a project",[12,51,52,53,59],{},"Inside the GitLab web interface, ",[54,55,58],"a",{"href":56,"rel":57},"https://gitlab.com/projects/new#blank_project",[],"create a blank project",". The default settings should be fine, but you should review to confirm that the default settings are appropriate. Pay particular attention to the \"Visibility Level\" - you may want to set this to private.",[12,61,62],{},[63,64],"img",{"alt":65,"src":66},"Creating a GitLab project","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782397754/blog/Content%20Images/2023-01-17-monitor-web-attack-surface-with-gitlab/screenshot-create-project.png",[46,68,70],{"id":69},"write-the-automation-scripts","Write the automation scripts",[12,72,73],{},"We'll use three scripts in this project:",[28,75,76,83,95],{},[31,77,78,82],{},[79,80,81],"code",{},"setup.sh",": Install the required tooling.",[31,84,85,88,89,94],{},[79,86,87],{},"discover-services.sh",": Identify web services using ",[54,90,93],{"href":91,"rel":92},"https://github.com/projectdiscovery/httpx",[],"httpx",".",[31,96,97,100,101,106],{},[79,98,99],{},"take-screenshots.sh",": Use ",[54,102,105],{"href":103,"rel":104},"https://github.com/sensepost/gowitness",[],"gowitness"," to generate a static website containing screenshots of each identified web service.",[12,108,109,110,113,114,119],{},"The HTML generated by gowitness will be placed into the ",[79,111,112],{},"public"," folder, which is used by ",[54,115,118],{"href":116,"rel":117},"https://docs.gitlab.com/user/project/pages/",[],"GitLab Pages"," to generate a website you can click through to review the findings.",[12,121,122],{},"Each script will generate output files which may be useful for additional analysis. These will be made available as GitLab job artifacts, so that you can download and review them at will.",[12,124,125,126,129],{},"To complete this step, first create a new folder in your project called ",[79,127,128],{},"scripts",". Then, add the following files into that folder:",[131,132,81],"h4",{"id":133},"setupsh",[135,136,141],"pre",{"className":137,"code":138,"language":139,"meta":140,"style":140},"language-bash shiki shiki-themes github-light","#!/bin/bash\n\n# create folder for downloaded binaries\nmkdir bin\n\n# install general pre-reqs\napt -qq update > /dev/null\napt -qq install -y wget unzip > /dev/null\n\n# install pre-reqs for gowitness\napt -qq install -y chromium > /dev/null\n\n# download tools\nwget -q https://github.com/projectdiscovery/httpx/releases/download/v1.2.5/httpx_1.2.5_linux_amd64.zip\nwget -q https://github.com/sensepost/gowitness/releases/download/2.4.2/gowitness-2.4.2-linux-amd64\n\n# unzip / move all relases to bin folder\nunzip httpx_1.2.5_linux_amd64.zip -d bin/\nmv gowitness-2.4.2-linux-amd64 bin/gowitness\nchmod u+x bin/gowitness\n","bash","",[79,142,143,152,159,165,176,181,187,207,230,235,241,259,264,270,282,292,297,303,318,330],{"__ignoreMap":140},[144,145,148],"span",{"class":146,"line":147},"line",1,[144,149,151],{"class":150},"sAwPA","#!/bin/bash\n",[144,153,155],{"class":146,"line":154},2,[144,156,158],{"emptyLinePlaceholder":157},true,"\n",[144,160,162],{"class":146,"line":161},3,[144,163,164],{"class":150},"# create folder for downloaded binaries\n",[144,166,168,172],{"class":146,"line":167},4,[144,169,171],{"class":170},"s7eDp","mkdir",[144,173,175],{"class":174},"sYBdl"," bin\n",[144,177,179],{"class":146,"line":178},5,[144,180,158],{"emptyLinePlaceholder":157},[144,182,184],{"class":146,"line":183},6,[144,185,186],{"class":150},"# install general pre-reqs\n",[144,188,190,193,197,200,204],{"class":146,"line":189},7,[144,191,192],{"class":170},"apt",[144,194,196],{"class":195},"sYu0t"," -qq",[144,198,199],{"class":174}," update",[144,201,203],{"class":202},"sD7c4"," >",[144,205,206],{"class":174}," /dev/null\n",[144,208,210,212,214,217,220,223,226,228],{"class":146,"line":209},8,[144,211,192],{"class":170},[144,213,196],{"class":195},[144,215,216],{"class":174}," install",[144,218,219],{"class":195}," -y",[144,221,222],{"class":174}," wget",[144,224,225],{"class":174}," unzip",[144,227,203],{"class":202},[144,229,206],{"class":174},[144,231,233],{"class":146,"line":232},9,[144,234,158],{"emptyLinePlaceholder":157},[144,236,238],{"class":146,"line":237},10,[144,239,240],{"class":150},"# install pre-reqs for gowitness\n",[144,242,244,246,248,250,252,255,257],{"class":146,"line":243},11,[144,245,192],{"class":170},[144,247,196],{"class":195},[144,249,216],{"class":174},[144,251,219],{"class":195},[144,253,254],{"class":174}," chromium",[144,256,203],{"class":202},[144,258,206],{"class":174},[144,260,262],{"class":146,"line":261},12,[144,263,158],{"emptyLinePlaceholder":157},[144,265,267],{"class":146,"line":266},13,[144,268,269],{"class":150},"# download tools\n",[144,271,273,276,279],{"class":146,"line":272},14,[144,274,275],{"class":170},"wget",[144,277,278],{"class":195}," -q",[144,280,281],{"class":174}," https://github.com/projectdiscovery/httpx/releases/download/v1.2.5/httpx_1.2.5_linux_amd64.zip\n",[144,283,285,287,289],{"class":146,"line":284},15,[144,286,275],{"class":170},[144,288,278],{"class":195},[144,290,291],{"class":174}," https://github.com/sensepost/gowitness/releases/download/2.4.2/gowitness-2.4.2-linux-amd64\n",[144,293,295],{"class":146,"line":294},16,[144,296,158],{"emptyLinePlaceholder":157},[144,298,300],{"class":146,"line":299},17,[144,301,302],{"class":150},"# unzip / move all relases to bin folder\n",[144,304,306,309,312,315],{"class":146,"line":305},18,[144,307,308],{"class":170},"unzip",[144,310,311],{"class":174}," httpx_1.2.5_linux_amd64.zip",[144,313,314],{"class":195}," -d",[144,316,317],{"class":174}," bin/\n",[144,319,321,324,327],{"class":146,"line":320},19,[144,322,323],{"class":170},"mv",[144,325,326],{"class":174}," gowitness-2.4.2-linux-amd64",[144,328,329],{"class":174}," bin/gowitness\n",[144,331,333,336,339],{"class":146,"line":332},20,[144,334,335],{"class":170},"chmod",[144,337,338],{"class":174}," u+x",[144,340,329],{"class":174},[131,342,87],{"id":343},"discover-servicessh",[135,345,347],{"className":137,"code":346,"language":139,"meta":140,"style":140},"#!/bin/bash\n\n# You may want to dynamically generate a target file with each run.\n# For this demo, we are using a list defined inside project CI variables.\nTARGETS=$TARGET_FILE\n\n# create output directory\nmkdir ./targets\n\n# Identify web services\necho \"Identifying web services across $(cat \"$TARGETS\" | wc -l) targets...\"\ncat \"$TARGETS\" | bin/httpx -o targets/web-services.txt -p 80,443\n\necho \"Discovered $(cat targets/web-services.txt | wc -l) web services.\"\n",[79,348,349,353,357,362,367,379,383,388,395,399,404,436,465,469],{"__ignoreMap":140},[144,350,351],{"class":146,"line":147},[144,352,151],{"class":150},[144,354,355],{"class":146,"line":154},[144,356,158],{"emptyLinePlaceholder":157},[144,358,359],{"class":146,"line":161},[144,360,361],{"class":150},"# You may want to dynamically generate a target file with each run.\n",[144,363,364],{"class":146,"line":167},[144,365,366],{"class":150},"# For this demo, we are using a list defined inside project CI variables.\n",[144,368,369,373,376],{"class":146,"line":178},[144,370,372],{"class":371},"sgsFI","TARGETS",[144,374,375],{"class":202},"=",[144,377,378],{"class":371},"$TARGET_FILE\n",[144,380,381],{"class":146,"line":183},[144,382,158],{"emptyLinePlaceholder":157},[144,384,385],{"class":146,"line":189},[144,386,387],{"class":150},"# create output directory\n",[144,389,390,392],{"class":146,"line":209},[144,391,171],{"class":170},[144,393,394],{"class":174}," ./targets\n",[144,396,397],{"class":146,"line":232},[144,398,158],{"emptyLinePlaceholder":157},[144,400,401],{"class":146,"line":237},[144,402,403],{"class":150},"# Identify web services\n",[144,405,406,409,412,415,418,421,424,427,430,433],{"class":146,"line":243},[144,407,408],{"class":195},"echo",[144,410,411],{"class":174}," \"Identifying web services across $(",[144,413,414],{"class":170},"cat",[144,416,417],{"class":174}," \"",[144,419,420],{"class":371},"$TARGETS",[144,422,423],{"class":174},"\" ",[144,425,426],{"class":202},"|",[144,428,429],{"class":170}," wc",[144,431,432],{"class":195}," -l",[144,434,435],{"class":174},") targets...\"\n",[144,437,438,440,442,444,447,450,453,456,459,462],{"class":146,"line":261},[144,439,414],{"class":170},[144,441,417],{"class":174},[144,443,420],{"class":371},[144,445,446],{"class":174},"\"",[144,448,449],{"class":202}," |",[144,451,452],{"class":170}," bin/httpx",[144,454,455],{"class":195}," -o",[144,457,458],{"class":174}," targets/web-services.txt",[144,460,461],{"class":195}," -p",[144,463,464],{"class":174}," 80,443\n",[144,466,467],{"class":146,"line":266},[144,468,158],{"emptyLinePlaceholder":157},[144,470,471,473,476,478,481,483,485,487],{"class":146,"line":272},[144,472,408],{"class":195},[144,474,475],{"class":174}," \"Discovered $(",[144,477,414],{"class":170},[144,479,480],{"class":174}," targets/web-services.txt ",[144,482,426],{"class":202},[144,484,429],{"class":170},[144,486,432],{"class":195},[144,488,489],{"class":174},") web services.\"\n",[131,491,99],{"id":492},"take-screenshotssh",[135,494,496],{"className":137,"code":495,"language":139,"meta":140,"style":140},"#!/bin/bash\n\n# Run gowitness\nbin/gowitness file -f targets/web-services.txt\nbin/gowitness report export -f report.zip\n\n# Move the report to pages outdir\nunzip report.zip\nmv gowitness public\n",[79,497,498,502,506,511,525,540,544,549,555],{"__ignoreMap":140},[144,499,500],{"class":146,"line":147},[144,501,151],{"class":150},[144,503,504],{"class":146,"line":154},[144,505,158],{"emptyLinePlaceholder":157},[144,507,508],{"class":146,"line":161},[144,509,510],{"class":150},"# Run gowitness\n",[144,512,513,516,519,522],{"class":146,"line":167},[144,514,515],{"class":170},"bin/gowitness",[144,517,518],{"class":174}," file",[144,520,521],{"class":195}," -f",[144,523,524],{"class":174}," targets/web-services.txt\n",[144,526,527,529,532,535,537],{"class":146,"line":178},[144,528,515],{"class":170},[144,530,531],{"class":174}," report",[144,533,534],{"class":174}," export",[144,536,521],{"class":195},[144,538,539],{"class":174}," report.zip\n",[144,541,542],{"class":146,"line":183},[144,543,158],{"emptyLinePlaceholder":157},[144,545,546],{"class":146,"line":189},[144,547,548],{"class":150},"# Move the report to pages outdir\n",[144,550,551,553],{"class":146,"line":209},[144,552,308],{"class":170},[144,554,539],{"class":174},[144,556,557,559,562],{"class":146,"line":232},[144,558,323],{"class":170},[144,560,561],{"class":174}," gowitness",[144,563,564],{"class":174}," public\n",[46,566,568],{"id":567},"set-up-the-pipeline","Set up the pipeline",[12,570,571,572,575,576,581],{},"Next, we need to create the ",[79,573,574],{},".gitlab-ci.yml"," file where we configure all of these scripts to run inside a ",[54,577,580],{"href":578,"rel":579},"https://docs.gitlab.com/ci/pipelines/",[],"pipeline",". Create this file in the root of the project with the following contents:",[135,583,587],{"className":584,"code":585,"language":586,"meta":140,"style":140},"language-yaml shiki shiki-themes github-light","stages:\n  - scan\n  - deploy\n\nscanner:\n  image: debian:bullseye\n  stage: scan\n  script:\n  - bash ./scripts/setup.sh\n  - bash ./scripts/discover-services.sh\n  - bash ./scripts/take-screenshots.sh\n  only:\n  - schedules\n  - web\n  artifacts:\n    paths:\n    - targets\n    - public\n\npages:\n  stage: deploy\n  script:\n  - echo \"\" # do nothing\n  only:\n  - schedules\n  - web\n  artifacts:\n    paths:\n    - public\n\n","yaml",[79,588,589,598,606,613,617,624,635,644,651,658,665,672,679,686,693,700,707,715,722,726,733,742,749,760,767,774,781,788,795],{"__ignoreMap":140},[144,590,591,595],{"class":146,"line":147},[144,592,594],{"class":593},"shJU0","stages",[144,596,597],{"class":371},":\n",[144,599,600,603],{"class":146,"line":154},[144,601,602],{"class":371},"  - ",[144,604,605],{"class":174},"scan\n",[144,607,608,610],{"class":146,"line":161},[144,609,602],{"class":371},[144,611,612],{"class":174},"deploy\n",[144,614,615],{"class":146,"line":167},[144,616,158],{"emptyLinePlaceholder":157},[144,618,619,622],{"class":146,"line":178},[144,620,621],{"class":593},"scanner",[144,623,597],{"class":371},[144,625,626,629,632],{"class":146,"line":183},[144,627,628],{"class":593},"  image",[144,630,631],{"class":371},": ",[144,633,634],{"class":174},"debian:bullseye\n",[144,636,637,640,642],{"class":146,"line":189},[144,638,639],{"class":593},"  stage",[144,641,631],{"class":371},[144,643,605],{"class":174},[144,645,646,649],{"class":146,"line":209},[144,647,648],{"class":593},"  script",[144,650,597],{"class":371},[144,652,653,655],{"class":146,"line":232},[144,654,602],{"class":371},[144,656,657],{"class":174},"bash ./scripts/setup.sh\n",[144,659,660,662],{"class":146,"line":237},[144,661,602],{"class":371},[144,663,664],{"class":174},"bash ./scripts/discover-services.sh\n",[144,666,667,669],{"class":146,"line":243},[144,668,602],{"class":371},[144,670,671],{"class":174},"bash ./scripts/take-screenshots.sh\n",[144,673,674,677],{"class":146,"line":261},[144,675,676],{"class":593},"  only",[144,678,597],{"class":371},[144,680,681,683],{"class":146,"line":266},[144,682,602],{"class":371},[144,684,685],{"class":174},"schedules\n",[144,687,688,690],{"class":146,"line":272},[144,689,602],{"class":371},[144,691,692],{"class":174},"web\n",[144,694,695,698],{"class":146,"line":284},[144,696,697],{"class":593},"  artifacts",[144,699,597],{"class":371},[144,701,702,705],{"class":146,"line":294},[144,703,704],{"class":593},"    paths",[144,706,597],{"class":371},[144,708,709,712],{"class":146,"line":299},[144,710,711],{"class":371},"    - ",[144,713,714],{"class":174},"targets\n",[144,716,717,719],{"class":146,"line":305},[144,718,711],{"class":371},[144,720,721],{"class":174},"public\n",[144,723,724],{"class":146,"line":320},[144,725,158],{"emptyLinePlaceholder":157},[144,727,728,731],{"class":146,"line":332},[144,729,730],{"class":593},"pages",[144,732,597],{"class":371},[144,734,736,738,740],{"class":146,"line":735},21,[144,737,639],{"class":593},[144,739,631],{"class":371},[144,741,612],{"class":174},[144,743,745,747],{"class":146,"line":744},22,[144,746,648],{"class":593},[144,748,597],{"class":371},[144,750,752,754,757],{"class":146,"line":751},23,[144,753,602],{"class":371},[144,755,756],{"class":174},"echo \"\"",[144,758,759],{"class":150}," # do nothing\n",[144,761,763,765],{"class":146,"line":762},24,[144,764,676],{"class":593},[144,766,597],{"class":371},[144,768,770,772],{"class":146,"line":769},25,[144,771,602],{"class":371},[144,773,685],{"class":174},[144,775,777,779],{"class":146,"line":776},26,[144,778,602],{"class":371},[144,780,692],{"class":174},[144,782,784,786],{"class":146,"line":783},27,[144,785,697],{"class":593},[144,787,597],{"class":371},[144,789,791,793],{"class":146,"line":790},28,[144,792,704],{"class":593},[144,794,597],{"class":371},[144,796,798,800],{"class":146,"line":797},29,[144,799,711],{"class":371},[144,801,721],{"class":174},[12,803,804],{},"This file defines a pipeline with two stages:",[28,806,807,816],{},[31,808,809,810,813,814,94],{},"The first stage (",[79,811,812],{},"scan",") runs all three scripts we created. This will generate a static website inside a folder called ",[79,815,112],{},[31,817,818,819,822],{},"The second stage (",[79,820,821],{},"deploy",") is required for GitLab Pages to work - this is where the static site is published on a publicly-accessible URL that can be configured to require GitLab authentication.",[12,824,825,826,829],{},"You can see we've added an ",[79,827,828],{},"only"," condition to both of these stages. This ensures that the tool doesn't run every time you make a change to the code - it runs only on a defined schedule or when you manually choose to run it via the web UI.",[46,831,833],{"id":832},"define-your-targets","Define your targets",[12,835,836,837,840,841,843,844,849],{},"You may have noticed the ",[79,838,839],{},"TARGETS_FILE"," variable inside the ",[79,842,87],{}," script from earlier. That's a ",[54,845,848],{"href":846,"rel":847},"https://docs.gitlab.com/ci/variables/#cicd-variable-types",[],"file variable"," that will contain the targets you wish to scan. Defining this as a variable allows us to create a generic, portable project that can be forked and cloned without containing any specific targets.",[12,851,852,853,856],{},"To add some targets to your project, go to \"Settings -> CI/CD -> Variables\" and click the \"Add variable\" button. Enter ",[79,854,855],{},"TARGET_FILE"," for the \"Key\" and fill in one host name or IP address per line in the \"Value\" section. Make sure to add a blank line to the end of the list - this is required for the scripts to run correctly.",[12,858,859],{},[63,860],{"alt":861,"src":862},"Creating a CI environment variable","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782397754/blog/Content%20Images/2023-01-17-monitor-web-attack-surface-with-gitlab/screenshot-create-variable.png",[12,864,865,866,869],{},"We run a similar version of this screenshot tool internally, and we generate this file dynamically by exporting all of our public addresses from our various cloud environments. If you decide to implement something like that, we'd recommend adding an additional stage to your ",[79,867,868],{},".gitlab-ci.yaml"," file.",[46,871,873],{"id":872},"schedule-and-run-the-pipeline","Schedule and run the pipeline",[12,875,876,877,882],{},"GitLab projects have a built-in ",[54,878,881],{"href":879,"rel":880},"https://docs.gitlab.com/ci/pipelines/schedules/",[],"scheduler"," that lets you automatically run pipelines at specific dates/times. You can access this inside your project at \"CI/CD -> Schedules\". Here, we'll create a new schedule to run once a week on Monday mornings.",[12,884,885],{},[63,886],{"alt":887,"src":888},"Creating a CI schedule","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782397756/blog/Content%20Images/2023-01-17-monitor-web-attack-surface-with-gitlab/screenshot-schedule.png",[12,890,891],{},"After saving the schedule, you can also choose to manually run the pipeline by clicking the play button shown below. If you are following along, go ahead and do this now.",[12,893,894],{},[63,895],{"alt":896,"src":897},"Running a CI schedule","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782397755/blog/Content%20Images/2023-01-17-monitor-web-attack-surface-with-gitlab/screenshot-play.png",[12,899,900],{},"You can follow the progress of your pipeline by navigating to \"CI/CD -> Pipelines\". Click on the status of the most recent pipeline. Here, you'll see the stages we configured earlier.  If all goes well, you'll see green check marks on each stage. You can click on an individual stage to watch the console logs and perform any necessary troubleshooting.",[12,902,903],{},[63,904],{"alt":905,"src":906},"CI pipeline success icons","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782397756/blog/Content%20Images/2023-01-17-monitor-web-attack-surface-with-gitlab/screenshot-success.png",[46,908,910],{"id":909},"enable-notifications","Enable notifications",[12,912,913],{},"Once you've set up a schedule, you might want to be alerted whenever a new report is ready. Luckily, GitLab has you covered here!",[12,915,916],{},"Inside your project, go to \"Settings -> Integrations -> Pipeline Status Emails\". Put your email address into the \"Recipients\" field and uncheck the box \"Notify only broken pipelines\". This will send you an email each time a pipeline completes and a new screenshot report has been published to your GitLab Pages site.",[12,918,919,920,94],{},"GitLab offers a slew of other notifcation options as well, including things like Slack and Teams. If you prefer one of those, you can ",[54,921,924],{"href":922,"rel":923},"https://docs.gitlab.com/integration/",[],"read more in the docs",[12,926,927],{},[63,928],{"alt":929,"src":930},"Create pipeline notifications","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782397755/blog/Content%20Images/2023-01-17-monitor-web-attack-surface-with-gitlab/screenshot-notifications.png",[46,932,934],{"id":933},"view-your-gitlab-pages-site","View your GitLab Pages site",[12,936,937],{},"Once the pipeline has completed successfully, your site should be available. You can obtain the site's public address by going to \"Settings -> Pages\".",[12,939,940],{},"If you set your project to be private, then by default only project members who are authenticated to the GitLab server can view the site. You can modify these settings under \"Settings -> General -> Visibility, project features, permissions -> Pages\".",[12,942,943],{},[63,944],{"alt":945,"src":946},"Screenshot of gowitness interface","https://res.cloudinary.com/about-gitlab-com/image/upload/v1782397754/blog/Content%20Images/2023-01-17-monitor-web-attack-surface-with-gitlab/screenshot-gowitness.png",[41,948,950],{"id":949},"summary","Summary",[12,952,953],{},"Web applications are everywhere. Often, they are deployed with vulnerable configurations and are left forgotten - unmonitored and awaiting exploitation. You can build a monitoring solution using GitLab and free, open-source tools that gives you a visual overview of the web apps running in your own environment.",[12,955,956,957,962],{},"This blog walked you through manually setting this up yourself. You can view our example project ",[54,958,961],{"href":959,"rel":960},"https://gitlab.com/gitlab-com/gl-security/threatmanagement/redteam/redteam-public/webapp-screenshots",[],"here",", which was created exactly as decribed above. Feel free to fork or clone that project as a shortcut to setting it up from scratch yourself.",[12,964,965],{},"We love to find creative uses for GitLab, and this is one we use ourselves internally. If you've come up with some of your own interesting use cases, we would love to hear about them! Or, if there is anything else you'd like our security team to write about, please do let us know! You can share your thoughts in the comments below or via issues and merge requests on any of our projects.",[12,967,968],{},"Thanks for reading!",[970,971,972],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}",{"title":140,"searchDepth":154,"depth":154,"links":974},[975,984],{"id":43,"depth":154,"text":44,"children":976},[977,978,979,980,981,982,983],{"id":48,"depth":161,"text":49},{"id":69,"depth":161,"text":70},{"id":567,"depth":161,"text":568},{"id":832,"depth":161,"text":833},{"id":872,"depth":161,"text":873},{"id":909,"depth":161,"text":910},{"id":933,"depth":161,"text":934},{"id":949,"depth":154,"text":950},"security","2023-01-11","Use this tutorial to build an automated web application screenshot report.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749682233/Blog/Hero%20Images/aleks-dahlberg-glass-unsplash.jpg",{},"/en-us/blog/monitor-web-attack-surface-with-gitlab",{"title":5,"description":987,"ogTitle":5,"ogDescription":987,"noIndex":990,"ogImage":991,"ogUrl":995,"ogSiteName":996,"ogType":997,"canonicalUrls":995},"https://about.gitlab.com/blog/monitor-web-attack-surface-with-gitlab","https://about.gitlab.com","article","monitor-web-attack-surface-with-gitlab","en-us/blog/monitor-web-attack-surface-with-gitlab",[985,1001],"open source","BlogPost","o2gI2GkpLJteWPUf_l7O_GrJgZb_ulinL_-k-Q0S9_k",{"logo":1005,"freeTrial":1010,"sales":1015,"login":1020,"items":1025,"search":1352,"minimal":1383,"duo":1402,"switchNav":1411,"pricingDeployment":1422},{"config":1006},{"href":1007,"dataGaName":1008,"dataGaLocation":1009},"/","gitlab logo","header",{"text":1011,"config":1012},"Get free trial",{"href":1013,"dataGaName":1014,"dataGaLocation":1009},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":1016,"config":1017},"Request a demo",{"href":1018,"dataGaName":1019,"dataGaLocation":1009},"/sales/?contact-topic=request-demo","sales",{"text":1021,"config":1022},"Sign in",{"href":1023,"dataGaName":1024,"dataGaLocation":1009},"https://gitlab.com/users/sign_in/","sign in",[1026,1055,1155,1160,1274,1330],{"text":1027,"config":1028,"menu":1030},"Platform",{"dataNavLevelOne":1029},"platform",{"type":1031,"columns":1032},"cards",[1033,1039,1047],{"title":1027,"description":1034,"link":1035},"The intelligent orchestration platform for DevSecOps",{"text":1036,"config":1037},"Explore our Platform",{"href":1038,"dataGaName":1029,"dataGaLocation":1009},"/platform/",{"title":1040,"description":1041,"link":1042},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":1043,"config":1044},"Meet GitLab Duo",{"href":1045,"dataGaName":1046,"dataGaLocation":1009},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":1048,"description":1049,"link":1050},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":1051,"config":1052},"Learn more",{"href":1053,"dataGaName":1054,"dataGaLocation":1009},"/why-gitlab/","why gitlab",{"text":1056,"left":157,"config":1057,"menu":1059},"Product",{"dataNavLevelOne":1058},"solutions",{"type":1060,"link":1061,"columns":1065,"feature":1134},"lists",{"text":1062,"config":1063},"View all Solutions",{"href":1064,"dataGaName":1058,"dataGaLocation":1009},"/solutions/",[1066,1090,1113],{"title":1067,"description":1068,"link":1069,"items":1074},"Automation","CI/CD and automation to accelerate deployment",{"config":1070},{"icon":1071,"href":1072,"dataGaName":1073,"dataGaLocation":1009},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[1075,1079,1082,1086],{"text":1076,"config":1077},"CI/CD",{"href":1078,"dataGaLocation":1009,"dataGaName":1076},"/solutions/continuous-integration/",{"text":1040,"config":1080},{"href":1045,"dataGaLocation":1009,"dataGaName":1081},"gitlab duo agent platform - product menu",{"text":1083,"config":1084},"Source Code Management",{"href":1085,"dataGaLocation":1009,"dataGaName":1083},"/solutions/source-code-management/",{"text":1087,"config":1088},"Automated Software Delivery",{"href":1072,"dataGaLocation":1009,"dataGaName":1089},"Automated software delivery",{"title":1091,"description":1092,"link":1093,"items":1098},"Security","Deliver code faster without compromising security",{"config":1094},{"href":1095,"dataGaName":1096,"dataGaLocation":1009,"icon":1097},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[1099,1103,1108],{"text":1100,"config":1101},"Application Security Testing",{"href":1095,"dataGaName":1102,"dataGaLocation":1009},"Application security testing",{"text":1104,"config":1105},"Software Supply Chain Security",{"href":1106,"dataGaLocation":1009,"dataGaName":1107},"/solutions/supply-chain/","Software supply chain security",{"text":1109,"config":1110},"Software Compliance",{"href":1111,"dataGaName":1112,"dataGaLocation":1009},"/solutions/software-compliance/","software compliance",{"title":1114,"link":1115,"items":1120},"Measurement",{"config":1116},{"icon":1117,"href":1118,"dataGaName":1119,"dataGaLocation":1009},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[1121,1125,1129],{"text":1122,"config":1123},"Visibility & Measurement",{"href":1118,"dataGaLocation":1009,"dataGaName":1124},"Visibility and Measurement",{"text":1126,"config":1127},"Value Stream Management",{"href":1128,"dataGaLocation":1009,"dataGaName":1126},"/solutions/value-stream-management/",{"text":1130,"config":1131},"Analytics & Insights",{"href":1132,"dataGaLocation":1009,"dataGaName":1133},"/solutions/analytics-and-insights/","Analytics and insights",{"title":1135,"type":1060,"items":1136},"GitLab for",[1137,1143,1149],{"text":1138,"config":1139},"Enterprise",{"icon":1140,"href":1141,"dataGaLocation":1009,"dataGaName":1142},"Building","/enterprise/","enterprise",{"text":1144,"config":1145},"Small Business",{"icon":1146,"href":1147,"dataGaLocation":1009,"dataGaName":1148},"Work","/small-business/","small business",{"text":1150,"config":1151},"Public Sector",{"icon":1152,"href":1153,"dataGaLocation":1009,"dataGaName":1154},"Organization","/solutions/public-sector/","public sector",{"text":1156,"config":1157},"Pricing",{"href":1158,"dataGaName":1159,"dataGaLocation":1009,"dataNavLevelOne":1159},"/pricing/","pricing",{"text":1161,"config":1162,"menu":1164},"Resources",{"dataNavLevelOne":1163},"resources",{"type":1060,"link":1165,"columns":1169,"feature":1263},{"text":1166,"config":1167},"View all resources",{"href":1168,"dataGaName":1163,"dataGaLocation":1009},"/resources/",[1170,1203,1230],{"title":1171,"items":1172},"Getting started",[1173,1178,1183,1188,1193,1198],{"text":1174,"config":1175},"Install",{"href":1176,"dataGaName":1177,"dataGaLocation":1009},"/install/","install",{"text":1179,"config":1180},"Quick start guides",{"href":1181,"dataGaName":1182,"dataGaLocation":1009},"/get-started/","quick setup checklists",{"text":1184,"config":1185},"Learn",{"href":1186,"dataGaLocation":1009,"dataGaName":1187},"https://university.gitlab.com/","learn",{"text":1189,"config":1190},"Product documentation",{"href":1191,"dataGaName":1192,"dataGaLocation":1009},"https://docs.gitlab.com/","product documentation",{"text":1194,"config":1195},"Best practice videos",{"href":1196,"dataGaName":1197,"dataGaLocation":1009},"/getting-started-videos/","best practice videos",{"text":1199,"config":1200},"Integrations",{"href":1201,"dataGaName":1202,"dataGaLocation":1009},"/integrations/","integrations",{"title":1204,"items":1205},"Discover",[1206,1211,1216,1221,1225],{"text":1207,"config":1208},"Customer success stories",{"href":1209,"dataGaName":1210,"dataGaLocation":1009},"/customers/","customer success stories",{"text":1212,"config":1213},"Blog",{"href":1214,"dataGaName":1215,"dataGaLocation":1009},"/blog/","blog",{"text":1217,"config":1218},"Demo Hub",{"href":1219,"dataGaName":1220,"dataGaLocation":1009},"/demo-hub/","demo hub",{"text":1222,"config":1223},"The Source",{"href":1224,"dataGaName":1215,"dataGaLocation":1009},"/the-source/",{"text":1226,"config":1227},"Remote",{"href":1228,"dataGaName":1229,"dataGaLocation":1009},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":1231,"items":1232},"Connect",[1233,1238,1243,1248,1253,1258],{"text":1234,"config":1235},"GitLab Services",{"href":1236,"dataGaName":1237,"dataGaLocation":1009},"/services/","services",{"text":1239,"config":1240},"Contribute",{"href":1241,"dataGaName":1242,"dataGaLocation":1009},"https://contributors.gitlab.com","contribute",{"text":1244,"config":1245},"Community",{"href":1246,"dataGaName":1247,"dataGaLocation":1009},"/community/","community",{"text":1249,"config":1250},"Forum",{"href":1251,"dataGaName":1252,"dataGaLocation":1009},"https://forum.gitlab.com/","forum",{"text":1254,"config":1255},"Events",{"href":1256,"dataGaName":1257,"dataGaLocation":1009},"/events/","events",{"text":1259,"config":1260},"Partners",{"href":1261,"dataGaName":1262,"dataGaLocation":1009},"/partners/","partners",{"config":1264,"title":1267,"text":1268,"link":1269},{"background":1265,"textColor":1266},"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":1270,"config":1271},"Read the latest",{"href":1272,"dataGaName":1273,"dataGaLocation":1009},"/whats-new/","whats new",{"text":1275,"config":1276,"menu":1278},"Company",{"dataNavLevelOne":1277},"company",{"type":1060,"columns":1279},[1280],{"items":1281},[1282,1287,1293,1295,1300,1305,1310,1315,1320,1325],{"text":1283,"config":1284},"About",{"href":1285,"dataGaName":1286,"dataGaLocation":1009},"/company/","about",{"text":1288,"config":1289,"footerGa":1292},"Jobs",{"href":1290,"dataGaName":1291,"dataGaLocation":1009},"/jobs/","jobs",{"dataGaName":1291},{"text":1254,"config":1294},{"href":1256,"dataGaName":1257,"dataGaLocation":1009},{"text":1296,"config":1297},"Leadership",{"href":1298,"dataGaName":1299,"dataGaLocation":1009},"/company/team/e-group/","leadership",{"text":1301,"config":1302},"Handbook",{"href":1303,"dataGaName":1304,"dataGaLocation":1009},"https://handbook.gitlab.com/","handbook",{"text":1306,"config":1307},"Investor relations",{"href":1308,"dataGaName":1309,"dataGaLocation":1009},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":1311,"config":1312},"Trust Center",{"href":1313,"dataGaName":1314,"dataGaLocation":1009},"/security/","trust center",{"text":1316,"config":1317},"AI Transparency Center",{"href":1318,"dataGaName":1319,"dataGaLocation":1009},"/ai-transparency-center/","ai transparency center",{"text":1321,"config":1322},"Newsletter",{"href":1323,"dataGaName":1324,"dataGaLocation":1009},"/company/contact/#contact-forms","newsletter",{"text":1326,"config":1327},"Press",{"href":1328,"dataGaName":1329,"dataGaLocation":1009},"/press/","press",{"text":1331,"config":1332,"menu":1333},"Contact us",{"dataNavLevelOne":1277},{"type":1060,"columns":1334},[1335],{"items":1336},[1337,1342,1347],{"text":1338,"config":1339},"Talk to sales",{"href":1340,"dataGaName":1341,"dataGaLocation":1009},"/sales/","talk to sales",{"text":1343,"config":1344},"Support portal",{"href":1345,"dataGaName":1346,"dataGaLocation":1009},"https://support.gitlab.com/hc/en-us","support portal",{"text":1348,"config":1349},"Customer portal",{"href":1350,"dataGaName":1351,"dataGaLocation":1009},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":1353,"login":1354,"suggestions":1361},"Close",{"text":1355,"link":1356},"To search repositories and projects, login to",{"text":1357,"config":1358},"gitlab.com",{"href":1023,"dataGaName":1359,"dataGaLocation":1360},"search login","search",{"text":1362,"default":1363},"Suggestions",[1364,1366,1370,1372,1376,1380],{"text":1040,"config":1365},{"href":1045,"dataGaName":1040,"dataGaLocation":1360},{"text":1367,"config":1368},"Code Suggestions (AI)",{"href":1369,"dataGaName":1367,"dataGaLocation":1360},"/solutions/code-suggestions/",{"text":1076,"config":1371},{"href":1078,"dataGaName":1076,"dataGaLocation":1360},{"text":1373,"config":1374},"GitLab on AWS",{"href":1375,"dataGaName":1373,"dataGaLocation":1360},"/partners/technology-partners/aws/",{"text":1377,"config":1378},"GitLab on Google Cloud",{"href":1379,"dataGaName":1377,"dataGaLocation":1360},"/partners/technology-partners/google-cloud-platform/",{"text":1381,"config":1382},"Why GitLab?",{"href":1053,"dataGaName":1381,"dataGaLocation":1360},{"freeTrial":1384,"mobileIcon":1389,"desktopIcon":1394,"secondaryButton":1397},{"text":1385,"config":1386},"Start free trial",{"href":1387,"dataGaName":1014,"dataGaLocation":1388},"https://gitlab.com/-/trials/new/","nav",{"altText":1390,"config":1391},"Gitlab Icon",{"src":1392,"dataGaName":1393,"dataGaLocation":1388},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1390,"config":1395},{"src":1396,"dataGaName":1393,"dataGaLocation":1388},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1398,"config":1399},"Get Started",{"href":1400,"dataGaName":1401,"dataGaLocation":1388},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1403,"mobileIcon":1407,"desktopIcon":1409},{"text":1404,"config":1405},"Learn more about GitLab Duo",{"href":1045,"dataGaName":1406,"dataGaLocation":1388},"gitlab duo",{"altText":1390,"config":1408},{"src":1392,"dataGaName":1393,"dataGaLocation":1388},{"altText":1390,"config":1410},{"src":1396,"dataGaName":1393,"dataGaLocation":1388},{"button":1412,"mobileIcon":1417,"desktopIcon":1419},{"text":1413,"config":1414},"/switch",{"href":1415,"dataGaName":1416,"dataGaLocation":1388},"#contact","switch",{"altText":1390,"config":1418},{"src":1392,"dataGaName":1393,"dataGaLocation":1388},{"altText":1390,"config":1420},{"src":1421,"dataGaName":1393,"dataGaLocation":1388},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1423,"mobileIcon":1428,"desktopIcon":1430},{"text":1424,"config":1425},"Back to pricing",{"href":1158,"dataGaName":1426,"dataGaLocation":1388,"icon":1427},"back to pricing","GoBack",{"altText":1390,"config":1429},{"src":1392,"dataGaName":1393,"dataGaLocation":1388},{"altText":1390,"config":1431},{"src":1396,"dataGaName":1393,"dataGaLocation":1388},{"title":1433,"titleMobile":1434,"button":1435,"config":1440},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":1051,"config":1436},{"href":1437,"dataGaName":1438,"dataGaLocation":1439},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":1441,"disabled":990},"release",{"data":1443},{"text":1444,"source":1445,"edit":1451,"contribute":1456,"config":1461,"items":1466,"minimal":1676},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1446,"config":1447},"View page source",{"href":1448,"dataGaName":1449,"dataGaLocation":1450},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1452,"config":1453},"Edit this page",{"href":1454,"dataGaName":1455,"dataGaLocation":1450},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1457,"config":1458},"Please contribute",{"href":1459,"dataGaName":1460,"dataGaLocation":1450},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1462,"facebook":1463,"youtube":1464,"linkedin":1465},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1467,1514,1568,1612,1644],{"title":1156,"links":1468,"subMenu":1483},[1469,1473,1478],{"text":1470,"config":1471},"View plans",{"href":1158,"dataGaName":1472,"dataGaLocation":1450},"view plans",{"text":1474,"config":1475},"Why Premium?",{"href":1476,"dataGaName":1477,"dataGaLocation":1450},"/pricing/premium/","why premium",{"text":1479,"config":1480},"Why Ultimate?",{"href":1481,"dataGaName":1482,"dataGaLocation":1450},"/pricing/ultimate/","why ultimate",[1484],{"title":1485,"links":1486},"Contact Us",[1487,1490,1492,1494,1499,1504,1509],{"text":1488,"config":1489},"Contact sales",{"href":1340,"dataGaName":1019,"dataGaLocation":1450},{"text":1343,"config":1491},{"href":1345,"dataGaName":1346,"dataGaLocation":1450},{"text":1348,"config":1493},{"href":1350,"dataGaName":1351,"dataGaLocation":1450},{"text":1495,"config":1496},"Status",{"href":1497,"dataGaName":1498,"dataGaLocation":1450},"https://status.gitlab.com/","status",{"text":1500,"config":1501},"Terms of use",{"href":1502,"dataGaName":1503,"dataGaLocation":1450},"/terms/","terms of use",{"text":1505,"config":1506},"Privacy statement",{"href":1507,"dataGaName":1508,"dataGaLocation":1450},"/privacy/","privacy statement",{"text":1510,"config":1511},"Cookie preferences",{"dataGaName":1512,"dataGaLocation":1450,"id":1513,"isOneTrustButton":157},"cookie preferences","ot-sdk-btn",{"title":1056,"links":1515,"subMenu":1524},[1516,1520],{"text":1517,"config":1518},"DevSecOps platform",{"href":1038,"dataGaName":1519,"dataGaLocation":1450},"devsecops platform",{"text":1521,"config":1522},"AI-Assisted Development",{"href":1045,"dataGaName":1523,"dataGaLocation":1450},"ai-assisted development",[1525],{"title":1526,"links":1527},"Topics",[1528,1533,1538,1543,1548,1553,1558,1563],{"text":1529,"config":1530},"CICD",{"href":1531,"dataGaName":1532,"dataGaLocation":1450},"/topics/ci-cd/","cicd",{"text":1534,"config":1535},"GitOps",{"href":1536,"dataGaName":1537,"dataGaLocation":1450},"/topics/gitops/","gitops",{"text":1539,"config":1540},"DevOps",{"href":1541,"dataGaName":1542,"dataGaLocation":1450},"/topics/devops/","devops",{"text":1544,"config":1545},"Version Control",{"href":1546,"dataGaName":1547,"dataGaLocation":1450},"/topics/version-control/","version control",{"text":1549,"config":1550},"DevSecOps",{"href":1551,"dataGaName":1552,"dataGaLocation":1450},"/topics/devsecops/","devsecops",{"text":1554,"config":1555},"Cloud Native",{"href":1556,"dataGaName":1557,"dataGaLocation":1450},"/topics/cloud-native/","cloud native",{"text":1559,"config":1560},"AI for Coding",{"href":1561,"dataGaName":1562,"dataGaLocation":1450},"/topics/devops/ai-for-coding/","ai for coding",{"text":1564,"config":1565},"Agentic AI",{"href":1566,"dataGaName":1567,"dataGaLocation":1450},"/topics/agentic-ai/","agentic ai",{"title":1569,"links":1570},"Solutions",[1571,1573,1575,1580,1584,1587,1591,1594,1596,1599,1602,1607],{"text":1100,"config":1572},{"href":1095,"dataGaName":1100,"dataGaLocation":1450},{"text":1089,"config":1574},{"href":1072,"dataGaName":1073,"dataGaLocation":1450},{"text":1576,"config":1577},"Agile development",{"href":1578,"dataGaName":1579,"dataGaLocation":1450},"/solutions/agile-delivery/","agile delivery",{"text":1581,"config":1582},"SCM",{"href":1085,"dataGaName":1583,"dataGaLocation":1450},"source code management",{"text":1529,"config":1585},{"href":1078,"dataGaName":1586,"dataGaLocation":1450},"continuous integration & delivery",{"text":1588,"config":1589},"Value stream management",{"href":1128,"dataGaName":1590,"dataGaLocation":1450},"value stream management",{"text":1534,"config":1592},{"href":1593,"dataGaName":1537,"dataGaLocation":1450},"/solutions/gitops/",{"text":1138,"config":1595},{"href":1141,"dataGaName":1142,"dataGaLocation":1450},{"text":1597,"config":1598},"Small business",{"href":1147,"dataGaName":1148,"dataGaLocation":1450},{"text":1600,"config":1601},"Public sector",{"href":1153,"dataGaName":1154,"dataGaLocation":1450},{"text":1603,"config":1604},"Education",{"href":1605,"dataGaName":1606,"dataGaLocation":1450},"/solutions/education/","education",{"text":1608,"config":1609},"Financial services",{"href":1610,"dataGaName":1611,"dataGaLocation":1450},"/solutions/finance/","financial services",{"title":1161,"links":1613},[1614,1616,1618,1620,1623,1625,1628,1630,1632,1634,1636,1638,1640,1642],{"text":1174,"config":1615},{"href":1176,"dataGaName":1177,"dataGaLocation":1450},{"text":1179,"config":1617},{"href":1181,"dataGaName":1182,"dataGaLocation":1450},{"text":1184,"config":1619},{"href":1186,"dataGaName":1187,"dataGaLocation":1450},{"text":1189,"config":1621},{"href":1191,"dataGaName":1622,"dataGaLocation":1450},"docs",{"text":1212,"config":1624},{"href":1214,"dataGaName":1215,"dataGaLocation":1450},{"text":1626,"config":1627},"What's new",{"href":1272,"dataGaName":1273,"dataGaLocation":1450},{"text":1207,"config":1629},{"href":1209,"dataGaName":1210,"dataGaLocation":1450},{"text":1226,"config":1631},{"href":1228,"dataGaName":1229,"dataGaLocation":1450},{"text":1234,"config":1633},{"href":1236,"dataGaName":1237,"dataGaLocation":1450},{"text":1239,"config":1635},{"href":1241,"dataGaName":1242,"dataGaLocation":1450},{"text":1244,"config":1637},{"href":1246,"dataGaName":1247,"dataGaLocation":1450},{"text":1249,"config":1639},{"href":1251,"dataGaName":1252,"dataGaLocation":1450},{"text":1254,"config":1641},{"href":1256,"dataGaName":1257,"dataGaLocation":1450},{"text":1259,"config":1643},{"href":1261,"dataGaName":1262,"dataGaLocation":1450},{"title":1275,"links":1645},[1646,1648,1650,1652,1654,1656,1660,1665,1667,1669,1671],{"text":1283,"config":1647},{"href":1285,"dataGaName":1277,"dataGaLocation":1450},{"text":1288,"config":1649},{"href":1290,"dataGaName":1291,"dataGaLocation":1450},{"text":1296,"config":1651},{"href":1298,"dataGaName":1299,"dataGaLocation":1450},{"text":1301,"config":1653},{"href":1303,"dataGaName":1304,"dataGaLocation":1450},{"text":1306,"config":1655},{"href":1308,"dataGaName":1309,"dataGaLocation":1450},{"text":1657,"config":1658},"Sustainability",{"href":1659,"dataGaName":1657,"dataGaLocation":1450},"/sustainability/",{"text":1661,"config":1662},"Diversity, inclusion and belonging (DIB)",{"href":1663,"dataGaName":1664,"dataGaLocation":1450},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":1311,"config":1666},{"href":1313,"dataGaName":1314,"dataGaLocation":1450},{"text":1321,"config":1668},{"href":1323,"dataGaName":1324,"dataGaLocation":1450},{"text":1326,"config":1670},{"href":1328,"dataGaName":1329,"dataGaLocation":1450},{"text":1672,"config":1673},"Modern Slavery Transparency Statement",{"href":1674,"dataGaName":1675,"dataGaLocation":1450},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1677},[1678,1681,1684],{"text":1679,"config":1680},"Terms",{"href":1502,"dataGaName":1503,"dataGaLocation":1450},{"text":1682,"config":1683},"Cookies",{"dataGaName":1512,"dataGaLocation":1450,"id":1513,"isOneTrustButton":157},{"text":1685,"config":1686},"Privacy",{"href":1507,"dataGaName":1508,"dataGaLocation":1450},[1688],{"id":1689,"title":7,"body":989,"config":1690,"content":1692,"description":989,"extension":1696,"meta":1697,"navigation":157,"path":1698,"seo":1699,"stem":1700,"__hash__":1701},"blogAuthors/en-us/blog/authors/chris-moberly.yml",{"template":1691},"BlogAuthor",{"name":7,"config":1693},{"headshot":1694,"ctfId":1695},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664235/Blog/Author%20Headshots/cmoberly-headshot.jpg","cmoberly","yml",{},"/en-us/blog/authors/chris-moberly",{},"en-us/blog/authors/chris-moberly","v83w571hHQ-Pp6FRXLR8j4NJ3-1mcNhD7eif5Q962QY",[1703,1711,1719],{"title":1704,"description":1705,"heroImage":1706,"category":985,"date":1707,"authors":1708,"slug":1710,"externalUrl":989},"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",[1709],"Julian Thome","improved-scope-offset-fingerprinting",{"title":1712,"description":1713,"heroImage":1706,"category":985,"date":1714,"authors":1715,"slug":1718,"externalUrl":989},"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",[1716,1717],"Erick Bajao","Joe Randazzo","gitlab-secrets-manager-add-eso-terraform-api-support",{"title":1720,"description":1721,"heroImage":1722,"category":985,"date":1723,"authors":1724,"slug":1726,"externalUrl":989},"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",[1725],"Alisa Ho","claude-security-and-gitlab",{"promotions":1728},[1729,1743,1755,1766],{"id":1730,"categories":1731,"header":1733,"text":1734,"button":1735,"image":1740},"ai-modernization",[1732],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1736,"config":1737},"Get your AI maturity score",{"href":1738,"dataGaName":1739,"dataGaLocation":1215},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1741},{"src":1742},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1744,"categories":1745,"header":1747,"text":1734,"button":1748,"image":1752},"devops-modernization",[1746,1552],"product","Are you just managing tools or shipping innovation?",{"text":1749,"config":1750},"Get your DevOps maturity score",{"href":1751,"dataGaName":1739,"dataGaLocation":1215},"/assessments/devops-modernization-assessment/",{"config":1753},{"src":1754},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1756,"categories":1757,"header":1758,"text":1734,"button":1759,"image":1763},"security-modernization",[985],"Are you trading speed for security?",{"text":1760,"config":1761},"Get your security maturity score",{"href":1762,"dataGaName":1739,"dataGaLocation":1215},"/assessments/security-modernization-assessment/",{"config":1764},{"src":1765},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1767,"paths":1768,"header":1771,"text":1772,"button":1773,"image":1778},"github-azure-migration",[1769,1770],"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":1774,"config":1775},"See how GitLab compares to GitHub",{"href":1776,"dataGaName":1777,"dataGaLocation":1215},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1779},{"src":1754},{"header":1781,"blurb":1782,"button":1783,"secondaryButton":1788},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1784,"config":1785},"Get your free trial",{"href":1786,"dataGaName":1014,"dataGaLocation":1787},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1488,"config":1789},{"href":1340,"dataGaName":1019,"dataGaLocation":1787},1786734819823]