Full-Stack SaaS · Cloud Security · Production Deployed
CyberShield
A production-grade web security SaaS for SMBs — scan any website for ~60 security vulnerabilities across SSL, headers, DNS, open ports, and source code. Built end-to-end from scan engine to commercial subscription model across 4 sprints.
Python 3.12 FastAPI Supabase Railway Vercel PostgreSQL Resend VirusTotal API Jan – Jun 2026
18
API Endpoints
~60
Security Checks (Max)
3
Subscription Tiers
4
Dev Sprints
12
Bilingual Blog Posts
01

Product Overview

SMBs face real website security risks — expired SSL certificates, misconfigured email authentication, exposed admin panels — but rarely have in-house security expertise or budget for dedicated tooling. CyberShield solves this by delivering professional-grade security scanning in under 90 seconds, with results in plain language, directly from a browser.

The platform was designed, built, and deployed across four sprints. A mid-project pivot from an agent-based monitoring model to a pure domain scanning SaaS — deliberately chosen for lower onboarding friction and better SMB fit — demonstrates product thinking alongside engineering execution.

02

Technical Architecture

Frontend
HTML5 / JS · Vercel
20 routes — landing, dashboard, register, blog. Deployed to Vercel Edge Network with custom domain (digitalcybershield.com).
Backend
FastAPI · Railway
18 endpoints across 3 modules (scan engine, customer API, dashboard API). Python 3.12, async throughout.
Database
Supabase · PostgreSQL
5 tables: customers, scan_history, scan_findings, scan_events, customer_domains. Row-level security policies.
Auth
Supabase Auth
Email/password + Google OAuth SSO. JWT-based session management on all dashboard API routes.
Realtime
Supabase WebSocket
scan_events table published to WebSocket — dashboard updates instantly on new findings without page refresh. 5-min polling fallback.
Email
Resend API
Branded HTML scan reports and security alerts. Sender: general@jihun.me.
Reputation
VirusTotal API
90-vendor consensus domain reputation scoring. Malware and phishing detection at scan time.
03

Scan Engine

The domain scanner executes all check categories concurrently using Python asyncio, returning a full scan in 5–15 seconds. Findings are scored and letter-graded automatically.

7 Check Categories — Domain Scanner
CategoryKey ChecksMax Severity
SSL / TLSCert expiry (7/30-day), TLS version, weak ciphers, CT log query, HSTS preloadCritical
HTTP HeadersHSTS, CSP (incl. unsafe-eval analysis), X-Frame-Options, SRI audit, TRACE detection, sensitive path exposure (/admin, /.env, /.git)High
Email AuthSPF (+all/~all/-all), DKIM (11 selectors), DMARC policy strength, MTA-STS, rua/ruf presence, SPF lookup countHigh
DNSDNSSEC, CAA records, AXFR zone transfer test, subdomain takeover (8 fingerprints), NS redundancyCritical
Open Ports15 high-risk ports (Free/Plus) · 50 ports at Max including Docker API (2376), K8s (6443), etcd (2379), Jupyter (8888), Consul (8500)Critical
Malware / ReputationVirusTotal 90-vendor consensus, domain reputation scoreCritical
Sensitive Info Leak/.env variants, /.git/config, backup files, phpinfo.php, wp-config.php.bak (Max tier)Critical
Scoring Algorithm
SeverityScore Deduction
Critical−22 pts
High−12 pts
Medium−6 pts
Low−2 pts
GradeScore Range
A+93 – 100
A85 – 92
B75 – 84
C60 – 74
D45 – 59
F0 – 44
Static Code Analyser — 14 Vulnerability Patterns

Users upload source files (Python, JS, PHP, etc.) for pattern-based static analysis. Detected categories:

Hardcoded API keys & secrets
SQL injection (unsanitised concat)
os.getenv() insecure fallbacks
JWT validation disabled (verify=False)
CORS wildcard (*)
Debug mode in production
Insecure deserialization (pickle.loads)
Open redirect (unvalidated URLs)
Path traversal (../ sequences)
Third-party secret patterns (Stripe, Twilio, SendGrid)
04

