---
phase: 15-osint_forums_collaboration_log_aggregators
plan: 02
type: execute
wave: 1
depends_on: []
files_modified:
  - pkg/recon/sources/trello.go
  - pkg/recon/sources/trello_test.go
  - pkg/recon/sources/notion.go
  - pkg/recon/sources/notion_test.go
  - pkg/recon/sources/confluence.go
  - pkg/recon/sources/confluence_test.go
  - pkg/recon/sources/googledocs.go
  - pkg/recon/sources/googledocs_test.go
autonomous: true
requirements:
  - RECON-COLLAB-01
  - RECON-COLLAB-02
  - RECON-COLLAB-03
  - RECON-COLLAB-04
must_haves:
  truths:
    - "Trello source searches public Trello boards for leaked API keys"
    - "Notion source searches publicly shared Notion pages for keys"
    - "Confluence source searches exposed Confluence instances for keys"
    - "Google Docs source searches public documents for keys"
  artifacts:
    - path: "pkg/recon/sources/trello.go"
      provides: "TrelloSource implementing ReconSource"
      contains: "func (s *TrelloSource) Sweep"
    - path: "pkg/recon/sources/notion.go"
      provides: "NotionSource implementing ReconSource"
      contains: "func (s *NotionSource) Sweep"
    - path: "pkg/recon/sources/confluence.go"
      provides: "ConfluenceSource implementing ReconSource"
      contains: "func (s *ConfluenceSource) Sweep"
    - path: "pkg/recon/sources/googledocs.go"
      provides: "GoogleDocsSource implementing ReconSource"
      contains: "func (s *GoogleDocsSource) Sweep"
  key_links:
    - from: "pkg/recon/sources/trello.go"
      to: "pkg/recon/sources/httpclient.go"
      via: "Client.Do for Trello API"
      pattern: "client\\.Do"
    - from: "pkg/recon/sources/confluence.go"
      to: "pkg/recon/sources/httpclient.go"
      via: "Client.Do for Confluence REST API"
      pattern: "client\\.Do"
---

<objective>
Implement four collaboration tool ReconSource implementations: Trello, Notion, Confluence, and Google Docs.

Purpose: Enable scanning publicly accessible collaboration tool pages and documents where API keys are inadvertently shared in team documentation, project boards, and shared docs.
Output: 4 source files + 4 test files in pkg/recon/sources/
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@pkg/recon/source.go
@pkg/recon/sources/httpclient.go
@pkg/recon/sources/travisci.go
@pkg/recon/sources/travisci_test.go

<interfaces>
From pkg/recon/source.go:
```go
type ReconSource interface {
    Name() string
    RateLimit() rate.Limit
    Burst() int
    RespectsRobots() bool
    Enabled(cfg Config) bool
    Sweep(ctx context.Context, query string, out chan<- Finding) error
}
```

From pkg/recon/sources/httpclient.go:
```go
func NewClient() *Client
func (c *Client) Do(ctx context.Context, req *http.Request) (*http.Response, error)
```

From pkg/recon/sources/register.go:
```go
func BuildQueries(reg *providers.Registry, sourceName string) []string
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Trello and Notion sources</name>
  <files>
    pkg/recon/sources/trello.go
    pkg/recon/sources/trello_test.go
    pkg/recon/sources/notion.go
    pkg/recon/sources/notion_test.go
  </files>
  <action>
Create two ReconSource implementations following the TravisCISource pattern.

**TrelloSource** (trello.go):
- Name: "trello"
- RateLimit: rate.Every(2*time.Second), Burst: 3
- RespectsRobots: false (API-based)
- Enabled: always true (credentialless — Trello public boards are accessible without auth)
- Sweep: Trello has a public search API for public boards. For each BuildQueries keyword, GET `{base}/1/search?query={keyword}&modelTypes=cards&card_fields=name,desc&cards_limit=10` (Trello REST API, public boards are searchable without API key). Parse JSON `cards[].desc` (card descriptions often contain pasted credentials). Run ciLogKeyPattern regex. Emit Finding with SourceType "recon:trello", Source set to card URL `https://trello.com/c/{id}`.
- BaseURL default: "https://api.trello.com"
- Read up to 256KB per response.

