---
phase: 14-osint_ci_cd_logs_web_archives_frontend_leaks
plan: 03
type: execute
wave: 1
depends_on: []
files_modified:
  - pkg/recon/sources/sourcemap.go
  - pkg/recon/sources/sourcemap_test.go
  - pkg/recon/sources/webpack.go
  - pkg/recon/sources/webpack_test.go
  - pkg/recon/sources/envleak.go
  - pkg/recon/sources/envleak_test.go
  - pkg/recon/sources/swagger.go
  - pkg/recon/sources/swagger_test.go
  - pkg/recon/sources/deploypreview.go
  - pkg/recon/sources/deploypreview_test.go
autonomous: true
requirements:
  - RECON-JS-01
  - RECON-JS-02
  - RECON-JS-03
  - RECON-JS-04
  - RECON-JS-05

must_haves:
  truths:
    - "Source map extraction discovers original source files containing API keys"
    - "Webpack/Vite bundle scanning finds inlined env vars with API keys"
    - "Exposed .env file scanning finds publicly accessible environment files"
    - "Swagger/OpenAPI doc scanning finds API keys in example fields"
    - "Vercel/Netlify deploy preview scanning finds keys in JS bundles"
  artifacts:
    - path: "pkg/recon/sources/sourcemap.go"
      provides: "SourceMapSource implementing ReconSource"
      contains: "func (s *SourceMapSource) Sweep"
    - path: "pkg/recon/sources/webpack.go"
      provides: "WebpackSource implementing ReconSource"
      contains: "func (s *WebpackSource) Sweep"
    - path: "pkg/recon/sources/envleak.go"
      provides: "EnvLeakSource implementing ReconSource"
      contains: "func (s *EnvLeakSource) Sweep"
    - path: "pkg/recon/sources/swagger.go"
      provides: "SwaggerSource implementing ReconSource"
      contains: "func (s *SwaggerSource) Sweep"
    - path: "pkg/recon/sources/deploypreview.go"
      provides: "DeployPreviewSource implementing ReconSource"
      contains: "func (s *DeployPreviewSource) Sweep"
  key_links:
    - from: "pkg/recon/sources/sourcemap.go"
      to: "pkg/recon/source.go"
      via: "implements ReconSource interface"
      pattern: "var _ recon\\.ReconSource"
    - from: "pkg/recon/sources/envleak.go"
      to: "pkg/recon/source.go"
      via: "implements ReconSource interface"
      pattern: "var _ recon\\.ReconSource"
---

<objective>
Implement five frontend leak scanning sources: SourceMapSource, WebpackSource, EnvLeakSource, SwaggerSource, and DeployPreviewSource. Each targets a different vector for API key exposure in client-facing web assets.

Purpose: Frontend JavaScript bundles, source maps, exposed .env files, API documentation, and deploy previews are high-value targets where developers accidentally ship server-side secrets to the client. These are often reachable without authentication.

Output: 5 source files + 5 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/queries.go
@pkg/recon/sources/npm.go
@pkg/recon/sources/npm_test.go

<interfaces>
From pkg/recon/source.go:
```go
type Finding = engine.Finding
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
type Client struct { HTTP *http.Client; MaxRetries int; UserAgent string }
func NewClient() *Client
func (c *Client) Do(ctx context.Context, req *http.Request) (*http.Response, error)
```

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

<tasks>

<task type="auto">
  <name>Task 1: Implement SourceMapSource, WebpackSource, and EnvLeakSource with tests</name>
  <files>pkg/recon/sources/sourcemap.go, pkg/recon/sources/sourcemap_test.go, pkg/recon/sources/webpack.go, pkg/recon/sources/webpack_test.go, pkg/recon/sources/envleak.go, pkg/recon/sources/envleak_test.go</files>
  <action>
Create SourceMapSource (RECON-JS-01):
- Struct fields: BaseURL string, Registry *providers.Registry, Limiters *recon.LimiterRegistry, Client *Client
- Name() returns "sourcemaps"
- RateLimit: rate.Every(3*time.Second), Burst: 2
- RespectsRobots: true (fetching web resources)
- Enabled: always true (credentialless)
- Sweep: For each query from BuildQueries(registry, "sourcemaps"), construct common source map URL patterns to probe. The source uses the query as a domain/URL hint and checks common paths: {url}.map, {url}/main.js.map, {url}/static/js/main.*.js.map. For each accessible .map file, the response contains a JSON object with "sources" and "sourcesContent" arrays -- the sourcesContent contains original source code that may have API keys. Emit findings with SourceType "recon:sourcemaps" and Source set to the map file URL.
- Since we cannot enumerate all domains, Sweep uses BuildQueries to get provider-related keywords and constructs probe URLs. The source is a lead generator -- it emits URLs where source maps were found accessible.
- Compile-time interface check: var _ recon.ReconSource = (*SourceMapSource)(nil)

