@vulcn/driver-browser 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { RecordOptions, RecordingHandle, Session, RunContext, RunResult, CrawlOptions, VulcnDriver } from '@vulcn/engine';
3
- import { Browser } from 'playwright';
2
+ import { RecordOptions, RecordingHandle, Session, RunContext, RunResult, CapturedRequest, CrawlOptions, Finding, PayloadCategory, RuntimePayload, FormCredentials, VulcnDriver } from '@vulcn/engine';
3
+ import { Browser, Page, BrowserContext } from 'playwright';
4
4
 
5
5
  /**
6
6
  * Browser Recorder Implementation
@@ -32,33 +32,31 @@ declare class BrowserRecorder {
32
32
  *
33
33
  * Replays browser sessions with security payloads.
34
34
  * Uses plugin hooks for detection.
35
+ *
36
+ * v2: Persistent browser with in-page payload cycling.
37
+ * - ONE browser for the entire scan (not per-session)
38
+ * - Uses page.goBack() between payloads instead of full page.goto()
39
+ * - Falls back to full navigation when goBack() fails
40
+ * - 5-10x faster on SPAs, same speed on simple sites
35
41
  */
36
42
 
37
43
  /**
38
44
  * Browser Runner - replays sessions with payloads
45
+ *
46
+ * Supports two modes:
47
+ * 1. Self-managed browser: launches its own browser (backward compat)
48
+ * 2. Shared browser: receives a browser instance via RunOptions
49
+ *
50
+ * In both modes, payload cycling uses goBack() for speed.
39
51
  */
40
52
  declare class BrowserRunner {
41
53
  /**
42
- * Execute a session with security payloads
43
- */
44
- static execute(session: Session, ctx: RunContext): Promise<RunResult>;
45
- /**
46
- * Replay session steps with payload injected at target step
54
+ * Execute a session with security payloads.
47
55
  *
48
- * IMPORTANT: We replay ALL steps, not just up to the injectable step.
49
- * The injection replaces the input value, but subsequent steps (like
50
- * clicking submit) must still execute so the payload reaches the server
51
- * and gets reflected back in the response.
56
+ * If ctx.options.browser is provided, reuses that browser (persistent mode).
57
+ * Otherwise, launches and closes its own browser (standalone mode).
52
58
  */
53
- private static replayWithPayload;
54
- /**
55
- * Check for payload reflection in page content
56
- */
57
- private static checkReflection;
58
- /**
59
- * Determine severity based on vulnerability category
60
- */
61
- private static getSeverity;
59
+ static execute(session: Session, ctx: RunContext): Promise<RunResult>;
62
60
  }
63
61
 
