跳到正文
DEV Community· Vinks Goyal·· 2 小時前AI 評分54

Gnosk 可離線掃描專案中的個人資料

Gnosk — Know what your code knows

AI 導讀

Vinks Goyal 開發 Gnosk,一款供自由工作者使用的離線個人資料掃描工具,可列出資料所在的檔案路徑、行號及信心分數。它先以規則檢查 Aadhaar、PAN、印度電話號碼、電郵及 12 種供應商 API 金鑰格式,僅將規則無法判定的結果交由本機 Gemma 2B 處理,並輸出遮蔽後的 Markdown 清單;離線亦可掃描。

正文

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

Gnosk is an offline PII scanner for freelance developers. You point it at a folder. It scans every file in milliseconds and reports where personal data lives — Aadhaar numbers, PAN cards, Indian phone numbers, emails, twelve provider API-key shapes — with the exact file path, line number, and a confidence score. Then it writes a redacted markdown inventory you can hand to a client without a second thought.

The problem it solves is legal, not technical. Freelancers in India are increasingly being asked by clients to sign data-protection clauses under the DPDP Act. Most of them have no idea what personal data their project actually touches. Enterprise DLP tools cost more than a freelancer's monthly income. Cloud-based scanners want you to upload the exact data you're trying to protect. Both of those answers are non-starters.

Gnosk is the third option. Deterministic validators run first — regex, Verhoeff checksums, provider-format patterns — and a local Gemma 2B model runs only on the findings the rules can't decide. Nothing leaves the machine. You can unplug the network cable and the scan still works.

Repo: https://github.com/vinksgoyal/gnosk
Tests: 215 passing, ruff clean
License: MIT
Platform: Linux and macOS. Windows requires WSL — documented, not polished.

Demo

Four things to watch for in the demo:

  1. Triage before the model. A scan produces five findings. Four are resolved by rules in ten milliseconds. One goes to the model. The AI sees one item, not five.
  2. Real-world scan. The same tool against my Downloads folder: 127 files, 6 findings, 0.13 seconds. Two of the findings were my own email in my own resume — sitting in a folder I'd forgotten about.
  3. Redaction. The report shows 23********24, not 234567890124. Two characters at each end, asterisks in the middle. The report cannot become the leak it's warning you about.
  4. Offline. Airplane mode on. Scan still runs.

Code

Repo: https://github.com/vinksgoyal/gnosk

git clone https://github.com/vinksgoyal/gnosk
cd gnosk
./install.sh

Then:

gnosk scan ./my-project             # quick scan
gnosk scan ./my-project --classify  # add local model verdicts
gnosk report ./my-project           # write a client-ready inventory

Stack: Python 3.11+, typer for the CLI, rich for terminal output, questionary for the REPL, ollama-python for the local model, gemma2:2b via Ollama.

The install script is ~190 lines of bash: version-checks Python, creates a virtualenv, installs the package, symlinks gnosk into ~/.local/bin. Idempotent, colored output, specific error messages — if Python 3.11 is missing, it tells you the exact apt install line instead of "command not found."

How I Built It

I'm on a 2-core Intel laptop with 7.7 GB RAM and no GPU. Every architectural decision in this project comes from that constraint.

Day 1 — the deterministic layer. Five validators (Aadhaar, PAN, email, Indian phone, API keys) plus tests. My hand-computed Verhoeff check digit was wrong on the first try — the validator caught it. That was the first evidence that deterministic-first would pay off: the failing test wasn't the code, it was my arithmetic.

Day 2 — the wall. I benchmarked the model: a 2 KB prompt takes 38 seconds to process on this hardware. My initial classifier prompt was 1,500 characters and timed out at 180 seconds. The first three attempts at building the classifier failed because I assumed the bottleneck was prompt size. It wasn't. Output tokens were. Capping num_predict at 180 and dropping the reason field from the model's response cut wall time from over 180 seconds to under 60.

Day 3 — the false-positive hunt. This is the part I'm most proud of.

Act 1. I scanned a Hacktoberfest project I had on disk — a bird-detection app called birdbuddy. It reported 14 phone numbers. Every one was a fragment of the same SHA256 hash: ...987d8227518893. The substring 8227518893 is 10 digits starting with 8, which matches my Indian-phone regex exactly. A 64-character hex string contains about 55 overlapping 10-digit windows; several will match a phone pattern by pure chance.

Act 2. I added a hex-run filter — if a finding's span sits inside a 32+ character hex run, drop it. My test suite caught the fix eating GitHub PATs. A 40-character ghp_AAAA... token looked like a hex run because 36 of its 40 characters are hex. Added a condition: the matched span itself must consist entirely of hex characters. ghp_ contains g, h, p, _ — not hex. Filter fixed.

