<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Checkpoint & Code]]></title><description><![CDATA[How a Git pre-commit hook and an AI code review skill worked together to catch a security mistake before it shipped.]]></description><link>https://ginnyibe.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Checkpoint &amp; Code</title><link>https://ginnyibe.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 10 Sep 2026 21:03:04 GMT</lastBuildDate><atom:link href="https://ginnyibe.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA["I Automated My Cloud Security Audit — But Kept the Trigger Finger Human"]]></title><description><![CDATA[It isn't.
"Working" and "secure" are two entirely different claims about a system, and the gap between them is where most real-world incidents live — not in some novel zero-day, but in the mundane stu]]></description><link>https://ginnyibe.hashnode.dev/i-automated-my-cloud-security-audit-but-kept-the-trigger-finger-human</link><guid isPermaLink="true">https://ginnyibe.hashnode.dev/i-automated-my-cloud-security-audit-but-kept-the-trigger-finger-human</guid><dc:creator><![CDATA[Ginny Ibe]]></dc:creator><pubDate>Tue, 01 Sep 2026 05:04:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a7f64ce8b1aa0f68358a3c5/5fe8113f-18cb-4d1f-8069-fac30ec01be1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It isn't.</p>
<p>"Working" and "secure" are two entirely different claims about a system, and the gap between them is where most real-world incidents live — not in some novel zero-day, but in the mundane stuff: a security group rule that still says "allow from anywhere," a storage account with public access quietly left on, a database that's technically reachable from the internet because nobody explicitly told it not to be. None of these mistakes break your application. That's exactly what makes them dangerous — they're invisible until someone goes looking.</p>
<p>So I went looking. Here's what I built, what I found, and why I think the way I found it matters more than the finding itself.</p>
<h3><strong>The Setup: Four Checks, One Script, No Guessing</strong></h3>
<p>The goal was narrow and deliberate: a Bash script that audits an Azure environment for four specific, well-understood misconfiguration classes.</p>
<ul>
<li><p><strong>Network Security Group rules</strong> that allow unrestricted inbound SSH or RDP — the digital equivalent of leaving your front door key under a mat that's visible from the street.</p>
</li>
<li><p><strong>Storage Account public blob access</strong> — can an anonymous, unauthenticated request read data out of your storage?</p>
</li>
<li><p><strong>Managed disk encryption-at-rest</strong> — if someone got physical or backend access to the underlying storage medium, is the data on it protected?</p>
</li>
<li><p><strong>Azure Database for MySQL public network access</strong> — can your database be reached from outside your private network at all?</p>
</li>
</ul>
<p>Each check follows the same shape: call a read-only Azure CLI command, parse the JSON response with <code>jq</code>, and classify the result as <code>PASS</code>, <code>WARN</code>, or <code>FAIL</code>. No creativity, no interpretation — just a deterministic yes/no/unknown against a known-risky pattern. The script uses an array of function names so adding a fifth check later is a two-line change, not a rewrite. It exits with distinct codes — 0 for healthy, 1 for warnings, 2 for failures — so anything downstream, whether that's a human or an AI agent, can react proportionally without re-parsing a wall of text.</p>
<p>That last part matters more than it sounds. A script that just prints "something's wrong" is a script you have to read carefully every single time. A script that hands you a structured status is a script you can build automation on top of — which is exactly what came next.</p>
<h3>Handing the Evidence to Claude Code — Without Handing Over the Keys</h3>
<p>Here's where it gets interesting, and where I think a lot of "AI + infrastructure" conversations go wrong.</p>
<p>The obvious move is to let an AI agent run commands against your cloud environment and fix whatever it finds. The obvious move is also a bad idea. An agent that can both <em>diagnose</em> and <em>treat</em> a production environment is an agent one confident misunderstanding away from an outage , or worse, a security incident it caused while trying to prevent one.</p>
<p>So I built the opposite: a Claude Code Skill, invoked with <code>/azure-audit</code>, that is structurally incapable of touching my infrastructure. Not "instructed not to" — <em>incapable</em>. The Skill's permission set is <code>Bash, Read, Grep</code>. There is no <code>Write</code> in that list. It can run the audit script, read the resulting report, and grep through evidence. It cannot create a file, cannot edit a file, and by extension cannot silently rewrite the audit script or plant a remediation command somewhere I'd never notice.</p>
<p>On top of that, a project-level <a href="http://CLAUDE.md"><code>CLAUDE.md</code></a> file spells out the rules in plain language: never create, modify, resize, start, stop, deallocate, or delete a resource; never run <code>az group delete</code>, <code>az vm delete</code>, or <code>az resource delete</code>; never execute a remediation command; and — the rule I think matters most — <em>do not claim a finding unless the report contains supporting evidence.</em></p>
<p>That last rule is a quiet but important piece of engineering discipline. It's easy for an AI system to sound confident about a problem it's only inferred. Forcing every claim to trace back to an actual line in a Bash-generated report means the AI's analysis is grounded in something falsifiable, not vibes.</p>
<p>When I ran the audit, Claude Code did exactly what the Skill defines: it ran the script, read the report, and for every WARN or FAIL, it explained the finding in plain language, described the real-world security or operational impact, proposed one exact Azure CLI command to fix it, and gave me a verification command to confirm the fix later. Then it stopped. It didn't run the fix command. It asked me to.</p>
<p><em>This post is part of the DevOps Micro Internship (DMI) with Agentic AI — Cohort 3 — led by Pravin Mishra. My graded progress is public:</em> <a href="https://dmi.pravinmishra.com/s/Ginny-Ibe.html">Ginny Ibe — DMI Cohort 3</a><em>. 
Start your DevOps journey:</em> <a href="https://dmi.pravinmishra.com/"><em>https://dmi.pravinmishra.com/</em></a><a href="https://dmi.pravinmishra.com/?utm_source=student&amp;utm_medium=ps-blog&amp;utm_campaign=cohort3"><em>?</em></a></p>
]]></content:encoded></item><item><title><![CDATA[How a Git pre-commit hook and an AI code review skill worked together to catch a security mistake before it shipped.]]></title><description><![CDATA[I watched Git reject my commit before it ever reached GitHub. At first, it felt like something had gone wrong. Then I realized something important: it worked exactly as I designed it to.
This week, du]]></description><link>https://ginnyibe.hashnode.dev/how-a-git-pre-commit-hook-and-an-ai-code-review-skill-worked-together-to-catch-a-security-mistake-before-it-shipped</link><guid isPermaLink="true">https://ginnyibe.hashnode.dev/how-a-git-pre-commit-hook-and-an-ai-code-review-skill-worked-together-to-catch-a-security-mistake-before-it-shipped</guid><category><![CDATA[Devops]]></category><category><![CDATA[Git]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[claude]]></category><category><![CDATA[cybersecurity]]></category><dc:creator><![CDATA[Ginny Ibe]]></dc:creator><pubDate>Fri, 14 Aug 2026 19:33:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a7f64ce8b1aa0f68358a3c5/72b0d7d2-61cb-4988-83e5-e9bfa3094f3a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I watched Git reject my commit before it ever reached GitHub. At first, it felt like something had gone wrong. Then I realized something important: it worked exactly as I designed it to.</p>
<p>This week, during Week 4 of my DevOps Micro-Internship, I learned that effective code reviews don't always start with a Pull Request. Sometimes the best review happens before the code ever leaves your laptop.</p>
<h2>Creating a Problem on Purpose</h2>
<p>To understand how developers prevent mistakes, I intentionally wrote a small script containing two common problems: a hardcoded AWS-style access key, and a leftover debug statement. Neither belonged in production code.</p>
<p>The goal wasn't to write perfect code. The goal was to build a safety system that could catch mistakes before another developer — or a production environment — ever saw them. That's the mindset behind DevSecOps: security should be built into the workflow, not added after something goes wrong.</p>
<h2>Building My First Line of Defense With Git Hooks</h2>
<p>The first safeguard I built was a Git pre-commit hook — a script that runs automatically before Git creates a commit. Think of it as a security checkpoint at the entrance of your repository. Before letting anything through, my hook scanned staged files for AWS-style access keys, private key signatures, and oversized files.</p>
<p>bash</p>
<pre><code class="language-bash"># .git/hooks/pre-commit (excerpt)
#!/bin/bash