64
62
  /**
@@ -105,6 +103,7 @@ declare function checkBrowsers(): Promise<{
105
103
  * - Links to follow for deeper crawling
106
104
  *
107
105
  * Outputs Session[] that are directly compatible with BrowserRunner.
106
+ * Also generates CapturedRequest[] metadata for Tier 1 HTTP fast scanning.
108
107
  *
109
108
  * This is the "auto-record" mode — instead of a human clicking around,
110
109
  * the crawler automatically discovers injection points.
@@ -123,12 +122,171 @@ interface BrowserCrawlConfig {
123
122
  height: number;
124
123
  };
125
124
  }
125
+ /** Result from crawling — sessions for browser replay + requests for HTTP scanning */
126
+ interface CrawlResult {
127
+ sessions: Session[];
128
+ capturedRequests: CapturedRequest[];
129
+ }
126
130
  /**
127
131
  * Crawl a URL and generate sessions.
128
132
  *
129
133
  * This is called by the browser driver's recorder.crawl() method.
134
+ * Returns both Session[] for Tier 2 browser replay and
135
+ * CapturedRequest[] for Tier 1 HTTP fast scanning.
136
+ */
137
+ declare function crawlAndBuildSessions(config: BrowserCrawlConfig, options?: CrawlOptions): Promise<CrawlResult>;
138
+
139
+ /**
140
+ * HTTP Scanner — Tier 1 Fast Scan
141
+ *
142
+ * Replays captured HTTP requests via fetch() instead of Playwright.
143
+ * Substitutes security payloads into injectable form fields and checks
144
+ * the response body for payload reflection or error patterns.
145
+ *
146
+ * Speed: ~50ms per payload (vs 2-5s for browser replay)
147
+ *
148
+ * Catches:
149
+ * - Reflected XSS (payload appears in response body)
150
+ * - Error-based SQLi (SQL error patterns in response)
151
+ * - Server-side reflection of any payload
152
+ *
153
+ * Misses:
154
+ * - DOM-based XSS (requires JS execution)
155
+ * - Client-side state bugs
156
+ * - CSP-blocked attacks
157
+ *
158
+ * When reflection IS found, the finding is marked as `needsBrowserConfirmation`
159
+ * so the caller can escalate to Tier 2 for execution-based proof.
160
+ */
161
+
162
+ interface HttpScanResult {
163
+ /** Total HTTP requests sent */
164
+ requestsSent: number;
165
+ /** How long the scan took (ms) */
166
+ duration: number;
167
+ /** Findings from reflection detection */
168
+ findings: Finding[];
169
+ /** Requests where reflection was found — should be escalated to Tier 2 */
170
+ reflectedRequests: ReflectedRequest[];
171
+ }
172
+ interface ReflectedRequest {
173
+ /** Original captured request */
174
+ request: CapturedRequest;
175
+ /** Payload that caused reflection */
176
+ payload: string;
177
+ /** Category of the payload */
178
+ category: PayloadCategory;
179
+ }
180
+ interface HttpScanOptions {
181
+ /** Request timeout in ms (default: 10000) */
182
+ timeout?: number;
183
+ /** Max concurrent requests (default: 10) */
184
+ concurrency?: number;
185
+ /** Cookie header to send with requests (for auth) */
186
+ cookies?: string;
187
+ /** Extra headers to send */
188
+ headers?: Record<string, string>;
189
+ /** Callback for progress reporting */
190
+ onProgress?: (completed: number, total: number) => void;
191
+ }
192
+ /**
193
+ * Run Tier 1 HTTP-level scan on captured requests.
194
+ *
195
+ * For each CapturedRequest × each payload:
196
+ * 1. Substitute the payload into the injectable field
197
+ * 2. Send via fetch()
198
+ * 3. Check response body for reflection patterns
199
+ * 4. If reflected, add finding + mark for Tier 2 escalation
200
+ */
201
+ declare function httpScan(requests: CapturedRequest[], payloads: RuntimePayload[], options?: HttpScanOptions): Promise<HttpScanResult>;
202
+ /**
203
+ * Convert discovered forms into CapturedRequest metadata.
204
+ *
205
+ * Called by the crawler after form discovery. Each injectable form
206
+ * produces one CapturedRequest per injectable input field.
207
+ */
208
+ declare function buildCapturedRequests(forms: Array<{
209
+ pageUrl: string;
210
+ action: string;
211
+ method: string;
212
+ inputs: Array<{
213
+ name: string;
214
+ injectable: boolean;
215
+ type: string;
216
+ }>;
217
+ sessionName: string;
218
+ }>): CapturedRequest[];
219
+
220
+ /**
221
+ * Login Form Auto-Detection & Auth Replay
222
+ *
223
+ * Detects login forms on a page and fills them with credentials.
224
+ * After login, captures the browser storage state (cookies + localStorage)
225
+ * for re-use in subsequent scans.
226
+ *
227
+ * Detection strategy:
228
+ * 1. Find forms with a password input (strongest signal)
229
+ * 2. Find username field via heuristics (name, id, autocomplete, type)
230
+ * 3. Find submit button
231
+ * 4. Fall back to custom selectors from credentials
232
+ */
233
+
234
+ interface LoginForm {
235
+ /** Username input selector */
236
+ usernameSelector: string;
237
+ /** Password input selector */
238
+ passwordSelector: string;
239
+ /** Submit button selector (may be null if not found) */
240
+ submitSelector: string | null;
241
+ /** Whether the form was detected automatically */
242
+ autoDetected: boolean;
243
+ }
244
+ interface LoginResult {
245
+ /** Whether login succeeded */
246
+ success: boolean;
247
+ /** Message for logging */
248
+ message: string;
249
+ /** Playwright storage state JSON (cookies + localStorage) */
250
+ storageState?: string;
251
+ }
252
+ /**
253
+ * Auto-detect a login form on the current page.
254
+ *
255
+ * Strategy:
256
+ * 1. Find `<form>` elements containing an `input[type="password"]`
257
+ * 2. Within that form, find the username field using heuristics
258
+ * 3. Find the submit button
259
+ *
260
+ * Falls back to page-wide search if no enclosing <form> is found.
261
+ */
262
+ declare function detectLoginForm(page: Page): Promise<LoginForm | null>;
263
+ /**
264
+ * Perform login using detected form or custom selectors.
265
+ *
266
+ * Flow:
267
+ * 1. Navigate to login URL (or target URL)
268
+ * 2. Detect login form (or use custom selectors from credentials)
269
+ * 3. Fill username + password
270
+ * 4. Submit form
271
+ * 5. Wait for navigation
272
+ * 6. Check for logged-in indicator
273
+ * 7. Capture storage state
274
+ */
275
+ declare function performLogin(page: Page, context: BrowserContext, credentials: FormCredentials, options: {
276
+ targetUrl: string;
277
+ loggedInIndicator?: string;
278
+ loggedOutIndicator?: string;
279
+ }): Promise<LoginResult>;
280
+ /**
281
+ * Check if the current session is still alive.
282
+ *
283
+ * Used during long-running scans to detect session expiry
284
+ * and trigger re-authentication.
130
285
  */
