argus-pentest 0.0.0-stage → 0.1.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.
Files changed (66) hide show
  1. package/README.md +645 -2
  2. package/package.json +51 -4
  3. package/src/cli.ts +559 -0
  4. package/src/core/agent.ts +4432 -0
  5. package/src/core/authorization.ts +83 -0
  6. package/src/core/browser.ts +355 -0
  7. package/src/core/config.ts +142 -0
  8. package/src/core/connectors.ts +260 -0
  9. package/src/core/cvss.ts +186 -0
  10. package/src/core/dependencies.ts +417 -0
  11. package/src/core/diff.ts +193 -0
  12. package/src/core/engine.ts +355 -0
  13. package/src/core/events.ts +83 -0
  14. package/src/core/http.ts +373 -0
  15. package/src/core/installer.ts +371 -0
  16. package/src/core/journal.ts +106 -0
  17. package/src/core/jwt.ts +176 -0
  18. package/src/core/mcp.ts +343 -0
  19. package/src/core/memory.ts +158 -0
  20. package/src/core/osv.ts +201 -0
  21. package/src/core/packages.ts +168 -0
  22. package/src/core/patches.ts +139 -0
  23. package/src/core/permissions.ts +132 -0
  24. package/src/core/pipeline.ts +327 -0
  25. package/src/core/planner.ts +149 -0
  26. package/src/core/probes/attacks.ts +1062 -0
  27. package/src/core/probes/network.ts +543 -0
  28. package/src/core/probes/repo.ts +522 -0
  29. package/src/core/probes/web.ts +1341 -0
  30. package/src/core/prompt-scope.ts +338 -0
  31. package/src/core/proxy.ts +380 -0
  32. package/src/core/recon-utils.ts +368 -0
  33. package/src/core/remediation.ts +71 -0
  34. package/src/core/report.ts +465 -0
  35. package/src/core/sandbox.ts +173 -0
  36. package/src/core/scope.ts +597 -0
  37. package/src/core/scratch.ts +29 -0
  38. package/src/core/session.ts +247 -0
  39. package/src/core/signatures.ts +326 -0
  40. package/src/core/skills.ts +243 -0
  41. package/src/core/store.ts +135 -0
  42. package/src/core/tools.ts +1197 -0
  43. package/src/core/traffic.ts +122 -0
  44. package/src/core/types.ts +331 -0
  45. package/src/core/util.ts +186 -0
  46. package/src/core/vectormemory.ts +173 -0
  47. package/src/core/web.ts +396 -0
  48. package/src/core/wsl.ts +189 -0
  49. package/src/tui/agent-runner.ts +369 -0
  50. package/src/tui/app.tsx +1474 -0
  51. package/src/tui/catalog.ts +179 -0
  52. package/src/tui/clipboard.ts +168 -0
  53. package/src/tui/commands.ts +1326 -0
  54. package/src/tui/credentials.ts +58 -0
  55. package/src/tui/dashboard.ts +369 -0
  56. package/src/tui/doctor.ts +278 -0
  57. package/src/tui/hooks.ts +59 -0
  58. package/src/tui/index.tsx +68 -0
  59. package/src/tui/intent.ts +162 -0
  60. package/src/tui/modals.tsx +1375 -0
  61. package/src/tui/preview.ts +171 -0
  62. package/src/tui/providers.ts +250 -0
  63. package/src/tui/state.ts +397 -0
  64. package/src/tui/terminal-spec.ts +59 -0
  65. package/src/tui/terminal.tsx +104 -0
  66. package/src/tui/theme.ts +121 -0
