[WIP] Add attack configuration into local dashboard - #532
Conversation
|
@claude[agent] resolve the merge conflicts in this pull request |
Merge conflicts resolved in f957eee. Only 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:
If you need me to access, download, or install something from one of these locations, you can either:
|
Agent-Logs-Url: https://github.com/AISecurityLab/hackagent/sessions/889278b6-d230-4e1b-aed7-a9b4da3661a8 Co-authored-by: franconicola <51865029+franconicola@users.noreply.github.com>
Agent-Logs-Url: https://github.com/AISecurityLab/hackagent/sessions/889278b6-d230-4e1b-aed7-a9b4da3661a8 Co-authored-by: franconicola <51865029+franconicola@users.noreply.github.com>
Agent-Logs-Url: https://github.com/AISecurityLab/hackagent/sessions/889278b6-d230-4e1b-aed7-a9b4da3661a8 Co-authored-by: franconicola <51865029+franconicola@users.noreply.github.com>
Co-authored-by: franconicola <51865029+franconicola@users.noreply.github.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
f957eee to
b27a11e
Compare
There was a problem hiding this comment.
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
HackAgentto accept a caller-providedStorageBackendso 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.
| 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 |
| 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) | ||
|
|
| def _builder_backend_supports_drafts(self) -> bool: | ||
| return hasattr(self.backend, "save_builder_draft") | ||
|
|
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.