<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Amey Pacharkar</title>
    <description>The latest articles on DEV Community by Amey Pacharkar (@ameypacharkar1896).</description>
    <link>https://dev.to/ameypacharkar1896</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3368049%2F8a74cbff-01d3-47fe-82ea-c6f2f61ca09c.jpg</url>
      <title>DEV Community: Amey Pacharkar</title>
      <link>https://dev.to/ameypacharkar1896</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ameypacharkar1896"/>
    <language>en</language>
    <item>
      <title>How I Built an Automated GitHub Security Bot in a Weekend</title>
      <dc:creator>Amey Pacharkar</dc:creator>
      <pubDate>Fri, 18 Jul 2025 18:23:41 +0000</pubDate>
      <link>https://dev.to/ameypacharkar1896/how-i-built-an-automated-github-security-bot-in-a-weekend-2fif</link>
      <guid>https://dev.to/ameypacharkar1896/how-i-built-an-automated-github-security-bot-in-a-weekend-2fif</guid>
      <description>&lt;p&gt;We've all felt that small pang of fear after a &lt;code&gt;git push&lt;/code&gt;. Did I accidentally commit an API key? Is one of my dependencies secretly vulnerable? In a fast-paced development cycle, these small but critical issues can easily slip through, creating massive security holes and technical debt.&lt;/p&gt;

&lt;p&gt;Manually reviewing every commit is impossible. We need a tireless, automated team member who never sleeps. So, I decided to build one.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Solution: "Sentry", a Proactive Guardian for Your Code&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I built Sentry, a simple but powerful, event-driven bot that hooks directly into the GitHub workflow. It proactively scans every new commit for two critical issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Accidentally committed secrets.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Outdated and vulnerable dependencies.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When it finds something, it doesn't just send a silent notification; it opens a detailed, actionable issue right in your repository, providing immediate feedback where developers live.&lt;/p&gt;

&lt;p&gt;Seeing is believing. Here it is in action, detecting an outdated dependency in a &lt;code&gt;requirements.txt&lt;/code&gt; file seconds after the push:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjy3i4b9uw7el4dzzeb7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjy3i4b9uw7el4dzzeb7.gif" alt="Sentry in Action-gif" width="760" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Tech Stack &amp;amp; The "Why"&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I wanted to build this with modern, high-performance tools that were right for the job.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Backend: Python &amp;amp; FastAPI&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Why?&lt;/strong&gt; FastAPI's asynchronous nature is perfect for an API-driven service that spends most of its time waiting for network responses (from the GitHub API, the PyPI API, etc.). Its speed and built-in data validation with Pydantic make it a robust choice for building reliable webhooks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Deployment: Render&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Why?&lt;/strong&gt; I needed a platform with a seamless Git-to-deployment workflow. Render's free tier for web services, automatic SSL, and simple environment variable management meant I could go from a local script to a live public endpoint in minutes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;GitHub Integration: PyGithub Library&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Why?&lt;/strong&gt; Instead of making raw HTTP requests, this library provides a clean, object-oriented way to interact with the GitHub API. This made tasks like fetching file content and creating issues much cleaner and more readable.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;My Biggest Challenge: Debugging a Serverless "Cold Start"&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The most interesting challenge wasn't a code bug; it was a ghost in the machine. After deploying, my bot stopped working. GitHub reported that every webhook delivery was failing with a &lt;code&gt;timeout&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;This was incredibly frustrating. My local tests with &lt;code&gt;ngrok&lt;/code&gt; had worked perfectly. The Render logs showed the application was "live," but my own log messages never appeared. The requests were vanishing into a void between the internet and my application code.&lt;/p&gt;

&lt;p&gt;After ruling out code errors, I had an "Aha!" moment. I remembered that &lt;strong&gt;Render's free tier puts services to "sleep" after 15 minutes of inactivity.&lt;/strong&gt; GitHub's webhooks, however, give up after just 10 seconds.&lt;/p&gt;

&lt;p&gt;My app was taking 30-40 seconds to "wake up" from a cold start, so GitHub always timed out. The problem wasn't that the service was broken, but that it was too slow on its &lt;em&gt;first&lt;/em&gt; request. This taught me a valuable lesson about the realities of serverless and free-tier hosting environments—sometimes the problem isn't in the code, but in the infrastructure's behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Under the Hood: Checking for Outdated Dependencies&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The core logic of the bot is straightforward. Here’s a look at the function that checks a &lt;code&gt;requirements.txt&lt;/code&gt; file for outdated packages. It's a great example of the bot's workflow: parse a file, call an external API, and compare the results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Part of app.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;packaging.version&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;parse_version&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_outdated_dependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_content&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Parses requirements.txt content, checks each package against the PyPI API,
    and returns a list of outdated packages.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;outdated_packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# First, strip away any inline comments
&lt;/span&gt;        &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Ignore empty lines or lines that aren't version-pinned
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;package_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;specified_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# Call the official PyPI API
&lt;/span&gt;            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://pypi.org/pypi/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;package_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;latest_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;info&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

                &lt;span class="c1"&gt;# Use the 'packaging' library for robust version comparison
&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;parse_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latest_version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;parse_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;specified_version&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;outdated_packages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;package_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;current&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;specified_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;latest_version&lt;/span&gt;
                    &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Could not parse or check dependency &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;. Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;outdated_packages&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;A Tool is Born&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This project was an incredible learning experience in building event-driven systems. It's a small step, but it's a real, working tool that solves a problem I care about. The next steps to make it even more robust would be to add a full test suite with &lt;code&gt;pytest&lt;/code&gt; and refactor the authentication to use a proper GitHub App, allowing for easy, one-click installation by other users.&lt;/p&gt;

&lt;p&gt;You can check out the complete source code and a detailed setup guide on GitHub.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;View the Code:&lt;/strong&gt; &lt;strong&gt;&lt;a href="https://github.com/AmeyPacharkar1896/sentry-bot" rel="noopener noreferrer"&gt;GitHub - AmeyPacharkar1896/sentry-bot&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Live Endpoint:&lt;/strong&gt; &lt;strong&gt;&lt;a href="https://sentry-guard-bot.onrender.com" rel="noopener noreferrer"&gt;sentry-guard-bot.onrender.com&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Join the Conversation!&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This was my approach to automated code quality. What other common security checks or best practices would you want to see a bot like this perform? I'd love to hear your ideas in the comments&lt;/p&gt;

</description>
      <category>python</category>
      <category>github</category>
      <category>devops</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