131
- declare function crawlAndBuildSessions(config: BrowserCrawlConfig, options?: CrawlOptions): Promise<Session[]>;
286
+ declare function checkSessionAlive(page: Page, config: {
287
+ loggedInIndicator?: string;
288
+ loggedOutIndicator?: string;
289
+ }): Promise<boolean>;
132
290
 
133
291
  /**
134
292
  * @vulcn/driver-browser
@@ -334,4 +492,4 @@ type BrowserStep = z.infer<typeof BrowserStepSchema>;
334
492
  */
335
493
  declare const browserDriver: VulcnDriver;
336
494
 
337
- export { BROWSER_STEP_TYPES, type BrowserConfig, BrowserRecorder, BrowserRunner, type BrowserStep, BrowserStepSchema, type BrowserStepType, checkBrowsers, configSchema, crawlAndBuildSessions, browserDriver as default, installBrowsers, launchBrowser };
495
+ export { BROWSER_STEP_TYPES, type BrowserConfig, BrowserRecorder, BrowserRunner, type BrowserStep, BrowserStepSchema, type BrowserStepType, type CrawlResult, type HttpScanOptions, type HttpScanResult, type LoginForm, type LoginResult, type ReflectedRequest, buildCapturedRequests, checkBrowsers, checkSessionAlive, configSchema, crawlAndBuildSessions, browserDriver as default, detectLoginForm, httpScan, installBrowsers, launchBrowser, performLogin };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { RecordOptions, RecordingHandle, Session, RunContext, RunResult, CrawlOptions, VulcnDriver } from '@vulcn/engine';
3
- import { Browser } from 'playwright';
2
+ import { RecordOptions, RecordingHandle, Session, RunContext, RunResult, CapturedRequest, CrawlOptions, Finding, PayloadCategory, RuntimePayload, FormCredentials, VulcnDriver } from '@vulcn/engine';
3
+ import { Browser, Page, BrowserContext } from 'playwright';
4
4
 
5
5
  /**
6
6
  * Browser Recorder Implementation
@@ -32,33 +32,31 @@ declare class BrowserRecorder {
32
32
  *
33
33
  * Replays browser sessions with security payloads.
34
34
  * Uses plugin hooks for detection.
35
+ *
36
+ * v2: Persistent browser with in-page payload cycling.
37
+ * - ONE browser for the entire scan (not per-session)
38
+ * - Uses page.goBack() between payloads instead of full page.goto()
39
+ * - Falls back to full navigation when goBack() fails
40
+ * - 5-10x faster on SPAs, same speed on simple sites
35
41
  */
36
42
 
37
43
  /**
38
44
  * Browser Runner - replays sessions with payloads
45
+ *
46
+ * Supports two modes:
47
+ * 1. Self-managed browser: launches its own browser (backward compat)
48
+ * 2. Shared browser: receives a browser instance via RunOptions
49
+ *
50
+ * In both modes, payload cycling uses goBack() for speed.
39
51
  */
40
52
  declare class BrowserRunner {
41
53
  /**
42
- * Execute a session with security payloads
43
- */
44
- static execute(session: Session, ctx: RunContext): Promise<RunResult>;
45
- /**
46
- * Replay session steps with payload injected at target step
54
+ * Execute a session with security payloads.
47
55
  *
48
- * IMPORTANT: We replay ALL steps, not just up to the injectable step.
49
- * The injection replaces the input value, but subsequent steps (like
50
- * clicking submit) must still execute so the payload reaches the server
51
- * and gets reflected back in the response.
56
+ * If ctx.options.browser is provided, reuses that browser (persistent mode).
57
+ * Otherwise, launches and closes its own browser (standalone mode).
52
58
  */
53
- private static replayWithPayload;
54
- /**
55
- * Check for payload reflection in page content
56
- */
57
- private static checkReflection;
58
- /**
59
- * Determine severity based on vulnerability category
60
- */
61
- private static getSeverity;
59
+ static execute(session: Session, ctx: RunContext): Promise<RunResult>;
62
60
  }