if git diff --cached | grep -E "AKIA[0-9A-Z]{16}"; then
  echo "❌ Commit blocked: AWS access key detected in staged changes."
  exit 1
fi

if git diff --cached | grep -E "BEGIN (RSA|EC|OPENSSH) PRIVATE KEY"; then
  echo "❌ Commit blocked: private key detected in staged changes."
  exit 1
fi
</code></pre>
<p>When I tried to commit my script, Git blocked the operation:</p>
<p>bash</p>
<pre><code class="language-bash">$ git commit -m "add config script"
❌ Commit blocked: AWS access key detected in staged changes.
</code></pre>
<p>The commit never happened. The vulnerable code never entered repository history. That moment changed how I viewed automation — security isn't only about finding problems, it's about preventing them from becoming problems in the first place.</p>
<h2>Adding AI as a Second Layer of Review</h2>
<p>The Git hook was excellent at detecting known patterns — it could recognize "this looks like an AWS key." What it couldn't do was understand <em>why</em> something was a risk, like a debug statement that leaks information without matching any obvious pattern.</p>
<p>That's where AI-assisted review became useful. I built a Claude Code <code>/pr-ready</code> skill that analyzed my staged changes and generated a Pull Request readiness review — explaining <em>why</em> the debug statement was risky, why sample credentials could still trip security scanners downstream, and where the PR description needed more context:</p>
<pre><code class="language-plaintext">$ /pr-ready