package/README.md CHANGED
@@ -1,3 +1,646 @@
1
- # Temporary Holding Version
1
+ # ARGUS
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/npm/v/argus-pentest.svg)](https://www.npmjs.com/package/argus-pentest)
4
+
5
+ Terminal-first, authorization-gated security assessment harness. Ask it in plain
6
+ English and a grounded agent plans scans, reads the source, searches the workspace and
7
+ reports only findings that real tools produced. It runs native probes plus optional
8
+ external adapters, saves every event and finding, and writes reports.
9
+
10
+ > Only assess systems you have written permission to test. Naming a target in the
11
+ > prompt is the authorization record; a manual scope file remains available for teams
12
+ > that need one.
13
+
14
+ ## Install and run
15
+
16
+ Global install (the npm name `argus` belongs to an unrelated package, so the package
17
+ is `argus-pentest`; the installed command is `argus`):
18
+
19
+ ```bash
20
+ npm install -g argus-pentest # needs Bun >= 1.3.14 on PATH
21
+ argus --version
22
+ argus # terminal UI
23
+ argus --help # command-line interface
24
+ argus doctor # environment and toolchain check
25
+ ```
26
+
27
+ From a checkout:
28
+
29
+ ```bash
30
+ bun install
31
+ bun run tui # terminal UI (alias: bun run dev)
32
+ bun run cli --help # command-line interface
33
+ ```
34
+
35
+ ## Terminal UI
36
+
37
+ The TUI is a normal prompt-driven terminal, laid out like a quiet instrument panel:
38
+
39
+ ```
40
+ ▌ ARGUS security assessment OpenAI · gpt-4.1-mini
41
+ scope example.com · valid · TICKET-42 scope.json
42
+ ──────────────────────────────────────────────────────────────────────────
43
+ ❯ audit example.com and rank what you find
44
+
45
+ RESULT ──────────────────────────────────────────────────────────────────
46
+ ...
47
+ FINDINGS ────────────────────────────────────────────────────────────────
48
+ MED SPF record missing · example.com
49
+
50
+ ⠹ HTTP baseline & technology fingerprint ███████░░░░░ 3/7 00:12
51
+ ╭──────────────────────────────────────────────────────────────────────╮
52
+ │ ❯ ask in plain English, or / for commands │
53
+ ╰──────────────────────────────────────────────────────────────────────╯
54
+ /help tab complete esc interrupt ctrl+c exit key from OPENAI_API_KEY
55
+ ```
56
+
57
+ The masthead carries the identity, the active provider/model and the authorization in
58
+ play. Operator prompts use the accent `❯`, findings render as coloured severity chips,
59
+ section headings get a hairline, and one status line above the prompt shows the live
60
+ phase with a block progress meter or the session's finding counts. Everything is a
61
+ single column, so narrow terminals keep the same layout. Panels and lists use a solid
62
+ `▌` selection bar (k9s/lazygit convention), and all markers are terminal glyphs — no
63
+ emoji, no bracketed tags, nothing that needs a Nerd Font. Plain text goes to the agent (`audit example.com and find real bugs`). Type `/`
64
+ to open the command menu; `Up`/`Down` select, `tab` completes, `enter` opens the highlighted
65
+ picker or runs the command. `esc` closes a modal, aborts the agent or clears the prompt.
66
+
67
+ Drag across transcript text to select it, then press `ctrl+c` to copy; the selection is
68
+ highlighted by the renderer and copying works through OSC 52 plus the native platform
69
+ clipboard, so it also works over SSH in terminals such as Windows Terminal, iTerm2,
70
+ kitty and WezTerm. `/copy` copies the last agent answer and `/copy all` copies the
71
+ whole transcript. With no selection, `ctrl+c` still aborts a running job or exits.
72
+
73
+ Mouse support is enabled by default: click the prompt to focus it, click a command
74
+ suggestion or a provider/model/MCP row to act on it, click `esc to close` in a modal,
75
+ and use the wheel over the transcript or a modal list. Dragging still selects text for
76
+ copying.
77
+
78
+ | Command | What it does |
79
+ | --- | --- |
80
+ | `/help` | list every command and scan flag |
81
+ | `/doctor [--chat]` | preflight provider, key, model, scope, memory, store and adapters; `--chat` sends one tiny completion |
82
+ | `/scan <target>` | start an assessment; scope follows the target (defaults to the `deep` profile) |
83
+ | `/dashboard` | overview, risk gauge, severity and category bars, probe coverage, dependency graph, outdated packages, fix plan and findings |
84
+ | `/providers` | provider picker modal |
85
+ | `/models` | model picker grouped by provider, with live discovery and search across providers and models |
86
+ | `/connect` | provider and MCP picker with search; `enter` activates a provider or opens key entry, or connects an MCP server |
87
+ | `/key [provider]` | add or replace an API key in one step (active provider by default) |
88
+ | `/copy [last\|all]` | copy the last answer or the whole transcript |
89
+ | `/memory` | show the AGENTS.md memory file and its newest entries |
90
+ | `/runs` | recent assessments |
91
+ | `/findings [runId]` | review findings for the last or a specific run |
92
+ | `/preview <path> [--start N] [--end N]` | read-only preview panel: numbered lines, scrollable, never writes |
93
+ | `/report [runId] [format]` | write markdown, json, sarif, csv or html reports |
94
+ | `/tools` | external adapter inventory and install hints |
95
+ | `/install [tool...] [--all] [--yes]` | plan platform-specific adapter installs; `--yes` runs only user-level plans |
96
+ | `/web <query>` / `/web read <url>` | keyless web search or one public page, with SSRF and size checks |
97
+ | `/http <url> ...` | send one request; scope follows the URL |
98
+ | `/jwt <token>` | decode a JWT locally and flag session risks without sending it anywhere |
99
+ | `/scope [path\|init\|target]` | show, derive or validate the active scope |
100
+ | `/config` | effective configuration, with key names but never key values |
101
+ | `/authorize` | one-time authorized-use agreement (`accept <name>`, `revoke`, status) |
102
+ | `/vim [file]` | edit a file (or the project) in the embedded terminal, using Kali's vim when WSL is present |
103
+ | `/terminal [cmd]` | open an interactive shell or run a command in the embedded terminal |
104
+ | `/clear`, `/quit` | clear the transcript, exit |
105
+
106
+ Scan flags: `--profile quick|recon|web|api|network|deep|repo`, `--scope <file>`,
107
+ `--passive`, `--intrusive`, `--no-external`, `--formats md,json,sarif`, `--retries 0-5`.
108
+ Scope is derived from the target you name; a manual `--scope` file is still honored.
109
+
110
+ ### Preflight
111
+
112
+ Run `/doctor` before a session when something looks wrong. It checks the active provider's
113
+ `/models` endpoint and reports whether the key was rejected, the server is down, the base
114
+ URL is missing, or the selected model is absent from the live catalog. It also validates
115
+ the active scope, reports the AGENTS.md path and entry count, checks that the data directory
116
+ is writable, inventories external adapters, reports the web-research mode, and lists
117
+ configured MCP servers. By default
118
+ it never sends a chat completion, so a preflight does not spend credits. Add `--chat` to
119
+ send one minimal completion and prove the exact chat endpoint, model and key work; that
120
+ option uses a small amount of credit and reports the round-trip latency. Status lines use plain ASCII tags: `[+]` ok,
121
+ `[!]` warning, `[-]` error, `[..]` in progress. No emoji or Nerd Font glyph is required.
122
+
123
+ ### Providers and models
124
+
125
+ `/providers` covers OpenAI, OpenRouter, Groq, DeepSeek, Together, Mistral, xAI, NVIDIA
126
+ NIM, local Ollama and LM Studio, plus any custom OpenAI-compatible endpoint
127
+ (`/providers url https://host/v1`). `/models` renders every provider as a group with
128
+ its models underneath, puts the active provider first, queries the active endpoint's
129
+ `/models` API, and keeps a known catalog as fallback. Typing filters both provider
130
+ names and model ids; `Up`/`Down` select, `enter` switches provider and model. Queries are
131
+ skipped when the provider still needs a key, so the picker never stalls on a request
132
+ that cannot succeed.
133
+
134
+ Adding a key takes one command: `/key` opens key entry for the active provider and
135
+ `/key nvidia` for a named one. From `/connect`, pressing `enter` on a provider that
136
+ has no key opens the same entry; on a provider that has a key it activates it
137
+ immediately. Paste with `ctrl+v` (or your terminal's paste shortcut), or type the key,
138
+ then press `enter` - it is saved to
139
+ `<dataDir>/credentials.json` with owner-only permissions, applied to the current
140
+ process, and the provider is activated in the same step. Key entry shows the
141
+ provider's key page (`https://platform.openai.com/api-keys`, `https://build.nvidia.com/settings/api-keys`,
142
+ and so on) so you can create one without leaving the flow. Keys render as bullets and
143
+ never enter the transcript. You can still export the environment variable named in
144
+ the config (`OPENAI_API_KEY` and friends) before launching. Use `r` on a provider
145
+ detail only if you want a manual endpoint check; activation itself does no network
146
+ request, so switching providers is instant. No secret value is ever printed by the
147
+ harness.
148
+
149
+ NVIDIA NIM is included as a first-class provider at
150
+ `https://integrate.api.nvidia.com/v1` (`NVIDIA_API_KEY`). Its `GET /v1/models`
151
+ endpoint exposes the whole hosted catalog - Nemotron, Llama, DeepSeek, Mistral, GLM,
152
+ Kimi and more - so `/models` lists the live set when NVIDIA is active, with a curated
153
+ fallback of chat/coding/reasoning ids before the key exists.
154
+
155
+ Selections persist to `argus.config.json` (or the file named by `ARGUS_CONFIG`).
156
+
157
+ ### The agent
158
+
159
+ Plain text starts a grounded loop - no magic words. "check example.com", "is my site
160
+ safe?", "look at this repo for bugs", "what is broken here?" and "harden this" are all
161
+ assessment requests. The interpreter understands:
162
+
163
+ - a literal domain, URL, IP, `localhost:port` or existing path;
164
+ - "my site", "this app" or "the target" when the active scope has exactly one entry;
165
+ - "my code", "this repo", "the code base", "the source tree" or "this project" as the workspace;
166
+ - a whole-codebase request such as "scan the whole code base and rank the vulns" without a target;
167
+
168
+ Scope is **prompt-derived**. When you name a URL, host, IP, CIDR, `localhost:port` or
169
+ existing path, Argus records exactly that surface and starts. A generated scope grows as
170
+ you name new targets; a manual scope file is preserved in place and a prompt-derived
171
+ sibling is used instead. `passive` / `read-only` / `offline` suppress active requests;
172
+ `full` / `aggressive` / `intrusive` widen checks; `no external` disables registry, OSV
173
+ and subdomain intelligence. A local-only request is reviewed as the workspace without
174
+ pretending it authorized a network target. A pure factual question with no target is
175
+ answered as conversation, not a scan.
176
+
177
+ A greeting, a question or an ordinary coding request needs no authorization at all:
178
+ that turn runs **unscoped** - reading, search, edits and shell keep working - and no
179
+ scope file is written. Naming a target, or asking for an assessment, derives the scope
180
+ for that turn. An existing scope file is reused as-is and is never rewritten for an
181
+ ordinary prompt, so "Hi" can no longer mint or update an authorization record.
182
+
183
+ Unambiguous natural-language shortcuts run locally, without a model call: `check
184
+ example.com`, `scan my app`, `find bugs in this repo`, `show findings`, `dashboard`,
185
+ `preview src/core/http.ts`, `add nvidia api key`, `search the web for a CVE`,
186
+ `jwt <token>` and `http <url>`.
187
+ That keeps common actions fast and lets them work before a key is configured. Local
188
+ scans derive scope from the target and start immediately; anything that needs reasoning
189
+ goes to the model.
190
+
191
+ If no provider key is configured, a free-form prompt now explains how to add one with
192
+ `/key` and lists the no-key actions instead of failing silently. If the model endpoint
193
+ is unreachable and the prompt maps to a local action, Argus runs that action instead of
194
+ dead-ending.
195
+
196
+ If the target is genuinely ambiguous (for example two scope rules and "the target"),
197
+ the agent asks one short question instead of guessing. The model gets thirty-one real
198
+ tools: the assessment tools `scan_target`, `get_findings`, `list_runs`, `web_search`,
199
+ `read_url`, `http_request`, `http_history`, `browser`, `analyze_jwt` and
200
+ `security_tools`; the
201
+ workspace tools `list_dir`, `glob`, `read_file`, `preview`, `search_code`, `write_file`,
202
+ `edit_file`, `apply_patch` and `run_command`; the package tools `install_package` and
203
+ `install_tool`; plus `todo_write`, `spawn_subagents`, `report_finding`, `remember` and
204
+ `load_skill`. It can scan, read findings, inspect and
205
+ edit source, run tests, install a dependency, list missing security adapters, research
206
+ current advisories, probe an authorized endpoint, load a per-class playbook, review the
207
+ traffic it already sent, review a token and run a second pass
208
+ where coverage was weak. On Windows it can also drive the host's Kali Linux through
209
+ `wsl`, including installing missing Kali tooling, and it asks for approval first when
210
+ the policy requires it. Its system prompt carries an explicit attacker-mindset layer:
211
+ start from trust boundaries and trace input to sink, work in falsifiable hypotheses,
212
+ compare account/role/request pairs, chain primitives instead of listing issues, and
213
+ rank by exploitability rather than count.
214
+ There is no fixed tool-step budget. The loop continues until the model finalizes, the
215
+ operator aborts, or it repeats the same call or produces invalid output. Failed probes
216
+ are still retried (three extra attempts for agent scans).
217
+
218
+ The read-only `preview` tool is a UI surface as well: when the model previews a file or
219
+ the exact diff a write would create, the preview panel opens on top with line numbers,
220
+ add/remove colouring and a short staggered reveal, and the transcript keeps the same
221
+ path. `/preview <path> [--start N] [--end N]` opens the same panel by hand. Nothing in it
222
+ writes, it is confined to the workspace and the authorized local roots, and `esc` closes
223
+ it.
224
+
225
+ Workspace tools behave like a normal terminal agent. Reads and writes are confined to
226
+ the workspace plus directories named in the active prompt scope; `.git`, `.ssh`, `.aws`
227
+ and `.gnupg` are never writable. `run_command` executes non-interactive `bash -lc`
228
+ inside an authorized root with a 120-second cap and truncated output. Privilege
229
+ escalation and host-destructive commands are refused. A command that can delete or
230
+ rewrite (`rm -rf`, `git reset --hard`, `git clean -fd`, a publish or a piped remote
231
+ script) opens an approval prompt: `y` allows once, `a` allows that action for the rest
232
+ of the run (high-risk actions still ask), `n` or `esc` denies. Approval is interactive
233
+ in the TUI and defaults to deny when no operator is attached.
234
+
235
+ The default posture is prompt-independent. Scoped network work follows the authorization
236
+ derived from the target; local writes, shell commands and package installs ask the
237
+ operator. The agent does not guess from words like "fix", "scan" or "review". Only an
238
+ explicit operator directive changes the posture: `read-only`, `don't change anything`
239
+ or `explain only` refuses local changes; `just do it`, `without asking` or `no questions`
240
+ pre-approves them for that turn. The policy is printed as `- perm | ...` at the start of
241
+ the run, and hard blocks still win: privilege escalation and host-destructive commands
242
+ are never available.
243
+
244
+ `install_package` detects the repository's manager from its lockfile, validates the
245
+ package name and version, and runs an argv-only install (`bun add`, `npm install`,
246
+ `pnpm add`, `yarn add`, `uv add`, `pip`, `cargo add`, `go get`, `composer require` or
247
+ `bundle add`). It never interpolates a shell string. `security_tools` returns installed
248
+ and missing adapters with versions and install commands; `scan_target` automatically
249
+ uses the installed ones for the selected profile.
250
+
251
+ The plan is visible while the agent works. `todo_write` publishes `[done]`, `[active]`
252
+ and `[queued]` rows above the status bar, and the same plan is included in the final
253
+ answer. A write or edit returns a unified diff so the transcript shows exactly what
254
+ changed, and the approval prompt for a write, edit or patch shows the same diff before
255
+ the operator decides.
256
+
257
+ `preview` is the read-only counterpart: `{"path":"src/a.ts"}` returns numbered lines,
258
+ and `{"path":"src/a.ts","content":"..."}` or `{"path":"src/a.ts","patch":"..."}`
259
+ returns the exact unified diff the change would produce with an `unchanged` flag when
260
+ there is nothing to do. It never writes and needs no approval, so the agent can inspect
261
+ a change - or show a file to the operator - before touching anything.
262
+
263
+ Transcript status lines use terminal marker glyphs instead of bracketed tags: `::` for
264
+ steps, `>` for a running tool or subagent, `+` for done, `!` for failed or refused, `-`
265
+ for notes, and `$` for the `argus resume -session` command. Everything is plain ASCII:
266
+ no emoji and no Nerd Font glyphs.
267
+
268
+ `spawn_subagents` runs one to three focused agents in parallel with explicit roles:
269
+ `recon`, `auth`, `input`, `secrets`, `deps`, `config`, `web` and `verify`. Each child
270
+ gets a role checklist, its parent's scope, permission posture and tools, and no fixed
271
+ step budget of its own. A child that needs to write or install raises the same operator
272
+ approval prompt as the parent. Duplicate assignments are rejected,
273
+ nested subagents are refused, and overlapping findings are deduped by title, target,
274
+ category and CWE before merging.
275
+ A timed-out child returns its partial verified findings and a gap instead of stalling
276
+ the whole run.
277
+
278
+ The agent talks to the provider the way a normal coding harness does: the tool list is
279
+ sent as native function schemas, and `tool_calls` in the reply are executed directly,
280
+ several per turn, with the results returned as `role: "tool"` messages. A model that
281
+ answers in plain text with no tool call is finished — that text is the answer. If a
282
+ provider rejects function calling (400/404/405/415/422/501 mentioning tools or
283
+ functions), the client downgrades itself to the JSON protocol for the rest of the
284
+ session and retries, so text-only servers, Ollama builds and gateways keep working.
285
+
286
+ Every model response is checked. If a provider ignores the JSON protocol, the raw
287
+ reply is shown immediately, and after a second prose reply it is rendered as the
288
+ answer (with its finding ids still passed through the anti-hallucination gate) rather
289
+ than dead-ending. Transient provider failures (429 and 5xx) are retried with backoff,
290
+ and errors name the endpoint and explain whether the key, URL or model is the problem.
291
+
292
+ ### Web research
293
+
294
+ Agents can search the web without a separate API key. `web_search` uses DuckDuckGo's
295
+ public HTML results; set `ARGUS_SEARCH_URL` to a SearXNG-compatible JSON endpoint for
296
+ a private or rate-limit-free instance. `read_url` reads a page through DNS,
297
+ private-address, redirect, content-type and size checks, so it cannot be pointed at
298
+ localhost, link-local metadata or a private network. Search snippets and page text
299
+ are explicitly marked untrusted: they can guide research, but they can never create a
300
+ finding or override the scope. The operator shortcuts are `/web <query>` and
301
+ `/web read <url>`.
302
+
303
+ ### Manual request testing
304
+
305
+ `/http` sends one request you specify, without spending a model call. The URL you name
306
+ is the authorization; a manual `--scope` file is still honored and redirects cannot
307
+ leave the active scope. Examples:
308
+
309
+ ```text
310
+ /http https://staging.example.com/api/me --bearer $TOKEN
311
+ /http https://staging.example.com/api/items --method POST --json '{"name":"x"}' --header "X-Trace: 1"
312
+ ```
313
+
314
+ Supported options: `--method GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS`, `--header "Name: value"`,
315
+ `--bearer <token>`, `--json <body>`, `--form <body>`, `--body <text>` and `--scope <file>`.
316
+ `POST` runs with prompt-derived active mode; `PUT`, `PATCH` and `DELETE` additionally
317
+ need `intrusive` / `full` language or a manual scope that allows them. Port rules and
318
+ deny rules are enforced on every hop. The
319
+ response prints status, latency, size, redirect chain, headers, TLS facts and a 12-line
320
+ body preview. `set-cookie` values are hidden. The agent exposes the same capability as
321
+ its `http_request` tool.
322
+
323
+ `/jwt <token>` decodes a JWT locally with no network call or provider key. It reports
324
+ `alg`, expiry, remote-key headers (`jku`/`x5u`/`jwk`), missing `iss`/`aud`/`sub`, and
325
+ sensitive claims with their values redacted. It never verifies the signature - confirm
326
+ each flag against the live service. Add `--json` for a machine-readable analysis.
327
+
328
+ ### Adapter installation
329
+
330
+ `/install` builds a platform-aware plan for every missing adapter. It prefers user-level
331
+ managers (`go install`, `pip`, `gem`, `brew`, `winget`) and prints admin plans
332
+ (`apt-get`, `dnf`, `pacman`, `choco`) with the exact `sudo` command but never executes
333
+ them. `/install --yes` alone shows the plan and refuses a mass install; add `--all` or
334
+ name the tools. `/install subfinder nuclei --yes` runs only those available user-level
335
+ plans, sequentially, with a five-minute cap per plan. Afterward, run `/tools` to confirm
336
+ and restart the shell if `PATH` changed.
337
+
338
+ ### Memory
339
+
340
+ Argus maintains `AGENTS.md` in the working directory. It is created automatically on
341
+ first launch, read before every agent turn, and appended after a run that produced
342
+ scans or verified findings. The agent can also add a durable note with `remember`.
343
+ The file is capped and pruned, credentials are redacted before writing, and the
344
+ content is injected as untrusted data explicitly above the hard rules, so a memory
345
+ entry can never override scope or authorization. Use `/memory` to inspect it or edit
346
+ it by hand to give the agent durable project context; never put secrets in it.
347
+
348
+ Hallucination is blocked deterministically, not by asking the model nicely:
349
+
350
+ - a scan target must be literally named or unambiguously inferred from a single active scope entry / the workspace;
351
+ - every claimed vulnerability must cite a finding id returned by a tool this turn;
352
+ - unknown ids and invented citations in the prose are removed and counted;
353
+ - if the model cites no finding ids, all real tool-observed findings are still shown with a warning;
354
+ - tool output is the only evidence source; web pages are untrusted research, never evidence;
355
+ - failures are reported as gaps; a named-target assessment must use at least one real tool before it may answer;
356
+ - subagents inherit the same allowed targets, are deduped, and merge through the same gate.
357
+
358
+ Source excerpts returned by `read_file` and `search_code` are sent to the configured
359
+ model provider. Keep that in mind when auditing code you cannot share; API keys stay
360
+ local and are never included in prompts.
361
+
362
+ ### Ruthless execution
363
+
364
+ Assessments are meant to be thorough: `/scan` defaults to the `deep` profile, retries
365
+ each failed probe twice with backoff, prints every retry and failure instead of hiding
366
+ it, streams phases and findings into the transcript as they land (not just at the end),
367
+ and reports the run directory and generated reports. The agent has no fixed step
368
+ ceiling; it must produce a final report from the evidence it already has instead of
369
+ stopping without an answer. An optional per-run cap can still be supplied by callers,
370
+ and it warns before that cap is reached. Findings are ordered
371
+ by a transparent risk score (severity, confidence and high-impact category), and the
372
+ terminal output shows that score. After the findings, a `FIXES` plan groups findings
373
+ that share a remediation, orders them P1/P2/P3 and names the concrete change to make;
374
+ `/findings` and markdown reports include the same summary. Scope is derived from the
375
+ request: a named target is authorization enough. Passive-only, intrusive and external
376
+ behavior follow plain-English switches (`passive`, `full`, `aggressive`, `offline`),
377
+ and a manual scope file is still honored and never overwritten.
378
+
379
+ The active web pass covers more than headers and CORS. In scope, it reviews client
380
+ JavaScript bundles for committed credentials, path traversal and local file disclosure,
381
+ error-based SQL injection, command injection (arithmetic echo plus an intrusive timing
382
+ check), SSRF against cloud metadata endpoints, CRLF and Host header injection, login
383
+ forms that submit credentials over GET/HTTP or lack CSRF protection, and default
384
+ administrator credentials with login-throttling checks. Default-credential and timing
385
+ checks require both `allowIntrusive` and an explicit `full`/`aggressive` request; the
386
+ ordinary pass is read-only and sends one request per payload. The credential check is
387
+ limited to three well-known default pairs, and the throttling check uses only a
388
+ non-existent account name - there is no credential dictionary. Every detected issue
389
+ arrives with a concrete remediation, and the `FIXES` plan groups related issues into
390
+ P1/P2/P3 actions.
391
+
392
+ ### Context and resume
393
+
394
+ Each successful or aborted agent turn is written to a per-workspace session file under
395
+ `<dataDir>/sessions/`. Source reviews are also folded into `AGENTS.md`, so a fresh
396
+ process remembers what was already inspected. Every turn ends with its session id and
397
+ the exact command to continue: `$ argus resume -session ses-...`. That command
398
+ reopens the recorded workspace, reloads the last objective, targets, verified findings,
399
+ gaps and next steps, and asks the agent to continue from the captured state. Inside the
400
+ TUI, `continue`, `resume`, `keep going` or `carry on` do the same thing without leaving
401
+ the process. The agent is told to call `get_findings` or `list_runs` before repeating a
402
+ completed scan. Session notes are untrusted historical data, never evidence, and
403
+ credentials are redacted before they are written.
404
+
405
+ `/dashboard` visualises what has been found so far: a weighted risk gauge, severity
406
+ and category bars, confidence split, probe coverage per phase, the dependency graph,
407
+ outdated direct packages, a prioritized fix plan and the finding list. It uses the live
408
+ session findings, or loads the most recent stored run. Bars are plain ASCII gaugessuch as `[=========-----]` with percentage labels and `+` / `!` status markers; no emoji,
409
+ hashtags or Nerd Font glyphs are required.
410
+
411
+ Dependency inventory is built locally from `package.json`, `package-lock.json`,
412
+ `bun.lock`, `requirements.txt`, `go.mod`, `Cargo.toml`, Maven and Gradle manifests.
413
+ The dashboard shows direct and transitive node counts, graph edges and a compact tree.
414
+ When the scope allows external intelligence, direct npm packages are compared with the
415
+ npm registry and version drift becomes a low/info finding with an upgrade command. With
416
+ `allowExternal: false`, the graph is still built and the latest-version check is marked
417
+ skipped; Argus never bypasses the scope to reach the registry.
418
+
419
+ ## Scope
420
+
421
+ There is no setup step. Name the target in plain English and Argus derives the active
422
+ scope from that sentence:
423
+
424
+ ```text
425
+ check example.com
426
+ scan the whole code base
427
+ review C:\work\api for auth bugs
428
+ full assessment of staging.example.com and its subdomains
429
+ ```
430
+
431
+ Argus writes the derived scope to `scope.json` (or `scope.prompt.json` when an operator
432
+ file already exists) and updates it as new targets are named. `prompt` mode is active
433
+ by default; say `passive`, `read-only` or `offline` to suppress active requests, or
434
+ `full`, `aggressive` or `intrusive` to widen checks. `no external` disables registry,
435
+ OSV and subdomain intelligence.
436
+
437
+ A manual scope file is still supported and is never overwritten:
438
+
439
+ ```json
440
+ {
441
+ "version": 1,
442
+ "name": "Example authorized assessment",
443
+ "authorizedBy": "Security Team",
444
+ "authorizationRef": "TICKET-SEC-0001 / signed rules of engagement",
445
+ "expiresAt": "2026-12-31T00:00:00.000Z",
446
+ "allow": ["https://staging.example.com", "*.example.com", "10.0.0.0/24"],
447
+ "deny": ["https://example.com/account/delete"],
448
+ "maxRps": 20,
449
+ "allowedPorts": [80, 443],
450
+ "allowActive": true,
451
+ "allowIntrusive": false,
452
+ "allowExternal": false
453
+ }
454
+ ```
455
+
456
+ `--scope <file>` uses that file strictly. Without it, the target you name is the scope.
457
+ Cloud metadata endpoints remain hard-blocked and deny rules still win for a manual
458
+ scope; those are safety boundaries, not configuration puzzles.
459
+
460
+ ## CLI
461
+
462
+ ```bash
463
+ argus init [--force]
464
+ argus authorize [--accept <name> | --revoke | --status]
465
+ argus scope init [path]
466
+ argus scope validate scope.json [target]
467
+ argus scan <target> [--scope scope.json] [--profile deep] [--retries 2]
468
+ argus resume --session <id>
469
+ argus tools [--json]
470
+ argus runs [--json]
471
+ argus report <runId> [--format md|json|sarif|csv|html] [--stdout]
472
+ argus mcp serve | tools | call <server> <tool> [json]
473
+ argus connectors list | send <runId>
474
+ argus config show | path
475
+ ```
476
+
477
+ ## Assessment pipeline
478
+
479
+ Every run is reported through the same stages, and the state is derived from real
480
+ evidence rather than declared:
481
+
482
+ ```
483
+ vulnerability discovery -> candidate findings
484
+ dynamic validation -> validated reports (evidence + confirmed/likely)
485
+ exploitability -> confirmed impact (and LPEs when the surface is local)
486
+ patch generation -> candidate patches (never applied)
487
+ human researchers -> final review gate
488
+ ```
489
+
490
+ Candidate patches are concrete for validated repository findings: each carries the
491
+ file, the line and the exact change, with a one-line unified diff where the fix is
492
+ mechanical (for example `innerHTML -> textContent`, `privileged: true -> false`).
493
+ Rules that need design work are listed as file/line instructions instead of invented
494
+ code, and nothing is written to disk.
495
+
496
+ `/pipeline [runId] [--all]` prints the stages for a stored run, the dashboard shows
497
+ a compact summary, and the markdown/JSON/HTML/SARIF reports include the full block.
498
+ Findings that never reach validation stay labelled as candidates.
499
+
500
+ ### Skill packs and traffic history
501
+
502
+ Two capabilities come from the open-source agentic-pentest landscape (Strix, PentAGI,
503
+ CAI): a per-class playbook the agent loads on demand, and structured access to the
504
+ traffic it already produced.
505
+
506
+ `load_skill` returns a concrete, non-destructive playbook for one vulnerability class -
507
+ IDOR, SQL injection, XSS, SSRF, authentication, JWT, path traversal, command
508
+ injection, file upload and secrets - with the smallest proof, the differential that
509
+ confirms it, and what not to touch. The playbooks live in `src/core/skills.ts`; aliases
510
+ such as `bola`, `rce`, `lfi` and `blind sqli` resolve to the same pack. A skill is a
511
+ plan, never evidence: the finding still needs a tool-observed reproduction and
512
+ `report_finding`.
513
+
514
+ `browser` drives a Chrome, Edge or Chromium that is already installed on the
515
+ machine, over its DevTools protocol - no Playwright dependency and no browser
516
+ download. Actions are `open`/`navigate`, `snapshot` (title, visible text, links,
517
+ forms, buttons), `click`, `type` (with optional submit), `screenshot` (PNG inside
518
+ the workspace) and `evaluate`. This is the authenticated-UI path: log in, walk a
519
+ JS-rendered route, then record what only the browser can see. URLs go through the
520
+ same prompt-scope host check as `http_request`, `ARGUS_BROWSER` can point at a
521
+ specific executable, and the process is closed at the end of every turn. The
522
+ `playwright`, `chrome` and `headless` aliases resolve to the same tool.
523
+
524
+ Findings accept CVSS. `report_finding` takes `cvss` as a v3.1 vector or a 0-10
525
+ score; base, temporal (`E`/`RL`/`RC`) and environmental (`CR`/`IR`/`AR` and the
526
+ modified metrics) scores are computed locally (`src/core/cvss.ts`), stored on the
527
+ finding, shown in `get_findings`, and rendered in the markdown and HTML reports.
528
+ Severity, confidence and the risk score are unchanged - CVSS is an additional,
529
+ standardised number rather than a replacement.
530
+
531
+ `sandbox` runs offensive tooling in an opt-in Docker container instead of on the
532
+ host. It mounts the workspace at `/work`, turns the network off by default, adds
533
+ `NET_RAW`/`NET_ADMIN` and `host.docker.internal` only when a network is requested,
534
+ and bounds container logs and PIDs so a runaway tool cannot take the machine down.
535
+ `status` reports Docker and the image, `pull` is an explicit (confirmed) action,
536
+ and `exec` is permission-gated like `run_command`. The image defaults to
537
+ `kalilinux/kali-rolling` and can be set with `ARGUS_SANDBOX_IMAGE` or per call.
538
+
539
+ `wsl` uses a real WSL distribution when Argus runs on Windows, so the host's own
540
+ Kali toolchain is available without Docker. `status` reports WSL availability,
541
+ every installed distro and whether Kali is present; `exec` runs any command inside
542
+ the Kali distro (nmap, sqlmap, nuclei, ffuf, impacket, netexec, msfconsole, or an
543
+ `apt-get install` of something missing) as the operator's WSL user. Set `root: true`
544
+ when a command must install packages or run privileged tooling; then the command runs
545
+ as root inside the distro without a sudo prompt. There is no fixed tool list - the
546
+ agent installs whatever the mission needs and uses it. `install` runs
547
+ `wsl --install -d kali-linux` only after the install approval prompt. Kali is
548
+ detected by name (`kali-linux` first, then any distro containing "kali"), a distro
549
+ can be forced per call, and `ARGUS_WSL_DISTRO`/`ARGUS_WSL` override the default and
550
+ the `wsl.exe` path. WSL commands share the operator's privileges, so the prompt
551
+ scope - not a container - is what bounds which hosts they may touch.
552
+
553
+ While an agent turn runs, the right-hand `LIVE` panel shows a `TERMINAL` section with
554
+ stdout/stderr streamed from `run_command` and `wsl exec` as the process writes it, so
555
+ a long nmap, sqlmap or install run is visible instead of arriving only at the end.
556
+ The full output is still returned to the model. The tail keeps the last lines; the
557
+ complete text stays in the tool result and the transcript.
558
+
559
+ The agent gets a dedicated scratch folder, `argus-workspace/`, created inside the
560
+ project on the first turn. Its system prompt tells it to keep scripts, wordlists,
561
+ payloads and generated artifacts there in subfolders (`recon/`, `payloads/`,
562
+ `notes/`) instead of dropping scratch files at the project root. The folder is inside
563
+ the authorized workspace root, so reads, edits and command `cwd` work there without a
564
+ second permission, and it can be inspected or deleted like any project directory.
565
+ `ARGUS_SCRATCH_DIR` renames it.
566
+
567
+ `/vim [file]` and `/terminal [command]` open a real interactive PTY inside Argus,
568
+ rendered with OpenTUI's embedded terminal: vim (Kali's vim through WSL when it is
569
+ installed, otherwise the host's), less, msfconsole or a plain login shell all get
570
+ full keyboard input, colours and resizing. While the terminal is focused every key
571
+ belongs to the program, so Enter, Esc and arrows work exactly as they do in a normal
572
+ terminal; leave the program (`:q`, `exit`) to return to Argus. `ctrl+shift+q` closes
573
+ the panel as an emergency escape.
574
+
575
+ `proxy` starts a loopback intercepting proxy (`http://127.0.0.1:<port>`). Point
576
+ curl, a scanner or the browser at it, then read `history`, and with `intercept`
577
+ on held requests wait for `allow` (which can rewrite the URL, headers or body) or
578
+ `drop`. Plain HTTP is fully inspectable; HTTPS is tunnelled with `CONNECT` and
579
+ recorded as metadata only - Argus does not install a CA or decrypt TLS. The
580
+ proxy stops with the turn, and captures stay in memory.
581
+
582
+ `vuln_lookup` researches packages and toolchains: it queries OSV.dev (no key) for a
583
+ package/version in npm, PyPI, crates.io, Go, Maven, NuGet, Packagist, RubyGems,
584
+ Hex, Pub or SwiftURL, and returns published advisories with CVE aliases, CVSS
585
+ vectors and fixed versions. `packagesFromLockfile` extracts `package@version`
586
+ pairs so a lockfile audit is one pass, not guesswork from model memory.
587
+
588
+ `export_report` writes a report for the current (or a named) run. `format:
589
+ "research"` is the narrative deliverable: executive summary, engagement facts,
590
+ validation state, supply chain, per-finding evidence and CVSS, coverage limits and
591
+ a finding index. It is also available as `argus report <runId> --format research`
592
+ and through MCP.
593
+
594
+ ### Authorized-use agreement and operator mode
595
+
596
+ Before the first network assessment, the agent shows a one-time agreement: written
597
+ permission for every target you name, no third parties or cloud metadata, no
598
+ destructive or persistence actions, and responsibility for what runs. Accept it
599
+ once with `/authorize accept <your name>` in the TUI or
600
+ `argus authorize --accept <your name>` from a shell; `--status` shows the record
601
+ and `--revoke` removes it. Acceptance is stored locally in
602
+ `<dataDir>/authorization.json` with a timestamp and turns on operator mode: local
603
+ writes, shell, installs, network and subagents run without confirmation prompts,
604
+ and the agent is told the agreement is in force. It does **not** widen scope — a
605
+ host you did not name is still refused, and read-only instructions still win.
606
+
607
+ Aggression is a setting, not a mystery: saying "do anything", "full hands",
608
+ "ruthless" or "no confirmation" switches the run to the auto approval policy
609
+ (write/shell/install/network/agents), while the prompt-scope guard and deny rules
610
+ still decide which hosts are reachable. The agent is told to chain tools, install
611
+ what it needs, run offensive tooling in the sandbox or WSL, push confirmed weaknesses
612
+ to real impact, and finish with the research report. It works toward the goal rather
613
+ than a checklist: scans are only the map, and it keeps going until the goal is reached
614
+ or a concrete blocker is named.
615
+
616
+ Every tool call is recorded automatically in an attack journal (tool, target or
617
+ package, and whether it worked). Confirmed breaks are pushed to `report_finding` the
618
+ moment they happen, and the research report appends a journal section listing tools
619
+ used, surfaces/packages touched and every attempt with its result - so the deliverable
620
+ names exactly what made the target break, not just the final finding count.
621
+
622
+ `recall` is local vector memory: verified notes and recorded findings are
623
+ indexed with hashed TF-IDF vectors and searched by cosine similarity. It is
624
+ lexical, not an embedding service, and a hit is a lead from earlier work rather
625
+ than new evidence.
626
+
627
+ `http_history` is the session's traffic record. Every `http_request` result is stored
628
+ with method, URL, host, path, status, size, duration and content type, and the tool
629
+ serves either a filtered list (host, status, method, substring, limit) or a per-host
630
+ sitemap of observed endpoints. It prevents blind re-firing of requests and gives the
631
+ model the endpoint map that a proxy-backed agent gets from its intercepting proxy. It
632
+ is in-memory for the session, not a capture file.
633
+
634
+ The agent drives the same pipeline. It works the kill chain
635
+ (map -> hypothesize -> probe -> validate -> exploit -> patch), and `report_finding`
636
+ records a reproduced vulnerability on the current run so it gets a real finding id,
637
+ appears in `get_findings`, `/pipeline` and the reports, and can be cited in the final
638
+ answer. After a scan the loop enforces one continuation when candidates are still
639
+ unvalidated or no exploit path was attempted, instead of accepting the first scan
640
+ as the answer.
641
+
642
+ ## Output
643
+
644
+ Each run is stored under `<dataDir>/runs/<runId>/` with `run.json`, `events.jsonl`,
645
+ `findings.jsonl` and reports. `dataDir` defaults to `~/.argus` and can be overridden
646
+ with `argus.config.json` or `ARGUS_DATA_DIR`.