Skip to content

[WIP] Add attack configuration into local dashboard - #532

Merged
Nicola Franco (franconicola) merged 5 commits into
mainfrom
claude/add-attack-configuration-local-dashboard
Jul 26, 2026
Merged

[WIP] Add attack configuration into local dashboard#532
Nicola Franco (franconicola) merged 5 commits into
mainfrom
claude/add-attack-configuration-local-dashboard

Conversation

@Claude

@Claude Claude AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Thanks for asking me to work on this. I will get started on it and keep this PR's description up to date as I form a plan and make progress.


This section details on the original issue you should resolve

<issue_title>Add Attack configuration into Local Dashboard</issue_title>
<issue_description>## Goal

A visual, drag-and-drop way to assemble an attack (Target, Dataset/Goals, Attack technique + params, optional Guardrails, optional chained fallback steps) from inside the Local Dashboard, run it, and see results in the same dashboard — without dropping to the CLI first.

Current state

The building blocks already exist and are well-factored on the CLI side — this should be a UI over them, not a new execution engine:

  • Attack catalog: cli/commands/attack/catalog.py lists all 14 supported attack_types (AdvPrefix, PAIR, TAP, AutoDAN-Turbo, BoN, CipherChat, h4rm3l, PAP, MML, FC/tFC-Attack, FlipAttack, Static Template, Baseline) with label + description — this is directly usable as the palette of draggable "attack" blocks.
  • Shared config shape: _build_attack_config in cli/commands/attack/config.py builds a plain dict — {"attack_type": ..., "goals": [...] | "dataset": {...}, ...} — that already separates the Dataset/Goals block from the technique block.
  • Target + guardrail blocks: cli/commands/attack/options.py defines the common fields every attack needs: target (agent-name/agent-type/endpoint), and optional before/after guardrails (identifier/agent_type/endpoint each, via _build_guardrail_config).
  • Dataset providers: hackagent/datasets/providers/huggingface.py, url_json.py, file.py, plus presets.py/intents.py — these are the concrete "Dataset" block variants.
  • Chaining already has fallback-ladder semanticshackagent eval chain (cli/commands/attack/chain.py) and HackAgent.hack_chain() (agent.py:327) run an ordered list of attack_configs against a shared goal pool: a goal that succeeds is dropped, a goal that's mitigated escalates to the next attack in the list. This is exactly the semantic a canvas's node connections should express — connecting attack-block A → attack-block B on the canvas is "B is A's fallback," not a generic graph. The board doesn't need to invent new orchestration; it needs to produce the attacks: [...] list hack_chain() already consumes.
  • Progress streaming already exists, just not wired to the dashboard: HackAgent.hack()/hack_chain() take a _tui_event_bus parameter, threaded through attacks/orchestrator.py and every technique's base.py/generation.py, currently consumed only by cli/tui/views/attacks/executor.py. Live progress in the new dashboard panel should plug into this same bus rather than poll.
  • What's missing today: the dashboard (server/dashboard/) is currently read-only — its mixins (_runs_mixin.py, _reports_mixin.py, etc.) only load and render runs/results that already exist in self.backend. There is no code path anywhere under server/dashboard/ that constructs a HackAgent or calls .hack()/.hack_chain(). This issue is the first time the dashboard becomes a place attacks are launched, not just reviewed.

