Skip to content

feat(cli): program config system — fetch, parse, store #364

Description

@JonathanVD43

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.tsProgramConfig: 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

  • Passing a HackerOne program URL produces a valid ProgramConfig JSON
  • Passing a copy-pasted text file produces the same result
  • Parsed config is saved to ~/.shannon/programs/<name>.json
  • Re-running with the same program name loads from disk without re-parsing
  • --refresh-program forces re-fetch and re-parse, overwriting the stored file
  • Zod validation rejects malformed LLM responses with a clear error message
  • Shopify program page (used as design reference) parses correctly: in-scope domains, out-of-scope exclusions, Authentication & ATO campaign multipliers, participation rules

Blocked by

None — can start immediately

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions