You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The tree-sitter discovery pipeline produces empty threat models for Python CLI projects. The Python query set covers web services (Flask, FastAPI, Django routes) and MCP servers (@mcp.tool decorators) but has no patterns for the dominant Python CLI frameworks: argparse, click, typer.
Sibling to #262 (Go cobra CLI coverage, addressed in feature 014). This issue covers the Python half of the same coverage gap.
Motivating example: darnit itself
darnit is the canonical dogfood target. Its surface is split:
MCP server (packages/darnit/src/darnit/tools/, packages/darnit-baseline/src/darnit_baseline/tools.py) — covered today via python.entry.mcp_tool.
CLI (packages/darnit/src/darnit/cli.py) — argparse.ArgumentParser with 8 subparsers (serve, audit, plan, profiles, validate, init, list, install), each registered via subparsers.add_parser(...). Not covered.
So running darnit's threat-model generator against the darnit repo produces a thread model for the MCP tools only — the CLI surface is invisible. This is structurally analogous to gittuf's situation before #262.
Why this matters
OSPS-SA-03.02 (Threat model documentation) is one of darnit-baseline's flagship auto-remediations. Without Python CLI coverage we can't dogfood it against our own CLI.
Python CLIs are everywhere in dev tooling (pip, pipx, uv, ruff, pre-commit, mypy, sphinx, etc.). Saying "darnit produces threat models for Python projects" without CLI coverage is a real overclaim.
Detect the add_subparsers → add_parser relationship to group sibling commands as a family (analogous to feature 014's cobra family-grouping)
Reuse feature 014's EntryPointKind.CLI_COMMAND and the import-based STRIDE-heuristic table (Tampering / Repudiation / Spoofing+Information Disclosure / Elevation of Privilege / Tampering fallback)
Render under the existing ### CLI Entry Points subsection (no document-structure changes — feature 014 already built that)
Tests: synthetic fixtures (argparse_minimal, argparse_subcommands); regression run against darnit itself
Phase 2 — click:
Match @click.command() and @<group>.command() decorators
Match @click.group() for family roots
Click commands often live in single-file CLIs; grouping defaults to one family per click.group plus an "ungrouped" family for top-level commands
Phase 3 — typer:
Match @app.command() and typer.Typer() instantiation
Sub-app patterns (app.add_typer(sub_app, ...)) → family
Effort estimate
Phase 1 (argparse): ~2-3 days incl. tests. Python query infrastructure is more mature than the Go pipeline that feature 014 had to build out, so several stages (extractor scaffolding, family grouping, STRIDE heuristic, renderer) can be reused with framework-specific extensions rather than ground-up additions.
Phase 2 (click): ~1.5-2 days additional.
Phase 3 (typer): ~1 day additional (very similar to click).
Phases can land independently.
Acceptance
Darnit cold-audit produces a non-empty ### CLI Entry Points section listing all 8 of darnit's argparse subcommands (one family).
Each finding includes a STRIDE category, a file:line location, and the "needs reviewer attention" marker.
Existing Python pipeline tests (Flask, FastAPI, MCP) pass with no regression.
README's "Threat-model coverage scope" table updates to acknowledge Python CLI coverage for whichever frameworks land.
Feature spec for the cobra work lives at specs/014-cobra-threat-model/. The grouping algorithm (filesystem-layout-based), the STRIDE heuristic table, and the CLI rendering section are framework-agnostic and reusable here.
Summary
The tree-sitter discovery pipeline produces empty threat models for Python CLI projects. The Python query set covers web services (Flask, FastAPI, Django routes) and MCP servers (
@mcp.tooldecorators) but has no patterns for the dominant Python CLI frameworks:argparse,click,typer.Sibling to #262 (Go cobra CLI coverage, addressed in feature 014). This issue covers the Python half of the same coverage gap.
Motivating example: darnit itself
darnitis the canonical dogfood target. Its surface is split:packages/darnit/src/darnit/tools/,packages/darnit-baseline/src/darnit_baseline/tools.py) — covered today viapython.entry.mcp_tool.packages/darnit/src/darnit/cli.py) —argparse.ArgumentParserwith 8 subparsers (serve,audit,plan,profiles,validate,init,list,install), each registered viasubparsers.add_parser(...). Not covered.So running darnit's threat-model generator against the darnit repo produces a thread model for the MCP tools only — the CLI surface is invisible. This is structurally analogous to gittuf's situation before #262.
Why this matters
Suggested approach
Phase 1 —
argparse(the stdlib standard):argparse.ArgumentParser(...)construction,subparsers.add_parser(\"<name>\", ...)calls,parser.add_subparsers(...)callsadd_subparsers→add_parserrelationship to group sibling commands as a family (analogous to feature 014's cobra family-grouping)EntryPointKind.CLI_COMMANDand the import-based STRIDE-heuristic table (Tampering / Repudiation / Spoofing+Information Disclosure / Elevation of Privilege / Tampering fallback)### CLI Entry Pointssubsection (no document-structure changes — feature 014 already built that)argparse_minimal,argparse_subcommands); regression run against darnit itselfPhase 2 —
click:@click.command()and@<group>.command()decorators@click.group()for family rootsclick.groupplus an "ungrouped" family for top-level commandsPhase 3 —
typer:@app.command()andtyper.Typer()instantiationapp.add_typer(sub_app, ...)) → familyEffort estimate
Phases can land independently.
Acceptance
### CLI Entry Pointssection listing all 8 of darnit's argparse subcommands (one family).Related
specs/014-cobra-threat-model/. The grouping algorithm (filesystem-layout-based), the STRIDE heuristic table, and the CLI rendering section are framework-agnostic and reusable here.