<?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: Zeyrian Faris</title>
    <description>The latest articles on DEV Community by Zeyrian Faris (@zeyrian_faris).</description>
    <link>https://dev.to/zeyrian_faris</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3950809%2F08e57afc-a6c6-4b72-bc60-0be9c27653db.jpg</url>
      <title>DEV Community: Zeyrian Faris</title>
      <link>https://dev.to/zeyrian_faris</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zeyrian_faris"/>
    <language>en</language>
    <item>
      <title>I Found a Source Code Disclosure Bug in My Own Flask App With Gobuster</title>
      <dc:creator>Zeyrian Faris</dc:creator>
      <pubDate>Sat, 20 Jun 2026 03:03:44 +0000</pubDate>
      <link>https://dev.to/zeyrian_faris/i-found-a-source-code-disclosure-bug-in-my-own-flask-app-with-gobuster-42n8</link>
      <guid>https://dev.to/zeyrian_faris/i-found-a-source-code-disclosure-bug-in-my-own-flask-app-with-gobuster-42n8</guid>
      <description>&lt;p&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm a first-year Cybersecurity and Digital Forensics student, currently working through TryHackMe's Jr Pentester path. While practicing directory enumeration with gobuster, I had an idea: rather than only running it against lab targets, why not point it at something I actually built and care about?&lt;/p&gt;

&lt;p&gt;That something is fourpointo. It Flask app I'm developing that generates AI task lists and rubric breakdowns from uploaded assignment PDFs, self-hosted on a Dell server and deployed via Cloudflare Tunnel.&lt;/p&gt;

