Dependabot flags packages. NotSudo flags only what your code can actually hit — then opens (and optionally merges) a fix PR with cited evidence.
| URL | Description | |
|---|---|---|
| Live app | https://notsudo-advisor.onrender.com/Dashboard.html | Production dashboard — scan any public GitHub repo |
| Video demo | Loom walkthrough | Screen recording of NotSudo in action |
| Health | https://notsudo-advisor.onrender.com/api/health | Live config status (LLM, OAuth, PAT, auto-merge) |
| Landing | https://notsudo-advisor.onrender.com/ | Product landing page |
| Source | github.com/ashokDevs/notsudo-advisor | Full codebase |
| Deploy guide | docs/DEPLOY_ONLINE.md |
Render / Docker / ngrok |
| Demo script | docs/demo_script.md |
90-second pitch talk track |
| System design | docs/system_design.md |
HLD, threat model, eval |
| Idea / product | docs/idea.md |
Framing & roadmap |
| OSV | osv.dev | Advisory data source (no API key) |
NotSudo is an agentic dependency security advisor. It:
- Ingests public vulnerability data from OSV for packages in
package.json/ lockfiles / Python manifests - Matches versions with proper semver / OSV ranges (not “present ⇒ vulnerable”)
- Finds call sites in your source (imports + syntactic calls)
- Judges reachability —
exposed/safe/unsure— via heuristics or LLM (OpenRouter / OpenAI-compatible) - Validates evidence quotes against real file text (anti-hallucination)
- Opens a lockfile-validated fix PR on GitHub (scanned repo) for human review
Opening a fix PR is deliberately a server-authorized operation. A completed scan creates a short-lived, signed remediation plan that binds the scanned repository, dependency, safe version, validated evidence, and affected advisory IDs. The browser sends only that token back to the API, so it cannot alter a package, target repository, verdict, or evidence to manufacture a PR.
For npm repositories, NotSudo resolves the change with npm install --package-lock-only --ignore-scripts before creating a PR. Both package.json and package-lock.json are updated, and the pull request always requires human review.
Most tools (Dependabot, basic SCA) report presence. Engineers mute them.
NotSudo answers: can production code reach the vulnerable path? and ships a version bump PR with evidence.
flowchart TB
subgraph Users
U[Developer / Demo]
end
subgraph NotSudo["NotSudo Advisor (Render)"]
UI[Dashboard + Landing<br/>frontend/]
API[FastAPI<br/>api/app.py]
SCAN[Scanner pipeline<br/>core/analysis/]
AGENT[LangGraph agent<br/>triage → locate → reason → preflight → act]
MCP[MCP tools<br/>capability isolation]
LLM[LLM client<br/>OpenAI-compatible / OpenRouter]
end
subgraph External
OSV[OSV API<br/>api.osv.dev]
GH[GitHub API<br/>clone · PR · merge]
OR[OpenRouter / OpenAI]
end
U -->|HTTPS| UI
UI -->|/api/scan · /api/pr · OAuth| API
API --> SCAN
API --> AGENT
SCAN --> OSV
SCAN --> GH
SCAN --> LLM
AGENT --> MCP
AGENT --> LLM
MCP --> GH
LLM --> OR
API -->|validated fix PR| GH
flowchart LR
A[GitHub URL or local path] --> B[Shallow clone / read tree]
B --> C[Parse package.json / lock / requirements]
C --> D[OSV querybatch + vuln details]
D --> E[Semver range gate]
E -->|not in range| S[safe]
E -->|in range| F[Find imports + call sites]
F --> G[Reachability<br/>LLM or heuristics]
G --> H{verdict}
H -->|exposed| I[Preflight + PR draft]
H -->|safe / unsure| J[Report only]
I --> K[GitHub fix PR]
K --> N[Human review + repository checks]
stateDiagram-v2
[*] --> triage
triage --> locate: dependency in range
triage --> act: not present / not in range
locate --> reason
reason --> locate: need more context (cap 2)
reason --> preflight: exposed
reason --> act: safe / unsure
preflight --> act
act --> [*]
| Node | Role |
|---|---|
| triage | Fetch advisory, match package + semver, early exit |
| locate | Call-site / import discovery |
| reason | Reachability + grounded evidence quotes |
| preflight | Lockfile / manifest bump sanity |
| act | Format PR; only this path may call side-effect tools |
Advisory text is attacker-controlled. Side-effecting tools (pr_create) are not available to nodes that ingest raw advisory prose.
flowchart TB
subgraph Untrusted
ADV[Advisory body from OSV/GHSA]
end
subgraph Read_only["Read-only nodes"]
T[triage]
L[locate]
R[reason]
end
subgraph Side_effects["Side-effect nodes only"]
A[act / draft_pr]
end
ADV -->|delimited as data| T
ADV -->|never raw in PR decision| A
T --> L --> R
R -->|structured verdict only| A
A -->|pr_create| GH[(GitHub API)]
style Side_effects fill:#2d4a3e,stroke:#3ecf8e
style Untrusted fill:#4a2d2d,stroke:#ef4444
Enforced in code: core/security/capability_graph.py · tested in tests/unit/test_capability_graph.py.
notsudo-advisor/
├── api/ # FastAPI — scan, OAuth, PR, health
├── frontend/ # Dashboard + landing (static HTML/JSX)
├── core/
│ ├── analysis/ # pipeline, semver, call sites, reachability, preflight
│ ├── orchestration/ # LangGraph graph + nodes
│ ├── security/ # TOOL_PERMISSIONS + CapabilityGraph
│ ├── llm/ # OpenAI-compatible client (OpenRouter)
│ ├── ingestion/ # OSV, chunker, repo/advisory ingesters
│ └── action/ # PR body formatting
├── mcp_server/ # MCP tools with authorize()
├── cli/ # notsudo scan | analyze | demo | run-pipeline
├── demo_app/ # Intentionally outdated npm deps for demos
├── eval/ # Ground-truth harness
├── docs/ # Design + deploy docs
├── Dockerfile # Production image (includes git)
└── render.yaml # Render blueprint
Watch the demo: https://www.loom.com/share/805e2a381d894a2799e90072d61e29ee
- Open Dashboard (free tier may take ~30–60s to wake)
- Scan:
demo_app(bundled vulnerable sample), orhttps://github.com/OWASP/NodeGoat
- Default filter: exposed
- Expand a row → call sites
file:line+ reasoning - Open fix PR after reviewing the cited evidence; merge through the repository's normal checks
- PRs open on the scanned GitHub repo (not a random demo repo, unless you only scanned local
demo_app)
| Feature | Status |
|---|---|
| Live OSV scan (npm + PyPI manifests) | ✅ |
| Semver / OSV range matching | ✅ |
| Call-site finder (imports + calls) | ✅ |
| Reachability: exposed / safe / unsure | ✅ |
| LLM via OpenRouter / OpenAI-compatible API | ✅ |
| Heuristic fallback without LLM key | ✅ |
| Evidence quote validation | ✅ |
| GitHub URL clone → scan → cleanup | ✅ |
npm fix PR (manifest + package-lock.json) on scanned repo |
✅ |
| Python remediation PR | Report-only (planned) |
| Automatic PR opening after a validated scan | ✅ |
| GitHub OAuth Sign in | ✅ |
PAT (GITHUB_TOKEN) without browser login |
✅ |
| Capability isolation tests | ✅ |
| Docker + Render deploy | ✅ |
| CLI + eval harness | ✅ |
| Ecosystem | Detection and reachability | Fix PR |
|---|---|---|
| npm | Yes | Yes: package.json + package-lock.json |
| PyPI | Yes | Report-only for now |
Multiple exposed advisories for the same npm dependency are combined into one remediation plan using the highest required fixed version, avoiding competing PRs.
- Advisory bodies are treated as untrusted data when used in prompts.
- Evidence quotes must match real source files before a remediation plan is generated.
- MCP draft authorization reads the node identity from server configuration, not an MCP caller argument.
- GitHub PR creation is bound to a signed scan result and review is mandatory.
- OSV change discovery uses its official reverse-chronological
modified_id.csvfeed. OSV documentation
| Layer | Technology |
|---|---|
| API | FastAPI, Uvicorn, Starlette sessions |
| Agent | LangGraph, LangChain OpenAI client |
| LLM | OpenRouter / any OpenAI-compatible base URL |
| Data | OSV HTTP API, GitHub REST API |
| Analysis | Pure Python semver, regex/call-site finder, tree-sitter chunker |
| Optional DB | Postgres + pgvector (index / RAG path) |
| Frontend | Static React-via-Babel JSX + CSS tokens |
| Deploy | Docker on Render |
# Python 3.12+
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux: source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env
# Edit .env — see Environment section below
python -m uvicorn api.app:app --reload --port 8080
# → http://127.0.0.1:8080/Dashboard.htmlpython -m cli.main scan demo_app
python -m cli.main scan https://github.com/OWASP/NodeGoat
python -m cli.main demo| Variable | Required | Description |
|---|---|---|
APP_BASE_URL |
Online yes | Site origin, e.g. https://notsudo-advisor.onrender.com (no /Dashboard.html) |
OPENAI_API_KEY |
For LLM | OpenRouter sk-or-… or OpenAI sk-… |
OPENAI_API_BASE |
For OpenRouter | https://openrouter.ai/api/v1 |
LLM_MODEL |
Optional | e.g. openrouter/auto |
GITHUB_TOKEN |
For PRs | Fine-grained PAT: Contents + Pull requests = write |
GITHUB_DEMO_REPO |
Local demo PRs | Fallback when scanning local demo_app |
GITHUB_CLIENT_ID / SECRET |
OAuth UI | GitHub OAuth App credentials |
SESSION_SECRET |
Online yes | Long random string |
NOTSUDO_HASH_EMBEDDINGS |
Optional | 1 skips heavy embedding model download |
OAuth callback (GitHub App settings):
https://notsudo-advisor.onrender.com/auth/github/callback
| Method | Path | Description |
|---|---|---|
GET |
/api/health |
Live status (no secrets) |
GET |
/api/me |
Session user + config flags |
GET |
/api/github/status |
Can this token push/merge? |
POST |
/api/scan |
{ "target": "owner/repo" | path | URL } |
POST |
/api/pr |
Open a PR from a short-lived server-issued remediation token |
GET |
/auth/github/login |
Start OAuth |
GET |
/auth/github/callback |
OAuth return |
# Docker
docker build -t notsudo .
docker run --rm -p 8080:8080 --env-file .env \
-e APP_BASE_URL=https://your-host \
notsudoOr connect this repo to Render with the included Dockerfile / render.yaml.
Details: docs/DEPLOY_ONLINE.md.
Render Free is good for interactive scans, but an idle web service sleeps and cannot reliably host a continuous OSV worker. The notsudo watch-osv command reads the correct OSV change feed; schedule it with a Render Cron Job, GitHub Actions, or another scheduler when continuous monitoring is needed.
ruff check .
mypy .
pytest tests/unit tests/test_smoke.py -qCI runs on every push to main: lint → mypy → pytest
→ Actions
- Call-site finding is syntactic (misses dynamic
obj[name]()dispatch) - Without an LLM key, verdicts use strong heuristics
- Online: scan GitHub URLs, not local disk paths like
D:\... - Fix PRs need write access on the target repo
- Free Render sleeps when idle — first request can be slow
| Document | Purpose |
|---|---|
docs/idea.md |
Product idea & scope |
docs/system_design.md |
Architecture deep dive |
docs/tdd_design.md |
Testing strategy |
docs/coding_rules.md |
Hard engineering rules |
docs/demo_script.md |
Live demo script |
docs/DEPLOY_ONLINE.md |
Production deploy |
CLAUDE.md |
Operator notes for AI assistants |
See repository owner for license terms.
Watch the Loom demo → · Open the live Dashboard → · Star on GitHub