git-audit: 42-repo hygiene audit Aug 8 - credentials, sprawl, nesting
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
# Git Audit Report — IT Pro Partner Gitea Organization
|
||||
|
||||
**Date:** 2026-08-08
|
||||
**Auditor:** Hermes Agent (automated)
|
||||
**Scope:** git.itpropartner.com / ippadmin (all 42 remote repos + 59 local clones in `/root/projects/`)
|
||||
|
||||
---
|
||||
|
||||
## Summary Verdict
|
||||
|
||||
**The Gitea organization suffers from repo sprawl, weak hygiene, and live secrets in history.** Of 42 remote repos, 32 are single-commit documentation stubs. Only 3–4 repos show active development. The `itpp-infrastructure/docs/` folder is a flat grab-bag and needs structured nesting. 23 local-only repos lack any off-server backup. Credentials are embedded in plaintext across at least 2 repos (`scripts`, `hermes-recovery`). Consolidation, cleanup, and a hygiene push are overdue.
|
||||
|
||||
---
|
||||
|
||||
## Quick Answers to User's Two Questions
|
||||
|
||||
### 1. Should `itpp-infrastructure/docs/` have more nested folders?
|
||||
|
||||
**Yes, absolutely.** The current structure is:
|
||||
|
||||
```
|
||||
docs/
|
||||
backup-restore/ ← already nested (good)
|
||||
legal/ ← already nested (good)
|
||||
ops-portal/ ← already nested (good)
|
||||
app2-caddyfile-audit-2026-07-21.md ← flat
|
||||
client-katie-watts-design.md ← flat
|
||||
cost-control-rollout-2026-07-24.md ← flat
|
||||
git-audit-2026-08-07.md ← flat
|
||||
infrastructure-gap-assessment-2026-08-04.md ← flat
|
||||
key-inventory.md ← flat
|
||||
mattermost-replacement-analysis.md ← flat
|
||||
model-chain.md ← flat
|
||||
project-log.md ← flat
|
||||
projects-master-readme.md ← flat
|
||||
super-search-cf-bypass.md ← flat
|
||||
super-search-enhancement-plan.md ← flat
|
||||
uptime-kuma-monitoring-plan.md ← flat
|
||||
```
|
||||
|
||||
**14 flat files is too many.** Recommended restructuring:
|
||||
|
||||
```
|
||||
docs/
|
||||
audit/ ← git-audit reports, gap assessments
|
||||
backup-restore/ ← (existing, keep)
|
||||
clients/ ← client-katie-watts-design.md
|
||||
infrastructure/ ← model-chain.md, cost-control-rollout, caddyfile-audit, key-inventory
|
||||
legal/ ← (existing, keep)
|
||||
monitoring/ ← uptime-kuma-monitoring-plan.md
|
||||
ops-portal/ ← (existing, keep)
|
||||
projects/ ← project-log.md, projects-master-readme.md
|
||||
super-search/ ← cf-bypass, enhancement-plan
|
||||
mattermost-replacement-analysis.md ← leave at top level (one-off)
|
||||
```
|
||||
|
||||
This gives every file a clear home without over-nesting.
|
||||
|
||||
### 2. Are there too many top-level repos? Should they be consolidated?
|
||||
|
||||
**Yes — 42 repos is far too many for the actual workload.** Here's the breakdown:
|
||||
|
||||
| Category | Count | Action |
|
||||
|----------|-------|--------|
|
||||
| Active development repos | 3–4 | Keep (`itpp-infrastructure`, `hermes-skills`, `scripts`, `homelab`) |
|
||||
| Single-commit documentation stubs | 32 | Consolidate into fewer repos |
|
||||
| Empty repos | 1 | Delete (`auth` — no commits, no content) |
|
||||
| MCP stub repos | 5 | Merge into one `mcp-catalog` repo |
|
||||
| Duplicate/stale repos | 2 | Resolve (`itpp-infra` vs `itpp-infrastructure`, `cartmylist-repo` vs `cartmylist`) |
|
||||
| Local-only (unbacked) | 23 | Push to Gitea or archive |
|
||||
|
||||
**Recommended consolidation:**
|
||||
|
||||
1. **Merge 4 MCP stubs** (`mcp-browser`, `mcp-email`, `mcp-filesystem`, `mcp-git`) into `mcp-servers/` as subdirectories
|
||||
2. **Merge related business ideas** into a `project-ideas` monorepo:
|
||||
- `apex-track`, `boxpilot`, `digital-signage`, `fleettracker360`, `gift-a-roast`, `hudu`, `launchcheck`, `mooresunnydaze`, `nvr-shield`, `osint-tool`, `shark-game`, `startup-studio`, `track-a-flock`, `personal-assistant`
|
||||
3. **Merge related operational repos**: `disaster-recovery` + `backup-restore` → `disaster-recovery/` with `backup-restore/` subdir
|
||||
4. **Merge website/docs stubs**: `itpropartner-website`, `content-creation-pipeline`, `ops-portal` → subdirectories in `itpp-infrastructure`
|
||||
5. **Resolve** `itpp-infra` (stale, SSH-only, 1 commit) → archive; use `itpp-infrastructure` as primary
|
||||
6. **Delete** `auth` (empty repo with `auth.db` — never committed)
|
||||
7. **Keep as-is**: `itpp-infrastructure`, `hermes-skills`, `hermes-recovery`, `scripts`, `homelab`, `dre`, `verdicttank`, `voipsimplicity`, `voipsimplicity-manual`, `forefront-wireless-portal`, `super-search-business`, `model-fallback`, `unifi`, `unms`, `pipeline`, `super-search`
|
||||
|
||||
**Target:** ~15–20 repos instead of 42.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Findings
|
||||
|
||||
### Step 1 — Full Inventory
|
||||
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| Remote repos on Gitea | 42 |
|
||||
| Local clones in `/root/projects/` | 59 |
|
||||
| On Gitea AND cloned locally | 36 |
|
||||
| On Gitea but NOT cloned locally | 6 |
|
||||
| Cloned locally but NOT on Gitea | 23 |
|
||||
| Public repos | 30 |
|
||||
| Private repos | 12 |
|
||||
| Repos using `master` as default branch | 30 |
|
||||
| Repos using `main` as default branch | 9 |
|
||||
| Other (HEAD/detached) | 20 |
|
||||
|
||||
**Repos on Gitea but not cloned locally:** `cartmylist`, `mcp-browser`, `mcp-email`, `mcp-filesystem`, `mcp-git`, `super-search`
|
||||
|
||||
**Local-only repos (no remote — no off-server backup):** `assistant`, `auth`, `capabilities`, `hear-read`, `intelsight`, `intelsight-landing`, `internal`, `mockup`, `my-itpropartner-portal`, `ops`, `ops-v2-portal`, `osint`, `proposals`, `pry`, `research-search-mcp`, `schedule`, `shonuff`, `shonuff-caller`, `static`, `status`, `voice-previews`, `itpp-infra`, `cartmylist-repo`
|
||||
|
||||
### Step 2 — Per-Repo Deep Scan
|
||||
|
||||
#### Hygiene Check: `.gitignore` and `README.md`
|
||||
|
||||
| Status | Count |
|
||||
|--------|-------|
|
||||
| Has `.gitignore` | 8 of 59 (13.6%) |
|
||||
| Has `README.md` | 56 of 59 (94.9%) |
|
||||
|
||||
**Repos missing `.gitignore`:** 51 repos. This is the single biggest hygiene gap.
|
||||
|
||||
**Repos missing `README.md`:** `cartmylist-repo`, `voipsimplicity-manual`, `auth`
|
||||
|
||||
#### Repos with Dirty Working Trees
|
||||
|
||||
| Repo | Dirty Files | Severity |
|
||||
|------|-------------|----------|
|
||||
| `hermes-skills` | 33 | **HIGH** — cache artifacts not committed |
|
||||
| `hermes-recovery` | 26 | **HIGH** — uncommitted recovery scripts |
|
||||
| `voipsimplicity-manual` | 11 | **MEDIUM** |
|
||||
| `research-search-mcp` | 10 | **MEDIUM** |
|
||||
| `auth` | 9 | **MEDIUM** |
|
||||
| `digital-signage` | 6 | **LOW** |
|
||||
| `itpp-infra` | 5 | **LOW** |
|
||||
| `pipeline` | 5 | **LOW** |
|
||||
| `disaster-recovery` | 4 | **LOW** |
|
||||
| `mooresunnydaze` | 4 | **LOW** |
|
||||
| `verdicttank` | 4 | **LOW** |
|
||||
| `itpp-infrastructure` | 3 | **LOW** |
|
||||
|
||||
**13 repos** have uncommitted changes. `hermes-skills` (33 files) and `hermes-recovery` (26 files) are the worst offenders.
|
||||
|
||||
### Step 3 — Secrets Scan
|
||||
|
||||
**CRITICAL findings in 2 repos:**
|
||||
|
||||
#### `scripts` — 10 potential secrets
|
||||
Real, hardcoded credentials found in Windows provisioning scripts:
|
||||
```
|
||||
+Password="LoveMyBoys73!"
|
||||
+Password="Liberty4All!"
|
||||
+Password="tire"
|
||||
+Username="ippadmin"
|
||||
+Username="liberty-admin"
|
||||
```
|
||||
|
||||
These are active Windows admin credentials embedded in PowerShell unattend scripts. **This is a data breach risk.** If these repos ever go public or are cloned outside ITPP infrastructure, client credentials are exposed.
|
||||
|
||||
#### `hermes-recovery` — 8 potential secrets
|
||||
Includes the Gitea API token used for this audit:
|
||||
```
|
||||
+TOKEN="1761daa2c537fb72b365e54619208329d8e3ad33"
|
||||
+TELEGRAM_BOT_TOKEN="8359374835:***"
|
||||
+password="K3E1ZZWvHDu0q8ZmoBCAhzKUZawEapdGBlbaPME1sOTKgGk9FCuYS"
|
||||
+token = "Ta9f9d1b462271a2f4-8d63a3f025eb89451edb16f2308c2e40"
|
||||
```
|
||||
|
||||
The Gitea token itself is committed. This means `hermes-recovery` as a public repo exposes admin credentials.
|
||||
|
||||
#### `hermes-skills` — 15 potential hits
|
||||
Most are false positives (example values, `process.env.` references, placeholder text). One real hit: a Comfy CLI API key in a SKILL.md.
|
||||
|
||||
### Step 4 — Structural Checks
|
||||
|
||||
#### Remote URL Audit
|
||||
|
||||
| Remote Type | Count | Action |
|
||||
|-------------|-------|--------|
|
||||
| HTTPS to Gitea | 36 | OK |
|
||||
| GitHub (upstream) | 1 | OK (`msp-claude-skills`) |
|
||||
| SSH to Gitea | 2 | **FIX** — `itpp-infra`, `cartmylist-repo` |
|
||||
| No remote | 23 | **FIX** — local-only, no backup |
|
||||
|
||||
`itpp-infra` uses `git@git.itpropartner.com:ippadmin/itpp-infra.git` (SSH) — this repo has no corresponding HTTPS clone and appears to be a stale/abandoned repo (1 commit, 5 dirty files).
|
||||
|
||||
`cartmylist-repo` (local) vs `cartmylist` (Gitea) is a naming mismatch. The local clone has an SSH remote to what is likely a different repo.
|
||||
|
||||
#### Branch Naming
|
||||
|
||||
- **30 repos use `master`** — industry standard is now `main`
|
||||
- **9 repos use `main`**
|
||||
- **20 repos have detached HEAD or no commits**
|
||||
|
||||
**Branch mismatch:** `itpp-infrastructure` has `main` locally but `master` on Gitea. This means the remote may have both branches.
|
||||
|
||||
#### Large Files
|
||||
|
||||
| Repo | File | Size |
|
||||
|------|------|------|
|
||||
| `hermes-skills` | `.hub/index-cache/hermes-index.json` | 38.9 MB |
|
||||
| `hermes-skills` | `.curator_backups/2026-07-12T15-48-44Z/skills.tar.gz` | 2.7 MB |
|
||||
|
||||
Both are cache artifacts that should be in `.gitignore`, not tracked.
|
||||
|
||||
#### Public vs Private
|
||||
|
||||
**30 of 42 repos (71%) are public.** This is a concern because:
|
||||
- `scripts` contains client admin passwords — **public**
|
||||
- `hermes-recovery` contains Gitea admin token — **public**
|
||||
- Many repos with sensitive infrastructure details are public
|
||||
|
||||
### Step 5 — Commit Quality
|
||||
|
||||
#### Commit Message Quality
|
||||
|
||||
| Pattern | Count | Assessment |
|
||||
|---------|-------|------------|
|
||||
| `Initial commit — YYYY-MM-DD` | 16 | Poor — conveys nothing |
|
||||
| `Update YYYY-MM-DD — root` | 5 | Meaningless |
|
||||
| `Initial: <project name>` | 8 | Barely adequate |
|
||||
| Descriptive conventional commits | 4 | Good (`homelab`, `verdicttank`, `forefront-wireless-portal`) |
|
||||
|
||||
**32 repos have only 1 commit** — these are documentation stubs, not developed projects.
|
||||
|
||||
#### Active vs Abandoned
|
||||
|
||||
| Status | Criteria | Repos |
|
||||
|--------|----------|-------|
|
||||
| **Active** | 3+ commits, recent activity | `itpp-infrastructure` (52), `scripts` (11), `homelab` (6), `forefront-wireless-portal` (5), `dre` (4), `verdicttank` (4), `personal-assistant` (3), `super-search-business` (3), `voipsimplicity` (3) |
|
||||
| **Stub** | 1–2 commits, last push July 2025 | 32 repos |
|
||||
| **Abandoned** | No commits or stale >3 months | `itpp-infra`, `auth`, `cartmylist-repo` |
|
||||
|
||||
### Step 6 — Local-Only Repos
|
||||
|
||||
23 repos in `/root/projects/` have no remote. Breakdown:
|
||||
|
||||
| Category | Repos | Action |
|
||||
|----------|-------|--------|
|
||||
| Uncommitted stubs (0 commits) | 17 | Push to Gitea or archive |
|
||||
| Has commits, no remote | 1 (`shonuff-caller`) | Push to Gitea |
|
||||
| Stale noise | 5 | Archive and delete (`auth`, `itpp-infra`, `cartmylist-repo`, etc.) |
|
||||
|
||||
The 17 repos with 0 commits and only detached HEAD are effectively just directories with a `.git` folder — not real repos. They should be either pushed as proper repos or archived.
|
||||
|
||||
---
|
||||
|
||||
## Prioritized Action Plan
|
||||
|
||||
### 🔴 Immediate (This Week)
|
||||
|
||||
| # | Action | Severity |
|
||||
|---|--------|----------|
|
||||
| 1 | **Rotate all credentials exposed in `scripts` repo** — Windows passwords, Gitea token, Telegram bot token. Then purge from Git history with `git filter-branch` or `bfg-repo-cleaner` | **CRITICAL** |
|
||||
| 2 | **Rotate Gitea API token** in `hermes-recovery` — it's publicly visible. Generate new token, update all consumers, purge old from history | **CRITICAL** |
|
||||
| 3 | **Make `scripts` and `hermes-recovery` PRIVATE** — they contain live credentials visible to anyone | **CRITICAL** |
|
||||
| 4 | **Add `.gitignore` to all 51 repos missing one** — start with the active repos first | **HIGH** |
|
||||
| 5 | **Commit or stash all dirty working trees** — 13 repos have uncommitted work at risk of loss | **HIGH** |
|
||||
|
||||
### 🟡 Short-Term (This Month)
|
||||
|
||||
| # | Action | Severity |
|
||||
|---|--------|----------|
|
||||
| 6 | **Reorganize `itpp-infrastructure/docs/`** into nested folders (audit/, clients/, infrastructure/, monitoring/, projects/, super-search/) | **MEDIUM** |
|
||||
| 7 | **Consolidate 4 MCP repos into `mcp-servers/`** as subdirectories — delete empty stubs after merge | **MEDIUM** |
|
||||
| 8 | **Merge 14 single-commit business idea repos** into a `project-ideas` monorepo | **MEDIUM** |
|
||||
| 9 | **Delete `auth`** (empty repo, 0 commits) | **MEDIUM** |
|
||||
| 10 | **Resolve `itpp-infra` vs `itpp-infrastructure`** — archive `itpp-infra`, standardize on `itpp-infrastructure` | **MEDIUM** |
|
||||
| 11 | **Add `.gitignore` entries to `hermes-skills`** for `.hub/`, `.curator_backups/` | **MEDIUM** |
|
||||
| 12 | **Rename `master` → `main` on repos where it matters** (at minimum `itpp-infrastructure` to fix branch mismatch) | **LOW** |
|
||||
|
||||
### 🔵 Pre-Public / Pre-Open-Source
|
||||
|
||||
| # | Action | Severity |
|
||||
|---|--------|----------|
|
||||
| 13 | **Audit all 30 public repos** — ensure no private infrastructure details, client names, IPs, or credentials are exposed | **HIGH** |
|
||||
| 14 | **Decide public/private policy** — which repos genuinely need to be public? Currently 71% are public. | **MEDIUM** |
|
||||
| 15 | **Push 23 local-only repos to Gitea** or archive them. No code living only on a single server. | **HIGH** |
|
||||
| 16 | **Clean commit history** — rebase repos with "Update YYYY-MM-DD — root" messages into meaningful commits | **LOW** |
|
||||
|
||||
---
|
||||
|
||||
## Template: Standard `.gitignore`
|
||||
|
||||
For any new or cleaned repo, use:
|
||||
|
||||
```gitignore
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.egg-info/
|
||||
.venv/
|
||||
venv/
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
|
||||
# Secrets — NEVER commit these
|
||||
.env
|
||||
.env.*
|
||||
*.pem
|
||||
*.key
|
||||
credentials.json
|
||||
*.token
|
||||
|
||||
# Cache / generated
|
||||
.hub/
|
||||
.curator_backups/
|
||||
*.tar.gz
|
||||
*.zip
|
||||
|
||||
# Data
|
||||
*.db
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Full Repo Inventory
|
||||
|
||||
### Active Repos (keep as standalone)
|
||||
|
||||
| Repo | Commits | Last Commit | Branch | `.gitignore` | Assessment |
|
||||
|------|---------|-------------|--------|-------------|------------|
|
||||
| `itpp-infrastructure` | 52 | 2026-08-07 | main/master mismatch | YES | **Primary hub** — healthy |
|
||||
| `hermes-skills` | 1 | 2026-07-15 | master | NO | Active mirror, 13.6MB |
|
||||
| `hermes-recovery` | 1 | 2026-07-15 | master | YES | Critical backup kit |
|
||||
| `scripts` | 11 | 2026-07-25 | master | NO | Active, **has secrets** |
|
||||
| `homelab` | 6 | 2026-07-24 | main | NO | Active, good commits |
|
||||
| `dre` | 4 | 2026-07-25 | master | NO | Active development |
|
||||
| `verdicttank` | 4 | 2026-08-07 | main | NO | Active, good commits |
|
||||
| `forefront-wireless-portal` | 5 | 2026-07-25 | master | NO | Active, good commits |
|
||||
| `super-search-business` | 3 | 2026-07-25 | main | NO | Active |
|
||||
| `voipsimplicity` | 3 | 2026-07-24 | master | NO | Active client work |
|
||||
| `voipsimplicity-manual` | 1 | 2026-08-05 | master | NO | Active client work |
|
||||
|
||||
### Stub Repos (consolidate)
|
||||
|
||||
All 32 repos below are single-commit documentation stubs with no ongoing development. Consolidate into `project-ideas/` monorepo or relevant parent repo:
|
||||
|
||||
`apex-track`, `backup-restore`, `boxpilot`, `content-creation-pipeline`, `digital-signage`, `disaster-recovery`, `fleettracker360`, `gift-a-roast`, `hudu`, `itpropartner-website`, `launchcheck`, `mcp-browser`, `mcp-email`, `mcp-filesystem`, `mcp-git`, `mcp-servers`, `model-fallback`, `mooresunnydaze`, `nvr-shield`, `ops-portal`, `osint-tool`, `personal-assistant`, `pipeline`, `shark-game`, `startup-studio`, `super-search`, `track-a-flock`, `unifi`, `unms`, `cartmylist`
|
||||
|
||||
### To Delete or Archive
|
||||
|
||||
| Repo | Reason |
|
||||
|------|--------|
|
||||
| `auth` | Empty (0 commits, 0 content) |
|
||||
| `itpp-infra` | Stale duplicate of `itpp-infrastructure`, SSH-only remote, 1 commit |
|
||||
| `cartmylist-repo` | Local clone with SSH remote, mismatched name (real one is `cartmylist` on Gitea) |
|
||||
|
||||
### Local-Only (push or archive)
|
||||
|
||||
`assistant`, `auth`, `capabilities`, `hear-read`, `intelsight`, `intelsight-landing`, `internal`, `mockup`, `my-itpropartner-portal`, `ops`, `ops-v2-portal`, `osint`, `proposals`, `pry`, `research-search-mcp`, `schedule`, `shonuff`, `shonuff-caller`, `static`, `status`, `voice-previews`
|
||||
|
||||
---
|
||||
|
||||
*Report generated by Hermes Agent git-audit workflow. Next audit recommended: 2026-11-08.*
|
||||
Reference in New Issue
Block a user