Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/badges/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions .github/workflows/coverage-badge.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
name: 📊 Coverage Badge

on:
workflow_run:
workflows: ["🐍 Python Tests"]
types:
- completed
branches:
- main

permissions:
contents: write

jobs:
badge:
name: 📊 Generate coverage badge
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success'

steps:
- name: Set up git repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1

- name: Download coverage artifact
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: coverage-report
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Python
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.12"

- name: Generate coverage badge
run: |
pip install "genbadge[coverage]"
mkdir -p .github/badges
genbadge coverage -i coverage.xml -o .github/badges/coverage.svg

- name: Commit badge
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add .github/badges/coverage.svg
git diff --staged --quiet || git commit -m "Update coverage badge [skip ci]"
git push
10 changes: 9 additions & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,12 @@ jobs:

- name: Run tests with coverage
run: |
pytest --cov=src --cov-report=term-missing || [ $? -eq 5 ] # Allow workflow to pass when no tests exist
pytest --cov=src --cov-report=xml --cov-report=term-missing || [ $? -eq 5 ] # Allow workflow to pass when no tests exist

- name: Upload coverage report
if: github.ref == 'refs/heads/main' && github.event_name == 'push' && matrix.python-version == '3.12'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: coverage-report
path: coverage.xml
retention-days: 1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ test-alerts/
TODO
.tool-versions
/reports/
/logs/

# Custom parquet storage
*.parquet
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ repos:
rev: v1.19.1
hooks:
- id: mypy
exclude: ^tests/
additional_dependencies:
- "types-PyYAML"
- "types-requests"
Expand Down
37 changes: 33 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Ares - Autonomous Security Operations Agent

<!-- BEGIN_AUTO_BADGES -->
<div align="center">

[![Pre-Commit](https://github.com/dreadnode/python-template/actions/workflows/pre-commit.yaml/badge.svg)](https://github.com/dreadnode/python-template/actions/workflows/pre-commit.yaml)
[![Renovate](https://github.com/dreadnode/python-template/actions/workflows/renovate.yaml/badge.svg)](https://github.com/dreadnode/python-template/actions/workflows/renovate.yaml)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

</div>
<!-- END_AUTO_BADGES -->

[![Pre-Commit](https://github.com/dreadnode/ares/actions/workflows/pre-commit.yaml/badge.svg)](https://github.com/dreadnode/ares/actions/workflows/pre-commit.yaml)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Python](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
Expand Down Expand Up @@ -161,7 +171,7 @@ uv run python -m ares \
--args.model claude-sonnet-4-20250514 \
--args.grafana-url https://grafana.example.com \
--args.poll-interval 30 \
--args.max-steps 150 \
--args.max-steps 30 \
--args.report-dir ./reports

# Run once and exit (process current alerts only)
Expand All @@ -176,7 +186,7 @@ Investigate a specific alert by providing it as JSON:
uv run python -m ares investigate-alert test-alerts/example-alert.json \
--args.model claude-sonnet-4-20250514 \
--args.grafana-url https://grafana.example.com \
--args.max-steps 150
--args.max-steps 30
```

#### Red Team - Penetration Testing
Expand Down Expand Up @@ -212,7 +222,7 @@ task ares:red: TARGET=192.168.1.100
# Or via CLI
uv run python -m ares red-team 192.168.1.100 \
--args.model claude-sonnet-4-20250514 \
--args.max-steps 150 \
--args.max-steps 30 \
--args.report-dir ./reports
```

Expand All @@ -229,8 +239,27 @@ bloodhound-python).
| `--args.model` | `claude-sonnet-4-20250514` | LLM model to use |
| `--args.grafana-url` | `https://grafana.dev.plundr.ai` | Grafana URL for alerts and MCP |
| `--args.poll-interval` | `30` | Seconds between alert polls |
| `--args.max-steps` | `150` | Maximum agent steps per investigation |
| `--args.max-steps` | `30` | Maximum LLM round trips per investigation |
| `--args.report-dir` | `./reports` | Directory for markdown reports |
| `--args.once` | `false` | Process current alerts once and exit |

**Stop Conditions:**

The agent stops when **any** of these conditions are met:

- `complete_investigation()` tool is called (normal completion)
- `escalate_investigation()` tool is called (escalation to human)
- 5 Loki/Prometheus queries executed
- 20 total tool calls made (prevents infinite loops)
- `max_steps` LLM round trips reached

**Timeout Behavior:**

The agent has multiple timeout layers:

- Hard timeout: `max_steps × 60 seconds` (1 minute per step)
- Watchdog thread: Force-exits if timeout exceeded
- When using Taskfile, defaults are 15 steps (once mode) or 50 steps (polling mode)

**Dreadnode Platform Arguments (`--dn-args.*`):**

Expand Down
Loading