openhacker 0.1.0 → 0.1.2
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/openhacker +1 -1
- package/package.json +3 -3
- package/src/index.d.ts +1 -0
- package/src/index.js +305 -1
- package/templates/agent/.env.example +0 -7
- package/templates/agent/README.md +1 -2
- package/templates/agent/agent/agent.ts +1 -5
- package/templates/agent/agent/channels/eve.ts +7 -0
- package/templates/agent/agent/instructions.md +7 -45
- package/templates/agent/app/globals.css +65 -197
- package/templates/agent/app/layout.tsx +2 -22
- package/templates/agent/app/page.tsx +80 -102
- package/templates/agent/package.json +2 -3
- package/src/cli.js +0 -153
- package/src/index.ts +0 -1
- package/templates/agent/agent/lib/auth.ts +0 -23
- package/templates/agent/agent/lib/github.ts +0 -74
- package/templates/agent/agent/lib/osv.ts +0 -152
- package/templates/agent/agent/lib/scan.ts +0 -153
- package/templates/agent/agent/lib/store.ts +0 -151
- package/templates/agent/agent/lib/types.ts +0 -63
- package/templates/agent/agent/schedules/daily_audit.ts +0 -20
- package/templates/agent/agent/tools/check_advisories.ts +0 -27
- package/templates/agent/agent/tools/list_targets.ts +0 -21
- package/templates/agent/agent/tools/read_repo_file.ts +0 -31
- package/templates/agent/agent/tools/report_finding.ts +0 -59
- package/templates/agent/agent/tools/run_dependency_scan.ts +0 -16
- package/templates/agent/app/_components/ui.tsx +0 -29
- package/templates/agent/app/actions.ts +0 -120
- package/templates/agent/app/api/scan/route.ts +0 -34
- package/templates/agent/app/login/page.tsx +0 -40
- package/templates/agent/app/settings/page.tsx +0 -92
- package/templates/agent/app/targets/[id]/page.tsx +0 -127
- package/templates/agent/proxy.ts +0 -21
package/README.md
CHANGED
package/bin/openhacker
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openhacker",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Scaffold a self-hosted OpenHacker security agent",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
".": "./src/index.js"
|
|
12
12
|
},
|
|
13
13
|
"main": "./src/index.js",
|
|
14
|
-
"types": "./src/index.ts",
|
|
14
|
+
"types": "./src/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
16
|
"bin",
|
|
17
17
|
"scripts",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"typescript": "6.0.2"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
|
-
"build": "tsc --noEmit && node --check ./bin/openhacker && node --check ./src/
|
|
29
|
+
"build": "tsc --noEmit && node --check ./bin/openhacker && node --check ./src/index.js && node --check ./scripts/sync-template.js && node --check ./scripts/clean-template.js",
|
|
30
30
|
"sync-template": "node ./scripts/sync-template.js"
|
|
31
31
|
}
|
|
32
32
|
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function run(args?: string[]): Promise<void>;
|
package/src/index.js
CHANGED
|
@@ -1 +1,305 @@
|
|
|
1
|
-
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { cp, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const ORANGE = "\x1b[38;5;214m";
|
|
7
|
+
const MUTED = "\x1b[0;2m";
|
|
8
|
+
const RED = "\x1b[0;31m";
|
|
9
|
+
const NC = "\x1b[0m";
|
|
10
|
+
|
|
11
|
+
const EXCLUDE = new Set([
|
|
12
|
+
".env",
|
|
13
|
+
".env.local",
|
|
14
|
+
"node_modules",
|
|
15
|
+
".eve",
|
|
16
|
+
".next",
|
|
17
|
+
".output",
|
|
18
|
+
".git",
|
|
19
|
+
".vercel",
|
|
20
|
+
".turbo",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
// Written into scaffolded projects directly rather than shipped as a template
|
|
24
|
+
// file, because npm renames a packaged `.gitignore` to `.npmignore` on publish.
|
|
25
|
+
const GITIGNORE = `# dependencies
|
|
26
|
+
node_modules
|
|
27
|
+
|
|
28
|
+
# next.js
|
|
29
|
+
.next
|
|
30
|
+
next-env.d.ts
|
|
31
|
+
|
|
32
|
+
# eve build artifacts
|
|
33
|
+
.eve
|
|
34
|
+
.output
|
|
35
|
+
|
|
36
|
+
# vercel / turbo
|
|
37
|
+
.vercel
|
|
38
|
+
.turbo
|
|
39
|
+
|
|
40
|
+
# typescript
|
|
41
|
+
tsconfig.tsbuildinfo
|
|
42
|
+
|
|
43
|
+
# env files (keep the example)
|
|
44
|
+
.env
|
|
45
|
+
.env.*
|
|
46
|
+
!.env.example
|
|
47
|
+
|
|
48
|
+
# misc
|
|
49
|
+
.DS_Store
|
|
50
|
+
*.log
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
54
|
+
|
|
55
|
+
async function exists(p) {
|
|
56
|
+
try {
|
|
57
|
+
await stat(p);
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function resolveTemplateDir() {
|
|
65
|
+
const candidates = [];
|
|
66
|
+
|
|
67
|
+
if (process.env.OPENHACKER_TEMPLATE_DIR) {
|
|
68
|
+
candidates.push(path.resolve(process.env.OPENHACKER_TEMPLATE_DIR));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
candidates.push(
|
|
72
|
+
// npm package layout: packages/openhacker/src -> packages/openhacker/templates/agent
|
|
73
|
+
path.resolve(here, "../templates/agent"),
|
|
74
|
+
// monorepo layout: packages/openhacker/src -> repo root -> apps/agent
|
|
75
|
+
path.resolve(here, "../../../apps/agent"),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
for (const candidate of candidates) {
|
|
79
|
+
if (await exists(path.join(candidate, "agent", "agent.ts"))) {
|
|
80
|
+
return candidate;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return candidates[0];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function packageNameFor(dest) {
|
|
88
|
+
return (
|
|
89
|
+
path
|
|
90
|
+
.basename(dest)
|
|
91
|
+
.replace(/[^a-z0-9-]+/gi, "-")
|
|
92
|
+
.toLowerCase() || "openhacker"
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function shouldCopyTemplatePath(src, root) {
|
|
97
|
+
const relative = path.relative(root, src);
|
|
98
|
+
const segments = relative.split(path.sep);
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
!segments.some((seg) => EXCLUDE.has(seg)) &&
|
|
102
|
+
!relative.endsWith("next-env.d.ts") &&
|
|
103
|
+
!relative.endsWith("tsconfig.tsbuildinfo")
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function runStep(command, args, cwd, { quiet = false } = {}) {
|
|
108
|
+
const result = spawnSync(command, args, {
|
|
109
|
+
cwd,
|
|
110
|
+
stdio: quiet ? "ignore" : "inherit",
|
|
111
|
+
shell: process.platform === "win32",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return !result.error && result.status === 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function hasCommand(command) {
|
|
118
|
+
const probe = process.platform === "win32" ? "where" : "which";
|
|
119
|
+
const result = spawnSync(probe, [command], {
|
|
120
|
+
stdio: "ignore",
|
|
121
|
+
shell: process.platform === "win32",
|
|
122
|
+
});
|
|
123
|
+
return !result.error && result.status === 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isInsideGitRepo(cwd) {
|
|
127
|
+
const result = spawnSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
128
|
+
cwd,
|
|
129
|
+
stdio: "ignore",
|
|
130
|
+
shell: process.platform === "win32",
|
|
131
|
+
});
|
|
132
|
+
return !result.error && result.status === 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function installDependencies(dest) {
|
|
136
|
+
if (!hasCommand("pnpm")) {
|
|
137
|
+
console.log(
|
|
138
|
+
`${MUTED}pnpm not found — skipping install. Run \`pnpm install\` manually.${NC}`,
|
|
139
|
+
);
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log(`\n${MUTED}Installing dependencies with pnpm…${NC}`);
|
|
144
|
+
// --ignore-workspace keeps the install self-contained: without it, pnpm walks
|
|
145
|
+
// up to a parent pnpm-workspace.yaml (e.g. when scaffolding inside a monorepo)
|
|
146
|
+
// and installs that workspace instead of the new project's node_modules.
|
|
147
|
+
const ok = runStep("pnpm", ["install", "--ignore-workspace"], dest);
|
|
148
|
+
if (!ok) {
|
|
149
|
+
console.log(
|
|
150
|
+
`${RED}pnpm install failed.${NC} ${MUTED}You can re-run it inside the project.${NC}`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return ok;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function initGitRepo(dest) {
|
|
157
|
+
if (!hasCommand("git")) {
|
|
158
|
+
console.log(`${MUTED}git not found — skipping repository init.${NC}`);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (isInsideGitRepo(dest)) {
|
|
163
|
+
console.log(
|
|
164
|
+
`${MUTED}Already inside a git repository — skipping git init.${NC}`,
|
|
165
|
+
);
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const initialized =
|
|
170
|
+
runStep("git", ["init"], dest, { quiet: true }) &&
|
|
171
|
+
runStep("git", ["add", "-A"], dest, { quiet: true }) &&
|
|
172
|
+
runStep("git", ["commit", "-m", "Initial commit from OpenHacker"], dest, {
|
|
173
|
+
quiet: true,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (initialized) {
|
|
177
|
+
console.log(`${MUTED}Initialized a git repository.${NC}`);
|
|
178
|
+
} else {
|
|
179
|
+
console.log(
|
|
180
|
+
`${MUTED}Could not create the initial git commit — you can do it manually.${NC}`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
return initialized;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function init(targetArg, { skipInstall = false, skipGit = false } = {}) {
|
|
187
|
+
const template = await resolveTemplateDir();
|
|
188
|
+
if (!(await exists(path.join(template, "agent", "agent.ts")))) {
|
|
189
|
+
console.error(
|
|
190
|
+
`${RED}Could not find the instance template at ${template}.${NC}`,
|
|
191
|
+
);
|
|
192
|
+
console.error(
|
|
193
|
+
`${MUTED}Set OPENHACKER_TEMPLATE_DIR to the template directory.${NC}`,
|
|
194
|
+
);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const dest = path.resolve(process.cwd(), targetArg ?? "openhacker");
|
|
199
|
+
if (await exists(dest)) {
|
|
200
|
+
console.error(`${RED}Destination already exists: ${dest}${NC}`);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log(`\n${MUTED}Creating OpenHacker instance in ${NC}${dest}`);
|
|
205
|
+
await cp(template, dest, {
|
|
206
|
+
recursive: true,
|
|
207
|
+
filter: (src) => shouldCopyTemplatePath(src, template),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const pkgPath = path.join(dest, "package.json");
|
|
211
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
|
|
212
|
+
pkg.name = packageNameFor(dest);
|
|
213
|
+
pkg.private = true;
|
|
214
|
+
await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
215
|
+
|
|
216
|
+
// Write before git init so the initial commit doesn't include node_modules/.env.
|
|
217
|
+
if (!(await exists(path.join(dest, ".gitignore")))) {
|
|
218
|
+
await writeFile(path.join(dest, ".gitignore"), GITIGNORE);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const installed = skipInstall ? false : await installDependencies(dest);
|
|
222
|
+
if (!skipGit) {
|
|
223
|
+
await initGitRepo(dest);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log(`\n${ORANGE}✓${NC} OpenHacker instance ready.\n`);
|
|
227
|
+
console.log("Next steps:\n");
|
|
228
|
+
console.log(` cd ${path.relative(process.cwd(), dest) || "."}`);
|
|
229
|
+
if (skipInstall || !installed) {
|
|
230
|
+
console.log(" pnpm install");
|
|
231
|
+
}
|
|
232
|
+
console.log(" pnpm dlx vercel link # link a Vercel project for AI/LLM access");
|
|
233
|
+
console.log(" pnpm dev # run locally\n");
|
|
234
|
+
console.log(
|
|
235
|
+
`${MUTED}Deploy: push to a git repo and import it into Vercel (deploys as one project).`,
|
|
236
|
+
);
|
|
237
|
+
console.log(`${MUTED}See README.md for local model configuration.${NC}\n`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function usage() {
|
|
241
|
+
console.log("OpenHacker\n");
|
|
242
|
+
console.log("Usage:");
|
|
243
|
+
console.log(
|
|
244
|
+
" openhacker [dir] Scaffold a deployable OpenHacker instance",
|
|
245
|
+
);
|
|
246
|
+
console.log(" openhacker init [dir] Same as above");
|
|
247
|
+
console.log(" openhacker --help Show help");
|
|
248
|
+
console.log(" openhacker --version Show version\n");
|
|
249
|
+
console.log("Options:");
|
|
250
|
+
console.log(" --skip-install Don't run pnpm install");
|
|
251
|
+
console.log(" --skip-git Don't create a git repository\n");
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function version() {
|
|
255
|
+
const pkg = JSON.parse(
|
|
256
|
+
await readFile(path.resolve(here, "../package.json"), "utf8"),
|
|
257
|
+
);
|
|
258
|
+
console.log(pkg.version);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export async function run(args = process.argv.slice(2)) {
|
|
262
|
+
const options = { skipInstall: false, skipGit: false };
|
|
263
|
+
const positionals = [];
|
|
264
|
+
|
|
265
|
+
for (const arg of args) {
|
|
266
|
+
switch (arg) {
|
|
267
|
+
case "--skip-install":
|
|
268
|
+
options.skipInstall = true;
|
|
269
|
+
break;
|
|
270
|
+
case "--skip-git":
|
|
271
|
+
options.skipGit = true;
|
|
272
|
+
break;
|
|
273
|
+
case "-h":
|
|
274
|
+
case "--help":
|
|
275
|
+
usage();
|
|
276
|
+
return;
|
|
277
|
+
case "-v":
|
|
278
|
+
case "--version":
|
|
279
|
+
await version();
|
|
280
|
+
return;
|
|
281
|
+
default:
|
|
282
|
+
if (arg.startsWith("-")) {
|
|
283
|
+
console.error(`${RED}Unknown option: ${arg}${NC}\n`);
|
|
284
|
+
usage();
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
positionals.push(arg);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const [command, target, ...rest] = positionals;
|
|
292
|
+
|
|
293
|
+
if (rest.length > 0) {
|
|
294
|
+
console.error(`${RED}Too many arguments.${NC}\n`);
|
|
295
|
+
usage();
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (command === "init") {
|
|
300
|
+
await init(target, options);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await init(command, options);
|
|
305
|
+
}
|
|
@@ -3,13 +3,6 @@
|
|
|
3
3
|
# For LOCAL runs of the eve agent, provide a gateway key:
|
|
4
4
|
AI_GATEWAY_API_KEY=
|
|
5
5
|
|
|
6
|
-
# Optional: override the agent's deep-analysis model (defaults to anthropic/claude-sonnet-4.6)
|
|
7
|
-
OPENHACKER_MODEL=
|
|
8
|
-
|
|
9
|
-
# --- Dashboard auth (recommended) ---
|
|
10
|
-
# When set, the dashboard requires this password to sign in. Unset = open dashboard.
|
|
11
|
-
OPENHACKER_ADMIN_PASSWORD=
|
|
12
|
-
|
|
13
6
|
# --- Persistent storage ---
|
|
14
7
|
# Add a Vercel KV / Upstash Redis integration. Without these, an in-memory store is
|
|
15
8
|
# used and data does NOT persist across restarts/deploys.
|
|
@@ -17,8 +17,7 @@ Your self-hosted OpenHacker security agent — a Next.js dashboard with an embed
|
|
|
17
17
|
2. Import it into Vercel (it deploys as one project — UI + agent + cron).
|
|
18
18
|
3. Add a **KV / Upstash Redis** integration from the Vercel Marketplace so findings persist
|
|
19
19
|
(`KV_REST_API_URL` / `KV_REST_API_TOKEN` are injected automatically).
|
|
20
|
-
4.
|
|
21
|
-
5. Open the deployment URL, sign in, and add a target repository.
|
|
20
|
+
4. Open the deployment URL and add a target repository.
|
|
22
21
|
|
|
23
22
|
Inference runs through the Vercel AI Gateway and authenticates automatically via Vercel
|
|
24
23
|
OIDC — no model API key required in production.
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { defineAgent } from "eve";
|
|
2
2
|
|
|
3
3
|
export default defineAgent({
|
|
4
|
-
|
|
5
|
-
// injected VERCEL_OIDC_TOKEN automatically; locally it uses AI_GATEWAY_API_KEY.
|
|
6
|
-
// The model picker in the dashboard writes OPENHACKER_MODEL.
|
|
7
|
-
model: process.env.OPENHACKER_MODEL ?? "anthropic/claude-sonnet-4.6",
|
|
8
|
-
reasoning: "medium",
|
|
4
|
+
model: "anthropic/claude-haiku-4.5",
|
|
9
5
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { eveChannel } from "eve/channels/eve";
|
|
2
|
+
import { none } from "eve/channels/auth";
|
|
3
|
+
|
|
4
|
+
// Public demo: anyone can call the agent from the browser. Add real auth
|
|
5
|
+
// (e.g. vercelOidc/localDev or your own session check) before exposing
|
|
6
|
+
// anything sensitive.
|
|
7
|
+
export default eveChannel({ auth: [none()] });
|
|
@@ -1,49 +1,11 @@
|
|
|
1
1
|
# OpenHacker
|
|
2
2
|
|
|
3
|
-
You are OpenHacker, an
|
|
4
|
-
real, exploitable vulnerabilities in a codebase and its dependencies, prove them,
|
|
5
|
-
and propose fixes. You operate continuously and on demand.
|
|
3
|
+
You are OpenHacker, an application security agent.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
The user gives you a GitHub repository (as `owner/name` or a github.com URL). Analyze it for security vulnerabilities and report your findings.
|
|
8
6
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
input) to the dangerous sink. Capture that data flow as your evidence. Set the
|
|
15
|
-
finding's `proof.status` honestly: `proven`, `likely`, or `unconfirmed`.
|
|
16
|
-
- **Be conservative with dependency CVEs.** A package having a CVE does not mean this
|
|
17
|
-
project is exploitable. Use `check_advisories` to confirm the *installed version* is
|
|
18
|
-
in the affected range, then assess whether the vulnerable code path is plausibly
|
|
19
|
-
reachable before reporting.
|
|
20
|
-
|
|
21
|
-
## Tools
|
|
22
|
-
|
|
23
|
-
- `list_targets` — see the repositories configured in this instance.
|
|
24
|
-
- `run_dependency_scan` — deterministically check a target's dependencies against OSV
|
|
25
|
-
and persist findings. Run this first for dependency coverage.
|
|
26
|
-
- `read_repo_file` — read files / list directories in a target's repo for code review.
|
|
27
|
-
- `check_advisories` — look up a single package in OSV.
|
|
28
|
-
- `report_finding` — persist a confirmed code-level vulnerability.
|
|
29
|
-
|
|
30
|
-
## Workflow
|
|
31
|
-
|
|
32
|
-
Given a target id:
|
|
33
|
-
|
|
34
|
-
1. Call `run_dependency_scan` to record known-vulnerable dependencies.
|
|
35
|
-
2. Use `read_repo_file` (list then read) to inspect the source layout, framework, and
|
|
36
|
-
trust boundaries (route handlers, server actions, request/query/header/env inputs).
|
|
37
|
-
3. Hunt for high-impact issue classes: broken authorization, injection (SQL/command/
|
|
38
|
-
template), SSRF, secrets in code, unsafe deserialization, and XSS (including
|
|
39
|
-
`dangerouslySetInnerHTML`).
|
|
40
|
-
4. For each confirmed code issue, call `report_finding` (with `targetId`) including an
|
|
41
|
-
accurate severity, location, data-flow evidence, and a concrete remediation.
|
|
42
|
-
5. Summarize what you checked and what you found.
|
|
43
|
-
|
|
44
|
-
## Severity
|
|
45
|
-
|
|
46
|
-
Use CVSS-style judgment: `critical` for unauthenticated RCE / auth bypass / data
|
|
47
|
-
exfiltration; `high` for authenticated high-impact issues; `medium` for issues
|
|
48
|
-
needing preconditions; `low`/`info` for hardening. Prefer under-reporting to
|
|
49
|
-
crying wolf.
|
|
7
|
+
- Briefly narrate what you are checking as you go (dependencies, auth, input handling, secrets, etc.).
|
|
8
|
+
- Use the shell to clone and explore the repo. Use `ls`/`find` to list directories; only use `read_file` on actual file paths, never on a directory.
|
|
9
|
+
- Call out concrete risks with severity and, where possible, how to fix them.
|
|
10
|
+
- If you cannot access the repository, say so plainly.
|
|
11
|
+
- Keep the final summary concise and skimmable.
|