**NotionSource** (notion.go):
- Name: "notion"
- RateLimit: rate.Every(3*time.Second), Burst: 2
- RespectsRobots: true (scrapes public pages found via dorking)
- Enabled: always true (credentialless — uses dorking to find public Notion pages)
- Sweep: Notion has no public search API. Use a dorking approach: for each BuildQueries keyword, GET `{base}/search?q=site:notion.site+OR+site:notion.so+{keyword}&format=json`. Parse search results for Notion page URLs. For each URL, fetch the page HTML and run ciLogKeyPattern against text content. Emit Finding with SourceType "recon:notion".
- BaseURL default: "https://search.notion.dev" (placeholder, overridden in tests via BaseURL)
- This is a best-effort source since Notion public pages require dorking to discover.

Test files: TestXxx_Name, TestXxx_Enabled, TestXxx_Sweep with httptest mock. Trello test mocks /1/search endpoint returning card JSON with API key in desc field. Notion test mocks search + page fetch endpoints.
  </action>
  <verify>
    <automated>cd /home/salva/Documents/apikey && go test ./pkg/recon/sources/ -run "TestTrello|TestNotion" -count=1 -v</automated>
  </verify>
  <done>Trello and Notion sources compile, pass interface checks, tests confirm Sweep emits findings from mock responses</done>
</task>

<task type="auto">
  <name>Task 2: Confluence and Google Docs sources</name>
  <files>
    pkg/recon/sources/confluence.go
    pkg/recon/sources/confluence_test.go
    pkg/recon/sources/googledocs.go
    pkg/recon/sources/googledocs_test.go
  </files>
  <action>
Create two more ReconSource implementations.

**ConfluenceSource** (confluence.go):
- Name: "confluence"
- RateLimit: rate.Every(3*time.Second), Burst: 2
- RespectsRobots: true (scrapes publicly exposed Confluence wikis)
- Enabled: always true (credentialless — targets exposed instances)
- Sweep: Exposed Confluence instances have a REST API at `/rest/api/content/search`. For each BuildQueries keyword, GET `{base}/rest/api/content/search?cql=text~"{keyword}"&limit=10&expand=body.storage`. Parse JSON `results[].body.storage.value` (HTML content). Strip HTML tags (simple regex or strings approach), run ciLogKeyPattern. Emit Finding with SourceType "recon:confluence", Source as page URL.
- BaseURL default: "https://confluence.example.com" (always overridden — no single default instance)
- In practice the query string from `keyhunter recon --sources=confluence --query="target.atlassian.net"` would provide the target. If no target can be determined from the query, return nil early.

**GoogleDocsSource** (googledocs.go):
- Name: "googledocs"
- RateLimit: rate.Every(3*time.Second), Burst: 2
- RespectsRobots: true (scrapes public Google Docs)
- Enabled: always true (credentialless)
- Sweep: Google Docs shared publicly are accessible via their export URL. Use dorking approach: for each BuildQueries keyword, GET `{base}/search?q=site:docs.google.com+{keyword}&format=json`. For each discovered doc URL, fetch `{docURL}/export?format=txt` to get plain text. Run ciLogKeyPattern. Emit Finding with SourceType "recon:googledocs".
- BaseURL default: "https://search.googledocs.dev" (placeholder, overridden in tests)
- Best-effort source relying on search engine indexing of public docs.

Test files: TestXxx_Name, TestXxx_Enabled, TestXxx_Sweep with httptest mock. Confluence test mocks /rest/api/content/search returning CQL results with key in body.storage.value. GoogleDocs test mocks search + export endpoints.
  </action>
  <verify>
    <automated>cd /home/salva/Documents/apikey && go test ./pkg/recon/sources/ -run "TestConfluence|TestGoogleDocs" -count=1 -v</automated>
  </verify>
  <done>Confluence and Google Docs sources compile, pass interface checks, tests confirm Sweep emits findings from mock responses</done>
</task>

</tasks>

<verification>
cd /home/salva/Documents/apikey && go build ./... && go vet ./pkg/recon/sources/
cd /home/salva/Documents/apikey && go test ./pkg/recon/sources/ -run "TestTrello|TestNotion|TestConfluence|TestGoogleDocs" -count=1
</verification>

<success_criteria>
- All 4 collaboration sources implement recon.ReconSource interface
- All 4 test files pass with httptest-based mocks
- Each source follows the established pattern (BuildQueries + Client.Do + ciLogKeyPattern)
- go vet and go build pass cleanly
</success_criteria>

<output>
After completion, create `.planning/phases/15-osint_forums_collaboration_log_aggregators/15-02-SUMMARY.md`
</output>