Proposed design

  1. New DashboardAttackBuilderMixin (server/dashboard/_attack_builder_mixin.py), following the existing mixin-per-concern pattern wired into DashboardPage in _page.py, with its own nav entry alongside dashboard/runs/history/reports.
  2. Canvas nodes map 1:1 to existing config sections, not to something new:
    • Target block → agent-name/agent-type/endpoint
    • Dataset/Goals block → goals list or dataset section (provider + params)
    • Attack block(s) → one per ATTACK_CATALOG entry, palette generated from that dict so a new CLI-supported technique automatically appears on the canvas with no dashboard code change
    • Guardrail block(s) → optional before/after, same three fields as the CLI
    • Connecting two attack blocks in sequence → append to an attacks: [...] chain list (fallback-ladder order), matching --config-file's documented chain shape in chain.py
  3. Submit → serialize to the exact same dict shape _build_attack_config/chain already build, then call HackAgent(...).hack(attack_config) or .hack_chain(attacks=[...]) in-process from the NiceGUI server (this is what "local mode" already means for hackagent web — see cli/commands/web.py), on a background task so the event loop isn't blocked, streaming progress into the panel via _tui_event_bus.
  4. Draft persistence: canvas layouts (node positions + the underlying config, before it's run) need to be saved/reopened. Reuse the active StorageBackend (server/storage/local.py for SQLite, server/storage/remote.py for the hosted API — see below) rather than inventing a separate file format, so drafts round-trip the same way runs do.
  5. Results: once a run is submitted this way, it's written through the same StorageBackend the read-only mixins already query — no new results-rendering code needed, the existing Runs/History/Reports panels pick it up automatically.

Open question to resolve before implementation: what does "local vs remote mode" mean here

The issue text says results should save "accordingly" to local or remote mode and still be visible in the local dashboard — but today those two things are coupled differently than that implies:

  • server/storage/ already has both a local.py (SQLite) and remote.py (hosted API) StorageBackend, so a HackAgent instance genuinely can persist either way.
  • But hackagent web itself (cli/commands/web.py) currently treats "remote mode" (API key configured) as "don't run the local NiceGUI dashboard at all — open app.hackagent.dev instead." So there is currently no scenario where the local dashboard is running and writing through the remote backend.

Before building the canvas, decide: (a) should hackagent web gain a mode where it still serves the local UI but a configured API key routes attack runs through the remote StorageBackend (diverging from today's all-or-nothing redirect), or (b) is "remote mode" out of scope for this issue and the builder only ever targets the local SQLite backend, with the cloud dashboard (a separate codebase, hackagent-webapp) getting its own equivalent builder later. This changes where the "run" button's write path goes.

Acceptance criteria

  • New attack-builder panel in the dashboard, palette driven from ATTACK_CATALOG (no hardcoded technique list to keep in sync)
  • Target, Dataset/Goals, Guardrail blocks with the same fields/validation as the CLI options
  • Chaining two-or-more attack blocks produces a hack_chain()-compatible attacks list; single block produces a plain hack() call
  • Submitted runs execute in-process against the active StorageBackend without blocking the NiceGUI event loop, with live progress via _tui_event_bus
  • Draft canvases can be saved and reopened
  • Resulting runs appear in the existing Runs/History/Reports panels with no special-casing
  • Local-vs-remote question above is explicitly decided (not left implicit) and documented in the PR description
    </issue_description>
  • Closes Add Attack configuration into Local Dashboard #445

Comments on the Issue (you are @claude[agent] in this section)

@franconicola

Copy link
Copy Markdown
Member

@claude[agent] resolve the merge conflicts in this pull request

@Claude

Claude AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

@claude[agent] resolve the merge conflicts in this pull request

Merge conflicts resolved in f957eee. Only _constants.py conflicted — main removed _VIEW_LABELS (no remaining usages), so I dropped it rather than keeping my builder entry. Main's header→sidebar nav rework merged cleanly with the builder nav/panel wiring; I updated test_layout_mixin.py to expect the builder nav button. ruff check/format clean, 2832 unit tests pass.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.openai.com
    • Triggering command: /home/REDACTED/work/hackagent/hackagent/.venv/bin/pytest pytest tests/unit -q --deselect tests/unit/datasets/test_dataset_integrity.py (dns block)
    • Triggering command: /home/REDACTED/work/hackagent/hackagent/.venv/bin/pytest pytest tests/unit -q --deselect tests/unit/datasets/test_dataset_integrity.py _TOKEN&#34;; }; f e/git /usr/bin/base64 --abbrev-ref HEAD /usr/bin/base64 base64 exit�� (dns block)
  • gitlab.com
    • Triggering command: /home/REDACTED/work/hackagent/hackagent/.venv/bin/pytest pytest tests/unit -q -x /hom�� noreply.github.cNVM_DIR=/home/REDACTED/.nvm git /usr/bin/base64 --global credential.usern-d [i] !~ /^[0-9]&#43;$/) exit(2); --abbrev-ref base64 ixin�� (dns block)
    • Triggering command: /home/REDACTED/work/hackagent/hackagent/.venv/bin/pytest pytest tests/unit -q --deselect tests/unit/datasets/test_dataset_integrity.py (dns block)
    • Triggering command: /home/REDACTED/work/hackagent/hackagent/.venv/bin/pytest pytest tests/unit -q --deselect tests/unit/datasets/test_dataset_integrity.py _TOKEN&#34;; }; f e/git /usr/bin/base64 --abbrev-ref HEAD /usr/bin/base64 base64 exit�� (dns block)
  • huggingface.co
    • Triggering command: /home/REDACTED/work/hackagent/hackagent/.venv/bin/pytest pytest tests/unit -q -x /hom�� noreply.github.cNVM_DIR=/home/REDACTED/.nvm git /usr/bin/base64 --global credential.usern-d [i] !~ /^[0-9]&#43;$/) exit(2); --abbrev-ref base64 ixin�� (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

@franconicola
Nicola Franco (franconicola) force-pushed the claude/add-attack-configuration-local-dashboard branch from f957eee to b27a11e Compare July 26, 2026 20:43
@franconicola
Nicola Franco (franconicola) marked this pull request as ready for review July 26, 2026 21:05
Copilot AI review requested due to automatic review settings July 26, 2026 21:05
@franconicola
Nicola Franco (franconicola) merged commit e1be269 into main Jul 26, 2026
44 of 45 checks passed
@franconicola
Nicola Franco (franconicola) deleted the claude/add-attack-configuration-local-dashboard branch July 26, 2026 21:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an initial “Attack Builder” panel to the local NiceGUI dashboard so users can assemble an attack configuration, persist drafts, and launch runs from the UI while reusing existing CLI/HackAgent config shapes and execution paths.

Changes:

  • Introduces a new dashboard view (“builder”) with UI for target/goals/attack-chain/guardrails, plus in-process run submission and progress log streaming.
  • Adds a pure serializer/validator (_builder_config.py) with unit tests, and persists builder drafts in the local SQLite backend.
  • Extends HackAgent to accept a caller-provided StorageBackend so the dashboard can reuse its already-open backend.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/unit/server/storage/test_local_backend.py Adds roundtrip tests for saving/listing/getting/deleting builder drafts in the local backend.
tests/unit/server/dashboard/test_layout_mixin.py Updates nav/view expectations to include the new “builder” view and button label.
tests/unit/server/dashboard/test_builder_config.py New unit tests for canvas serialization/validation and attack palette coverage.
hackagent/server/storage/local.py Adds attack_builder_drafts table and CRUD methods for persisting draft canvases.
hackagent/server/dashboard/_page.py Wires in builder mixin and stores builder panel state (canvas, draft id, log queue, widgets).
hackagent/server/dashboard/_layout_mixin.py Adds the “Attack Builder” nav item and panel construction hook.
hackagent/server/dashboard/_data_mixin.py Refreshes builder drafts when navigating to the builder view.
hackagent/server/dashboard/_builder_config.py New: pure canvas → run payload translation with validation and summary helpers.
hackagent/server/dashboard/_attack_builder_mixin.py New: NiceGUI builder panel UI, draft persistence actions, and background run execution with log streaming.
hackagent/agent.py Adds optional backend parameter to reuse an existing StorageBackend (e.g., from the dashboard).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +112 to +127
def _build_dataset(block: Dict[str, Any]) -> Dict[str, Any]:
"""Drop empty fields from a dataset block so provider defaults apply."""
dataset: Dict[str, Any] = {}
for key in ("preset", "provider", "path", "goal_field", "split", "url", "name"):
value = _clean(block.get(key))
if value:
dataset[key] = value
limit = block.get("limit")
if limit not in (None, ""):
try:
dataset["limit"] = int(limit)
except (TypeError, ValueError) as exc:
raise CanvasValidationError(
"Dataset limit must be a whole number."
) from exc
return dataset
Comment on lines +340 to +350
def _builder_apply_canvas(self, canvas: Dict[str, Any]) -> None:
"""Replace the live canvas and rebuild the whole panel from it."""
merged = new_canvas()
merged.update({k: v for k, v in canvas.items() if v is not None})
self._builder_canvas = merged
panel = self.all_panels.get("builder")
if panel is None:
return
panel.clear()
self._build_builder_panel(panel)

Comment on lines +353 to +355
def _builder_backend_supports_drafts(self) -> bool:
return hasattr(self.backend, "save_builder_draft")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Attack configuration into Local Dashboard

3 participants