63
61
 
64
62
  /**
@@ -105,6 +103,7 @@ declare function checkBrowsers(): Promise<{
105
103
  * - Links to follow for deeper crawling
106
104
  *
107
105
  * Outputs Session[] that are directly compatible with BrowserRunner.
106
+ * Also generates CapturedRequest[] metadata for Tier 1 HTTP fast scanning.
108
107
  *
109
108
  * This is the "auto-record" mode — instead of a human clicking around,
110
109
  * the crawler automatically discovers injection points.
@@ -123,12 +122,171 @@ interface BrowserCrawlConfig {
123
122
  height: number;
124
123
  };
125
124
  }
125
+ /** Result from crawling — sessions for browser replay + requests for HTTP scanning */
126
+ interface CrawlResult {
127
+ sessions: Session[];
128
+ capturedRequests: CapturedRequest[];
129
+ }
126
130
  /**
127
131
  * Crawl a URL and generate sessions.
128
132
  *
129
133
  * This is called by the browser driver's recorder.crawl() method.
134
+ * Returns both Session[] for Tier 2 browser replay and
135
+ * CapturedRequest[] for Tier 1 HTTP fast scanning.
136
+ */
137
+ declare function crawlAndBuildSessions(config: BrowserCrawlConfig, options?: CrawlOptions): Promise<CrawlResult>;
138
+
139
+ /**
140
+ * HTTP Scanner — Tier 1 Fast Scan
141
+ *
142
+ * Replays captured HTTP requests via fetch() instead of Playwright.
143
+ * Substitutes security payloads into injectable form fields and checks
144
+ * the response body for payload reflection or error patterns.
145
+ *
146
+ * Speed: ~50ms per payload (vs 2-5s for browser replay)
147
+ *
148
+ * Catches:
149
+ * - Reflected XSS (payload appears in response body)
150
+ * - Error-based SQLi (SQL error patterns in response)
151
+ * - Server-side reflection of any payload
152
+ *
153
+ * Misses:
154
+ * - DOM-based XSS (requires JS execution)
155
+ * - Client-side state bugs
156
+ * - CSP-blocked attacks
157
+ *
158
+ * When reflection IS found, the finding is marked as `needsBrowserConfirmation`
159
+ * so the caller can escalate to Tier 2 for execution-based proof.
160
+ */
161
+
162
+ interface HttpScanResult {
163
+ /** Total HTTP requests sent */
164
+ requestsSent: number;
165
+ /** How long the scan took (ms) */
166
+ duration: number;
167
+ /** Findings from reflection detection */
168
+ findings: Finding[];
169
+ /** Requests where reflection was found — should be escalated to Tier 2 */
170
+ reflectedRequests: ReflectedRequest[];
171
+ }
172
+ interface ReflectedRequest {
173
+ /** Original captured request */
174
+ request: CapturedRequest;
175
+ /** Payload that caused reflection */
176
+ payload: string;
177
+ /** Category of the payload */
178
+ category: PayloadCategory;
179
+ }
180
+ interface HttpScanOptions {
181
+ /** Request timeout in ms (default: 10000) */
182
+ timeout?: number;
183
+ /** Max concurrent requests (default: 10) */
184
+ concurrency?: number;
185
+ /** Cookie header to send with requests (for auth) */
186
+ cookies?: string;
187
+ /** Extra headers to send */
188
+ headers?: Record<string, string>;
189
+ /** Callback for progress reporting */
190
+ onProgress?: (completed: number, total: number) => void;
191
+ }
192
+ /**
193
+ * Run Tier 1 HTTP-level scan on captured requests.
194
+ *
195
+ * For each CapturedRequest × each payload:
196
+ * 1. Substitute the payload into the injectable field
197
+ * 2. Send via fetch()
198
+ * 3. Check response body for reflection patterns
199
+ * 4. If reflected, add finding + mark for Tier 2 escalation
200
+ */
201
+ declare function httpScan(requests: CapturedRequest[], payloads: RuntimePayload[], options?: HttpScanOptions): Promise<HttpScanResult>;
202
+ /**
203
+ * Convert discovered forms into CapturedRequest metadata.
204
+ *
205
+ * Called by the crawler after form discovery. Each injectable form
206
+ * produces one CapturedRequest per injectable input field.
207
+ */
208
+ declare function buildCapturedRequests(forms: Array<{
209
+ pageUrl: string;
210
+ action: string;
211
+ method: string;
212
+ inputs: Array<{
213
+ name: string;
214
+ injectable: boolean;
215
+ type: string;
216
+ }>;
217
+ sessionName: string;
218
+ }>): CapturedRequest[];
219
+
220
+ /**
221
+ * Login Form Auto-Detection & Auth Replay
222
+ *
223
+ * Detects login forms on a page and fills them with credentials.
224
+ * After login, captures the browser storage state (cookies + localStorage)
225
+ * for re-use in subsequent scans.
226
+ *
227
+ * Detection strategy:
228
+ * 1. Find forms with a password input (strongest signal)
229
+ * 2. Find username field via heuristics (name, id, autocomplete, type)
230
+ * 3. Find submit button
231
+ * 4. Fall back to custom selectors from credentials
232
+ */
233
+
234
+ interface LoginForm {
235
+ /** Username input selector */
236
+ usernameSelector: string;
237
+ /** Password input selector */
238
+ passwordSelector: string;
239
+ /** Submit button selector (may be null if not found) */
240
+ submitSelector: string | null;
241
+ /** Whether the form was detected automatically */
242
+ autoDetected: boolean;
243
+ }
244
+ interface LoginResult {
245
+ /** Whether login succeeded */
246
+ success: boolean;
247
+ /** Message for logging */
248
+ message: string;
249
+ /** Playwright storage state JSON (cookies + localStorage) */
250
+ storageState?: string;
251
+ }
252
+ /**
253
+ * Auto-detect a login form on the current page.
254
+ *
255
+ * Strategy:
256
+ * 1. Find `<form>` elements containing an `input[type="password"]`
257
+ * 2. Within that form, find the username field using heuristics
258
+ * 3. Find the submit button
259
+ *
260
+ * Falls back to page-wide search if no enclosing <form> is found.
261
+ */
262
+ declare function detectLoginForm(page: Page): Promise<LoginForm | null>;
263
+ /**
264
+ * Perform login using detected form or custom selectors.
265
+ *
266
+ * Flow:
267
+ * 1. Navigate to login URL (or target URL)
268
+ * 2. Detect login form (or use custom selectors from credentials)
269
+ * 3. Fill username + password
270
+ * 4. Submit form
271
+ * 5. Wait for navigation
272
+ * 6. Check for logged-in indicator
273
+ * 7. Capture storage state
274
+ */
275
+ declare function performLogin(page: Page, context: BrowserContext, credentials: FormCredentials, options: {
276
+ targetUrl: string;
277
+ loggedInIndicator?: string;
278
+ loggedOutIndicator?: string;
279
+ }): Promise<LoginResult>;
280
+ /**
281
+ * Check if the current session is still alive.
282
+ *
283
+ * Used during long-running scans to detect session expiry
284
+ * and trigger re-authentication.
130
285
  */
