Module 9 Release Notes — Bug Bounty Platform + Collaboration + Reporting Suite
Full decision record: docs/adr/0009-bug-bounty-operating-system.md.
Implementation record: docs/modules/09-bug-bounty-operating-system.md.
Permission reference: docs/security/permission-matrix.md.
Features added
Backend — extends Module 6's bug-bounty workspace into a team platform, additive to the existing schema throughout:
- Program Manager:
BugBountyProgramgainedsafeHarbor,rateLimits,bountyTable/severityMatrix(structured JSON),favorite, andBugBountyProgramTargetlinking to Module 2 Targets. - Program Importer: update detection —
computeImportFingerprint()(SHA-256, order-insensitive) for cheap reimport comparison, plus a separatediffProgramImport()for a human-readable field-by-field diff on demand. Reimports never destructively remove or reclassify existing scope items. - Scope Manager: three new scope-item types (
HOST,INTERNAL_ASSET,GITHUB_ORG) and two new classification states (REVIEW_NEEDED,DEPRECATED). - Finding Manager: four new lifecycle stages (
DRAFT,INFORMATIVE,ACCEPTED,REJECTED), a spec-accurate CVSS 3.1 calculator (POST /bugbounty/cvss/calculate), and CWE/CAPEC/OWASP/MITRE ATT&CK mapping fields. - Report Builder: custom report templates and an AI grammar/technical review capability layered onto Module 6's five built-in renderers.
- Collaboration (new):
Comment/Mention/Assignment/TaskItem/WorkspaceInvite, an activity feed, and a six-level role hierarchy (OWNER > ADMIN > MANAGER > TRIAGER > REVIEWER > MEMBER) enforced viarequireRole()/requireCanGrantRole()on every collaboration route. - Bug Bounty Dashboard: assigned-to-me findings/tasks and team-activity widgets, read-time aggregation.
- AI Bug Bounty Assistant (new): nine capabilities off one
BugBountyAiAssistantService—review,suggest-payloads,generate-poc,generate-repro-steps,generate-impact,generate-remediation,calculate-severity(per-finding), plussuggest-attack-pathsandsummarize-engagement(engagement-level). Every capability returns a suggestion rather than auto-writing to a live finding or report. - Knowledge Base:
kindclassification (general/playbook/CVE/tool/ AI-conversation-snapshot),tags,cveId, and a newsave-conversation-snapshotendpoint that captures an AI chat as a point-in-time Markdown KB note. - Automation (new):
ScheduledJob+ScheduledJobRunwith run history. - Notifications: multi-channel delivery (
NotificationChannelConfig+NotificationDelivery) on top of Module 6's in-app notifications, with per-field encrypted secrets. - Analytics (new):
GET /bugbounty/analytics, filterable by project/program/date-range — severity distribution, acceptance/duplicate rate, top vuln types/targets, recon coverage, response time, activity timeline. Kept separate from Module 6's unfilteredGET /bugbounty/statistics. - Search: three new result types —
AI_CHAT,TASK,TAG. - Public REST API (new):
ApiKeyandWebhookTokenCRUD + revoke, webhook test-delivery with HMAC signing, underpublic-api/. - Security: AES-256-GCM encryption at rest for
WebhookToken.secretandNotificationChannelConfig.config(per-field, reusing Phase 1 BYOK's encryption service); the Collaboration role hierarchy; full workspace- membership/audit coverage on every new route.
Frontend / offline: the Desktop Agent's Sync Engine gained one fix
(findings, built in Module 8, was never registered in the ENTITIES
table — now is). Module 9's own new server-side entities (Program/Scope,
Scheduled Jobs, Collaboration, Notification Channels, API Keys/Webhooks)
are deliberately not mirrored locally — see the module doc's Offline
Support section for the full reasoning.
Bugs found and fixed
- Wrong import path for
AuditEventType:api-keys.commands.tsandwebhook-tokens.commands.tsboth imported it from@pentesthub/shared, which doesn't export it (it's a Prisma-generated enum) — a genuine TypeScript compile error had it gone unnoticed. Fixed to import fromprisma-types.js. WebhookToken.secretstored in plaintext despite a doc comment claiming AES-256-GCM encryption: the comment was aspirational, not implemented. Fixed by actually callingencryptCredential()/decryptCredential()around the raw secret.- Audit trail gap:
WORKSPACE_MEMBER_INVITED,WORKSPACE_MEMBER_ROLE_CHANGED, andWORKSPACE_MEMBER_REMOVEDexisted asAuditEventTypeenum values but were never written by any handler — found by grep. Fixed by addingauditEvent.create()calls toworkspace-invites.commands.ts's three handlers. - Desktop Agent sync gap: the
findingslocal table (Module 8) had the correct sync-ready column shape but was never registered in the Sync Engine'sENTITIESarray, so it silently never pushed or pulled. Fixed by adding the missingEntityConfigentry.
Known limitations
- Analytics and Statistics both compute from in-memory lists capped at ~1000 rows rather than a SQL aggregate — the same accepted tradeoff Module 6 already made and documented, carried forward.
- Tag search does one unfiltered, capped fetch per tag-bearing table and matches substrings in memory rather than pushing the match into SQL.
- Collaboration/Automation/Notification-Channel/Public-API entities
(~10 tables) use direct
PrismaServiceinjection in their command/query handlers rather than the full repository-interface pattern Modules 1-6 use everywhere — a deliberate scope decision for simple, non-reused CRUD shapes, documented in ADR 0009 §2. - None of Module 9's new server-side entities have an offline/local-first story on the Desktop Agent (see module doc's Offline Support section for the full reasoning and scope boundary).
/desktop-sync/*remains unimplemented (a pre-existing, documented Phase 2 item per ADR 0007) — unchanged by this module.
Testing
Three new Jest spec files added, matching the project's established
@jest/globals / typed jest.fn<Type>() style: program-update-detector. spec.ts (fingerprint determinism/order-insensitivity, diff correctness),
permissions.util.spec.ts (role ranking, requireRole/
requireCanGrantRole guards, last-owner-demotion), and
notification-channel-config-crypto.util.spec.ts (per-field encrypt/
decrypt round-tripping, non-secret passthrough, graceful fallback on
malformed input). None of Module 6's existing bug-bounty spec files were
modified.
Verification was constrained the same way it has been since Module 6:
packages/shared's tsc --noEmit was re-run after every shared-type change
and stayed clean throughout. A full apps/api project-wide tsc --noEmit
and the Jest suite could not be run to completion in this sandbox — the
Jest failure (Module ts-jest in the transform option was not found) was
confirmed to reproduce on a pre-existing, previously-passing spec file too,
ruling out a regression from this module's changes; it's the same category
of environment limitation as the already-documented tsc timeout and the
absent Rust/Cargo toolchain. All changes were reviewed by hand against exact
source signatures. Run pnpm build && pnpm lint && pnpm check-types && pnpm test from the repo root before merging.