Act 3. Re-ran birdbuddy. Still 8 phone numbers. These were decimal fragments of per-token LLM prices from a .backboard/sessions/*.jsonl gateway log. 0.000000234375 produces 234375000000 if you squint at the digits. Added an entropy filter — real Indian phone numbers don't have five or more zeros — and a skip-list entry for tool-generated directories.

After all three fixes, birdbuddy returns zero findings. Real bug, three iterations, real numbers. None of this came from test fixtures. All of it came from running the tool on data that wasn't designed to make the tests pass.

Day 4 — triage. The --classify flag was initially sending every finding to the model, which took 120 seconds per file on this hardware. That made the AI look like a bolt-on. Triage rules now resolve the easy cases first:

  • Known fixture values (alice@example.com, AKIAIOSFODNN7EXAMPLE) → fixture
  • Files in tests/, fixtures/, examples/ → fixture, except API keys and checksum-valid Aadhaar, which always defer to the model
  • Checksum-valid Aadhaar outside a test path → likely_real (format validity only, not personhood)
  • Everything else → model

On the demo folder, that's four triaged plus one modeled. On a real client project, it means the model is called once or twice per scan, not once per finding.

Day 5 — the rename. I renamed the project from VaultScan to Gnosk. This broke the venv, because Python venvs embed absolute paths at creation time. rm -rf .venv && ./install.sh fixed it in thirty seconds. Renaming a project directory is never just mv.

The result is 215 tests, zero failures, an install script verified from a fresh shell, and a terminal UI that respects NO_COLOR and refuses to emit ANSI codes when piped to a file — because a security tool that dumps escape sequences into a log file isn't a tool you'd trust.

Why Does Open Innovation Matter?

A cloud-based scanner cannot solve this problem at any price.

The DPDP Act asks freelancers to take responsibility for personal data. The only way to know what personal data a project touches is to scan it. The only safe way to scan client data is not to send it to someone else's server. OpenAI, Anthropic, Google — they all require uploading data to their API. That's a non-starter for the exact use case Gnosk exists for.

What open-weight models unlock here isn't a cheaper API call. It's a category of tool that couldn't exist otherwise: an AI that runs on the same laptop as the data, with no network path out.

The specific pieces that make Gnosk work:

  • Gemma 2B via Ollama. Runs on a 2-core CPU with no GPU. About 60 seconds for a bounded classification task. Slow, but usable, and free forever.
  • Localhost inference. The only network call is localhost:11434. Turn off Wi-Fi and everything still works.
  • Open weights mean I can tune the prompt against my own hardware. With a closed API, every prompt iteration costs tokens. On my machine, it costs three minutes and no money — and I iterated dozens of times.

If Gemma weren't open-weight, the honest alternative for Gnosk would be no AI at all. The deterministic scanner works fine. It just can't tell a real client email from a test fixture. That judgment call is exactly what the model handles, and it's the difference between a tool that produces 14 false positives on a real project and one that produces zero.

The "open" in open-source AI isn't a licensing footnote here. It's the entire reason the project is possible.

My Agent Session

I built Gnosk mostly in conversation with an AI coding assistant. The real number: roughly 15 hours across 5 days and about 200 messages. Every commit in the repo is a real step in that process.

What worked:

  • Tight feedback loops. "Run the tests, paste the output" beat "explain how this should work." Most bugs got fixed in a single turn because the loop closed fast.
  • The agent pushed back on my bad ideas. When I tried to build a full-screen textual TUI, it said "3–6 hours on 2-core hardware, DEV post unstarted — this is the wrong call." It was right. We shipped a REPL instead, in an hour.
  • The agent was honest about trade-offs. When I asked for a continuously animating signature, it suggested a static gradient first and explained why a looping color cycle was a worse product. That small moment shaped three later decisions.

What didn't work:

  • Long conversations degrade. Around message 100, the agent started forgetting context from earlier in the session. I had to re-explain the Finding dataclass twice. A CLAUDE.md file at the repo root would have prevented this — that lesson is now in the repo as ARCHITECTURE.md.
  • Suggested nano edits were unreliable. Twice, an edit corrupted the file (merged lines, deleted a function body). The fix was full-file overwrites via cat > file <<'EOF'. Slower to read, but it never corrupts.
  • The agent can't verify its own suggestions on my hardware. "This should run in ~30 seconds" was sometimes 90. The only fix was to run it and paste the real numbers back.

The tool worked. The prompt did not do the work — the iteration did. My best commits came from the agent being wrong and me running the code to find out why.

Prize Categories

Gemma — Best Use of Gemma

Gnosk uses gemma2:2b via Ollama for ambiguous-finding classification. Measured on 2-core CPU, no GPU: about 60 seconds per file with five findings. Full prompt strategy and benchmark numbers are in ARCHITECTURE.md section 8 of the repo.

The model is not decoration. It's the difference between a scan that produces 14 false positives on a real project and one that produces zero. Removing it would turn Gnosk from a tool a freelancer could actually hand to a client into a noisy regex scanner they'd uninstall after one run.

來源:DEV Community · dev.to