&lt;p&gt;I wasn't expecting to find anything. I was mostly testing whether I understood the tool. I ended up finding a real, textbook misconfiguration  and figured it was worth documenting properly, both as a learning exercise and as a small case study in why this category of bug is so common.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To do this safely, I didn't touch the live production app (fourpointo has real registered users and testing against it directly wasn't worth the risk). Instead, I cloned the public GitHub repo and ran a local dev copy on an isolated Kali VM, hitting localhost only.&lt;/p&gt;

&lt;p&gt;I then deliberately introduced a misconfiguration I wanted to study: I copied database.py, one of the app's core server-side source files, into the static/ directory.&lt;/p&gt;

&lt;p&gt;Why this matters: in Flask, static/ is auto-served by the framework with no route required. Anything dropped in there, intentionally or by accident, is publicly reachable. It's meant to hold CSS, JS, images: client-facing assets. Files in templates/, by contrast, are not directly reachable; they only render through actual app routes. Mixing a server-side source file into static/ blurs a trust boundary that should be kept firm.&lt;/p&gt;

&lt;p&gt;This is a realistic mistake. It's easy to imagine a developer copying a file for a quick test, or a build script misplacing something, and forgetting to remove it before deploying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the dev server running locally, I ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gobuster dir -u http://localhost:5000/static -w /usr/share/wordlists/dirb/common.txt -x html,htm,php,py,js,json,txt,xml,bak,old,zip,sql,env,config,yml -t 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database.py     (Status: 200) [Size: 7739]
script.js        (Status: 200) [Size: 8746]
Progress: 73808 / 73808 (100.00%)
Finished
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuogrlkqv7u3z0bu7smtb.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuogrlkqv7u3z0bu7smtb.png" alt=" " width="765" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;script.js showing up is expected as it is a legitimate static asset. database.py showing up alongside it is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confirming the Finding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A 200 status from gobuster tells you a path exists and returned content but it doesn't tell you what that content is. To confirm actual impact, I pulled the file directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl http://localhost:5000/static/database.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returned the full, raw source of database.py. No authentication, no warning, just plain text served like any other static asset.&lt;/p&gt;

&lt;p&gt;The file contained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full database schema (table definitions for projects, tasks, and others, including column names and relationships)&lt;/li&gt;
&lt;li&gt;Application logic for handling user data, including how passwords are hashed before being written to the database&lt;/li&gt;
&lt;li&gt;Enough structural detail to understand exactly how the app's data model and core functions work, without needing to guess&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be clear: no real user data, hardcoded secrets, or API keys were exposed in this case. Fourpointo correctly keeps those in environment variables, not in source. But that's somewhat beside the point. The schema and logic disclosure alone is a meaningful leak on its own, and in a less careful codebase, this exact misconfiguration could just as easily have exposed live credentials.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F72akl4qqd6jjtmcqh10z.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F72akl4qqd6jjtmcqh10z.png" alt=" " width="787" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Source code disclosure is generally underrated as a finding because it doesn't look as dramatic as, say, SQL injection or an exposed admin panel. But it directly enables both:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Targeted attacks: knowing exact table names, column names, and queries removes the guesswork an attacker would otherwise need for SQL injection, IDOR, or logic-abuse attempts.&lt;/li&gt;
&lt;li&gt;Reconnaissance for the next bug: reading real application logic often reveals other weaknesses (missing validation, unsafe assumptions, auth logic quirks) that would be far harder to find through black-box testing alone.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's also a good demonstration of why directory brute-forcing matters in a real pentest. In practice you rarely know a target's folder structure in advance. You fingerprint the stack (Flask, in this case, identifiable through default error pages, cookie naming, etc.), pick a wordlist suited to that stack, and brute-force from there. Knowing Flask serves static/ by default is exactly the kind of framework-specific knowledge that turns a generic scan into a targeted one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The fix is straightforward: keep server-side source code out of any directory the web server treats as public:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never place .py (or other server-side source) files inside static/&lt;/li&gt;
&lt;li&gt;At the web server/proxy level, explicitly deny serving source file extensions as a defense-in-depth measure, e.g. for Nginx:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;location ~* \.py$ {
      deny all;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Periodically audit what's actually inside static/ as it's easy for stray files to accumulate over time without anyone noticing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The lesson that stuck with me most: a directory's purpose and a directory's actual contents can quietly drift apart, and nothing forces you to notice until someone runs a scan. Treating static/ (or any auto-served directory) as inherently safe just because of what it's meant to hold, rather than auditing what's actually in there, is exactly the kind of assumption that creates this category of bug.&lt;/p&gt;

&lt;p&gt;Testing this against my own app, rather than only against lab targets, was a useful exercise precisely because the stakes felt real even at a small scale: this is a live app with actual users, and the same gap could plausibly have existed in production without me thinking to check.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>python</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I self-hosted my Flask app on an old laptop for almost free</title>
      <dc:creator>Zeyrian Faris</dc:creator>
      <pubDate>Mon, 25 May 2026 14:37:30 +0000</pubDate>
      <link>https://dev.to/zeyrian_faris/how-i-self-hosted-my-flask-app-on-an-old-laptop-for-almost-free-52h9</link>
      <guid>https://dev.to/zeyrian_faris/how-i-self-hosted-my-flask-app-on-an-old-laptop-for-almost-free-52h9</guid>
      <description>&lt;p&gt;I recently built fourpointo, a Flask web app for polytechnic students to manage assignments. Once it was working locally, I needed to get it live without paying for cloud hosting every month.&lt;/p&gt;

&lt;p&gt;Then I realised I had an old, unused laptop which I was trying to find a purpose for. I figured it would be great to use it as a home server to host my website and it saves costs.&lt;/p&gt;

&lt;p&gt;I wiped the laptop and went with Ubuntu Server since it's lightweight and widely used. There's plenty of documentation if I got stuck.&lt;/p&gt;

&lt;p&gt;I then cloned fourpointo from my new laptop onto my server.&lt;/p&gt;

&lt;p&gt;When developing fourpointo, I used Python's Flask library. However, Flask's built-in server is single-threaded and not designed to handle multiple requests at once. Gunicorn is a production-grade server that runs multiple workers so it can handle real traffic.&lt;/p&gt;

&lt;p&gt;After installing Gunicorn, I got the domain fourpointo.app from Namecheap which serves as the URL for my app.&lt;/p&gt;

&lt;p&gt;I then used Cloudflare Tunnel because it routes traffic through Cloudflare's servers, so my home IP stays hidden rather than being exposed publicly.&lt;/p&gt;

&lt;p&gt;One issue I ran into was fourpointo going down every time I closed the lid of my server. Then I realised the server cannot go to sleep, even if the lid is closed. So I edited a systemd script using the Command Line Interface to set it so that it doesn't go to sleep when the lid is closed.&lt;/p&gt;

&lt;p&gt;Another issue I faced was fixing bugs and pushing updates remotely. In order to update fourpointo, I needed to SSH into the server. However, that only worked if I was on the same wifi as my server. So remote fixing was impossible. To solve this issue, I used Tailscale which acts as a bridge between my server and my laptop. Tailscale generated an IP for me to use when I want to SSH into the server remotely.&lt;/p&gt;

&lt;p&gt;Through this experience, I learnt the basics of server and network administration using Linux. I will continue to push updates to fourpointo to enhance users' experiences and hopefully in the future, I will be able to scale the app up.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>linux</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
