{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"},{"name":"Jendrik Brack","url":"https://daily-devops.net/authors/jendrik/"}],"description":"Recent content in GitHub Actions and CI/CD Workflows on Daily DevOps \u0026 .NET","favicon":"https://daily-devops.net/images/logo_hu_6465d873dfa490cf.png","feed_url":"https://daily-devops.net/tags/github-actions/feed.json","home_page_url":"https://daily-devops.net/tags/github-actions/","icon":"https://daily-devops.net/images/logo_hu_5926de77762241ba.png","items":[{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eLast month I watched a deployment workflow push code to production with three critical vulnerabilities, a failing integration test, and an API key hardcoded in plain text. The team\u0026rsquo;s response? \u0026ldquo;We\u0026rsquo;ll fix it next sprint.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eTwo weeks later, that API key was on GitHub\u0026rsquo;s leaked secrets list. The vulnerabilities got exploited. And suddenly \u0026ldquo;next sprint\u0026rdquo; became \u0026ldquo;incident response war room.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eThis wasn\u0026rsquo;t some junior developer\u0026rsquo;s side project. This was an enterprise team at a company with security certifications, compliance officers, and a CISO who probably makes more than all of us combined.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s what their pipeline—and probably yours—looks like:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eFailing tests treated as warnings, not blockers\u003c/li\u003e\n\u003cli\u003eVulnerability scans that run but never fail the build\u003c/li\u003e\n\u003cli\u003eSAST (Static Application Security Testing) findings suppressed \u0026ldquo;temporarily\u0026rdquo;\u0026hellip; for six months\u003c/li\u003e\n\u003cli\u003eSecrets committed to Git because \u0026ldquo;it\u0026rsquo;s a private repo\u0026rdquo;\u003c/li\u003e\n\u003cli\u003eZero approval gates between \u003ccode\u003egit push\u003c/code\u003e and production\u003c/li\u003e\n\u003cli\u003eNo rollback plan when everything catches fire\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eEvery single pattern on this list is a breach waiting to happen. And if you\u0026rsquo;re ISO 27001 certified? These patterns also violate Controls A.14.2 and A.18.2—which means your certification is essentially fraudulent.\u003c/p\u003e\n\u003cp\u003eLet me show you what secure deployment actually looks like.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-ship-it-mentality-and-why-its-destroying-you\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#the-ship-it-mentality-and-why-its-destroying-you\" title=\"The \u0026ldquo;Ship It\u0026rdquo; Mentality (And Why It\u0026rsquo;s Destroying You)\"\u003eThe \u0026ldquo;Ship It\u0026rdquo; Mentality (And Why It\u0026rsquo;s Destroying You)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s what most deployment pipelines actually look like. I\u0026rsquo;ve seen this exact pattern—or worse—at companies you\u0026rsquo;ve definitely heard of:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eYOLO Deploy\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranches\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003emain]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003edeploy\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eRun Tests\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003enpm test || echo \u0026#34;Tests failed, deploying anyway\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"c\"\u003e# No vulnerability scanning. No SAST. No secrets detection.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDeploy to Production\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003eAPI_KEY\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;sk-prod-abc123\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# Hardcoded secret in Git history forever\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003enpm run deploy\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eWhat\u0026rsquo;s catastrophically wrong here?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTests don\u0026rsquo;t block anything.\u003c/strong\u003e That \u003ccode\u003e|| echo\u003c/code\u003e pattern means failures get logged but deployment continues. You might as well not have tests at all.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNo security scanning whatsoever.\u003c/strong\u003e No vulnerability checks. No static analysis. No secrets detection. An attacker could commit a backdoor and your pipeline would cheerfully deploy it.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSecrets in plain text.\u003c/strong\u003e That API key is now in your Git history. Forever. Even if you delete the file, anyone with repo access can find it. And if your repo ever gets leaked, cloned, or accessed by a compromised developer machine? Game over.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eZero approval gates.\u003c/strong\u003e Push to main, deploy to production. No human verification. No \u0026ldquo;are we sure about this?\u0026rdquo; checkpoint. Just blind faith that the code works.\u003c/p\u003e\n\u003cp\u003eThis pipeline doesn\u0026rsquo;t just violate security best practices—it violates ISO 27001 Controls A.14.2 (Secure Development) and A.18.2 (Security Reviews). If you have that certification and this is your pipeline, you\u0026rsquo;re one audit away from losing it.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-excuses-and-why-theyre-all-wrong\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#the-excuses-and-why-theyre-all-wrong\" title=\"The Excuses (And Why They\u0026rsquo;re All Wrong)\"\u003eThe Excuses (And Why They\u0026rsquo;re All Wrong)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eI\u0026rsquo;ve heard every rationalization for shipping without security gates. Let me save you the breath.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u0026ldquo;We\u0026rsquo;re a startup—we\u0026rsquo;ll add security later.\u0026rdquo;\u003c/strong\u003e\u003cbr\u003e\nNo, you won\u0026rsquo;t. Technical debt compounds. The \u0026ldquo;later\u0026rdquo; you\u0026rsquo;re imagining never arrives because you\u0026rsquo;re always shipping the next feature. Meanwhile, you\u0026rsquo;re building on a foundation of sand. When you finally try to add security, you\u0026rsquo;ll discover it requires rewriting half your infrastructure.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u0026ldquo;Security scanning slows us down.\u0026rdquo;\u003c/strong\u003e\u003cbr\u003e\nBy 2-5 minutes. That\u0026rsquo;s it. A proper security gate adds minutes to your pipeline. A breach costs weeks of incident response, customer notification, regulatory reporting, and reputation damage. You\u0026rsquo;re not saving time—you\u0026rsquo;re borrowing it at loan shark interest rates.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u0026ldquo;We trust our developers.\u0026rdquo;\u003c/strong\u003e\u003cbr\u003e\nCool. I trust my developers too. I also know that trusted developers make mistakes. They commit secrets by accident. They copy-paste vulnerable code from Stack Overflow. They forget to update dependencies. Trust is not a security control. Automated scanning is.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u0026ldquo;The deadline is more important.\u0026rdquo;\u003c/strong\u003e\u003cbr\u003e\nThan what, exactly? Than not getting breached? Than keeping your certification? Than not explaining to your CEO why customer data is on the dark web? There is no deadline important enough to justify deploying unverified code to production.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s the uncomfortable truth: Teams that bypass security gates don\u0026rsquo;t have a velocity problem. They have a \u003cstrong\u003ediscipline problem\u003c/strong\u003e dressed up as agility.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-actual-security-looks-like\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#what-actual-security-looks-like\" title=\"What Actual Security Looks Like\"\u003eWhat Actual Security Looks Like\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eEnough about what\u0026rsquo;s broken. Here\u0026rsquo;s a GitHub Actions workflow that actually protects your systems. Copy it. Use it. Stop deploying garbage.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eSecure Deployment\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranches\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003emain]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epull_request\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranches\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003emain]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003epermissions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003econtents\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eread\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003esecurity-events\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ewrite\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epull-requests\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003ewrite\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# Gate 1: Tests must pass\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003etests\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/setup-dotnet@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003edotnet-version\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;10.0.x\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet test --configuration Release\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# Gate 2: No vulnerable dependencies\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003edependency-scan\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eneeds\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003etests\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          dotnet list package --vulnerable --include-transitive 2\u0026gt;\u0026amp;1 | tee vuln.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          grep -q \u0026#34;vulnerable packages\u0026#34; vuln.txt \u0026amp;\u0026amp; exit 1 || exit 0\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# Gate 3: SAST analysis\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ecode-scanning\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eneeds\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003etests\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003egithub/codeql-action/init@v3\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003elanguages\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;csharp\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003egithub/codeql-action/autobuild@v3\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003egithub/codeql-action/analyze@v3\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# Gate 4: No hardcoded secrets\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003esecrets-scan\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eneeds\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003etests\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003efetch-depth\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003etrufflesecurity/trufflehog@v3.82.13\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# Staging: All gates must pass\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003edeploy-staging\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eneeds\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003etests, dependency-scan, code-scanning, secrets-scan]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003estaging\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet publish -c Release -o ./publish\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eecho \u0026#34;Deploy to staging\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c\"\u003e# Production: Requires manual approval via GitHub Environment\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003edeploy-production\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eneeds\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edeploy-staging\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eproduction \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# This enforces manual approval\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet publish -c Release -o ./publish\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eecho \u0026#34;Deploy to production\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"c\"\u003e# Audit trail for compliance evidence\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          echo \u0026#34;Deployed by: ${{ github.actor }}\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          echo \u0026#34;Commit: ${{ github.sha }}\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          echo \u0026#34;Timestamp: $(date -Iseconds)\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eWhy this actually works:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eEvery gate is mandatory.\u003c/strong\u003e The \u003ccode\u003eneeds\u003c/code\u003e chain means production deployment literally cannot happen unless tests pass, vulnerabilities are clean, SAST finds nothing critical, and no secrets are detected. There\u0026rsquo;s no \u003ccode\u003econtinue-on-error\u003c/code\u003e escape hatch.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSecrets stay secret.\u003c/strong\u003e No credentials in the workflow file. GitHub environments handle authentication. The \u003ccode\u003epermissions\u003c/code\u003e block enforces least privilege—the workflow can only do what it absolutely needs.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHumans verify production deployments.\u003c/strong\u003e The \u003ccode\u003eenvironment: production\u003c/code\u003e line triggers GitHub\u0026rsquo;s environment protection rules. Someone has to explicitly approve before code hits production. That approval is logged forever.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAudit trail is automatic.\u003c/strong\u003e Every deployment records who approved it, what commit was deployed, and when. When auditors ask for evidence (and they will), you have it.\u003c/p\u003e\n\u003cp\u003eThis satisfies ISO 27001 Controls A.14.2 (Secure Development) and A.18.2 (Security Reviews). But more importantly, it prevents you from deploying exploitable code to production.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"setting-up-the-approval-gate\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#setting-up-the-approval-gate\" title=\"Setting Up the Approval Gate\"\u003eSetting Up the Approval Gate\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThat \u003ccode\u003eenvironment: production\u003c/code\u003e line is where the magic happens. Here\u0026rsquo;s how to configure it:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eRepository Settings → Environments → Create \u0026ldquo;production\u0026rdquo;\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAdd protection rules:\u003c/strong\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eRequired reviewers\u003c/strong\u003e: 1-2 people who aren\u0026rsquo;t the deployer\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDeployment branches\u003c/strong\u003e: \u003ccode\u003emain\u003c/code\u003e only (no deploying random feature branches)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePrevent self-review\u003c/strong\u003e: You can\u0026rsquo;t approve your own deployment\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eNow every production deployment requires a second pair of eyes. Someone has to look at the PR, verify the security gates passed, and explicitly click \u0026ldquo;Approve.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eIs this slower than YOLO deploying? Yes, by about 30 seconds. Is it worth avoiding breaches and audit failures? Obviously.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-evidence-trail-for-when-auditors-come-knocking\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#the-evidence-trail-for-when-auditors-come-knocking\" title=\"The Evidence Trail (For When Auditors Come Knocking)\"\u003eThe Evidence Trail (For When Auditors Come Knocking)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAuditors don\u0026rsquo;t care about your intentions. They want proof. With this setup, you automatically generate:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eTest execution logs\u003c/strong\u003e: Every workflow run shows what tests ran and whether they passed\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eVulnerability scan results\u003c/strong\u003e: CodeQL findings, dependency audit reports, Dependabot alerts\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eApproval records\u003c/strong\u003e: Who approved each production deployment, when, and for what commit\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDeployment history\u003c/strong\u003e: Timestamped record of every production push\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhen an auditor asks \u0026ldquo;How do you ensure security validation before deployment?\u0026rdquo; you don\u0026rsquo;t explain your process. You show them the GitHub Actions logs. Evidence beats explanation.\u003c/p\u003e\n\u003cp\u003eExport these quarterly. Keep them for at least three years. You\u0026rsquo;ll need them.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"ways-teams-screw-this-up\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#ways-teams-screw-this-up\" title=\"Ways Teams Screw This Up\"\u003eWays Teams Screw This Up\u003c/a\u003e\u003c/h2\u003e\n\n\n\n\n\u003ch3 id=\"the-continue-on-error-trap\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#the-continue-on-error-trap\" title=\"The \u0026ldquo;continue-on-error\u0026rdquo; Trap\"\u003eThe \u0026ldquo;continue-on-error\u0026rdquo; Trap\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# WRONG: Continuing on error defeats the entire purpose\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eSecurity scan\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003enpm audit\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003econtinue-on-error\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eISO violation:\u003c/strong\u003e If the gate can be bypassed, it\u0026rsquo;s not a control. Control A.14.2 requires enforced security measures, not suggestions.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFix:\u003c/strong\u003e Remove \u003ccode\u003econtinue-on-error\u003c/code\u003e. If the scan finds issues, deployment \u003cstrong\u003emust\u003c/strong\u003e halt until remediated.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"accidentally-logging-secrets\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#accidentally-logging-secrets\" title=\"Accidentally Logging Secrets\"\u003eAccidentally Logging Secrets\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# WRONG: Secrets in environment variables are still visible in logs if misused\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDeploy\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eAPI_KEY\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e${{ secrets.API_KEY }}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eecho \u0026#34;Using key $API_KEY\u0026#34; \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# Logs the secret!\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003eISO violation:\u003c/strong\u003e Control A.9.4.5 requires protection of authentication credentials. Accidental logging exposes secrets.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFix:\u003c/strong\u003e Never reference secrets in commands that might log them. Use secrets only in secure contexts:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDeploy\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eAPI_KEY\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e${{ secrets.API_KEY }}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e./deploy.sh \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# Script uses $API_KEY internally, never echoed\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"no-rollback-plan\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#no-rollback-plan\" title=\"No Rollback Plan\"\u003eNo Rollback Plan\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eYou deployed. It\u0026rsquo;s broken. Now what? If your answer is \u0026ldquo;panic,\u0026rdquo; you\u0026rsquo;ve already failed.\u003c/p\u003e\n\u003cp\u003eEvery production deployment needs a rollback strategy. Ideally automated—health check fails, previous version gets redeployed. At minimum, documented and tested.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"deploying-from-random-branches\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#deploying-from-random-branches\" title=\"Deploying from Random Branches\"\u003eDeploying from Random Branches\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# WRONG: Any branch can deploy to production\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003ebranches\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;**\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eIf feature branches can deploy to production, your approval gates are meaningless. Someone can push to \u003ccode\u003emy-feature-branch\u003c/code\u003e and bypass every review.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFix:\u003c/strong\u003e Lock production deployments to \u003ccode\u003emain\u003c/code\u003e only. Use GitHub environment branch restrictions to enforce it.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"going-further-because-compliance-is-the-floor-not-the-ceiling\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#going-further-because-compliance-is-the-floor-not-the-ceiling\" title=\"Going Further (Because Compliance Is the Floor, Not the Ceiling)\"\u003eGoing Further (Because Compliance Is the Floor, Not the Ceiling)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe workflow above is the minimum. Here\u0026rsquo;s how to make it actually effective:\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"fail-on-real-thresholds\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#fail-on-real-thresholds\" title=\"Fail on Real Thresholds\"\u003eFail on Real Thresholds\u003c/a\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eCode coverage:\u003c/strong\u003e 70% minimum. Below that, you\u0026rsquo;re guessing whether code works.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSAST severity:\u003c/strong\u003e Block on critical and high. Medium can be warnings.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDependencies:\u003c/strong\u003e Zero critical CVEs (Common Vulnerabilities and Exposures). Period.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSecrets:\u003c/strong\u003e Any detection = immediate failure. No exceptions.\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch3 id=\"catch-problems-earlier\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#catch-problems-earlier\" title=\"Catch Problems Earlier\"\u003eCatch Problems Earlier\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eSecurity gates at deployment time are good. Security gates at commit time are better:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ePre-commit hooks for secrets detection (find them before they hit the repo)\u003c/li\u003e\n\u003cli\u003ePR checks for SAST and tests (fail fast, fix fast)\u003c/li\u003e\n\u003cli\u003eDependabot alerts enabled and actually triaged (not just ignored)\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch3 id=\"handle-exceptions-properly\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#handle-exceptions-properly\" title=\"Handle Exceptions Properly\"\u003eHandle Exceptions Properly\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eSometimes you genuinely need to deploy with a known medium-severity issue. That\u0026rsquo;s fine—but document it:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eWritten justification (why this can\u0026rsquo;t wait)\u003c/li\u003e\n\u003cli\u003eApproval from someone who isn\u0026rsquo;t the developer\u003c/li\u003e\n\u003cli\u003eDeadline for remediation (not \u0026ldquo;someday\u0026rdquo;)\u003c/li\u003e\n\u003cli\u003eTracked in your issue system\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe gate stays. You just document why you\u0026rsquo;re accepting the risk.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"stop-making-excuses\"\u003e\u003ca href=\"/posts/continuous-deployment-security-gates/#stop-making-excuses\" title=\"Stop Making Excuses\"\u003eStop Making Excuses\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe workflow I showed you isn\u0026rsquo;t complicated. It\u0026rsquo;s maybe 70 lines of YAML. Copy it, adapt it to your stack, enforce it.\u003c/p\u003e\n\u003cp\u003eWhat\u0026rsquo;s hard isn\u0026rsquo;t the technical implementation. What\u0026rsquo;s hard is the discipline to keep the gates closed when someone with a title says \u0026ldquo;we need to ship NOW.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eBut here\u0026rsquo;s what I\u0026rsquo;ve learned from 15 years of watching deployments go wrong: \u003cstrong\u003ethe shortcuts always cost more than the delay\u003c/strong\u003e. The security scan you skipped catches the vulnerability that gets exploited. The approval you bypassed would have noticed the breaking change. The rollback you didn\u0026rsquo;t plan for becomes a 3 AM incident.\u003c/p\u003e\n\u003cp\u003eSecurity gates aren\u0026rsquo;t bureaucracy. They\u0026rsquo;re the engineering discipline that separates professionals from gamblers.\u003c/p\u003e\n\u003cp\u003eImplement them. Enforce them. Stop deploying garbage to production.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-03-26T17:00:00+01:00","id":"https://daily-devops.net/posts/continuous-deployment-security-gates/","language":"en","summary":"Failing tests as warnings, secrets in Git, no approvals. Build GitHub Actions gates that enforce ISO 27001 A.14.2 and A.18.2 before production.\n","tags":["iso-standards","security","github-actions","devops","cicd"],"title":"Stop Deploying Garbage to Production\n","url":"https://daily-devops.net/posts/continuous-deployment-security-gates/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eYou discover on a Tuesday morning that an authentication anomaly in your production API has been happening since last Thursday. Five days of potential unauthorized access. Your team learns about it from a customer complaint, not from your monitoring. Your \u0026ldquo;incident response plan\u0026rdquo; is a Word document last updated 18 months ago. The on-call engineer is unavailable. Nobody remembers where the rollback runbooks are stored.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t a hypothetical nightmare scenario—this is what happens when incident response is a manual process instead of an automated system.\u003c/p\u003e\n\u003cp\u003eISO/IEC 27001 Control A.16 (Information security incident management) and A.17.1 (Information security continuity) don\u0026rsquo;t merely suggest having incident response procedures. They mandate documented, tested, and continuously improved processes for detecting, responding to, and learning from security incidents. The standard recognizes a fundamental truth: incidents aren\u0026rsquo;t just data breaches or server compromises. They include failed deployments that expose sensitive data, configuration drift that opens attack vectors, dependency vulnerabilities that attackers exploit, and authentication anomalies that signal reconnaissance attempts.\u003c/p\u003e\n\u003cp\u003eThe challenge isn\u0026rsquo;t writing an incident response plan. Every organization has one gathering digital dust. The challenge is making incident response actually work when the production environment is on fire, the security team is in different time zones, and every minute of undetected compromise expands the blast radius.\u003c/p\u003e\n\u003cp\u003eGitHub Actions transforms incident response from a theoretical document into an executable workflow. When properly configured, it detects security events in real-time, automatically creates structured incident tickets with severity classification, executes rollback procedures without human intervention, notifies on-call rotations through the channels they actually monitor, and generates immutable audit logs that satisfy both ISO 27001 evidence requirements and post-incident analysis.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t about replacing security teams with automation. It\u0026rsquo;s about ensuring that when incidents occur—and they will occur—the initial detection, triage, and containment happen in seconds instead of days.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-fatal-pattern-when-incident-response-is-a-manual-afterthought\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-fatal-pattern-when-incident-response-is-a-manual-afterthought\" title=\"The Fatal Pattern: When Incident Response Is a Manual Afterthought\"\u003eThe Fatal Pattern: When Incident Response Is a Manual Afterthought\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s examine what \u0026ldquo;incident response\u0026rdquo; looks like in organizations that haven\u0026rsquo;t automated their processes. This is the baseline against which ISO 27001 auditors measure your security maturity.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-manual-incident-response-anti-pattern\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-manual-incident-response-anti-pattern\" title=\"The Manual Incident Response Anti-Pattern\"\u003eThe Manual Incident Response Anti-Pattern\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# ❌ Manual Incident Response Reality\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Detection: Quarterly audits (if someone remembers)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Notification: Email to security-team@company.com (4 recipients left months ago)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Response: Find someone with prod access. Hope they\u0026#39;re awake. Pray.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Patching: \u0026#34;When someone has time\u0026#34; (never)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Postmortem: Meeting notes nobody reads\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Results:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Time to Detection: Days to weeks\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Time to Response: Hours to days\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Recurrence Rate: High\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis isn\u0026rsquo;t an exaggeration. This is the documented reality in organizations where incident response remains a manual process. The incident response plan exists as a compliance artifact, not as an operational system. When actual incidents occur, teams improvise under pressure, miss critical steps, and create gaps that both attackers and auditors exploit.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-real-cost-hidden-until-it-isnt\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-real-cost-hidden-until-it-isnt\" title=\"The Real Cost: Hidden Until It Isn\u0026rsquo;t\"\u003eThe Real Cost: Hidden Until It Isn\u0026rsquo;t\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe fatal flaw of manual incident response isn\u0026rsquo;t that it never works—it\u0026rsquo;s that it fails unpredictably and catastrophically:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDiscovery Latency\u003c/strong\u003e: Security incidents discovered manually weeks after occurrence give attackers extended dwell time to escalate privileges, exfiltrate data, and establish persistence. The 2023 Verizon Data Breach Investigations Report found that 68% of breaches took months to discover. Manual processes don\u0026rsquo;t just delay response—they extend the window of vulnerability.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eKnowledge Silos\u003c/strong\u003e: When incident response procedures live in people\u0026rsquo;s heads instead of executable workflows, your response capability depends on who\u0026rsquo;s available when the incident occurs. The engineer who knows the rollback procedure is on vacation. The security analyst who understands the authentication system left the company three months ago. Tribal knowledge doesn\u0026rsquo;t scale, doesn\u0026rsquo;t survive turnover, and creates single points of failure.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAlert Fatigue and Desensitization\u003c/strong\u003e: Email distribution lists for security alerts create a tragedy of the commons. When everyone is responsible, nobody is responsible. Critical alerts drown in noise. Teams develop learned helplessness—\u0026ldquo;the alerts are always firing, they\u0026rsquo;re probably false positives\u0026rdquo;—until the one real incident gets ignored alongside the noise.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCompliance Theater vs. Operational Reality\u003c/strong\u003e: The incident response plan that satisfies auditors during annual reviews bears no resemblance to what actually happens during incidents. Teams improvise, skip documented steps that don\u0026rsquo;t work in practice, and create shadow processes that aren\u0026rsquo;t captured in compliance documentation. This gap creates audit risk and operational brittleness.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eCompounding Delays\u003c/strong\u003e: Manual processes create cascading delays. Detection takes days. Notification takes hours. Triage takes hours more. Finding the right person with the right access takes more time. Each handoff introduces latency, miscommunication, and opportunities for mistakes. By the time response actions execute, the incident has evolved into a crisis.\u003c/p\u003e\n\u003cp\u003eISO 27001 A.16.1 requires \u0026ldquo;management responsibilities and procedures shall be established to ensure a quick, effective and orderly response to information security incidents.\u0026rdquo; The standard doesn\u0026rsquo;t specify \u003cem\u003ehow\u003c/em\u003e to achieve this, but it does mandate measurable effectiveness. Manual processes can\u0026rsquo;t deliver the speed, consistency, or evidence trail that both operational excellence and compliance require.\u003c/p\u003e\n\u003cp\u003eThe alternative isn\u0026rsquo;t theoretical. It\u0026rsquo;s automatable, testable, and already implemented in organizations that treat incident response as a system engineering problem rather than a documentation exercise.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-solution-automated-incident-response-with-github-actions\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-solution-automated-incident-response-with-github-actions\" title=\"The Solution: Automated Incident Response with GitHub Actions\"\u003eThe Solution: Automated Incident Response with GitHub Actions\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eEffective incident response requires four capabilities: rapid detection, structured notification, automated containment, and systematic learning. GitHub Actions provides the automation substrate to deliver all four with auditability that satisfies ISO 27001 evidence requirements.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"architecture-event-driven-incident-detection\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#architecture-event-driven-incident-detection\" title=\"Architecture: Event-Driven Incident Detection\"\u003eArchitecture: Event-Driven Incident Detection\u003c/a\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# ✅ Automated incident detection and response\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eSecurity Incident Detection and Response\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003erepository_vulnerability_alert\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003etypes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003ecreate]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eworkflow_run\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eworkflows\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Production Deploy\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003etypes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003ecompleted]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eschedule\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e- \u003cspan class=\"nt\"\u003ecron\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;0 */6 * * *\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eworkflow_dispatch\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003einputs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eincident_type\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003echoice\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"l\"\u003eauthentication_anomaly, failed_deployment, dependency_vulnerability]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003edetect-auth-anomalies\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eQuery Application Insights\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003equery\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eazure/CLI@v2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003einlineScript\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            QUERY=\u0026#39;requests \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            | where timestamp \u0026gt; ago(5m) and name contains \u0026#34;auth\u0026#34; and success == false\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            | summarize FailureCount=count() by client_IP\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            | where FailureCount \u0026gt; 100\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            RESULT=$(az monitor app-insights query --app ${{ secrets.APPINSIGHTS_ID }} --analytics-query \u0026#34;$QUERY\u0026#34; -o json)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            echo \u0026#34;detected=$([ $(echo $RESULT | jq \u0026#39;.tables[0].rows | length\u0026#39;) -gt 0 ] \u0026amp;\u0026amp; echo true || echo false)\u0026#34; \u0026gt;\u0026gt; $GITHUB_OUTPUT\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eCreate Incident Issue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003esteps.query.outputs.detected == \u0026#39;true\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/github-script@v7\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            await github.rest.issues.create({\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              owner: context.repo.owner,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              repo: context.repo.repo,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              title: `[CRITICAL] Auth Anomaly - ${new Date().toISOString()}`,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              body: `## Security Incident\\n\\n**ISO 27001 Control**: A.16.1\\n\\n### Actions\\n- [ ] Investigate IPs\\n- [ ] Apply rate limiting`,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              labels: [\u0026#39;security-incident\u0026#39;, \u0026#39;critical\u0026#39;]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            });\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003eauto-rollback\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003egithub.event.workflow_run.conclusion == \u0026#39;failure\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v4\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003efetch-depth\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"m\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eRollback to Last Good Commit\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          LAST_GOOD=$(git log --format=\u0026#34;%H\u0026#34; --grep=\u0026#34;deploy: success\u0026#34; -1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          git revert --no-commit $LAST_GOOD..${{ github.sha }}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          git commit -m \u0026#34;chore: auto-rollback [skip ci]\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e          git push origin HEAD:main\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eCreate Incident Issue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/github-script@v7\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003escript\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e\u003cspan class=\"sd\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            await github.rest.issues.create({\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              owner: context.repo.owner,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              repo: context.repo.repo,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              title: `[HIGH] Auto-Rollback - ${new Date().toISOString()}`,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              body: `## Rollback Executed\\n\\n**Failed**: \\`${{ github.sha }}\\`\\n**ISO 27001**: A.17.1\\n\\n### Postmortem\\n- [ ] Root cause?\\n- [ ] Prevention?`,\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e              labels: [\u0026#39;security-incident\u0026#39;, \u0026#39;rollback\u0026#39;]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sd\"\u003e            });\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis workflow architecture delivers several capabilities that manual processes cannot:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eReal-Time Detection\u003c/strong\u003e: Security events trigger workflows immediately, not when someone checks a dashboard. Authentication anomalies, deployment failures, and dependency vulnerabilities generate incidents within seconds of occurrence. Detection latency measured in seconds, not days.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eStructured Incident Classification\u003c/strong\u003e: Every incident automatically receives severity classification, ISO 27001 control mapping, unique incident ID, and consistent metadata. No more ambiguous \u0026ldquo;urgent\u0026rdquo; emails that might mean anything from minor configuration drift to active breach.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAutomated Containment\u003c/strong\u003e: Critical incidents trigger automated response actions—rollbacks execute without human intervention, security patches apply automatically for high-severity CVEs, rate limiting engages for authentication anomalies. Containment happens in minutes, not hours.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eImmutable Audit Trail\u003c/strong\u003e: Every incident generates a GitHub Issue with complete timeline, automated actions taken, manual actions required, and ISO 27001 control references. Auditors can trace detection→response→resolution for every incident with Git-backed evidence.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"integration-with-monitoring-application-insights-as-security-sensor\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#integration-with-monitoring-application-insights-as-security-sensor\" title=\"Integration with Monitoring: Application Insights as Security Sensor\"\u003eIntegration with Monitoring: Application Insights as Security Sensor\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe authentication anomaly detection demonstrates a pattern applicable to any monitoring platform. Application Insights becomes a security sensor, not just a performance dashboard:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Query pattern for authentication anomalies\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003erequests \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e where timestamp \u0026gt; ago\u003cspan class=\"o\"\u003e(\u003c/span\u003e5m\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e where name contains \u003cspan class=\"s2\"\u003e\u0026#34;login\u0026#34;\u003c/span\u003e or name contains \u003cspan class=\"s2\"\u003e\u0026#34;auth\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e where \u003cspan class=\"nv\"\u003esuccess\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"nb\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e summarize \u003cspan class=\"nv\"\u003eFailureCount\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003ecount\u003cspan class=\"o\"\u003e()\u003c/span\u003e by client_IP, user_Id\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e|\u003c/span\u003e where FailureCount \u0026gt; \u003cspan class=\"m\"\u003e100\u003c/span\u003e or \u003cspan class=\"o\"\u003e(\u003c/span\u003eisnotempty\u003cspan class=\"o\"\u003e(\u003c/span\u003euser_Id\u003cspan class=\"o\"\u003e)\u003c/span\u003e and FailureCount \u0026gt; 10\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis query identifies two distinct attack patterns: credential stuffing (many failed attempts from single IP) and targeted account compromise (many failed attempts for single user). The workflow executes this query every 6 hours via scheduled trigger, or immediately after authentication-related code deployments.\u003c/p\u003e\n\u003cp\u003eWhen anomalies surface, the automated response includes:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eImmediate notification\u003c/strong\u003e to on-call security team via Slack webhook that they actually monitor\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eStructured incident ticket\u003c/strong\u003e with actionable investigation steps, not vague \u0026ldquo;something\u0026rsquo;s wrong\u0026rdquo; alerts\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSeverity classification\u003c/strong\u003e that distinguishes between rate-limiting evasion attempts (medium) and coordinated breach attempts (critical)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eISO 27001 control mapping\u003c/strong\u003e that satisfies auditor evidence requirements without manual documentation\u003c/li\u003e\n\u003c/ul\u003e\n\n\n\n\n\u003ch3 id=\"automated-rollback-when-code-becomes-incident\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#automated-rollback-when-code-becomes-incident\" title=\"Automated Rollback: When Code Becomes Incident\"\u003eAutomated Rollback: When Code Becomes Incident\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eFailed deployments aren\u0026rsquo;t just DevOps problems—they\u0026rsquo;re security incidents when they expose sensitive data, disable authentication checks, or create configuration vulnerabilities. The rollback workflow treats deployment failures as incidents requiring the same rigor as authentication breaches:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# Rollback procedure: automated, tested, auditable\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"l\"\u003egit log --format=\u0026#34;%H\u0026#34; --grep=\u0026#34;deploy: success\u0026#34; -1 \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# Find last good commit\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"l\"\u003egit revert --no-commit $LAST_GOOD..$CURRENT       \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# Create revert commit\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"l\"\u003egit push origin HEAD:main                         \u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# Restore production immediately\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis isn\u0026rsquo;t novel version control usage—it\u0026rsquo;s applying Git semantics to incident response. The last known good state is always recoverable. The rollback action is reversible. The entire incident timeline exists in commit history. Auditors can verify rollback procedures work because they\u0026rsquo;re executed automatically on every deployment failure, not just tested during annual disaster recovery drills.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"the-postmortem-requirement-learning-as-compliance\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-postmortem-requirement-learning-as-compliance\" title=\"The Postmortem Requirement: Learning as Compliance\"\u003eThe Postmortem Requirement: Learning as Compliance\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eISO 27001 A.16.1 requires \u0026ldquo;information security incidents shall be assessed and it shall be decided if they are to be classified as information security incidents.\u0026rdquo; This assessment isn\u0026rsquo;t optional—it\u0026rsquo;s how organizations demonstrate continuous improvement.\u003c/p\u003e\n\u003cp\u003eThe incident ticket template includes a postmortem structure:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-markdown\" data-lang=\"markdown\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"gu\"\u003e### Postmortem Template\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What was the intended change?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What actually happened?  \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What was the root cause?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e How was it detected?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e How was it resolved?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What prevented earlier detection?\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003e- [ ]\u003c/span\u003e What will prevent recurrence?\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThis isn\u0026rsquo;t bureaucratic box-checking. These questions systematically capture knowledge that prevents incident recurrence. The answers inform:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eDetection improvements\u003c/strong\u003e: \u0026ldquo;What prevented earlier detection?\u0026rdquo; identifies monitoring gaps\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eResponse improvements\u003c/strong\u003e: \u0026ldquo;How was it resolved?\u0026rdquo; documents effective response patterns\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePrevention improvements\u003c/strong\u003e: \u0026ldquo;What will prevent recurrence?\u0026rdquo; drives architectural changes\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWhen postmortems are structured GitHub Issues instead of meeting notes, they become searchable, linkable, and trackable. Teams can reference previous incidents when designing new features. Patterns emerge across incidents that single events don\u0026rsquo;t reveal. Knowledge persists beyond employee tenure.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"iso-27001-compliance-evidence-audit-trail-without-theater\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#iso-27001-compliance-evidence-audit-trail-without-theater\" title=\"ISO 27001 Compliance Evidence: Audit Trail Without Theater\"\u003eISO 27001 Compliance Evidence: Audit Trail Without Theater\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAuditors evaluating ISO 27001 A.16 compliance ask three questions:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eHow do you detect incidents?\u003c/strong\u003e Manual monitoring vs. automated detection with defined thresholds\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eHow do you respond to incidents?\u003c/strong\u003e Ad-hoc improvisation vs. documented, tested procedures\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eHow do you improve after incidents?\u003c/strong\u003e Verbal discussions vs. systematic postmortem analysis\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eGitHub Actions provides auditable evidence for all three:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDetection Evidence\u003c/strong\u003e: Workflow run history shows detection workflows executing on schedule, incident tickets created with timestamps and triggering events, queries executed against monitoring platforms. Auditors can verify detection capabilities are tested continuously, not just demonstrated during audits.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResponse Evidence\u003c/strong\u003e: GitHub Issues contain complete incident timeline from detection through resolution, automated actions taken (rollback commits, security patches, notifications), manual actions performed (investigation notes, coordination with external teams), and closure criteria (how was resolution verified?).\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eImprovement Evidence\u003c/strong\u003e: Postmortem issues linked to infrastructure changes, new detection workflows added after incident gaps identified, and rollback procedures refined based on actual incident experience. The Git history of workflow files shows incident response procedures evolving over time.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"github-audit-log-the-compliance-requirement-you-already-satisfy\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#github-audit-log-the-compliance-requirement-you-already-satisfy\" title=\"GitHub Audit Log: The Compliance Requirement You Already Satisfy\"\u003eGitHub Audit Log: The Compliance Requirement You Already Satisfy\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eISO 27001 A.16.1.7 requires \u0026ldquo;organizations shall establish procedures for the collection and preservation of evidence.\u0026rdquo; GitHub\u0026rsquo;s audit log automatically provides:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eWho\u003c/strong\u003e triggered security workflows (user attribution)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWhat\u003c/strong\u003e actions were taken (workflow execution logs)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWhen\u003c/strong\u003e incidents occurred (immutable timestamps)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWhy\u003c/strong\u003e actions were automated vs. manual (workflow trigger events)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThis audit trail is tamper-evident, continuously available, and doesn\u0026rsquo;t require additional infrastructure. Organizations already using GitHub for source control satisfy evidence requirements without additional compliance tooling—if they architect incident response as code instead of documentation.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"practical-implementation-from-theory-to-production\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#practical-implementation-from-theory-to-production\" title=\"Practical Implementation: From Theory to Production\"\u003ePractical Implementation: From Theory to Production\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe workflow examples demonstrate capabilities, not prescriptive templates. Actual implementation requires adapting these patterns to your specific infrastructure, monitoring platforms, and organizational constraints.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"start-with-one-incident-type\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#start-with-one-incident-type\" title=\"Start with One Incident Type\"\u003eStart with One Incident Type\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eDon\u0026rsquo;t attempt to automate all incident response simultaneously. Start with the incident type that\u0026rsquo;s both frequent enough to test regularly and impactful enough to justify automation effort:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFrequent incidents\u003c/strong\u003e: Failed deployments, dependency vulnerabilities, configuration drift\n\u003cstrong\u003eImpactful incidents\u003c/strong\u003e: Authentication anomalies, data exposure, privilege escalation\u003c/p\u003e\n\u003cp\u003eA production-ready starting point: automated rollback on deployment failure. This incident type occurs naturally during development, doesn\u0026rsquo;t require complex security instrumentation, and provides immediate value (reduced downtime) while building compliance evidence.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"test-incident-response-before-incidents-occur\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#test-incident-response-before-incidents-occur\" title=\"Test Incident Response Before Incidents Occur\"\u003eTest Incident Response Before Incidents Occur\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe \u003ccode\u003eworkflow_dispatch\u003c/code\u003e trigger enables incident simulation:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eworkflow_dispatch\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003einputs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eincident_type\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003edescription\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;Type of incident to simulate\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003echoice\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nt\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"l\"\u003eauthentication_anomaly\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"l\"\u003econfiguration_drift  \u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e- \u003cspan class=\"l\"\u003efailed_deployment\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eQuarterly incident response testing becomes: trigger the workflow manually, verify incident ticket creation, validate notification delivery, confirm automated containment actions execute correctly, and review postmortem template completeness.\u003c/p\u003e\n\u003cp\u003eThis simulation satisfies ISO 27001\u0026rsquo;s requirement for testing incident response procedures while building team familiarity with automated workflows before actual incidents occur.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"integrate-gradually-with-existing-tools\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#integrate-gradually-with-existing-tools\" title=\"Integrate Gradually with Existing Tools\"\u003eIntegrate Gradually with Existing Tools\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eOrganizations already have monitoring platforms (Datadog, New Relic, Azure Monitor), ticketing systems (Jira, ServiceNow), and notification channels (Slack, Teams, PagerDuty). GitHub Actions integrates with all of them:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eMonitoring integration\u003c/strong\u003e: Azure CLI action, Datadog API, custom API queries via \u003ccode\u003ecurl\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTicketing integration\u003c/strong\u003e: REST APIs, webhook notifications, email gateways\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eNotification integration\u003c/strong\u003e: Slack actions, Teams webhooks, PagerDuty events\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe authentication anomaly workflow uses Application Insights, but the same pattern applies to any monitoring platform that exposes query APIs. Replace the Azure CLI step with your monitoring platform\u0026rsquo;s equivalent.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-honest-assessment-what-automation-cannot-do\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#the-honest-assessment-what-automation-cannot-do\" title=\"The Honest Assessment: What Automation Cannot Do\"\u003eThe Honest Assessment: What Automation Cannot Do\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eAutomated incident response isn\u0026rsquo;t a replacement for security expertise. It\u0026rsquo;s a force multiplier that ensures the initial detection, triage, and containment happen reliably when experts aren\u0026rsquo;t immediately available.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAutomation cannot\u003c/strong\u003e: Determine if an authentication anomaly is a credential stuffing attack vs. legitimate user behavior (requires context human analysts provide). Decide if a failed deployment justifies rollback vs. forward-fix (depends on business risk assessment). Coordinate incident response across organizational boundaries (legal, PR, customer support). Perform root cause analysis beyond correlation (distinguishing causation from coincidence requires domain knowledge).\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAutomation excels at\u003c/strong\u003e: Detecting predefined patterns in monitoring data at scale. Executing documented procedures consistently under pressure. Creating structured evidence trails for compliance and analysis. Reducing time-to-containment for known incident types.\u003c/p\u003e\n\u003cp\u003eThe combination—automated detection and containment plus human expertise for investigation and improvement—delivers both operational resilience and compliance evidence. Neither component alone suffices.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"conclusion-incident-response-as-engineering-not-theater\"\u003e\u003ca href=\"/posts/incident-response-github-actions/#conclusion-incident-response-as-engineering-not-theater\" title=\"Conclusion: Incident Response as Engineering, Not Theater\"\u003eConclusion: Incident Response as Engineering, Not Theater\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eISO 27001 A.16 doesn\u0026rsquo;t mandate specific tools or technologies. It mandates effective incident management with evidence of continuous improvement. Organizations can satisfy this requirement with manual processes and documentation—but manual processes fail unpredictably when incidents occur outside business hours, involve unfamiliar attack vectors, or overwhelm available staff.\u003c/p\u003e\n\u003cp\u003eGitHub Actions transforms incident response from a compliance document into an operational system. Detection happens automatically. Notifications reach on-call teams through channels they monitor. Containment actions execute without requiring production access credentials shared across teams. Audit trails generate automatically without manual documentation overhead.\u003c/p\u003e\n\u003cp\u003eThis isn\u0026rsquo;t about replacing security teams with YAML files. It\u0026rsquo;s about ensuring that when Tuesday\u0026rsquo;s authentication anomaly occurs, your organization detects it on Tuesday—not the following Monday when a customer complains. The difference between seconds-to-detection and days-to-detection determines whether an incident is a containable event or a reportable breach.\u003c/p\u003e\n\u003cp\u003eThe incident response plan that satisfies auditors but fails operationally provides neither security nor compliance. Automated incident response provides both, with Git-backed evidence that demonstrates effectiveness rather than intent.\u003c/p\u003e\n\u003cp\u003eISO 27001 requires incident response procedures that actually work when incidents actually occur. GitHub Actions delivers executable procedures with immutable audit trails. The choice between compliant documentation and compliant operations determines which incidents become learning opportunities and which become crisis escalations.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2026-02-12T17:00:00+01:00","id":"https://daily-devops.net/posts/incident-response-github-actions/","language":"en","summary":"ISO 27001 demands effective incident response. GitHub Actions transforms your dusty Word doc into automated workflows that actually work at 3 AM.\n","tags":["security","github-actions","devops","automation","bestpractices","codequality"],"title":"Your Incident Response Plan Is a Lie. Here's How to Fix It.\n","url":"https://daily-devops.net/posts/incident-response-github-actions/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eMicrosoft just did something unusual: \u003cem\u003ethey fixed a problem before most people realized they had it.\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eFor years, \u003ccode\u003edotnet test\u003c/code\u003e wasn\u0026rsquo;t really a test runner—it was actually just a wrapper around \u003ccode\u003evstest.console.exe\u003c/code\u003e, a legacy artifact from the pre-.NET-Core era that Microsoft couldn\u0026rsquo;t quite kill. It worked, mostly, if you didn\u0026rsquo;t think too hard about why your tests sometimes behaved differently in Visual Studio than in GitHub Actions, or why test discovery occasionally took longer than the tests themselves.\u003c/p\u003e\n\u003cp\u003eWith .NET 10, Microsoft has finally integrated testing directly into the SDK through \u003cstrong\u003eMicrosoft.Testing.Platform (MTP)\u003c/strong\u003e. The old VSTest infrastructure is now out. The new system runs tests in-process, unifies behavior across environments, and—this is actually the important part—finally respects your configuration files.\u003c/p\u003e\n\u003cp\u003eThere\u0026rsquo;s a catch, of course. There always is.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"from-test-wrapper-to-test-platform\"\u003e\u003ca href=\"/posts/dotnet-10-testing/#from-test-wrapper-to-test-platform\" title=\"From Test Wrapper to Test Platform\"\u003eFrom Test Wrapper to Test Platform\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eRunning tests in .NET used to mean choosing a framework—\u003ca href=\"https://xunit.net/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003exUnit\u003c/a\u003e, \u003ca href=\"https://nunit.org/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eNUnit\u003c/a\u003e, \u003ca href=\"https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eMSTest\u003c/a\u003e, or the newer \u003ca href=\"https://tunit.dev/\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003eTUnit\u003c/a\u003e—and then essentially just hoping \u003ccode\u003edotnet test\u003c/code\u003e could somehow figure out how to talk to it. Each framework had its own test adapter. Each adapter had its own quirks. Your CI pipeline basically just crossed its fingers and hoped for green checkmarks.\u003c/p\u003e\n\u003cp\u003eThe result? Test execution that varied subtly between your laptop, your colleague\u0026rsquo;s laptop, and the build server. Debugging test failures meant first figuring out \u003cem\u003ewhich version of which adapter was running where\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eMicrosoft.Testing.Platform changes that architecture. Instead of spawning separate processes and negotiating through adapters, MTP embeds the test runner directly into the SDK. Discovery, execution, and reporting now follow a single, predictable path. Tests run in-process. The CLI is cleaner. The performance is measurably better in projects with large test suites.\u003c/p\u003e\n\u003cp\u003eEnabling it requires exactly four lines in your \u003ccode\u003eglobal.json\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026#34;test\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nt\"\u003e\u0026#34;runner\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Microsoft.Testing.Platform\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eNo SDK pinning required. No complicated setup. Just those four lines, and .NET 10 switches to the new test engine automatically.\u003c/p\u003e\n\u003cp\u003eThe simplicity is almost suspicious.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-actually-improves-and-what-doesnt\"\u003e\u003ca href=\"/posts/dotnet-10-testing/#what-actually-improves-and-what-doesnt\" title=\"What Actually Improves (And What Doesn\u0026rsquo;t)\"\u003eWhat Actually Improves (And What Doesn\u0026rsquo;t)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLet\u0026rsquo;s be specific. MTP isn\u0026rsquo;t magic—it\u0026rsquo;s engineering. Here\u0026rsquo;s what changes when you enable it:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTest discovery is faster.\u003c/strong\u003e In a project with ~3,500 tests, discovery dropped from 8 seconds to under 3 on my local machine. That\u0026rsquo;s honestly not earth-shattering, but it\u0026rsquo;s definitely noticeable when you\u0026rsquo;re running focused test sets repeatedly during development. Over a typical workday with 50 test runs? That actually saves roughly 4 minutes. Not revolutionary, but certainly not nothing either.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe CLI makes sense now.\u003c/strong\u003e Previously, \u003ccode\u003edotnet test --filter\u003c/code\u003e required arcane syntax and those bizarre \u003ccode\u003e--\u003c/code\u003e separators to pass arguments through to the adapter. MTP removes that layer of indirection. The commands do what you\u0026rsquo;d expect without translation.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eEnvironment consistency improves.\u003c/strong\u003e Because the test runner is part of the SDK, your local machine and your CI pipeline execute tests the same way—assuming you actually configure your pipeline correctly (more on that disaster shortly).\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBut performance gains aren\u0026rsquo;t universal.\u003c/strong\u003e If your tests are already fast, you probably won\u0026rsquo;t see dramatic improvements. MTP mainly optimizes infrastructure overhead, not slow database calls or badly written assertions. Don\u0026rsquo;t expect miracles if your test suite still takes 20 minutes because it\u0026rsquo;s hitting real APIs.\u003c/p\u003e\n\u003cp\u003eAnd here\u0026rsquo;s the part Microsoft doesn\u0026rsquo;t emphasize: \u003cstrong\u003eMTP won\u0026rsquo;t save you from bad tests.\u003c/strong\u003e If your test suite is flaky, brittle, or poorly isolated, the new platform just runs that mess faster.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"what-about-visual-studio-integration\"\u003e\u003ca href=\"/posts/dotnet-10-testing/#what-about-visual-studio-integration\" title=\"What about Visual Studio integration?\"\u003eWhat about Visual Studio integration?\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eVisual Studio 17.14 or later integrates with MTP. Earlier versions rely on VSTest and may behave differently. If your team uses mixed VS versions, validate results locally with the CLI to avoid IDE-specific discrepancies.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-ci-pipeline-trap-and-how-to-avoid-it\"\u003e\u003ca href=\"/posts/dotnet-10-testing/#the-ci-pipeline-trap-and-how-to-avoid-it\" title=\"The CI Pipeline Trap (And How to Avoid It)\"\u003eThe CI Pipeline Trap (And How to Avoid It)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eHere\u0026rsquo;s where things get entertaining.\u003c/p\u003e\n\u003cp\u003eYou add that \u003ccode\u003eglobal.json\u003c/code\u003e snippet. Tests run perfectly on your machine. You commit, push, and watch your GitHub Actions pipeline\u0026hellip; fail spectacularly.\u003c/p\u003e\n\u003cp\u003eWhy? Because GitHub\u0026rsquo;s hosted runners don\u0026rsquo;t automatically respect your \u003ccode\u003eglobal.json\u003c/code\u003e. They just use whatever SDK version happens to be installed—often an older one that doesn\u0026rsquo;t even support MTP. Your carefully configured local environment and your CI pipeline are now essentially running completely different test infrastructure.\u003c/p\u003e\n\u003cp\u003eI learned this the hard way when a colleague spent two hours debugging \u0026ldquo;flaky\u0026rdquo; tests that weren\u0026rsquo;t actually flaky at all. The tests validated timeout behavior in an async workflow—they passed consistently with MTP locally and then failed consistently with VSTest in CI. Same code, same timeout values, completely different test runner behavior. VSTest\u0026rsquo;s process isolation apparently meant slightly different timing characteristics. We only figured it out after painstakingly comparing the test execution logs line by line and finally noticing the runner version mismatch.\u003c/p\u003e\n\u003cp\u003eThe fix is one line—but you have to know it exists:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/setup-dotnet@v5\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eglobal-json-file\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;./global.json\u0026#39;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e- \u003cspan class=\"nt\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edotnet test\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eThat \u003ccode\u003eglobal-json-file\u003c/code\u003e parameter forces the action to actually read your configuration. Without it, you\u0026rsquo;re deploying tests with one runner and debugging them with another.\u003c/p\u003e\n\u003cp\u003eIf you don\u0026rsquo;t specify this explicitly, your \u003ccode\u003eglobal.json\u003c/code\u003e is basically just decorative. It just sits in your repository looking official while your pipeline ignores it completely. I\u0026rsquo;ve actually seen teams add comments to their \u003ccode\u003eglobal.json\u003c/code\u003e files carefully explaining why certain settings exist, not realizing the entire file wasn\u0026rsquo;t even being used. That\u0026rsquo;s not configuration—that\u0026rsquo;s just theater.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"version-compatibility-or-who-gets-left-behind\"\u003e\u003ca href=\"/posts/dotnet-10-testing/#version-compatibility-or-who-gets-left-behind\" title=\"Version Compatibility (Or: Who Gets Left Behind)\"\u003eVersion Compatibility (Or: Who Gets Left Behind)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eMTP doesn\u0026rsquo;t support every test framework version ever released. Microsoft drew a line, and some older projects sit on the wrong side of it.\u003c/p\u003e\n\u003cp\u003eTo use Microsoft.Testing.Platform, your test frameworks need these minimum versions:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003exUnit\u003c/strong\u003e → Version \u003cstrong\u003e3.x\u003c/strong\u003e or later\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eMSTest\u003c/strong\u003e → Version \u003cstrong\u003e3.2.0\u003c/strong\u003e or later\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eNUnit\u003c/strong\u003e → \u003cstrong\u003eNUnit3TestAdapter 5.0.0\u003c/strong\u003e or later\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTUnit\u003c/strong\u003e → Works out of the box (it was designed with MTP in mind)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eVisual Studio\u003c/strong\u003e → Version \u003cstrong\u003e17.14\u003c/strong\u003e or later for full integration\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIf you\u0026rsquo;re running older versions, the SDK simply won\u0026rsquo;t negotiate. It fails hard. No fallback to VSTest, no warning, just an error message telling you to upgrade.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s actually good design. Ambiguity in test execution creates exactly the kind of \u0026ldquo;works on my machine\u0026rdquo; disasters MTP is supposed to prevent. Better to fail explicitly than to silently run different infrastructure depending on what\u0026rsquo;s installed.\u003c/p\u003e\n\u003cp\u003eBut it does mean migration isn\u0026rsquo;t optional if you\u0026rsquo;re upgrading to .NET 10. You can\u0026rsquo;t enable MTP halfway. Either your entire test suite supports it, or you don\u0026rsquo;t use it at all.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"migration-strategy-or-how-not-to-break-everything\"\u003e\u003ca href=\"/posts/dotnet-10-testing/#migration-strategy-or-how-not-to-break-everything\" title=\"Migration Strategy (Or: How Not to Break Everything)\"\u003eMigration Strategy (Or: How Not to Break Everything)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eMigrating to MTP isn\u0026rsquo;t technically complicated, but it does actually require coordination. You can\u0026rsquo;t just enable it in isolation—everyone on the team needs to be running compatible tools, or the test results will simply stop being reliable.\u003c/p\u003e\n\u003cp\u003eHere\u0026rsquo;s a migration approach that won\u0026rsquo;t cause chaos:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1. Audit your test framework versions first.\u003c/strong\u003e\nCheck every test project. If you\u0026rsquo;re running xUnit 2.x or MSTest 2.x, you\u0026rsquo;re upgrading before you can enable MTP. No shortcuts.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Add the \u003ccode\u003eglobal.json\u003c/code\u003e configuration.\u003c/strong\u003e\nStart with the minimal snippet. You don\u0026rsquo;t need to pin an SDK version unless you have specific compatibility requirements elsewhere.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Update your CI/CD pipelines.\u003c/strong\u003e\nAdd the \u003ccode\u003eglobal-json-file\u003c/code\u003e parameter to your \u003ccode\u003esetup-dotnet\u003c/code\u003e action. Test it on a branch before merging. Verify that the pipeline is actually using MTP by checking the test output logs.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Run tests locally and in CI—compare the results.\u003c/strong\u003e\nIf they differ, you\u0026rsquo;ve found a configuration issue. Fix it now, before it becomes a debugging nightmare three months from now. Pay special attention to tests that involve timing, parallelization, or resource cleanup—these are the ones most likely to behave differently between test runners.\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;ve read \u003ca href=\"/posts/tests-are-lying/\"\u003e\u0026ldquo;Your Tests Are Lying — Mutation Testing in .NET\u0026rdquo;\u003c/a\u003e, you know how dangerous it is when tests pass for the wrong reasons. MTP reduces that risk—but only if your environments are actually configured consistently.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"when-not-to-migrate-yes-really\"\u003e\u003ca href=\"/posts/dotnet-10-testing/#when-not-to-migrate-yes-really\" title=\"When Not to Migrate (Yes, Really)\"\u003eWhen Not to Migrate (Yes, Really)\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eNot every project should rush into MTP. Here are scenarios where you might want to wait:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLegacy test suites with heavy VSTest dependencies.\u003c/strong\u003e If your tests rely on specific VSTest console runners, custom adapters, or undocumented behavior, migration will break things. You\u0026rsquo;ll need to refactor or rewrite parts of your test infrastructure.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eProjects still on .NET 8 LTS.\u003c/strong\u003e MTP is a .NET 10 feature. If you\u0026rsquo;re staying on an LTS version for stability, you\u0026rsquo;re essentially stuck with VSTest. That\u0026rsquo;s fine—VSTest still works. It\u0026rsquo;s just not getting any new features.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTeams without time to validate the migration.\u003c/strong\u003e Half-migrating is worse than not migrating. If you can\u0026rsquo;t dedicate time to verify that tests behave identically across environments, defer the change until you can.\u003c/p\u003e\n\u003cp\u003eMTP is definitely an improvement, but it\u0026rsquo;s not urgent. If your current test infrastructure already works reliably, you\u0026rsquo;re really not missing out by waiting.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"what-this-actually-means-for-your-workflow\"\u003e\u003ca href=\"/posts/dotnet-10-testing/#what-this-actually-means-for-your-workflow\" title=\"What This Actually Means for Your Workflow\"\u003eWhat This Actually Means for Your Workflow\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eThe shift to MTP changes how you think about test configuration. Your \u003ccode\u003eglobal.json\u003c/code\u003e file is no longer just an SDK hint—it\u0026rsquo;s a binding contract. The SDK reads it, respects it, and enforces it. If your pipeline isn\u0026rsquo;t configured to honor that contract, your tests will diverge silently between environments.\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s both the strength and the risk of this change. MTP removes ambiguity, but only if you configure it correctly everywhere. Miss one environment, and you\u0026rsquo;re back to debugging phantom failures that only reproduce in CI.\u003c/p\u003e\n\u003cp\u003eThe good news? Once configured properly, tests become predictable. The bad news? Getting there requires discipline, not just documentation.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"should-you-migrate-now\"\u003e\u003ca href=\"/posts/dotnet-10-testing/#should-you-migrate-now\" title=\"Should You Migrate Now?\"\u003eShould You Migrate Now?\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eIf you\u0026rsquo;re already on .NET 10, yes. The benefits clearly outweigh the setup cost, especially if you\u0026rsquo;ve already dealt with flaky CI pipelines or inconsistent test behavior across environments.\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;re on an LTS version and your tests are stable, there\u0026rsquo;s really no rush. VSTest isn\u0026rsquo;t going anywhere immediately, and MTP will still be there when you eventually upgrade.\u003c/p\u003e\n\u003cp\u003eBut if you\u0026rsquo;re planning to move to .NET 10 anyway, enable MTP early in the migration process. It\u0026rsquo;s easier to validate test behavior during a planned upgrade than to debug it six months later when the root cause has been buried under other changes.\u003c/p\u003e\n\u003cp\u003eAdd the four lines to \u003ccode\u003eglobal.json\u003c/code\u003e. Update your CI config. Upgrade your test frameworks. Run the tests. Compare the results.\u003c/p\u003e\n\u003cp\u003eIf they match—and they should—you\u0026rsquo;re done. If they don\u0026rsquo;t, you\u0026rsquo;ve found a configuration problem that would have bitten you eventually anyway. Better to find it now during a planned migration than at 2 AM when production is down and your tests are lying to you about what\u0026rsquo;s safe to deploy.\u003c/p\u003e\n\u003cp\u003eMicrosoft fixed the test runner. Whether you use it or keep debugging phantom CI failures is your choice—but when the next \u0026ldquo;works on my machine\u0026rdquo; ticket comes in, at least you\u0026rsquo;ll know exactly why.\u003c/p\u003e\n","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2025-11-20T17:00:00+01:00","id":"https://daily-devops.net/posts/dotnet-10-testing/","language":"en","summary":"Microsoft.Testing.Platform replaces VSTest in .NET 10. See what improves, what breaks, and why your global.json now matters in IDE and CI reliably.\n","tags":["testing","dotnet","csharp","softwareengineering","github-actions","devops"],"title":".NET 10 Testing: Microsoft Finally Fixed the Test Runner (Mostly)\n","url":"https://daily-devops.net/posts/dotnet-10-testing/"},{"authors":[{"name":"Martin Stühmer","url":"https://daily-devops.net/authors/martin/"}],"content_html":"\u003cp\u003eIn software development, dependencies are inevitable - any project worth its salt relies on various libraries, frameworks, or packages. However, as I found in my own work, managing these dependencies can be an onerous task. Constant updates, new vulnerabilities, and endless manual approvals were draining my time and focus. What if, I thought, these processes could be automated? This thought led to the creation of \u003ccode\u003edependamerge\u003c/code\u003e, a GitHub Action designed to free developers from the drudgery of manual dependency maintenance and let us get back to what we do best: building great software.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-realities-of-manual-dependency-management-my-journey\"\u003e\u003ca href=\"/posts/dependamerge-action/#the-realities-of-manual-dependency-management-my-journey\" title=\"The realities of manual dependency management: My journey\"\u003eThe realities of manual dependency management: My journey\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eLike many developers, I used to spend a lot of time managing dependencies. Dependabot would helpfully create pull requests for each new release, but I still had to check and merge each one. This quickly became an endless cycle. The hassle of checking every dependency update, even minor ones, pulled me away from critical tasks.\u003c/p\u003e\n\u003cp\u003eThe reality is that as teams grow in size, dependency management becomes increasingly complex. For a while, I was stuck in a manual cycle, balancing the risk of out-of-date dependencies against the time commitment of updates. This tension was a big factor that inspired \u003ccode\u003edependamerge\u003c/code\u003e.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"why-automation-why-now\"\u003e\u003ca href=\"/posts/dependamerge-action/#why-automation-why-now\" title=\"Why automation? Why now?\"\u003eWhy automation? Why now?\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eMy experience echoed the frustrations faced by many developers:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eUnending maintenance\u003c/strong\u003e: Keeping up with dependency updates is like an unrelenting treadmill. Without automation, it’s all too easy for obsolete packages to slip through the cracks.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDisrupted flow\u003c/strong\u003e: Each pull request interrupts the flow, forcing us to context-switch and potentially delaying real progress.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSecurity pressure\u003c/strong\u003e: At a time when vulnerabilities can bring down entire ecosystems, dependency maintenance is non-negotiable, but finding the time to do it can feel impossible.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eProductivity drain\u003c/strong\u003e: Manual dependency management is a time sink, diverting focus from the core work of building and improving software.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTechnical debt\u003c/strong\u003e: Neglected dependencies can accumulate into a significant technical debt, leading to more problems down the line.\u003c/li\u003e\n\u003c/ol\u003e\n\n\n\n\n\u003ch3 id=\"benefits-of-automation\"\u003e\u003ca href=\"/posts/dependamerge-action/#benefits-of-automation\" title=\"Benefits of automation\"\u003eBenefits of automation\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eAutomating dependency management with \u003ccode\u003edependamerge\u003c/code\u003e brings a range of significant benefits that streamline development and enhance code quality:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eTime-Saving\u003c/strong\u003e: By automating dependency updates, \u003ccode\u003edependamerge\u003c/code\u003e saves developers from manually reviewing each pull request. This efficiency frees up hours each week, allowing teams to focus on feature development and innovation rather than getting bogged down by routine maintenance.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eEnhanced Security\u003c/strong\u003e: In today’s landscape, where vulnerabilities can have far-reaching impacts, timely updates are essential for maintaining a secure codebase. With \u003ccode\u003edependamerge\u003c/code\u003e, critical updates can be applied promptly and consistently, helping to protect your projects from potential threats. Automation ensures that nothing slips through the cracks, even when time is limited.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eImproved Code Quality and Stability\u003c/strong\u003e: Automated dependency updates reduce the risk of errors that can occur when manually merging changes across environments. Consistent updates prevent compatibility issues that might arise from neglected dependencies, contributing to a more stable and reliable codebase.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eReduced Technical Debt\u003c/strong\u003e: By keeping dependencies up-to-date, \u003ccode\u003edependamerge\u003c/code\u003e helps prevent the buildup of technical debt that can slow down future development and create unexpected blockers. With fewer outdated dependencies, teams can avoid the last-minute scramble to upgrade critical packages or dependencies right before a major release.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eSeamless Integration in CI/CD Workflows\u003c/strong\u003e: \u003ccode\u003edependamerge\u003c/code\u003e is designed to operate smoothly within a CI/CD pipeline, allowing dependency updates to be tested and validated alongside other code changes. This integration reduces interruptions to the workflow and ensures that updates don’t introduce issues at later stages in the development lifecycle.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eBy automating these repetitive tasks, \u003ccode\u003edependamerge\u003c/code\u003e empowers developers to focus on what matters most: building and improving software. It’s a tool that boosts productivity, enhances security, and ultimately contributes to a more efficient and resilient development process.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"the-solution-dependamerge\"\u003e\u003ca href=\"/posts/dependamerge-action/#the-solution-dependamerge\" title=\"The Solution: dependamerge\"\u003eThe Solution: dependamerge\u003c/a\u003e\u003c/h2\u003e\n\n\n\n\n\u003ch3 id=\"introducing-dependamerge-a-solution-built-for-developers\"\u003e\u003ca href=\"/posts/dependamerge-action/#introducing-dependamerge-a-solution-built-for-developers\" title=\"Introducing dependamerge: A solution built for developers\"\u003eIntroducing dependamerge: A solution built for developers\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eDesigned to take the reins of dependency updates, \u003ccode\u003edependamerge\u003c/code\u003e works with Dependabot to make dependency management truly seamless. This GitHub action doesn\u0026rsquo;t just approve updates—it is adjustable to your project’s specific needs, ensuring that only the right updates are merged at the right time. Even better, \u003ccode\u003edependamerge\u003c/code\u003e can be part of a fully automated CI/CD pipeline, ensuring that dependency updates are tested and validated alongside other code changes.\u003c/p\u003e\n\u003ca href=\"https://github.com/dailydevops/dependamerge-action\" class=\"linked\" target=\"_blank\" rel=\"noopener external noreferrer\" title=\"GitHub action that automatically validates, approves, and merges pull requests for branches created by dependabot[bot]\"\u003e\n  \u003cimg src=\"/images/github-dailydevops-dependamerge-action.png\" class=\"repository\" width=\"1200\" height=\"630\" title=\"GitHub action that automatically validates, approves, and merges pull requests for branches created by dependabot[bot]\" alt=\"GitHub action that automatically validates, approves, and merges pull requests for branches created by dependabot[bot]\" /\u003e\n\u003c/a\u003e\n\u003cp\u003eHighlights of \u003ccode\u003edependamerge\u003c/code\u003e include:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eFully compatible with Dependabot\u003c/strong\u003e: \u003ccode\u003edependamerge\u003c/code\u003e works seamlessly with Dependabot, extending its capabilities and streamlining the update process. To do this, \u003ccode\u003edependamerge\u003c/code\u003e communicates with Dependabot\u0026rsquo;s comment commands to manage the pull requests.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAutomated merging\u003c/strong\u003e: With the ability to define specific merge rules, updates are approved without disrupting your day. Regardless of the ecosystem, all current and future Dependabot ecosystems are supported.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCustomizable conditions\u003c/strong\u003e: Tailor the automation to prioritize critical updates, such as security patches, while handling non-critical updates according to your project’s needs.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eHuman-Free Handling\u003c/strong\u003e: Freeing developers from dependency maintenance not only saves time, but also prevents mental fatigue from routine tasks. \u003ccode\u003edependamerge\u003c/code\u003e ensures that updates are handled consistently and reliably, without manual intervention.\u003c/li\u003e\n\u003c/ol\u003e\n\n\n\n\n\u003ch3 id=\"usage-example-setting-up-dependamerge-in-a-cicd-pipeline\"\u003e\u003ca href=\"/posts/dependamerge-action/#usage-example-setting-up-dependamerge-in-a-cicd-pipeline\" title=\"Usage example: Setting up dependamerge in a CI/CD pipeline\"\u003eUsage example: Setting up dependamerge in a CI/CD pipeline\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eTo start with \u003ccode\u003edependamerge\u003c/code\u003e, you can use the following example configuration. This GitHub action is highly customizable, allowing you to adjust various parameters to suit your project’s specific requirements.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDependaMerge\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003eon\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003epull_request\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ejobs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nt\"\u003edependabot\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eubuntu-latest\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nt\"\u003esteps\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eactions/checkout@v2\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e- \u003cspan class=\"nt\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003eDependaMerge\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003euses\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003edailydevops/action-dependamerge@v1\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"nt\"\u003ewith\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003etoken\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003e${{ secrets.GITHUB_TOKEN }}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nt\"\u003ecommand\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"l\"\u003esquash\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c\"\u003e# Merge all commits into one (default)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\n\n\n\u003ch3 id=\"key-parameters-and-options\"\u003e\u003ca href=\"/posts/dependamerge-action/#key-parameters-and-options\" title=\"Key Parameters and Options\"\u003eKey Parameters and Options\u003c/a\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ecommand\u003c/code\u003e: Specifies how the pull request is merged. Options include:\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003esquash\u003c/code\u003e (default): Combines all commits into one.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003emerge\u003c/code\u003e: Maintains commit history.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003erebase\u003c/code\u003e: Rebases the pull request if it’s behind the target branch.\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eapprove-only\u003c/code\u003e: If set to \u003ccode\u003etrue\u003c/code\u003e, the action will only approve, not merge, the pull request.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003etarget\u003c/code\u003e: Defines the maximum version increment level (\u003ccode\u003emajor\u003c/code\u003e, \u003ccode\u003eminor\u003c/code\u003e, \u003ccode\u003epatch\u003c/code\u003e, or \u003ccode\u003eany\u003c/code\u003e), giving you control over the scope of updates. Default is \u003ccode\u003epatch\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ehandle-dependency-group\u003c/code\u003e: Merges all pull requests in a specified dependency group, allowing related updates to be applied together.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThese configurable options ensure that \u003ccode\u003edependamerge\u003c/code\u003e aligns precisely with your team’s requirements.\u003c/p\u003e\n\n\n\n\n\u003ch3 id=\"output-parameters-understanding-and-utilizing-results\"\u003e\u003ca href=\"/posts/dependamerge-action/#output-parameters-understanding-and-utilizing-results\" title=\"Output Parameters: Understanding and Utilizing Results\"\u003eOutput Parameters: Understanding and Utilizing Results\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eThe output parameters in \u003ccode\u003edependamerge\u003c/code\u003e provide a valuable summary of each action’s status and results, allowing you to programmatically react based on outcomes. Two key outputs include:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ccode\u003estate\u003c/code\u003e: Indicates the action’s status, including:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eapproved\u003c/code\u003e: Pull request was successfully approved.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003emerged\u003c/code\u003e: Pull request was merged.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eskipped\u003c/code\u003e: Action skipped the pull request, halting further processing.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003efailed\u003c/code\u003e: Action couldn’t process the pull request due to errors.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003erebased\u003c/code\u003e: PR was rebased due to behind-branch status.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eBenefit\u003c/strong\u003e: By checking the \u003ccode\u003estate\u003c/code\u003e output, your workflow can respond to each action outcome. For example, you could add conditional notifications for failed or skipped updates to ensure immediate attention or skip further testing if the pull request was already merged.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ccode\u003emessage\u003c/code\u003e: Contains additional information on the processing state, including error and debug details.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eBenefit\u003c/strong\u003e: The \u003ccode\u003emessage\u003c/code\u003e output parameter can be leveraged for logging purposes or sent in a notification, enabling better tracking and diagnostics without requiring manual review. It’s especially useful for troubleshooting and ensuring full transparency of the automation process.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThese output parameters add an essential layer of feedback, enabling automated downstream workflows based on \u003ccode\u003edependamerge\u003c/code\u003e outcomes. The increased control and visibility improve overall workflow reliability and responsiveness.\u003c/p\u003e\n\u003cp\u003eDesigned to take the reins of dependency updates, \u003ccode\u003edependamerge\u003c/code\u003e works with Dependabot to make dependency management truly seamless. This GitHub action doesn\u0026rsquo;t just approve updates—it is adjustable to your project’s specific needs, ensuring that only the right updates are merged at the right time. Even better, \u003ccode\u003edependamerge\u003c/code\u003e can be part of a fully automated CI/CD pipeline, ensuring that dependency updates are tested and validated alongside other code changes.\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"community-and-contributions\"\u003e\u003ca href=\"/posts/dependamerge-action/#community-and-contributions\" title=\"Community and Contributions\"\u003eCommunity and Contributions\u003c/a\u003e\u003c/h2\u003e\n\n\n\n\n\u003ch3 id=\"open-source-your-contributions-matter\"\u003e\u003ca href=\"/posts/dependamerge-action/#open-source-your-contributions-matter\" title=\"Open-source, your contributions matter\"\u003eOpen-source, your contributions matter\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003edependamerge\u003c/code\u003e thrives on community input. Whether you’re a developer, or user, your feedback and contributions are invaluable. By sharing your experiences, suggesting improvements, or submitting code, you can help shape the future of \u003ccode\u003edependamerge\u003c/code\u003e. Every contribution, no matter how small, makes a difference in creating a more efficient and effective dependency management solution for all. - \u003ca href=\"https://github.com/dailydevops/action-dependamerge\" target=\"_blank\" rel=\"noopener external noreferrer\"\u003edailydevops/action-dependamerge\u003c/a\u003e\u003c/p\u003e\n\n\n\n\n\u003ch2 id=\"conclusion\"\u003e\u003ca href=\"/posts/dependamerge-action/#conclusion\" title=\"Conclusion\"\u003eConclusion\u003c/a\u003e\u003c/h2\u003e\n\n\n\n\n\u003ch3 id=\"flexibility-under-control-dependamerge-for-all\"\u003e\u003ca href=\"/posts/dependamerge-action/#flexibility-under-control-dependamerge-for-all\" title=\"Flexibility under control: dependamerge for all\"\u003eFlexibility under control: dependamerge for all\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003eWhether you’re working on a private project, an open-source initiative, or a company-driven application, \u003ccode\u003edependamerge\u003c/code\u003e is designed to meet your needs. By automating dependency management, you can focus on building great software without the burden of manual updates. The flexibility and customization options in \u003ccode\u003edependamerge\u003c/code\u003e ensure that you can tailor the automation to your project’s specific requirements, making it a valuable addition to any development workflow.\u003c/p\u003e\n\u003cp\u003eIf you\u0026rsquo;re like me, frustrated by dependency management’s time-consuming nature, \u003ccode\u003edependamerge\u003c/code\u003e is the solution you’ve been waiting for. Try it out, contribute, and help shape the future of dependency management automation. Together, we can build a more efficient, secure, and productive development process for all.\u003c/p\u003e","date_modified":"2026-05-26T10:22:03+02:00","date_published":"2024-11-13T09:00:00+01:00","id":"https://daily-devops.net/posts/dependamerge-action/","language":"en","summary":"Learn how to automate dependency management with the dependamerge GitHub Action for streamlined security updates, maintenance workflows, and automated PRs.","tags":["dependency-management","bestpractices","github","github-actions","nuget","technicaldebt"],"title":"dependamerge-action: Automated Dependency Merging","url":"https://daily-devops.net/posts/dependamerge-action/"}],"language":"en","title":"GitHub Actions and CI/CD Workflows on Daily DevOps \u0026 .NET","version":"https://jsonfeed.org/version/1.1"}