Subscription Tiers

Feature
Free
Plus · $19/mo
Max · $49/mo
Domain Scans / Month
5
50
Unlimited
Security Checks
~15
~35
~60
Registered Domains
1
5
20
Code File Scans
3 · 500 KB
30 · 2 MB
Unlimited · 10 MB
Event Retention
7 days
90 days
1 year
Auto-Scan Schedule
Weekly
Daily
Notification Channels
Email
Email + Slack + Discord
All channels
Extended Port Scan (50 ports)
Sensitive Info Leak Detection
05

Customer Dashboard

WidgetDescription
Security Score0–100 hex badge with letter grade (A+–F) and week-over-week delta
Action ItemsTop 3 unresolved vulnerabilities by severity with remediation guidance links
30-Day Score ChartChart.js trend line; colour-coded green ≥80 / amber ≥55 / red <55
Vulnerability ListFull findings with severity filter and per-finding resolve button
Security Event FeedChronological events within retention window; marks reviewed via ✓
Blocked ThreatsMonthly malicious IP connections with country breakdown
Scan QuotaMonthly scan usage vs. plan limit in nav bar
Real-Time Infrastructure
Supabase Realtime WebSocket subscription pushes new scan_events to the dashboard instantly. 5-minute silent polling fallback when WebSocket is unavailable. A _pollingStarted guard prevents duplicate setInterval registration across multiple Supabase auth events (SIGNED_IN / TOKEN_REFRESHED / INITIAL_SESSION).
06

Notable Bugs Resolved

Production bugs encountered and resolved across the development cycle — each one a real debugging problem, not a tutorial exercise.

S4
Login button completely unresponsive
Root cause: SyntaxError: const quota declared twice inside renderDashboard(). A JS SyntaxError prevents the entire script from parsing — no event listeners attach, nothing runs.
Fix: Removed duplicate declaration. Lesson: always check the browser console before assuming the issue is in the logic.
S4
Railway deployment crash on every push
Root cause: UnboundLocalError: datetime used before assignment — a from datetime import datetime inside a function body created a forward reference in Python's scoping rules.
Fix: Replaced agent_api.py with a lightweight deprecated stub; moved all imports to module level.
S3
Dashboard polling every 2 seconds, creating UI flicker
Root cause: Supabase fired three auth events (SIGNED_IN, TOKEN_REFRESHED, INITIAL_SESSION) per page load, each calling startPolling() and creating independent setInterval loops.
Fix: Added _pollingStarted boolean guard — polling initialises exactly once per page load regardless of auth event count.
S4
Register page auto-advancing to Step 2 during typing
Root cause: getSession() completing while the user was still typing; the catch(() => goStep(2)) handler triggered step change on API failure.
Fix: Removed auto-advance logic entirely; pre-fills email and updates button text only on session detection.
S2
Windows service startup timeout
Root cause: Go process did not send the Running signal to Windows Service Control Manager (SCM), causing SCM to time out and kill the service.
Fix: Added service_windows.go with golang.org/x/sys SCM handler.
S2
PowerShell install script JSON parse failure
Root cause: PowerShell's default encoding added a UTF-8 BOM to the output file, which Go's json.Decoder cannot parse (BOM is not valid JSON).
Fix: Switched to UTF8Encoding(false) — BOM-less UTF-8 output.
07

Roadmap — What's Next

ItemStatusDescription
Stripe Integration Pending Connect Stripe Checkout to auto-update customers.tier on subscription, enabling Plus/Max feature gates
Railway Cron Pending Schedule POST /internal/run-scheduled-scans daily — enables automated weekly/daily scans for paid tiers
CVE Correlation Planned Match detected server versions (Apache, Nginx, OpenSSL) against NVD CVE records
PDF Reports Planned Downloadable PDF scan reports for B2B client reporting