Parent
#360
What to build
A new apps/cli/src/programs/ module that ingests a bug bounty program page (from a URL or text file), parses it into a structured ProgramConfig using an LLM, and persists it to ~/.shannon/programs/<name>.json for reuse across scans.
End-to-end path:
types.ts — ProgramConfig: name, platform ('hackerone' | 'bugcrowd'), in_scope_domains (string[], wildcard-aware), out_of_scope_patterns (string[]), focus_classes (agent names), active_campaign (optional: asset, multipliers by severity), rules (free-text participation rules)
fetcher.ts — accepts a URL (plain HTTP GET, no auth) or file path (UTF-8 read); returns raw text
parser.ts — calls Claude haiku with a structured output schema matching ProgramConfig; validates response with Zod before returning
store.ts — reads/writes ~/.shannon/programs/<slug>.json; slug derived from ProgramConfig.name (lowercased, hyphenated); --refresh-program deletes existing file before re-fetch
Acceptance criteria
Blocked by
None — can start immediately
Parent
#360
What to build
A new
apps/cli/src/programs/module that ingests a bug bounty program page (from a URL or text file), parses it into a structuredProgramConfigusing an LLM, and persists it to~/.shannon/programs/<name>.jsonfor reuse across scans.End-to-end path:
types.ts—ProgramConfig:name,platform('hackerone' | 'bugcrowd'),in_scope_domains(string[], wildcard-aware),out_of_scope_patterns(string[]),focus_classes(agent names),active_campaign(optional: asset, multipliers by severity),rules(free-text participation rules)fetcher.ts— accepts a URL (plain HTTP GET, no auth) or file path (UTF-8 read); returns raw textparser.ts— calls Claude haiku with a structured output schema matchingProgramConfig; validates response with Zod before returningstore.ts— reads/writes~/.shannon/programs/<slug>.json; slug derived fromProgramConfig.name(lowercased, hyphenated);--refresh-programdeletes existing file before re-fetchAcceptance criteria
ProgramConfigJSON~/.shannon/programs/<name>.json--refresh-programforces re-fetch and re-parse, overwriting the stored fileBlocked by
None — can start immediately