Create WebpackSource (RECON-JS-02):
- Struct fields: BaseURL string, Registry *providers.Registry, Limiters *recon.LimiterRegistry, Client *Client
- Name() returns "webpack"
- RateLimit: rate.Every(3*time.Second), Burst: 2
- RespectsRobots: true (fetching web resources)
- Enabled: always true (credentialless)
- Sweep: For each query, probe common Webpack/Vite build artifact paths: /_next/static/chunks/*, /static/js/main.*.js, /assets/index-*.js, /dist/bundle.js. Look for patterns like process.env.NEXT_PUBLIC_, REACT_APP_, VITE_ prefixed variables that often contain API keys. Emit findings with SourceType "recon:webpack". The source emits leads for URLs containing webpack build artifacts with env var patterns.

Create EnvLeakSource (RECON-JS-03):
- Struct fields: BaseURL string, Registry *providers.Registry, Limiters *recon.LimiterRegistry, Client *Client
- Name() returns "dotenv"
- RateLimit: rate.Every(2*time.Second), Burst: 2
- RespectsRobots: true (probing web servers)
- Enabled: always true (credentialless)
- Sweep: For each query (used as domain hint), probe common exposed .env paths: /.env, /.env.local, /.env.production, /.env.development, /app/.env, /api/.env, /.env.backup, /.env.example. Check if the response contains key=value patterns (specifically lines matching provider keywords). Emit findings with SourceType "recon:dotenv" and Source set to the accessible .env URL. This is a common web vulnerability -- many frameworks serve .env if misconfigured.

Tests for all three: httptest.NewServer returning appropriate fixture content (JSON source map, JS bundle with process.env references, .env file content). Verify Sweep emits findings with correct SourceType. Test empty/404 responses produce no findings. Test context cancellation.
  </action>
  <verify>
    <automated>cd /home/salva/Documents/apikey && go test ./pkg/recon/sources/ -run "TestSourceMap|TestWebpack|TestEnvLeak" -count=1 -v</automated>
  </verify>
  <done>SourceMapSource, WebpackSource, EnvLeakSource implement ReconSource, emit findings from mocked web responses, all tests pass</done>
</task>

<task type="auto">
  <name>Task 2: Implement SwaggerSource and DeployPreviewSource with tests</name>
  <files>pkg/recon/sources/swagger.go, pkg/recon/sources/swagger_test.go, pkg/recon/sources/deploypreview.go, pkg/recon/sources/deploypreview_test.go</files>
  <action>
Create SwaggerSource (RECON-JS-04):
- Struct fields: BaseURL string, Registry *providers.Registry, Limiters *recon.LimiterRegistry, Client *Client
- Name() returns "swagger"
- RateLimit: rate.Every(3*time.Second), Burst: 2
- RespectsRobots: true (fetching web resources)
- Enabled: always true (credentialless)
- Sweep: For each query (domain hint), probe common Swagger/OpenAPI documentation paths: /swagger.json, /openapi.json, /api-docs, /v2/api-docs, /swagger/v1/swagger.json, /docs/openapi.json. Parse the JSON response and look for "example" or "default" fields in security scheme definitions or parameter definitions that contain actual API key values (a common misconfiguration where developers put real keys as examples). Emit findings with SourceType "recon:swagger" and Source set to the accessible docs URL.

Create DeployPreviewSource (RECON-JS-05):
- Struct fields: BaseURL string, Registry *providers.Registry, Limiters *recon.LimiterRegistry, Client *Client
- Name() returns "deploypreview"
- RateLimit: rate.Every(3*time.Second), Burst: 2
- RespectsRobots: true (fetching web resources)
- Enabled: always true (credentialless)
- Sweep: For each query, construct Vercel/Netlify deploy preview URL patterns. Vercel previews follow: {project}-{hash}-{team}.vercel.app, Netlify: deploy-preview-{n}--{site}.netlify.app. The source uses BuildQueries to get keywords and searches for deploy preview artifacts. Probe /_next/data/ and /__NEXT_DATA__ script tags on Vercel previews, and /static/ on Netlify previews. Deploy previews often have different (less restrictive) environment variables than production. Emit findings with SourceType "recon:deploypreview".

Tests for both: httptest.NewServer with fixture responses (Swagger JSON with example API keys, HTML with __NEXT_DATA__ containing env vars). Verify Sweep emits findings. Test 404/empty responses. Test context cancellation.
  </action>
  <verify>
    <automated>cd /home/salva/Documents/apikey && go test ./pkg/recon/sources/ -run "TestSwagger|TestDeployPreview" -count=1 -v</automated>
  </verify>
  <done>SwaggerSource and DeployPreviewSource implement ReconSource, emit findings from mocked responses, all tests pass</done>
</task>

</tasks>

<verification>
cd /home/salva/Documents/apikey && go test ./pkg/recon/sources/ -run "TestSourceMap|TestWebpack|TestEnvLeak|TestSwagger|TestDeployPreview" -count=1 -v
cd /home/salva/Documents/apikey && go vet ./pkg/recon/sources/
</verification>

<success_criteria>
- 5 new source files compile and implement ReconSource (var _ check)
- 5 test files pass with httptest mocks
- All 5 sources use BuildQueries + Client + LimiterRegistry pattern
- All are credentialless (always enabled)
- Each source has distinct SourceType: recon:sourcemaps, recon:webpack, recon:dotenv, recon:swagger, recon:deploypreview
</success_criteria>

<output>
After completion, create `.planning/phases/14-osint_ci_cd_logs_web_archives_frontend_leaks/14-03-SUMMARY.md`
</output>