⚠️ Debug statement found in src/config.py:14
   → Exposes internal config values in logs. Remove before merging.

⚠️ Sample credential pattern detected in src/config.py:22
   → Even placeholder keys can trigger downstream secret scanners.
   → Recommend replacing with an obviously fake value (e.g. "REDACTED").

ℹ️ PR description is missing a "Testing" section.
</code></pre>
<p>Automation detects patterns. AI explains context. That's the real difference between the two layers.</p>
<h2>Two Tools, Two Different Jobs</h2>
<p>This assignment showed me that Git hooks and AI reviewers aren't competitors — they solve different problems.</p>
<table>
<thead>
<tr>
<th></th>
<th>Git Pre-Commit Hook</th>
<th>AI Code Review</th>
</tr>
</thead>
<tbody><tr>
<td>Speed</td>
<td>Fast, deterministic enforcement</td>
<td>Slower, more exploratory</td>
</tr>
<tr>
<td>Consistency</td>
<td>Same rules, every time</td>
<td>Context-dependent judgment</td>
</tr>
<tr>
<td>Output</td>
<td>Blocks or allows</td>
<td>Explains and suggests</td>
</tr>
<tr>
<td>Best at</td>
<td>Protection before code enters history</td>
<td>Human-like reasoning about risk</td>
</tr>
</tbody></table>
<p>Together, they create layered protection: one stops the obvious mistakes instantly, the other catches the subtler ones and explains why they matter.</p>
<h2>Why Humans Still Matter</h2>
<p>The biggest lesson wasn't about Git, and it wasn't about AI. It was about responsibility.</p>
<p>AI can help write code, review changes, and generate Pull Request summaries. But it shouldn't automatically commit code, push changes, merge Pull Requests, or modify repository history. Those decisions require human judgment — the developer remains responsible for understanding what actually enters the system.</p>
<h2>Final Reflection</h2>
<p>Before this assignment, I viewed Git mainly as a place to store and manage code. Now I see it differently: Git can be an intelligent checkpoint in the development lifecycle. A pre-commit hook protects the repository. AI provides additional insight. The developer provides judgment.</p>
<p>That combination creates a safer engineering workflow — not by removing humans from the process, but by giving developers better tools to make better decisions. The best security improvement is often the one that stops a mistake before anyone has to fix it.</p>
<hr />
<p><em>This post is part of the DevOps Micro Internship (DMI) with Agentic AI — Cohort 3 — led by Pravin Mishra. My graded progress is public:</em> <a href="https://dmi.pravinmishra.com/s/Ginny-Ibe.html">Ginny Ibe — DMI Cohort 3</a><em>. Start your DevOps journey:</em> <a href="https://dmi.pravinmishra.com/"><em>https://dmi.pravinmishra.com/</em></a><a href="https://dmi.pravinmishra.com/?utm_source=student&amp;utm_medium=ps-blog&amp;utm_campaign=cohort3"><em>?</em></a></p>
]]></content:encoded></item></channel></rss>