GitHygiene is a dependency health and security intelligence platform for GitHub repositories. It scans your repos, identifies every vulnerable and outdated package, assesses whether vulnerable code paths are actually reachable in your project, and generates AI-powered remediation plans β grounded entirely in your real scan data.
Built for Hacktoberfest Hack Day β Coimbatore 2026 Β· INIT CLUB Γ iDEA CLUB Γ MLH
Team Name: HTF-014 TetraFourge
| Member | Contribution |
|---|---|
| G Prajwal Priyadarshan | API Gateway, GitHub integration, scan pipeline |
| Rahul L S | AI service, Gemma 4 integration, reachability engine |
| Kabilan K | React dashboard, graph visualisation, UI/UX |
| Kishore B | Neo4j graph model, dependency extraction, DevOps |
Modern repositories are mostly other people's code. A typical project pulls in hundreds of open-source packages β most of them transitively, most of them invisible to the maintainer. When a vulnerability is published, answering "am I affected, and through which package?" means jumping between GitHub, a scanner's output, advisory databases, and package registries.
Existing tools make this worse by treating all findings equally: a CRITICAL advisory for a function your code never calls is ranked the same as one for a function called on every request. Developers end up with a wall of alerts and no way to know where to start.
The people hit hardest are the ones with the least tooling β students, solo maintainers, and small open-source teams who look after several repositories with no dedicated security team.
- Hacktoberfest is about the health of open source, and dependency hygiene is the least glamorous, most neglected part of it.
- Vulnerability scanners report findings as lists of advisory IDs. They rarely answer the cross-repository question ("which of my projects does this one advisory reach?") or explain what a finding means for someone who isn't a security specialist.
- All the data needed is already open: GitHub's API, the OSV vulnerability database, and the public package registries β no proprietary feeds required.
- Gemma 4's structured output capability makes it possible to build an AI reachability engine whose verdicts are grounded, auditable, and never hallucinated.
GitHygiene connects to your GitHub account, imports your repositories, and runs a three-stage pipeline:
- Scan β reads dependency manifests and lockfiles, checks every package version against OSV.dev, and queries npm/PyPI registries for the latest versions.
- Assess β a Gemma 4-powered AI engine extracts the vulnerable surface from each advisory (the specific functions, subpaths, and trigger conditions named in the advisory text), then judges whether your repository's code actually calls those symbols.
- Remediate β produces an ordered upgrade plan, a plain-language explanation for each finding, and a draft GitHub issue ready to assign to a contributor β all grounded in the evidence the static analysis found.
- Security scoring β every repository gets a 0β100 score with a documented penalty formula, so each score can be explained, not just displayed.
- Reachability triage β findings are classified as
reachable,likely_reachable,not_evidenced, orunused, so you focus on what matters. - Cross-repository blast radius β pick any advisory and see every repository it reaches and the exact dependency chain it travels through.
- AI upgrade planner β ordered remediation steps for a scan's findings, major-version breaking changes flagged, with a "direct-bump" or "override" strategy depending on whether the vulnerable package is a direct or transitive dependency.
- Draft GitHub issues β the AI engine writes a contributor-ready GitHub issue for each triaged finding, with file citations from the actual static analysis evidence.
- Multi-ecosystem β npm (
package-lock.json,package.json) and PyPI (requirements.txt) in the first build; the model is designed to add more. - Open data only β OSV.dev, the GitHub API, and public registries. No proprietary vulnerability feeds, no paid API required to run the core scanner.
Most scanners stop at "you have this CVE". GitHygiene goes further on three axes:
-
Reachability, not just presence. GitHygiene uses Gemma 4 to extract the vulnerable surface (the exact function names, subpaths, and trigger conditions) from the advisory text, then runs static analysis over the repository's source to check whether those symbols are actually called. A finding that can't reach your code is classified differently from one that runs on every request.
-
Cross-repository impact. The Neo4j dependency graph shares
Packagenodes across all of a user's repositories. One advisory attached to one package node is immediately connected to every repository that depends on it, at any depth. The blast-radius query answers "which of my repos are affected" in a single Cypher traversal. -
Structured AI output, not prose. The AI engine uses Gemma 4's structured output (response schema) mode β every verdict validates against a Pydantic schema before it reaches the user. The engine fails closed on a schema mismatch rather than returning half-parsed prose. All file and line citations in a verdict must come from the static analysis evidence; the model is instructed to return an empty evidence list rather than invent a path.
graph TD
subgraph Client["π₯οΈ Frontend"]
React["βοΈ React 19 + TypeScript\nTailwind CSS Β· Recharts\nreact-force-graph-2d Β· Zustand"]
end
subgraph Gateway["π’ API Gateway"]
Express["Node.js / Express 5\nAuth Β· GitHub Sync Β· Scanner\nStatic Analysis Β· Graph Writes"]
end
subgraph AIService["π AI Service β Gemma 4 Engine"]
S1["Stage 1: Extract Surface\nPOST /v1/extract-surface"]
S3["Stage 3: Assess Reachability\nPOST /v1/assess"]
DI["Draft Issue\nPOST /v1/draft-issue"]
GC["gemini_client.py\nStructured Output Mode"]
S1 & S3 & DI --> GC
end
subgraph Data["πΎ Persistence"]
PG[("Supabase Postgres")]
N4J[("Neo4j Aura\nDependency Graph")]
Storage[("Supabase Storage")]
end
subgraph OpenData["π Open Data β No API Key Needed"]
OSV(OSV.dev)
NPM(npm registry)
PYPI(PyPI JSON API)
GH(GitHub REST API)
end
React <-->|REST / JWT| Express
Express --> GH & OSV & NPM & PYPI
Express --> PG & N4J & Storage
Express -->|Advisory + Evidence| S1
Express -->|Surface + Evidence| S3
Express -->|Triaged Finding| DI
GC -->|Gemma 4 via Gemini API| Gemma["π€ Gemma 4\ngemma-4-26b-a4b-it\ngemma-4-31b-it"]
| Category | Technologies |
|---|---|
| Frontend | React 19, Vite, TypeScript, Tailwind CSS 4, Zustand, Recharts, react-force-graph-2d, Framer Motion |
| Backend | Node.js, Express 5, JOSE / jsonwebtoken |
| Database | Supabase Postgres (via @supabase/supabase-js), Supabase Storage, Supabase Auth |
| Graph | Neo4j Aura Free, neo4j-driver, Cypher |
| AI / ML | Gemma 4 (gemma-4-26b-a4b-it, gemma-4-31b-it) via Gemini API, FastAPI, Pydantic structured output |
| Infrastructure | Vercel (frontend), Supabase (auth + database + storage), Neo4j Aura |
| APIs / Services | GitHub REST API, OSV.dev batch API, npm registry, PyPI JSON API |
sequenceDiagram
participant GW as API Gateway
participant S1 as Stage 1: Extract Surface
participant GW2 as API Gateway (static analysis)
participant S3 as Stage 3: Assess
participant DI as Draft Issue
GW->>S1: POST /v1/extract-surface\n{ osv_id, summary, details, ecosystem, package }
S1->>S1: Gemma 4 extracts vulnerable_symbols,\nsubpaths, trigger_conditions, attack_vector
S1-->>GW: VulnerableSurface { symbols, confidence }
GW2->>GW2: Static search over repo source\nfor import sites + call sites of named symbols
Note over GW2: Evidence assembled: package_imported,\nimport_sites, call_sites, files_searched
GW->>S3: POST /v1/assess\n{ surface, evidence, dependency, repository }
S3->>S3: Gemma 4 judges reachability\nand recommends remediation
S3->>S3: Grounding check: every cited file\nmust be in the Stage 2 evidence
S3-->>GW: AssessResponse { reachability, recommendation,\nreasoning, remediation_mechanics }
GW->>DI: POST /v1/draft-issue\n{ ranked finding + verdict }
DI->>DI: Gemma 4 writes contributor-ready\nGitHub issue with file citations
DI-->>GW: IssueDraft { title, scope, acceptance_criteria }
flowchart TD
A([Trigger scan]) --> B[Return 202 immediately]
B --> C[Fetch manifests from GitHub\npackage-lock.json Β· package.json Β· requirements.txt]
C --> D{Manifest found?}
D -- No --> FAIL([Mark failed])
D -- Yes --> E[Parse full package tree\nwith direct / transitive flags]
E --> F[Batch query OSV.dev\nfor all package + version pairs]
F --> G[Query npm / PyPI\nlatest version + deprecation status]
G --> H[Compute security score\npenalty formula Β· risk level]
H --> I[Save dependencies + vulnerabilities\nto Postgres]
I --> J[Write Repository β Package β Vulnerability\ngraph to Neo4j]
J --> K[Create in-app notifications]
K --> DONE([Mark done])
erDiagram
profiles {
uuid id PK
text github_login
text avatar_url
}
repositories {
uuid id PK
uuid user_id FK
bigint github_id
text full_name
text default_branch
text language
timestamptz last_scanned_at
}
scans {
uuid id PK
uuid repository_id FK
text status
int security_score
text risk_level
text error
timestamptz started_at
timestamptz finished_at
}
dependencies {
uuid id PK
uuid scan_id FK
text ecosystem
text name
text version
text latest_version
bool is_direct
bool is_deprecated
}
vulnerabilities {
uuid id PK
uuid scan_id FK
uuid dependency_id FK
text osv_id
text[] aliases
text severity
text summary
text fixed_version
}
ai_reports {
uuid id PK
uuid repository_id FK
uuid scan_id FK
text type
text content
text model
}
notifications {
uuid id PK
uuid user_id FK
text type
text title
bool is_read
}
profiles ||--o{ repositories : owns
repositories ||--o{ scans : has
scans ||--o{ dependencies : contains
scans ||--o{ vulnerabilities : contains
dependencies ||--o{ vulnerabilities : linked_to
repositories ||--o{ ai_reports : has
profiles ||--o{ notifications : receives
graph LR
R1["ποΈ :Repository\nuser/api"] -->|DEPENDS_ON| P1["π¦ :Package\nnpm:lodash@4.17.20"]
R2["ποΈ :Repository\nuser/web"] -->|DEPENDS_ON| P1
P1 -->|DEPENDS_ON| P3["π¦ :Package\nnpm:minimist@1.2.5"]
P3 -->|AFFECTED_BY| V1["β οΈ :Vulnerability\nGHSA-xvch-5gv4-984h Β· CRITICAL"]
P1 -->|AFFECTED_BY| V2["β οΈ :Vulnerability\nGHSA-p6mc-m468-83gw Β· HIGH"]
Package nodes are shared across all repositories β one advisory attached to one package is instantly reachable from every repository that depends on it, at any depth.
Gemma 4 with structured output, not prose. The AI engine uses the Gemini API's responseMimeType: "application/json" + responseSchema mode. Every verdict validates against a Pydantic schema before it leaves the AI service. The engine retries once on a schema mismatch, then fails closed β the user sees "AI analysis unavailable" rather than a half-parsed or unverified response.
OSV.dev for vulnerability data. OSV is open, needs no API key, matches by exact package and version, and aggregates GitHub Security Advisories and ecosystem databases. It removes the need to load and maintain a CVE dataset.
Neo4j for blast radius, Postgres for everything else. Scan results live in Postgres. The graph holds only packages, their edges, and advisories, and answers the multi-hop questions (blast radius, shared dependencies) that recursive SQL can't do efficiently. If Neo4j is unavailable, scanning and the dashboard still work.
AI service owns no database credentials. The gateway assembles all the context (advisory text, static analysis evidence, dependency facts) and sends it to the AI service in the request body. The AI service only needs a Gemini API key. This means the AI service can be replaced or mocked without touching the database layer.
Grounding check on every verdict. Stage 3 validates that every file cited in the AI's evidence list is a file Stage 2 actually returned. A verdict citing a file not in the evidence fails with a 502 rather than being displayed β a wrong citation is more harmful than no citation.
Everything in this repository was built during Hacktoberfest Hack Day β Coimbatore 2026:
- GitHub OAuth sign-in and repository import
- Dependency manifest parsing (
package-lock.json,package.json,requirements.txt) with direct/transitive flags - OSV.dev batch vulnerability lookup and npm/PyPI registry checks for outdated and deprecated packages
- Security scoring formula with per-finding penalty breakdown
- Neo4j dependency graph with cross-repository blast-radius queries
- Three-stage AI engine (surface extraction β static analysis β reachability verdict β issue draft) powered by Gemma 4
- React dashboard with graph visualisation, vulnerability tables, score badges, and in-app notifications
- Express API gateway connecting all services with JWT auth, row-level security, and graceful degradation when AI or Neo4j is unavailable
- G Prajwal Priyadarshan: API gateway architecture, GitHub integration, scanner pipeline, Neo4j graph writes
- Rahul L S: AI service, Gemma 4 integration, Stage 1/3 prompts, structured output and grounding logic
- Kabilan K: React dashboard, dependency graph visualisation, landing page, UI components
- Kishore B: Dependency extraction and parsing, Neo4j schema and Cypher queries, deployment
Live Application: https://git-hygiene.vercel.app/
Sign in with GitHub, import any repository, and run a scan. The scanner works on any public repository β try one with an older lockfile to see vulnerability findings. The AI reachability engine requires GEMINI_API_KEY to be configured on the deployed instance.
Demo Video: https://youtu.be/d-u_tdBHLm0
The demo covers: sign in β import β scan β vulnerability findings with score β blast radius across repositories β AI reachability verdict β draft GitHub issue.
- Gemma 4 (
gemma-4-26b-a4b-it) β Stage 1: extracts vulnerable symbols, subpaths, and trigger conditions from OSV advisory text using structured output (JSON schema-constrained generation). Licence: Gemma Terms of Use. - Gemma 4 (
gemma-4-31b-it) β Stage 3: judges whether the repository's code actually reaches the vulnerable surface, recommends remediation, and identifies files to change. Also used for drafting contributor-ready GitHub issues. Licence: Gemma Terms of Use. - Gemini API β the inference provider for both Gemma 4 models; structured output mode (
responseMimeType: application/json) ensures every response validates against a Pydantic schema.
- OSV.dev β open vulnerability database; batch API used to look up advisories for every package version in a scan. No API key required.
- FastAPI β Python web framework for the AI microservice.
- Pydantic β request/response validation and the structured output schemas that constrain Gemma 4's responses.
- LangGraph / LangChain β agent orchestration framework (planned for multi-step workflows).
- React 19 β frontend framework.
- Tailwind CSS 4 β utility-first CSS.
- Recharts β dashboard charts.
- react-force-graph-2d β dependency graph visualisation.
- Zustand β frontend state management.
- Framer Motion β UI animations.
- Express 5 β Node.js API gateway.
- neo4j-driver β Neo4j graph database client.
- @supabase/supabase-js β Supabase client for Postgres, auth, and storage.
- GitHub REST API β repository listing and manifest fetching.
- npm registry / PyPI JSON API β latest version and deprecation status lookups.
- Node.js 20+
- Python 3.9+
- A Supabase project
- A Neo4j Aura Free instance
- A Google AI Studio API key (for Gemma 4 via Gemini API)
- A GitHub OAuth App β set as the GitHub provider under Supabase Auth β Providers (for sign-in), and its client id/secret also go in
api-gateway/.env(for the manifest-fetching code exchange)
git clone https://github.com/Tetra4ge/GitHygiene.git
cd GitHygiene
# API Gateway
cd api-gateway && npm install && cd ..
# Frontend
cd frontend && npm install && cd ..
# AI Service
cd ai-service
python -m venv venv
source venv/bin/activate # macOS / Linux
# .\venv\Scripts\Activate.ps1 # Windows PowerShell
pip install -r requirements.txt
cd ..Copy .env.example β .env in each service directory. The blocks below mirror the actual .env.example files β only the secrets are placeholders.
api-gateway/.env
PORT=5000
NODE_ENV=development
# ai-service β see services/ai.service.js
AI_SERVICE_DEV_URL=http://127.0.0.1:8000
AI_SERVICE_PRO_URL=https://your-ai-service.onrender.com
SUPABASE_URL=https://xxx.supabase.co
SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...
# SUPABASE_JWT_SECRET is only read by the local test-* scripts in utils/ β
# session JWTs are verified against Supabase's published JWKS, not this secret.
SUPABASE_JWT_SECRET=your-super-secret-jwt-string
# IMPORTANT: use the IPv4 transaction pooler host (port 6543), not the direct
# db.<ref>.supabase.co host β that one is IPv6-only and fails on most hosts.
DATABASE_URL=postgresql://postgres.xxx:PASSWORD@aws-0-us-west-1.pooler.supabase.com:6543/postgres
NEO4J_URI=neo4j+s://xxx.databases.neo4j.io
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=your_neo4j_password
GITHUB_CLIENT_ID=your_github_oauth_id
GITHUB_CLIENT_SECRET=your_github_oauth_secretai-service/.env
PORT=8000
GEMINI_API_KEY=your_google_ai_studio_key
GEMMA_MODEL_EXTRACT=gemma-4-26b-a4b-it
GEMMA_MODEL_REASON=gemma-4-31b-it
GEMINI_TIMEOUT_SECONDS=60
# Optional β reserved for a future local/offline inference mode, not wired
# into the engine yet. Leave at the defaults.
LLM_PROVIDER=gemini
OLLAMA_BASE_URL=http://localhost:11434
# Fallback only β never the primary path. Leave blank unless you need it.
OPENROUTER_API_KEY=frontend/.env
VITE_API_DEV_URL=http://localhost:5000
VITE_API_PRO_URL=https://your-api-gateway.onrender.com
VITE_MODE=development
VITE_SUPABASE_URL=https://xxx.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGci...cd api-gateway
node utils/init-db.jsThis reads the full DDL from docs/DB_SCHEMA.md β organizations, users, projects, repositories, dependencies, osv_vulnerabilities, ai_assessments, notifications, and the rest β drops and recreates every table, and tries to install the handle_new_user trigger that copies each new Supabase Auth sign-in into public.users.
On Supabase Cloud, the script usually can't create that trigger directly (it needs owner privileges on the auth schema), and logs a warning instead of failing. If you see that warning, run this once in the Supabase SQL Editor:
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS trigger AS $$
BEGIN
INSERT INTO public.users (user_id, email, full_name, role)
VALUES (
new.id,
new.email,
COALESCE(new.raw_user_meta_data->>'full_name', split_part(new.email, '@', 1)),
'developer'
);
RETURN new;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user();init-db.js also backfills public.users from any accounts that already exist in auth.users, so re-running it is safe after the trigger is in place.
Terminal 1 β API Gateway
cd api-gateway && npm run dev
# β http://localhost:5000Terminal 2 β AI Service
cd ai-service && source venv/bin/activate
uvicorn main:app --reload --port 8000
# β http://localhost:8000Terminal 3 β Frontend
cd frontend && npm run dev
# β http://localhost:5173Verify GET http://localhost:5000/api/v1/health returns "databases": { "postgres": "ONLINE", "neo4j": "ONLINE" } before using the app.
- Sign in with GitHub.
- Go to Repositories and import the ones you want tracked.
- Go to Security, pick a repository, and click Run OSV Scan.
- Review the findings β grouped by severity, with the fixed version and a direct/transitive badge for each.
- Click Assess on any finding to run the AI reachability engine.
- Click Draft GitHub Issue on an assessed finding to generate a contributor-ready issue.
Building GitHygiene required navigating a complex intersection of static analysis, graph databases, and generative AI. Here are the most significant challenges we encountered and what we learned from them:
The Challenge: Language models are great at explaining CVEs, but they are notoriously bad at determining if a vulnerability is actually exploitable in a codebase. If we just asked the AI, "Does this repo use the vulnerable code?", it would often guess or hallucinate paths. The Learning: We learned to decouple the analysis. We only use Gemma 4 for what it's good at: extracting function names from advisory prose (Stage 1) and judging contextual risk (Stage 3). The actual "Code Evidence Search" (Stage 2) is strictly deterministic regex searching against the repo's tarball. By forcing the AI to only cite files found by the deterministic search, we completely eliminated hallucinated call paths.
The Challenge: We needed the AI service to communicate reliably with our Node.js API Gateway, but standard text generation is too unpredictable for automated pipelines.
The Learning: We integrated the Gemini API's responseSchema feature with Pydantic in our Python FastAPI service. This forced Gemma 4 to respond in strict JSON formats. We learned that failing closed (e.g., throwing a 502 if the JSON didn't match) is far safer than trying to parse a broken AI response in a security tool.
The Challenge: Modeling transitive dependencies in a standard SQL database requires heavy, recursive CTE queries that are difficult to scale. The Learning: We adopted a polyglot persistence strategy. We learned to use Postgres for what it does best (auth, static scan logs, and caching AI assessments) and offloaded the cross-repository dependency mappings to Neo4j. This allowed us to calculate the exact "Blast Radius" of a vulnerability across an entire organization using a single, highly efficient Cypher graph traversal.
The Challenge: Handling user codebase data alongside AI prompts introduced a risk of exposing database credentials if the AI service was ever compromised.
The Learning: We strictly isolated our services. The FastAPI (ai-service) holds the Gemini API keys but has zero database credentials. The Node.js (api-gateway) holds the database keys but handles no AI prompts. The Gateway fetches the data, passes the isolated context to the AI service, and caches the result.
Devpost Project: https://devpost.com/software/githygiene
Blog Post: GitHygiene: Does This Vulnerability Actually Matter in My Code?
- OSV.dev β open vulnerability database by Google
- Gemma 4 β open model by Google DeepMind, accessed via the Gemini API
- Neo4j Aura β free-tier graph database
- Supabase β open-source Firebase alternative
- react-force-graph-2d β force-directed graph renderer by Vasco Asturiano
- GitHub REST API, npm registry, PyPI JSON API β open public data sources
- Project title and description added
- All team members listed
- Problem clearly explained
- Reason for choosing the problem explained
- Solution and key features documented
- Innovation and differentiation explained
- Architecture included
- Technical implementation documented
- Work completed during the hackathon documented
- Team contributions documented
- Working application is functional
- Live application link added where applicable
- Demo video added
- AI and open-source components documented
- Setup and usage instructions tested
- Challenges and learnings documented
- Devpost submission completed
- Devpost link added
- Credits added
- License added
- Repository is organized and complete

