@pensar/apex 0.0.111 → 0.0.112
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/README.md +2 -3
- package/bin/pensar.js +31 -276
- package/build/agent-5qdmmchx.js +206 -0
- package/build/agent-s2z0dasf.js +16 -0
- package/build/auth-jvq72ekc.js +263 -0
- package/build/authentication-nya4td5k.js +310 -0
- package/build/blackboxAgent-qa9ze2hn.js +17 -0
- package/build/blackboxPentest-85hwznet.js +41 -0
- package/build/cli-15vxn9zj.js +1358 -0
- package/build/cli-2ckm5es2.js +50 -0
- package/build/cli-49cd9yfk.js +4475 -0
- package/build/cli-5d6cs4dq.js +53 -0
- package/build/cli-6gtnyaqf.js +109 -0
- package/build/cli-7ckctq7a.js +45 -0
- package/build/cli-8rxa073f.js +104 -0
- package/build/cli-bp6d08sg.js +110 -0
- package/build/cli-e20q3hqz.js +307 -0
- package/build/cli-f9shhcxf.js +1498 -0
- package/build/cli-hmrzx8am.js +507 -0
- package/build/cli-j66pect7.js +202 -0
- package/build/cli-jb0gcnrs.js +60 -0
- package/build/cli-jh38b6zv.js +1074 -0
- package/build/cli-kqtgcdzn.js +54784 -0
- package/build/cli-r8r90gka.js +96700 -0
- package/build/cli-va4y0089.js +395 -0
- package/build/cli-w04ggbe4.js +104 -0
- package/build/cli-x1msjf55.js +103 -0
- package/build/cli-yj3dy0vg.js +180 -0
- package/build/cli.js +509 -0
- package/build/doctor-b7612pzw.js +117 -0
- package/build/fixes-1r6v7kh2.js +49 -0
- package/build/index-5ke2yd32.js +17 -0
- package/build/index-9ze42wn7.js +68412 -0
- package/build/index-rd11fk7h.js +1257 -0
- package/build/index-tke6896d.js +1097 -0
- package/build/index-vwvh1rdw.js +535 -0
- package/build/issues-kx721wja.js +94 -0
- package/build/logs-hav7d0nm.js +77 -0
- package/build/main-2483qzbq.js +397 -0
- package/build/multipart-parser-r38qdp5v.js +350 -0
- package/build/pentest-zzebnfa0.js +25 -0
- package/build/pentests-s9fwd71b.js +70 -0
- package/build/projects-tr719twv.js +35 -0
- package/build/targetedPentest-w2c85whf.js +32 -0
- package/build/token-6x6aavpc.js +58 -0
- package/build/token-util-na95bqjj.js +6 -0
- package/build/uninstall-2j0pymb0.js +231 -0
- package/build/utils-jky0th19.js +107 -0
- package/package.json +3 -4
- package/build/auth.js +0 -625
- package/build/highlights-eq9cgrbb.scm +0 -604
- package/build/highlights-ghv9g403.scm +0 -205
- package/build/highlights-hk7bwhj4.scm +0 -284
- package/build/highlights-r812a2qc.scm +0 -150
- package/build/highlights-x6tmsnaa.scm +0 -115
- package/build/index.js +0 -292069
- package/build/injections-73j83es3.scm +0 -27
- package/build/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/build/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/build/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/build/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/build/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/src/core/installation/index.ts +0 -223
- package/src/core/installation/installation.test.ts +0 -454
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import {
|
|
3
|
+
disconnect,
|
|
4
|
+
fetchWorkspaces,
|
|
5
|
+
isConnected,
|
|
6
|
+
pollForWorkspaceCreation,
|
|
7
|
+
pollLegacyToken,
|
|
8
|
+
pollWorkOSToken,
|
|
9
|
+
selectWorkspace,
|
|
10
|
+
startDeviceFlow
|
|
11
|
+
} from "./cli-j66pect7.js";
|
|
12
|
+
import {
|
|
13
|
+
config,
|
|
14
|
+
getPensarApiUrl,
|
|
15
|
+
getPensarConsoleUrl
|
|
16
|
+
} from "./cli-bp6d08sg.js";
|
|
17
|
+
import"./cli-jb0gcnrs.js";
|
|
18
|
+
import"./cli-yj3dy0vg.js";
|
|
19
|
+
import {
|
|
20
|
+
__require
|
|
21
|
+
} from "./cli-8rxa073f.js";
|
|
22
|
+
|
|
23
|
+
// src/cli/auth.ts
|
|
24
|
+
import * as readline from "readline";
|
|
25
|
+
function openUrl(url) {
|
|
26
|
+
try {
|
|
27
|
+
const { spawn } = __require("child_process");
|
|
28
|
+
const platform = process.platform;
|
|
29
|
+
let cmd;
|
|
30
|
+
if (platform === "darwin") {
|
|
31
|
+
cmd = spawn("open", [url]);
|
|
32
|
+
} else if (platform === "win32") {
|
|
33
|
+
cmd = spawn("cmd", ["/c", "start", url]);
|
|
34
|
+
} else {
|
|
35
|
+
cmd = spawn("xdg-open", [url]);
|
|
36
|
+
}
|
|
37
|
+
cmd.on("error", () => {});
|
|
38
|
+
} catch {}
|
|
39
|
+
}
|
|
40
|
+
function prompt(question) {
|
|
41
|
+
const rl = readline.createInterface({
|
|
42
|
+
input: process.stdin,
|
|
43
|
+
output: process.stdout
|
|
44
|
+
});
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
rl.question(question, (answer) => {
|
|
47
|
+
rl.close();
|
|
48
|
+
resolve(answer.trim());
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function promptWorkspaceSelection(workspaces) {
|
|
53
|
+
console.log(`
|
|
54
|
+
Select a workspace:
|
|
55
|
+
`);
|
|
56
|
+
workspaces.forEach((ws, i) => {
|
|
57
|
+
console.log(` ${i + 1}. ${ws.name} (${ws.slug}) — $${ws.balance.toFixed(2)}`);
|
|
58
|
+
});
|
|
59
|
+
const answer = await prompt(`
|
|
60
|
+
Enter number (1-${workspaces.length}): `);
|
|
61
|
+
const index = parseInt(answer, 10) - 1;
|
|
62
|
+
if (isNaN(index) || index < 0 || index >= workspaces.length) {
|
|
63
|
+
console.error("Invalid selection.");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
return workspaces[index];
|
|
67
|
+
}
|
|
68
|
+
async function login() {
|
|
69
|
+
const appConfig = await config.get();
|
|
70
|
+
if (isConnected(appConfig)) {
|
|
71
|
+
console.log("Already connected to Pensar Console.");
|
|
72
|
+
if (appConfig.workspaceSlug) {
|
|
73
|
+
console.log(` Workspace: ${appConfig.workspaceSlug}`);
|
|
74
|
+
}
|
|
75
|
+
const answer = await prompt(`
|
|
76
|
+
Reconnect? (y/N): `);
|
|
77
|
+
if (answer.toLowerCase() !== "y") {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
console.log(`
|
|
82
|
+
Pensar Console — Managed Inference
|
|
83
|
+
Connect for usage-based AI inference. No API keys needed.
|
|
84
|
+
`);
|
|
85
|
+
const apiUrl = getPensarApiUrl();
|
|
86
|
+
const flowInfo = await startDeviceFlow(apiUrl);
|
|
87
|
+
if (flowInfo.mode === "workos") {
|
|
88
|
+
const { clientId, deviceInfo } = flowInfo;
|
|
89
|
+
openUrl(deviceInfo.verification_uri_complete);
|
|
90
|
+
console.log(`A browser window should have opened.
|
|
91
|
+
If not, open this URL:
|
|
92
|
+
${deviceInfo.verification_uri_complete}
|
|
93
|
+
|
|
94
|
+
Your code: ${deviceInfo.user_code}
|
|
95
|
+
|
|
96
|
+
Waiting for browser authorization...`);
|
|
97
|
+
const tokens = await pollWorkOSToken({
|
|
98
|
+
clientId,
|
|
99
|
+
deviceCode: deviceInfo.device_code,
|
|
100
|
+
interval: deviceInfo.interval,
|
|
101
|
+
expiresIn: deviceInfo.expires_in
|
|
102
|
+
});
|
|
103
|
+
await config.update({
|
|
104
|
+
accessToken: tokens.accessToken,
|
|
105
|
+
refreshToken: tokens.refreshToken
|
|
106
|
+
});
|
|
107
|
+
console.log(`
|
|
108
|
+
Authenticated successfully. Fetching workspaces...`);
|
|
109
|
+
await handleWorkspaces(apiUrl, tokens.accessToken);
|
|
110
|
+
} else {
|
|
111
|
+
const { deviceInfo } = flowInfo;
|
|
112
|
+
openUrl(deviceInfo.verificationUriComplete);
|
|
113
|
+
console.log(`A browser window should have opened.
|
|
114
|
+
If not, open this URL:
|
|
115
|
+
${deviceInfo.verificationUriComplete}
|
|
116
|
+
|
|
117
|
+
Your code: ${deviceInfo.userCode}
|
|
118
|
+
|
|
119
|
+
Waiting for browser authorization...`);
|
|
120
|
+
const data = await pollLegacyToken({
|
|
121
|
+
apiUrl,
|
|
122
|
+
deviceCode: deviceInfo.deviceCode,
|
|
123
|
+
interval: deviceInfo.interval,
|
|
124
|
+
expiresIn: deviceInfo.expiresIn
|
|
125
|
+
});
|
|
126
|
+
await config.update({
|
|
127
|
+
pensarAPIKey: data.apiKey,
|
|
128
|
+
gatewaySigningKey: data.signingKey ?? null
|
|
129
|
+
});
|
|
130
|
+
if (data.workspace) {
|
|
131
|
+
await config.update({
|
|
132
|
+
workspaceId: data.workspace.id,
|
|
133
|
+
workspaceSlug: data.workspace.slug
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
console.log(`
|
|
137
|
+
✓ Connected to Pensar Console`);
|
|
138
|
+
if (data.workspace) {
|
|
139
|
+
console.log(` Workspace: ${data.workspace.name} (${data.workspace.slug})`);
|
|
140
|
+
}
|
|
141
|
+
if (data.credits) {
|
|
142
|
+
console.log(` Credits: $${data.credits.balance.toFixed(2)}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function handleWorkspaces(apiUrl, accessToken) {
|
|
147
|
+
const wsResult = await fetchWorkspaces(apiUrl, accessToken);
|
|
148
|
+
let workspaces = wsResult.workspaces;
|
|
149
|
+
const consoleUrl = wsResult.consoleUrl ?? getPensarConsoleUrl();
|
|
150
|
+
if (workspaces.length === 0) {
|
|
151
|
+
console.log(`
|
|
152
|
+
No workspaces found. Opening browser to create one...
|
|
153
|
+
If the browser didn't open, visit: ${consoleUrl}/create-workspace?redirect=/credits
|
|
154
|
+
`);
|
|
155
|
+
openUrl(`${consoleUrl}/create-workspace?redirect=/credits`);
|
|
156
|
+
console.log("Waiting for workspace creation...");
|
|
157
|
+
workspaces = await pollForWorkspaceCreation(apiUrl, accessToken);
|
|
158
|
+
}
|
|
159
|
+
let workspace;
|
|
160
|
+
if (workspaces.length === 1) {
|
|
161
|
+
workspace = workspaces[0];
|
|
162
|
+
} else {
|
|
163
|
+
workspace = await promptWorkspaceSelection(workspaces);
|
|
164
|
+
}
|
|
165
|
+
const result = await selectWorkspace(apiUrl, accessToken, workspace.id);
|
|
166
|
+
await config.update({
|
|
167
|
+
workspaceId: workspace.id,
|
|
168
|
+
workspaceSlug: workspace.slug,
|
|
169
|
+
gatewaySigningKey: result.signingKey ?? null
|
|
170
|
+
});
|
|
171
|
+
console.log(`
|
|
172
|
+
✓ Connected to Pensar Console
|
|
173
|
+
Workspace: ${workspace.name} (${workspace.slug})
|
|
174
|
+
Credits: $${result.billing.balance.toFixed(2)}`);
|
|
175
|
+
if (!result.confirmed && result.billingUrl) {
|
|
176
|
+
console.log(`
|
|
177
|
+
⚠ Your workspace needs credits. Add them at:
|
|
178
|
+
${result.billingUrl}`);
|
|
179
|
+
} else if (result.billing.balance < 1) {
|
|
180
|
+
const billingUrl = `${getPensarConsoleUrl()}/${workspace.slug}/settings/billing`;
|
|
181
|
+
console.log(`
|
|
182
|
+
⚠ Low credit balance. We recommend at least $30 for uninterrupted pentests.
|
|
183
|
+
Add credits: ${billingUrl}`);
|
|
184
|
+
}
|
|
185
|
+
console.log("\nPensar models are now available. Run `pensar` to get started.");
|
|
186
|
+
}
|
|
187
|
+
async function logout() {
|
|
188
|
+
const appConfig = await config.get();
|
|
189
|
+
if (!isConnected(appConfig)) {
|
|
190
|
+
console.log("Not currently connected to Pensar Console.");
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
await disconnect();
|
|
194
|
+
console.log("✓ Disconnected from Pensar Console.");
|
|
195
|
+
}
|
|
196
|
+
async function status() {
|
|
197
|
+
const appConfig = await config.get();
|
|
198
|
+
if (!isConnected(appConfig)) {
|
|
199
|
+
console.log("Not connected to Pensar Console.\n\nRun `pensar auth login` to connect.");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (!appConfig.accessToken && appConfig.pensarAPIKey && !appConfig.workspaceSlug) {
|
|
203
|
+
try {
|
|
204
|
+
const apiUrl = getPensarApiUrl();
|
|
205
|
+
const res = await fetch(`${apiUrl}/auth/validate`, {
|
|
206
|
+
headers: { Authorization: `Bearer ${appConfig.pensarAPIKey}` }
|
|
207
|
+
});
|
|
208
|
+
if (res.ok) {
|
|
209
|
+
const data = await res.json();
|
|
210
|
+
if (data.workspace) {
|
|
211
|
+
await config.update({
|
|
212
|
+
workspaceId: data.workspace.id,
|
|
213
|
+
workspaceSlug: data.workspace.slug
|
|
214
|
+
});
|
|
215
|
+
appConfig.workspaceId = data.workspace.id;
|
|
216
|
+
appConfig.workspaceSlug = data.workspace.slug;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} catch {}
|
|
220
|
+
}
|
|
221
|
+
const authMethod = appConfig.accessToken ? "WorkOS" : "API key";
|
|
222
|
+
console.log(`✓ Connected to Pensar Console
|
|
223
|
+
Workspace: ${appConfig.workspaceSlug ?? "not set"}
|
|
224
|
+
Auth: ${authMethod}`);
|
|
225
|
+
}
|
|
226
|
+
function showHelp() {
|
|
227
|
+
console.log(`Pensar Auth — Connect to Pensar Console
|
|
228
|
+
|
|
229
|
+
Usage:
|
|
230
|
+
pensar auth Login to Pensar Console (or show status if connected)
|
|
231
|
+
pensar auth login Login to Pensar Console
|
|
232
|
+
pensar auth logout Disconnect from Pensar Console
|
|
233
|
+
pensar auth status Show connection status
|
|
234
|
+
|
|
235
|
+
Options:
|
|
236
|
+
-h, --help Show this help message`);
|
|
237
|
+
}
|
|
238
|
+
async function main() {
|
|
239
|
+
const args = process.argv.slice(2);
|
|
240
|
+
const subcommand = args[0];
|
|
241
|
+
if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
242
|
+
showHelp();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
if (!subcommand || subcommand === "login") {
|
|
247
|
+
await login();
|
|
248
|
+
} else if (subcommand === "logout") {
|
|
249
|
+
await logout();
|
|
250
|
+
} else if (subcommand === "status") {
|
|
251
|
+
await status();
|
|
252
|
+
} else {
|
|
253
|
+
console.error(`Unknown auth subcommand: ${subcommand}`);
|
|
254
|
+
console.error("Run 'pensar auth --help' for usage information");
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
} catch (err) {
|
|
258
|
+
console.error(`
|
|
259
|
+
Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
main();
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectOSAndEnhancePrompt
|
|
3
|
+
} from "./cli-6gtnyaqf.js";
|
|
4
|
+
import {
|
|
5
|
+
OffensiveSecurityAgent
|
|
6
|
+
} from "./cli-r8r90gka.js";
|
|
7
|
+
import"./cli-jh38b6zv.js";
|
|
8
|
+
import {
|
|
9
|
+
hasToolCall
|
|
10
|
+
} from "./cli-kqtgcdzn.js";
|
|
11
|
+
import"./cli-j66pect7.js";
|
|
12
|
+
import"./cli-bp6d08sg.js";
|
|
13
|
+
import"./cli-jb0gcnrs.js";
|
|
14
|
+
import"./cli-yj3dy0vg.js";
|
|
15
|
+
import"./cli-15vxn9zj.js";
|
|
16
|
+
import"./cli-7ckctq7a.js";
|
|
17
|
+
import"./cli-8rxa073f.js";
|
|
18
|
+
|
|
19
|
+
// src/core/agents/specialized/authenticationAgent/agent.ts
|
|
20
|
+
import { existsSync, readFileSync } from "fs";
|
|
21
|
+
import { join } from "path";
|
|
22
|
+
|
|
23
|
+
// src/core/agents/specialized/authenticationAgent/prompts.ts
|
|
24
|
+
var AUTH_SUBAGENT_SYSTEM_PROMPT = `You are an authentication specialist for autonomous penetration testing.
|
|
25
|
+
|
|
26
|
+
# Mission
|
|
27
|
+
|
|
28
|
+
Authenticate against the target using browser or API methods — whichever is applicable.
|
|
29
|
+
Obtain valid credentials/sessions that other agents can use for authenticated testing.
|
|
30
|
+
|
|
31
|
+
# Operating Mode
|
|
32
|
+
|
|
33
|
+
- Fully autonomous — no human intervention
|
|
34
|
+
- If you encounter barriers (CAPTCHA, MFA), report failure with details
|
|
35
|
+
- Call \`complete_authentication\` when done (success or failure)
|
|
36
|
+
|
|
37
|
+
# Available Tools
|
|
38
|
+
|
|
39
|
+
## API Authentication
|
|
40
|
+
- \`authenticate_session\` — Submit credentials via form POST, JSON POST, or HTTP Basic auth.
|
|
41
|
+
Params: loginUrl, username, password, method (form_post|json_post|basic_auth), usernameField, passwordField, additionalFields.
|
|
42
|
+
- \`execute_command\` — Run curl or other shell commands for custom/complex API auth flows.
|
|
43
|
+
|
|
44
|
+
## Browser Authentication
|
|
45
|
+
- \`browser_navigate\` — Navigate to login page
|
|
46
|
+
- \`browser_snapshot\` — Get page accessibility tree with element refs (REQUIRED before click/fill)
|
|
47
|
+
- \`browser_fill\` — Fill a form field using its ref from the snapshot
|
|
48
|
+
- \`browser_click\` — Click a button/link using its ref from the snapshot
|
|
49
|
+
- \`browser_screenshot\` — Capture page state for verification
|
|
50
|
+
- \`browser_evaluate\` — Run JS to extract tokens from localStorage/sessionStorage
|
|
51
|
+
- \`browser_console\` — Check console output for errors
|
|
52
|
+
- \`browser_get_cookies\` — Extract session cookies (including httpOnly)
|
|
53
|
+
|
|
54
|
+
## Completion (two-step — BOTH are required)
|
|
55
|
+
- \`complete_authentication\` — Persist cookies/headers to disk for downstream agents. Call this FIRST.
|
|
56
|
+
- \`response\` — Submit your final structured result and end the run. Call this AFTER complete_authentication.
|
|
57
|
+
- \`delegate_to_auth_subagent\` — Fallback for complex auth flows (OAuth, SAML, CSRF chains).
|
|
58
|
+
Use only if direct API and browser approaches both fail.
|
|
59
|
+
|
|
60
|
+
# Strategy
|
|
61
|
+
|
|
62
|
+
## Step 1: Determine the auth method
|
|
63
|
+
|
|
64
|
+
If credentials AND a loginUrl hint are provided, skip probing and go straight to Step 2.
|
|
65
|
+
|
|
66
|
+
Otherwise, probe the target to detect the auth mechanism:
|
|
67
|
+
\`\`\`
|
|
68
|
+
execute_command: curl -i -s -o /dev/null -w "%{http_code}" <target>
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
Look for:
|
|
72
|
+
- **401/403 response** → API auth likely. Check WWW-Authenticate header for Basic/Bearer.
|
|
73
|
+
- **302 redirect to login page** → Browser or form-based auth.
|
|
74
|
+
- **HTML with login form** → Browser-based or form POST auth.
|
|
75
|
+
- **JSON error (e.g. "unauthorized")** → JSON API auth.
|
|
76
|
+
|
|
77
|
+
## Step 2: Authenticate
|
|
78
|
+
|
|
79
|
+
### API path (use when target is an API or has a simple form):
|
|
80
|
+
1. Call \`authenticate_session\` with the correct method and credentials.
|
|
81
|
+
- \`form_post\` for HTML forms
|
|
82
|
+
- \`json_post\` for JSON APIs
|
|
83
|
+
- \`basic_auth\` for HTTP Basic
|
|
84
|
+
2. Check the response — if authenticated is true, you have a session cookie.
|
|
85
|
+
|
|
86
|
+
### Browser path (use for SPAs, OAuth, JS-rendered forms):
|
|
87
|
+
1. \`browser_navigate\` to the login URL
|
|
88
|
+
2. \`browser_snapshot\` to find form fields and their refs
|
|
89
|
+
3. \`browser_fill\` the username/email field (use ref from snapshot)
|
|
90
|
+
4. \`browser_snapshot\` again (page may update after input)
|
|
91
|
+
5. \`browser_fill\` the password field
|
|
92
|
+
6. \`browser_click\` the submit/login button
|
|
93
|
+
7. \`browser_snapshot\` or \`browser_screenshot\` to verify the result
|
|
94
|
+
8. \`browser_get_cookies\` to extract session cookies
|
|
95
|
+
9. Optionally \`browser_evaluate\` to extract tokens from localStorage/sessionStorage:
|
|
96
|
+
\`\`\`javascript
|
|
97
|
+
(() => {
|
|
98
|
+
const tokens = {};
|
|
99
|
+
['token', 'access_token', 'accessToken', 'auth_token', 'jwt', 'id_token'].forEach(key => {
|
|
100
|
+
const val = localStorage.getItem(key) || sessionStorage.getItem(key);
|
|
101
|
+
if (val) tokens[key] = val;
|
|
102
|
+
});
|
|
103
|
+
return JSON.stringify(tokens);
|
|
104
|
+
})()
|
|
105
|
+
\`\`\`
|
|
106
|
+
|
|
107
|
+
### Fallback:
|
|
108
|
+
If both API and browser approaches fail, use \`delegate_to_auth_subagent\` for complex flows.
|
|
109
|
+
|
|
110
|
+
## Step 3: Complete (two-step)
|
|
111
|
+
|
|
112
|
+
**First**, call \`complete_authentication\` with:
|
|
113
|
+
- \`success\`: whether auth succeeded
|
|
114
|
+
- \`summary\`: what happened and what credentials/cookies were obtained
|
|
115
|
+
- \`exportedCookies\`: the cookie string for downstream HTTP requests (from browser_get_cookies cookieHeader or authenticate_session response)
|
|
116
|
+
- \`exportedHeaders\`: auth headers map, e.g. {"Authorization": "Bearer <token>"} (from browser_evaluate localStorage tokens or API response)
|
|
117
|
+
- \`strategy\`: method used ("browser", "form_post", "json_post", "basic_auth", "bearer")
|
|
118
|
+
- \`authBarrier\`: if a barrier was encountered (captcha, mfa, etc.)
|
|
119
|
+
|
|
120
|
+
CRITICAL: You MUST include exportedCookies and exportedHeaders when authentication succeeds.
|
|
121
|
+
These are the ONLY way downstream agents receive the session credentials.
|
|
122
|
+
- After browser auth: get cookies from \`browser_get_cookies\` (use the cookieHeader field) and tokens from \`browser_evaluate\`
|
|
123
|
+
- After API auth: use the session cookie and any auth headers from the response
|
|
124
|
+
|
|
125
|
+
**Then**, call the \`response\` tool with your final summary to end the run. This is required — complete_authentication persists the data, response terminates the agent.
|
|
126
|
+
|
|
127
|
+
# Error Recovery
|
|
128
|
+
|
|
129
|
+
If authentication fails:
|
|
130
|
+
1. Try alternative field names (email vs username, passwd vs password)
|
|
131
|
+
2. Check for CSRF token requirements (look in page source or hidden form fields)
|
|
132
|
+
3. Verify the endpoint URL is correct
|
|
133
|
+
4. Try a different method (form_post vs json_post)
|
|
134
|
+
5. If all else fails, call \`complete_authentication\` with success=false
|
|
135
|
+
|
|
136
|
+
## Rate Limiting
|
|
137
|
+
|
|
138
|
+
If you encounter rate-limiting errors (e.g. "Rate limit exceeded", HTTP 429, "too many requests"):
|
|
139
|
+
1. Do NOT give up or report failure immediately — rate limits are temporary
|
|
140
|
+
2. Use \`execute_command\` to sleep and wait for the rate limit to reset: \`sleep 120\`
|
|
141
|
+
3. After sleeping, retry the authentication attempt
|
|
142
|
+
4. If rate-limited again, sleep for another 120 seconds and retry
|
|
143
|
+
5. Continue this retry loop — be persistent. Only report failure after 5+ consecutive rate-limited retries
|
|
144
|
+
6. NEVER treat rate-limiting as a finding or vulnerability — it is an operational obstacle to work through
|
|
145
|
+
|
|
146
|
+
# Key Rules
|
|
147
|
+
|
|
148
|
+
1. **Be direct** — if credentials are provided, authenticate immediately. Don't over-analyze.
|
|
149
|
+
2. **Snapshot before interact** — always call \`browser_snapshot\` before \`browser_fill\` or \`browser_click\`.
|
|
150
|
+
3. **Always complete** — call \`complete_authentication\` to persist credentials, then call \`response\` to end the run. Both are required.
|
|
151
|
+
4. **No human intervention** — return failure instead of requesting input.
|
|
152
|
+
5. **Don't log passwords** — never output actual password values in summaries.
|
|
153
|
+
|
|
154
|
+
# Screenshot Evidence
|
|
155
|
+
|
|
156
|
+
- Use \`browser_screenshot\` to capture the page state at key moments during authentication
|
|
157
|
+
- **Always screenshot after login attempt** to document success (dashboard/welcome page) or failure (error message)
|
|
158
|
+
- Screenshot any error pages, CAPTCHA challenges, MFA prompts, or unexpected barriers
|
|
159
|
+
- Use descriptive filenames: "login-form", "auth-success", "auth-error", "mfa-prompt", "captcha-detected"
|
|
160
|
+
- Screenshots are automatically stored and displayed in the console logs for debugging
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
// src/core/agents/specialized/authenticationAgent/agent.ts
|
|
164
|
+
class AuthenticationAgent extends OffensiveSecurityAgent {
|
|
165
|
+
constructor(opts) {
|
|
166
|
+
const {
|
|
167
|
+
model,
|
|
168
|
+
target,
|
|
169
|
+
session,
|
|
170
|
+
authHints,
|
|
171
|
+
authConfig,
|
|
172
|
+
onStepFinish,
|
|
173
|
+
abortSignal
|
|
174
|
+
} = opts;
|
|
175
|
+
const cm = session.credentialManager;
|
|
176
|
+
super({
|
|
177
|
+
system: detectOSAndEnhancePrompt(AUTH_SUBAGENT_SYSTEM_PROMPT),
|
|
178
|
+
prompt: buildAuthPrompt(target, authHints, cm),
|
|
179
|
+
model,
|
|
180
|
+
session,
|
|
181
|
+
target,
|
|
182
|
+
authConfig,
|
|
183
|
+
onStepFinish,
|
|
184
|
+
abortSignal,
|
|
185
|
+
toolChoice: "auto",
|
|
186
|
+
activeTools: [
|
|
187
|
+
"execute_command",
|
|
188
|
+
"authenticate_session",
|
|
189
|
+
"complete_authentication",
|
|
190
|
+
"browser_navigate",
|
|
191
|
+
"browser_snapshot",
|
|
192
|
+
"browser_screenshot",
|
|
193
|
+
"browser_click",
|
|
194
|
+
"browser_fill",
|
|
195
|
+
"browser_evaluate",
|
|
196
|
+
"browser_console",
|
|
197
|
+
"browser_get_cookies",
|
|
198
|
+
"email_list_inboxes",
|
|
199
|
+
"email_list_messages",
|
|
200
|
+
"email_search_messages",
|
|
201
|
+
"email_get_message",
|
|
202
|
+
"web_search",
|
|
203
|
+
"get_page"
|
|
204
|
+
],
|
|
205
|
+
stopWhen: hasToolCall("complete_authentication"),
|
|
206
|
+
resolveResult: () => {
|
|
207
|
+
const authDataPath = join(session.rootPath, "auth", "auth-data.json");
|
|
208
|
+
return loadAuthResult(authDataPath);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function loadAuthResult(authDataPath) {
|
|
214
|
+
if (!existsSync(authDataPath)) {
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
summary: "Authentication completed but no auth-data.json was written.",
|
|
218
|
+
exportedCookies: "",
|
|
219
|
+
exportedHeaders: {},
|
|
220
|
+
strategy: "unknown",
|
|
221
|
+
authBarrier: undefined,
|
|
222
|
+
authDataPath: ""
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
const raw = JSON.parse(readFileSync(authDataPath, "utf-8"));
|
|
227
|
+
return {
|
|
228
|
+
success: raw.authenticated ?? false,
|
|
229
|
+
summary: raw.summary ?? "Authentication process completed.",
|
|
230
|
+
exportedCookies: raw.cookies ?? "",
|
|
231
|
+
exportedHeaders: raw.headers ?? {},
|
|
232
|
+
strategy: raw.strategy ?? "unknown",
|
|
233
|
+
authBarrier: raw.authBarrier,
|
|
234
|
+
authDataPath
|
|
235
|
+
};
|
|
236
|
+
} catch {
|
|
237
|
+
return {
|
|
238
|
+
success: false,
|
|
239
|
+
summary: "Failed to parse auth-data.json.",
|
|
240
|
+
exportedCookies: "",
|
|
241
|
+
exportedHeaders: {},
|
|
242
|
+
strategy: "unknown",
|
|
243
|
+
authBarrier: undefined,
|
|
244
|
+
authDataPath
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function buildAuthPrompt(target, authHints, credentialManager) {
|
|
249
|
+
const parts = [`TARGET: ${target}
|
|
250
|
+
`];
|
|
251
|
+
const credBlock = credentialManager?.formatForPrompt();
|
|
252
|
+
if (credBlock) {
|
|
253
|
+
parts.push(credBlock);
|
|
254
|
+
parts.push("");
|
|
255
|
+
} else {
|
|
256
|
+
parts.push(`No credentials provided. Probe the target to discover auth requirements.
|
|
257
|
+
`);
|
|
258
|
+
}
|
|
259
|
+
if (authHints) {
|
|
260
|
+
parts.push("AUTH HINTS:");
|
|
261
|
+
if (authHints.authScheme)
|
|
262
|
+
parts.push(`- Auth scheme: ${authHints.authScheme}`);
|
|
263
|
+
if (authHints.csrfRequired)
|
|
264
|
+
parts.push("- CSRF protection detected");
|
|
265
|
+
if (authHints.browserRequired)
|
|
266
|
+
parts.push("- Browser automation may be needed");
|
|
267
|
+
if (authHints.protectedEndpoints?.length) {
|
|
268
|
+
parts.push(`- Protected endpoints: ${authHints.protectedEndpoints.join(", ")}`);
|
|
269
|
+
}
|
|
270
|
+
parts.push("");
|
|
271
|
+
}
|
|
272
|
+
if (credBlock) {
|
|
273
|
+
parts.push(`INSTRUCTIONS:
|
|
274
|
+
You have credentials available via credential IDs — authenticate immediately.
|
|
275
|
+
1. Try authenticate_session first (pass the credentialId — secrets are resolved automatically)
|
|
276
|
+
2. If authenticate_session fails, use browser tools. For browser_fill on password/secret fields,
|
|
277
|
+
pass credentialId + credentialField (e.g. credentialField="password") instead of the raw value —
|
|
278
|
+
the secret is resolved securely at execution time. NEVER type a password directly.
|
|
279
|
+
3. Call complete_authentication with exported cookies/headers to persist credentials and end the run`);
|
|
280
|
+
} else {
|
|
281
|
+
parts.push(`INSTRUCTIONS:
|
|
282
|
+
1. Probe the target with execute_command (curl) to determine the auth mechanism
|
|
283
|
+
2. Attempt authentication with authenticate_session or the browser tools
|
|
284
|
+
3. If that fails, try delegate_to_auth_subagent as a fallback
|
|
285
|
+
4. Call complete_authentication with exported cookies/headers to persist credentials and end the run`);
|
|
286
|
+
}
|
|
287
|
+
return parts.join(`
|
|
288
|
+
`);
|
|
289
|
+
}
|
|
290
|
+
async function runAuthenticationAgent(input) {
|
|
291
|
+
const agent = new AuthenticationAgent(input);
|
|
292
|
+
const result = await agent.consume({
|
|
293
|
+
onTextDelta: (d) => input.callbacks?.onTextDelta?.(d),
|
|
294
|
+
onToolCallStreaming: (d) => input.callbacks?.onToolCallStreaming?.(d),
|
|
295
|
+
onToolCallDelta: (d) => input.callbacks?.onToolCallDelta?.(d),
|
|
296
|
+
onToolCall: (d) => input.callbacks?.onToolCall?.(d),
|
|
297
|
+
onToolResult: (d) => input.callbacks?.onToolResult?.(d),
|
|
298
|
+
onError: (e) => input.callbacks?.onError?.(e),
|
|
299
|
+
subagentCallbacks: input.callbacks?.subagentCallbacks
|
|
300
|
+
});
|
|
301
|
+
console.log(`
|
|
302
|
+
Authentication ${result.success ? "succeeded" : "failed"}: ${result.summary}`);
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/core/api/authentication.ts
|
|
307
|
+
var runAuthenticationAgent2 = runAuthenticationAgent;
|
|
308
|
+
export {
|
|
309
|
+
runAuthenticationAgent2 as runAuthenticationAgent
|
|
310
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BlackboxAttackSurfaceAgent
|
|
3
|
+
} from "./cli-hmrzx8am.js";
|
|
4
|
+
import"./cli-6gtnyaqf.js";
|
|
5
|
+
import"./cli-r8r90gka.js";
|
|
6
|
+
import"./cli-jh38b6zv.js";
|
|
7
|
+
import"./cli-kqtgcdzn.js";
|
|
8
|
+
import"./cli-j66pect7.js";
|
|
9
|
+
import"./cli-bp6d08sg.js";
|
|
10
|
+
import"./cli-jb0gcnrs.js";
|
|
11
|
+
import"./cli-yj3dy0vg.js";
|
|
12
|
+
import"./cli-15vxn9zj.js";
|
|
13
|
+
import"./cli-7ckctq7a.js";
|
|
14
|
+
import"./cli-8rxa073f.js";
|
|
15
|
+
export {
|
|
16
|
+
BlackboxAttackSurfaceAgent
|
|
17
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runPentestWorkflow
|
|
3
|
+
} from "./cli-f9shhcxf.js";
|
|
4
|
+
import"./cli-e20q3hqz.js";
|
|
5
|
+
import"./cli-w04ggbe4.js";
|
|
6
|
+
import"./cli-2ckm5es2.js";
|
|
7
|
+
import"./cli-hmrzx8am.js";
|
|
8
|
+
import"./cli-6gtnyaqf.js";
|
|
9
|
+
import"./cli-r8r90gka.js";
|
|
10
|
+
import"./cli-jh38b6zv.js";
|
|
11
|
+
import"./cli-kqtgcdzn.js";
|
|
12
|
+
import"./cli-j66pect7.js";
|
|
13
|
+
import"./cli-bp6d08sg.js";
|
|
14
|
+
import"./cli-jb0gcnrs.js";
|
|
15
|
+
import"./cli-yj3dy0vg.js";
|
|
16
|
+
import"./cli-15vxn9zj.js";
|
|
17
|
+
import"./cli-7ckctq7a.js";
|
|
18
|
+
import"./cli-8rxa073f.js";
|
|
19
|
+
|
|
20
|
+
// src/core/api/blackboxPentest.ts
|
|
21
|
+
async function runPentestAgent(input) {
|
|
22
|
+
const { findings, findingsPath, pocsPath, reportPath } = await runPentestWorkflow({
|
|
23
|
+
target: input.target,
|
|
24
|
+
cwd: input.cwd,
|
|
25
|
+
model: input.model,
|
|
26
|
+
session: input.session,
|
|
27
|
+
authConfig: input.authConfig,
|
|
28
|
+
abortSignal: input.abortSignal,
|
|
29
|
+
callbacks: input.callbacks
|
|
30
|
+
});
|
|
31
|
+
console.log(`
|
|
32
|
+
Found ${findings.length} vulnerabilities`);
|
|
33
|
+
console.log(`Findings: ${findingsPath}`);
|
|
34
|
+
console.log(`POCs: ${pocsPath}`);
|
|
35
|
+
if (reportPath)
|
|
36
|
+
console.log(`Report: ${reportPath}`);
|
|
37
|
+
return { findings, findingsPath, pocsPath, reportPath };
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
runPentestAgent
|
|
41
|
+
};
|