131
- declare function crawlAndBuildSessions(config: BrowserCrawlConfig, options?: CrawlOptions): Promise<Session[]>;
286
+ declare function checkSessionAlive(page: Page, config: {
287
+ loggedInIndicator?: string;
288
+ loggedOutIndicator?: string;
289
+ }): Promise<boolean>;
132
290
 
133
291
  /**
134
292
  * @vulcn/driver-browser
@@ -334,4 +492,4 @@ type BrowserStep = z.infer<typeof BrowserStepSchema>;
334
492
  */
335
493
  declare const browserDriver: VulcnDriver;
336
494
 
337
- export { BROWSER_STEP_TYPES, type BrowserConfig, BrowserRecorder, BrowserRunner, type BrowserStep, BrowserStepSchema, type BrowserStepType, checkBrowsers, configSchema, crawlAndBuildSessions, browserDriver as default, installBrowsers, launchBrowser };
495
+ export { BROWSER_STEP_TYPES, type BrowserConfig, BrowserRecorder, BrowserRunner, type BrowserStep, BrowserStepSchema, type BrowserStepType, type CrawlResult, type HttpScanOptions, type HttpScanResult, type LoginForm, type LoginResult, type ReflectedRequest, buildCapturedRequests, checkBrowsers, checkSessionAlive, configSchema, crawlAndBuildSessions, browserDriver as default, detectLoginForm, httpScan, installBrowsers, launchBrowser, performLogin };