Skip to content

Repository files navigation

/EN

Argus - CVE Vulnerability Management Platform

Argus is a web platform designed for SOC analysts who want to centralize and track CVEs detected across their managed infrastructure. The core idea: receive scan reports directly via API, visualize by host, open remediation tickets, and get automatically notified when something critical comes out.

No external cloud dependency, no third-party account, everything runs on your premises.

Argus Dashboard

Features

  • Multi-scanner ingestion - Vuls, Trivy, OSV-Scanner, CVE Binary Tool, Grype/Syft, Clair v4, Wazuh, OpenVAS/GVM
  • Multi-tenant - CVE isolation per tenant (client), global workspace for admins, fine-grained per-user access control
  • Differential scan tracking - a new scan replaces the previous state for that host only: fixed CVEs automatically move to "resolved", no duplicates. The diff is strictly scoped per host, never per tenant, so a targeted rescan of one or a few machines (e.g. via AWX) never touches the others
  • Ticket <-> CVE synchronization - resolving a ticket marks the vuln as handled; a vuln that disappears on the next scan automatically closes the related ticket. Matching is keyed on (hostname, tenant), not on the scanner that reported it, so a CVE found by Vuls and absent from a later Trivy scan of the same host closes its ticket just as well - as long as the same ?host= is used across scanners (see API)
  • Real-time dashboard - active CVEs per host, CVSS scores, KEV flag (CISA), EPSS
  • Threat Intelligence - automatic enrichment via CISA KEV (active exploitation), EPSS (30-day exploit probability) and NVD; combined risk score (CVSS x EPSS, KEV bonus) that prioritizes tickets and the dashboard
  • Asset criticality - 1->5 rating per host ("crown jewel"), adjustable on the host page - informational for now: not yet reflected in the stored risk score (see Threat Intelligence)
  • Remediation SLA - deadlines by severity (Critical 7d, High 30d...), overdue table
  • Executive report - printable view / PDF export per tenant (KEV, top-risk, KPIs)
  • CVE search - full-text search on CVE IDs, packages, descriptions
  • Remediation tickets - creation, assignment, statuses (open -> in progress -> resolved), Markdown comments with images (upload + copy-paste)
  • AWX/Ansible remediation - launches an AWX Job Template on a specific host (--limit) directly from its page or a ticket
  • Webhooks & notifications - HMAC-SHA256 signed JSON payload, or formatted Slack / Discord / Microsoft Teams messages
  • 3 user roles - admin, analyst, viewer (+ per-tenant role)
  • Two-factor authentication (2FA TOTP) - Google Authenticator, Aegis, 1Password... per account
  • Audit log - all actions tracked with IP and timestamp
  • Keycloak SSO - optional, via OIDC
  • Security - strict CSP with nonce (no unsafe-inline for scripts), Argon2id, CSRF, rate-limiting
  • Dark / light mode
  • Bilingual interface - French / English, one-click toggle (language icon), saved in session
  • Built-in documentation - step-by-step guides in the app (/docs): scanner configuration, security, webhooks (+ Zulip/ntfy recipes), Keycloak, deployment...

Technical Stack

Component Technology
Backend Python 3.11 + Flask 3
ORM SQLAlchemy 2
Database SQLite (dev) or MariaDB (prod)
Authentication Flask-Login + Argon2id + optional Keycloak OIDC
API Security Bearer keys (SHA-256 in DB), HMAC-SHA256 signatures
Frontend Vanilla HTML/CSS/JS - no SPA framework (2 vendored libraries: Chart.js for graphs, marked.js for Markdown rendering)

Quick Start (Docker)

The recommended method. No dependencies to install other than Docker.

git clone https://github.com/your-org/argus.git
cd argus

cp .env.example .env
# Edit .env and set a real SECRET_KEY:
#   python -c "import secrets; print(secrets.token_hex(32))"

docker compose up -d --build

Open http://localhost:5000 and complete the setup wizard to create the first admin account.

Persistence - the SQLite database and images uploaded in tickets are stored in the Docker volume argus_data (mounted on /data). They survive docker compose down / rebuilds. The database is created and migrated automatically at container startup.

Database - SQLite by default. For MariaDB/MySQL, uncomment the db service in docker-compose.yml and set DATABASE_URL in .env (see Configuration).

Update:

git pull
docker compose up -d --build      # schema migrations apply automatically

Manual Installation (without Docker)

Python 3.11+ required.

git clone https://github.com/your-org/argus.git
cd argus

python3 -m venv .venv
source .venv/bin/activate         # Windows: .venv\Scripts\activate

pip install -r requirements.txt

cp .env.example .env              # Edit variables, especially SECRET_KEY

# Development / first launch (creates and migrates the database automatically):
python app.py

# Production (gunicorn - initialize the database first):
python -c "import app; app.init_db()"
gunicorn -w 4 -b 0.0.0.0:5000 --timeout 120 app:app

In production, always run app.init_db() once before gunicorn: gunicorn workers do not execute the startup block of app.py. (The Docker image handles this automatically.)

Images uploaded in tickets are written to static/uploads/ - remember to back up this folder and the argus.db file.

Quick Launch for Testing (without Docker)

The fastest way to try Argus on a machine that has only Python 3.11+ (no Docker). A temporary SECRET_KEY is generated on the fly - nothing else to configure.

Linux / macOS

git clone https://github.com/your-org/argus.git && cd argus
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
python app.py

Windows (PowerShell)

git clone https://github.com/your-org/argus.git; cd argus
python -m venv .venv; .\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
$env:SECRET_KEY = python -c "import secrets; print(secrets.token_hex(32))"
python app.py

Then open http://localhost:5000 and complete the setup wizard to create the first admin account. The SQLite database (argus.db) is created and migrated automatically on first launch.

This mode is reserved for local testing (Flask dev server, ephemeral key). In production, set a persistent SECRET_KEY and use gunicorn as described above.

To load demo data (6 hosts, 11 real CVEs), create an API key in Administration -> API Keys, then:

python argus_test_data.py --url http://localhost:5000 --key argus_YOUR_KEY

Configuration

Everything is configured through environment variables (or the .env file):

Variable Default Description
SECRET_KEY required Flask signing key - generate with python3 -c "import secrets; print(secrets.token_hex(32))"
DATABASE_URL local SQLite MariaDB format: mysql+pymysql://user:pass@host/db
HTTPS false Set to true to enable Secure cookies and HSTS
FLASK_DEBUG false Never enable in production
PORT 5000 Listening port
KEYCLOAK_ENABLED false Enables Keycloak OIDC SSO
KEYCLOAK_SERVER_URL - E.g.: https://keycloak.example.com
KEYCLOAK_REALM argus Keycloak realm name
KEYCLOAK_CLIENT_ID argus OIDC client ID
KEYCLOAK_CLIENT_SECRET - OIDC client secret

Things to Change Before First Startup

Argus is delivered with no real data, accounts, or secrets. Before deploying, check this list:

  • SECRET_KEY - set a real random value (python -c "import secrets; print(secrets.token_hex(32))"). The Docker image default (change_me) and the .env.example placeholder must be replaced.
  • First admin account - created via the wizard on first launch (/setup). Choose a strong password; there are no default credentials.
  • DATABASE_URL - empty for SQLite (dev), or point to MariaDB/MySQL in production.
  • HTTPS=true - enable behind TLS so that session cookies are Secure and HSTS is sent.
  • FLASK_DEBUG - leave false in production.
  • Keycloak SSO (optional) - only fill in the KEYCLOAK_* variables if you use it; otherwise leave KEYCLOAK_ENABLED=false.
  • Demo data - the argus_test_data.py script and tests/fixtures/ contain fictional clients (Acme Corp, Globex...) and sample hosts. Do not load them on a production instance.
  • argus.db - the local database is git-ignored; never commit it. A new database is created on first startup.
  • Customization (optional) - adjust the app name / login subtitle in templates/login.html and templates/base.html.

Production Deployment

server {
    listen 443 ssl http2;
    server_name argus.yourdomain.com;

    ssl_certificate     /etc/ssl/argus.crt;
    ssl_certificate_key /etc/ssl/argus.key;
    ssl_protocols       TLSv1.3;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
    }
}

API

All write routes require an API key generated in Administration -> API Keys.

Authorization: Bearer argus_<your-key>
Content-Type: application/json

Rate limit: 200 requests/minute per IP on all /api/v1/import/* and /api/v1/vuls/report endpoints.

Healthcheck

GET /api/v1/status

Returns {"status": "ok", "app": "Argus", "version": "1.0.0"} - no authentication required.

Import Endpoints

Each endpoint accepts the native JSON from the corresponding scanner, plus two optional query string parameters:

Parameter Required Description
?host= Sometimes Hostname. Required for OSV-Scanner, CVE Binary Tool, and OpenVAS when it cannot be detected automatically.
?client= No Client name to group hosts. Default: Unknown.

Cross-scanner correlation - the resolved/auto-close diff (see Differential scan tracking) is keyed on the exact pair (hostname, tenant), never on the scanner that produced the report. If the same machine is scanned by several tools (e.g. Vuls, then later Trivy), always pass the same ?host=<name> explicitly on every import. Otherwise, scanners that auto-detect a name from scan metadata (Trivy's ArtifactName, Grype's source.target.userInput, Clair's manifest_hash...) will create a separate host and correlation silently breaks.

Common response for all imports:

{ "ok": true, "host": "srv-01", "cves_processed": 14, "new_cves": 3 }

Vuls (native)

POST /api/v1/vuls/report

curl -X POST "https://argus.yourdomain.com/api/v1/vuls/report" \
  -H "Authorization: Bearer argus_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d @/var/vuls/results/current/srv-01.json

Trivy

POST /api/v1/import/trivy

# Scan + send in one command
trivy image --format json --quiet ubuntu:22.04 | \
  curl -X POST "https://argus.yourdomain.com/api/v1/import/trivy?client=Prod" \
    -H "Authorization: Bearer argus_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d @-

The hostname is extracted from ArtifactName. Override with ?host=.

OSV-Scanner

?host= required - OSV-Scanner does not include the machine name in its report.

POST /api/v1/import/osv

osv-scanner --format json --output report.json /path/to/project

curl -X POST "https://argus.yourdomain.com/api/v1/import/osv?host=my-server&client=Prod" \
  -H "Authorization: Bearer argus_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d @report.json

CVE Binary Tool

?host= required - same reason as OSV-Scanner.

POST /api/v1/import/cve-binary-tool

cve-bin-tool --format json --output report.json /usr/bin/

curl -X POST "https://argus.yourdomain.com/api/v1/import/cve-binary-tool?host=my-server&client=Prod" \
  -H "Authorization: Bearer argus_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d @report.json

Grype / Syft

POST /api/v1/import/grype

# Direct scan
grype ubuntu:22.04 -o json | \
  curl -X POST "https://argus.yourdomain.com/api/v1/import/grype?client=Prod" \
    -H "Authorization: Bearer argus_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d @-

# Via Syft SBOM
syft ubuntu:22.04 -o json > sbom.json
grype sbom:sbom.json -o json | \
  curl -X POST "https://argus.yourdomain.com/api/v1/import/grype?host=ubuntu-22.04&client=Prod" \
    -H "Authorization: Bearer argus_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d @-

Clair v4

POST /api/v1/import/clair

MANIFEST="sha256:abc123..."
curl -s "http://clair:6060/matcher/api/v1/vulnerability_report/${MANIFEST}" | \
  curl -X POST "https://argus.yourdomain.com/api/v1/import/clair?host=my-image&client=Prod" \
    -H "Authorization: Bearer argus_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d @-

Clair v4 does not provide numeric CVSS scores - the text severity (Critical/High/Medium/Low/Negligible) is used directly.

Wazuh

Two accepted formats:

Format 1 - Wazuh REST API (GET /vulnerability/{agent_id}):

POST /api/v1/import/wazuh

TOKEN=$(curl -su admin:admin -X POST "https://wazuh-manager:55000/security/user/authenticate" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['token'])")

curl -sk -H "Authorization: Bearer $TOKEN" \
  "https://wazuh-manager:55000/vulnerability/001?limit=500" | \
  curl -X POST "https://argus.yourdomain.com/api/v1/import/wazuh?client=Prod" \
    -H "Authorization: Bearer argus_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d @-

Format 2 - Alerts array (export from Wazuh Indexer/OpenSearch):

curl -X POST "https://argus.yourdomain.com/api/v1/import/wazuh?host=agent-name&client=Prod" \
  -H "Authorization: Bearer argus_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d @wazuh-alerts.json

OpenVAS / Greenbone GVM

OpenVAS exports in XML. Convert first with xmltodict:

POST /api/v1/import/openvas

pip install python-gvm xmltodict

REPORT_ID="your-report-uuid"
gvm-cli --gmp-username admin --gmp-password admin socket \
  --xml "<get_reports report_id='${REPORT_ID}' filter='rows=-1'/>" | \
  python3 -c "import sys,json,xmltodict; print(json.dumps(xmltodict.parse(sys.stdin.read())))" | \
  curl -X POST "https://argus.yourdomain.com/api/v1/import/openvas?host=target&client=Prod" \
    -H "Authorization: Bearer argus_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d @-

CVEs are extracted from the nvt.refs.ref field (type="cve"). Results without a valid CVE are ignored.

Webhooks & Notifications

Webhook administration

Configure outbound destinations in Administration -> Webhooks. Choose the destination type:

Type Format sent
generic Full HMAC-SHA256 signed JSON payload (X-Argus-Signature header)
slack Formatted message for a Slack Incoming Webhook ({"text": "..."})
discord Formatted message for a Discord Webhook
teams MessageCard for a Microsoft Teams Connector

Three available events:

Event Trigger
new_cve New CVE detected on a host
critical_cve New CRITICAL CVE detected
new_ticket New remediation ticket created

Slack/Discord/Teams receive a readable message (severity, host, CVSS, KEV flag). The generic type receives the raw signed payload below.

Zulip / ntfy / other tools - no native type, but Argus exposes the same generic endpoint (Slack-compatible for Zulip, or raw JSON for ntfy). The built-in documentation (/docs?section=zulip, /docs?section=ntfy) provides the URL to use and a Python relay script example to reformat the payload if needed.

Payload:

{
  "event": "critical_cve",
  "timestamp": "2026-06-18T10:00:00+00:00",
  "argus_version": "1.0.0",
  "host": { "name": "srv-01", "client": "Acme", "ip": "10.0.0.1" },
  "cve": {
    "id": "CVE-2024-6387",
    "severity": "HIGH",
    "cvss": 8.1,
    "package": "openssh-server",
    "fixed_in": "9.7p1"
  },
  "source": "trivy"
}

Signature verification on the receiver side:

import hmac, hashlib

def verify_signature(payload: bytes, secret: str, header: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)

The signature is sent in the X-Argus-Signature header.

Security

Protection Implementation
Password hashing Argon2id (64 MB memory, 3 iterations, 4 threads)
Session cookies HttpOnly, SameSite=Strict, Secure (if HTTPS=true)
CSRF protection Flask-WTF, token in all forms
Rate limiting 10 login attempts per minute per IP
Account lockout 5 failures -> 15-minute lockout
SQL injection SQLAlchemy ORM only (parameterized queries)
XSS bleach on server fields (titles, descriptions, names); Markdown comments sanitized client-side after rendering (DOMParser, removal of active tags/attributes)
API keys SHA-256 storage only - the raw key is displayed once
Webhook signatures HMAC-SHA256
Two-factor authentication TOTP (RFC 6238) per account, verified at login
Security headers CSP, X-Frame-Options, X-Content-Type-Options, HSTS
Webhook URLs Schema validation - only http:// and https://

User Roles

Role Access
admin Full access: users, tenants, webhooks, API keys, AWX, Threat Intel, audit log
analyst Dashboard, hosts, CVEs, tickets (create, edit, comment)
viewer Read-only: dashboard, hosts, CVEs

A role can also be assigned per tenant (see below).

Multi-Tenant

Tenant selector and administration

Each host belongs to a tenant (a client / organization). CVEs, hosts, and tickets never mix between tenants.

  • Workspace selector in the sidebar: switch tenants at any time, the choice is preserved from page to page (stored in session).
  • Admins: have a Global (all tenants) view in addition to each tenant individually.
  • Non-admins: only see the tenants they are assigned to.
  • Access assignment: Admin -> Tenants -> tenant card -> Add member (user + role).
  • Creation: a tenant is created automatically for each new ?client= received on import, or manually in Admin -> Tenants.
  • Import routing: associate an API key with a tenant (Admin -> API Keys) to route its reports there; otherwise the tenant is inferred from the ?client= parameter.

AWX / Ansible Remediation

Launching an AWX playbook from a host page

Argus can trigger an AWX / Ansible Tower Job Template on a specific host.

  1. Admin -> AWX -> add a connection (AWX base URL + Bearer OAuth2 token).
  2. Declare one or more Job Templates (name + template ID in AWX).
  3. A Launch playbook button then appears on each host page and in each ticket.

On launch, Argus calls POST {awx_url}/api/v2/job_templates/{id}/launch/ with {"limit": "<hostname>"} - the playbook therefore only runs on the relevant machine. The AWX job number is displayed and logged in the audit.

As long as no Job Template is configured, the host page displays a reminder pointing to Admin -> AWX.

Partial rescans are safe - the resolved/auto-close diff is strictly scoped to the host being re-imported (see Differential scan tracking), so rescanning one or a few machines via AWX never closes tickets on hosts outside that batch, even if the tenant has hundreds of others. If your playbook feeds the rescan result back into Argus, call the same import endpoint with ?host=<hostname> per machine, and re-run the same scanner with the same coverage as the original scan - not a narrow single-CVE check, which would make Argus wrongly resolve every other CVE on that host that the check didn't look at.

Markdown Comments & Images

Ticket detail with Markdown comments

Ticket comments and descriptions support Markdown (bold, italic, lists, code, tables, links, quotes).

  • Toolbar + Preview button in the comment form.
  • Images: camera button to upload, or paste a screenshot directly (Ctrl+V) - the image is uploaded and inserted as ![](...).
  • Accepted formats: PNG, JPG/JPEG, GIF, WebP (10 MB max). Storage in static/uploads/.
  • Markdown is converted to HTML client-side (marked.js), then sanitized before display by a dedicated DOM sanitizer (removal of active tags and attributes - script, on*, javascript:...). Other fields (titles, descriptions, names) are sanitized server-side with bleach.

Threat Intelligence & Risk Score

Admin Threat Intel page

Argus enriches each CVE with official public sources, free and without an API key:

Source Contribution
CISA KEV Marks CVEs actively exploited in the wild
FIRST EPSS 30-day exploitation probability (0->100%)
NVD 2.0 Missing description + CVSS score (optional, slower)

Enrichment is automatic after each import (as a background task) and can be relaunched manually in Admin -> Threat Intel.

Risk score (0->100) that prioritizes beyond CVSS alone:

risk = 100 x (0.6 x CVSS/10 + 0.4 x EPSS)
       x KEV bonus (x1.3 +10 if exploited)

It feeds the dashboard ranking, host page, and executive report.

Set the asset criticality (1 = low -> 5 = crown jewel) on the host page.

Implementation note - intel.compute_risk() accepts a criticality weighting factor (0.8 to 1.2 based on 1->5 rating), but no caller currently passes it: the risk_score is stored per CVE (not per host+CVE pair), so it is the same regardless of which host reports it. Asset criticality remains for now an informational field displayed on the host page.

Remediation SLA

SLA view

Each ticket receives a deadline calculated from the CVE severity:

Severity Deadline
Critical 7 days
High 30 days
Medium 90 days
Low 180 days

The SLA view lists open tickets sorted by deadline and highlights overdue tickets and imminent deadlines (< 3 days).

Executive Report

Executive report

Report (side menu) generates a summary view of the current scope (active tenant or global): KPIs, priority KEV vulnerabilities, top-risk, open tickets. The Print / Export to PDF button opens the browser's print dialog ("Save as PDF").

Two-Factor Authentication (2FA TOTP)

2FA activation

Each user activates 2FA from the footer -> 2FA: scan the QR code with Google Authenticator / Aegis / 1Password, confirm with a 6-digit code. A code is then required at each login. Deactivation requires the account password.

Test Data

An injection script is provided to quickly populate a dev instance:

# First create an API key in Administration -> API Keys
python argus_test_data.py --url http://localhost:5000 --key argus_YOUR_KEY

Injects 6 servers (CentOS, Rocky, Windows Server, Ubuntu) with 11 real CVEs including regreSSHion (CVE-2024-6387), XZ backdoor (CVE-2024-3094), Looney Tunables (CVE-2023-4911).

By default, each host is routed to its own tenant (the Client field in the fixture): Acme Corp (3 hosts), Globex, Initech, Umbrella - 4 tenants created automatically. Two additional options:

# Force all hosts sent to a single tenant, ignoring the fixture Client field
python argus_test_data.py --url http://localhost:5000 --key argus_YOUR_KEY --client "My Client"

# Only inject a specific host (combinable with --client)
python argus_test_data.py --url http://localhost:5000 --key argus_YOUR_KEY --host srv-mail-01

If the API key used is linked to a specific tenant (Admin -> API Keys), that tenant takes precedence over --client and over each host's Client field: everything ends up in the key's tenant. Use a key without an assigned tenant ("Auto (from the ?client= parameter)") to benefit from automatic distribution across 4 tenants.

Tests

The automated suite (unit + integration) uses pytest on an isolated temporary SQLite database (the real argus.db is never touched):

pip install -r requirements-dev.txt
pytest                       # full suite
pytest tests/unit            # parsers, utils, scoring, helpers
pytest tests/integration     # auth, 2FA, imports, tenant isolation, tickets...
pytest --cov=app --cov=intel --cov=parsers --cov-report=term-missing

Organization:

Folder Content
tests/unit/ Unit tests (parsers, parsers/utils, intel, app helpers)
tests/integration/ End-to-end via Flask test client
tests/fixtures/ Scanner JSON reports
tests/manual/ Scripts for sending to a real server (not collected by pytest - see their README)

/FR

Argus - Plateforme de gestion des vulnérabilités CVE

Argus est une plateforme web conçue pour les analystes SOC qui veulent centraliser et suivre les CVE détectées sur leur parc infogéré. L'idée de base : recevoir les rapports de scan directement via API, visualiser par hôte, ouvrir des tickets de remédiation et être notifié automatiquement quand quelque chose de critique sort.

Pas de dépendance à un cloud externe, pas de compte tiers, tout tourne chez vous.

Dashboard Argus

Ce que ça fait

  • Ingestion multi-scanners - Vuls, Trivy, OSV-Scanner, CVE Binary Tool, Grype/Syft, Clair v4, Wazuh, OpenVAS/GVM
  • Multi-tenant - isolation des CVE par tenant (client), workspace global pour les admins, gestion fine des accès par utilisateur
  • Suivi différentiel des scans - un nouveau scan remplace l'état précédent de cet hôte uniquement : les CVE corrigées passent automatiquement en « résolu », pas de doublons. Le diff est strictement scopé par hôte, jamais par tenant : un rescan ciblé d'une ou plusieurs machines (via AWX par exemple) ne touche jamais les autres
  • Synchronisation tickets ↔ CVE - résoudre un ticket marque la vuln traitée ; une vuln qui disparaît au scan suivant ferme automatiquement le ticket lié. La correspondance se fait sur le couple (nom d'hôte, tenant), pas sur l'outil qui l'a remontée : une CVE vue par Vuls et absente d'un scan Trivy ultérieur sur le même hôte ferme aussi bien son ticket - à condition d'utiliser le même ?host= entre scanners (voir API)
  • Dashboard temps réel - CVE actives par hôte, scores CVSS, flag KEV (CISA), EPSS
  • Threat Intelligence - enrichissement automatique via CISA KEV (exploitation active), EPSS (probabilité d'exploit à 30 j) et NVD ; score de risque combiné (CVSS × EPSS, bonus KEV) qui priorise les tickets et le dashboard
  • Criticité d'actif - note 1→5 par hôte (« joyau de la couronne »), réglable sur la fiche hôte - informatif pour l'instant : pas encore répercutée dans le score de risque stocké (voir Threat Intelligence)
  • SLA de remédiation - échéances par sévérité (Critique 7 j, Haute 30 j…), tableau des dépassements
  • Rapport exécutif - vue imprimable / export PDF par tenant (KEV, top-risque, KPIs)
  • Recherche CVE - recherche plein texte sur les CVE IDs, packages, descriptions
  • Tickets de remédiation - création, assignation, statuts (ouvert → en cours → résolu), commentaires Markdown avec images (upload + copier-coller)
  • Remédiation AWX/Ansible - lance un Job Template AWX sur un hôte précis (--limit) directement depuis sa fiche ou un ticket
  • Webhooks & notifications - payload JSON signé HMAC-SHA256, ou messages formatés Slack / Discord / Microsoft Teams
  • 3 rôles utilisateur - admin, analyste, lecteur (+ rôle par tenant)
  • Double authentification (2FA TOTP) - Google Authenticator, Aegis, 1Password… par compte
  • Journal d'audit - toutes les actions tracées avec IP et timestamp
  • SSO Keycloak - optionnel, via OIDC
  • Sécurité - CSP stricte avec nonce (aucun unsafe-inline pour les scripts), Argon2id, CSRF, rate-limiting
  • Mode sombre / clair
  • Interface bilingue - français / anglais, bascule en un clic (icône langue), conservée en session
  • Documentation intégrée - guides pas-à-pas dans l'app (/docs) : configuration par scanner, sécurité, webhooks (+ recettes Zulip/ntfy), Keycloak, déploiement…

Stack technique

Composant Technologie
Backend Python 3.11 + Flask 3
ORM SQLAlchemy 2
Base de données SQLite (dev) ou MariaDB (prod)
Authentification Flask-Login + Argon2id + Keycloak OIDC optionnel
Sécurité API Clés Bearer (SHA-256 en base), signatures HMAC-SHA256
Frontend HTML/CSS/JS vanilla - aucun framework SPA (2 librairies vendorisées : Chart.js pour les graphiques, marked.js pour le rendu Markdown)

Démarrage rapide (Docker)

La méthode recommandée. Aucune dépendance à installer hormis Docker.

git clone https://github.com/your-org/argus.git
cd argus

cp .env.example .env
# Éditez .env et mettez une vraie SECRET_KEY :
#   python -c "import secrets; print(secrets.token_hex(32))"

docker compose up -d --build

Ouvrez http://localhost:5000 et terminez l'assistant de configuration pour créer le premier compte admin.

Persistance - la base SQLite et les images uploadées dans les tickets sont stockées dans le volume Docker argus_data (monté sur /data). Elles survivent aux docker compose down / rebuilds. La base est créée et migrée automatiquement au démarrage du conteneur.

Base de données - SQLite par défaut. Pour MariaDB/MySQL, décommentez le service db dans docker-compose.yml et renseignez DATABASE_URL dans .env (voir Configuration).

Mise à jour :

git pull
docker compose up -d --build      # les migrations de schéma s'appliquent seules

Installation manuelle (sans Docker)

Python 3.11+ requis.

git clone https://github.com/your-org/argus.git
cd argus

python3 -m venv .venv
source .venv/bin/activate         # Windows : .venv\Scripts\activate

pip install -r requirements.txt

cp .env.example .env              # Éditez les variables, surtout SECRET_KEY

# Développement / premier lancement (crée et migre la base automatiquement) :
python app.py

# Production (gunicorn - initialisez d'abord la base) :
python -c "import app; app.init_db()"
gunicorn -w 4 -b 0.0.0.0:5000 --timeout 120 app:app

En production, lancez toujours app.init_db() une fois avant gunicorn : les workers gunicorn n'exécutent pas le bloc de démarrage de app.py. (L'image Docker s'en charge automatiquement.)

Les images uploadées dans les tickets sont écrites dans static/uploads/ - pensez à sauvegarder ce dossier et le fichier argus.db.

Lancement rapide pour tester (sans Docker)

La façon la plus rapide d'essayer Argus sur une machine qui n'a que Python 3.11+ (pas de Docker). Une SECRET_KEY temporaire est générée à la volée - rien d'autre à configurer.

Linux / macOS

git clone https://github.com/your-org/argus.git && cd argus
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
python app.py

Windows (PowerShell)

git clone https://github.com/your-org/argus.git; cd argus
python -m venv .venv; .\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
$env:SECRET_KEY = python -c "import secrets; print(secrets.token_hex(32))"
python app.py

Ouvrez ensuite http://localhost:5000 et complétez l'assistant d'installation pour créer le premier compte admin. La base SQLite (argus.db) est créée et migrée automatiquement au premier lancement.

Ce mode est réservé au test local (serveur de dev Flask, clé éphémère). En production, fixez une SECRET_KEY persistante et utilisez gunicorn comme indiqué plus haut.

Pour charger des données de démo (6 hôtes, 11 CVE réelles), créez une clé API dans Administration → Clés API, puis :

python argus_test_data.py --url http://localhost:5000 --key argus_VOTRE_CLE

Configuration

Tout passe par les variables d'environnement (ou le fichier .env) :

Variable Par défaut Description
SECRET_KEY obligatoire Clé de signature Flask - générez avec python3 -c "import secrets; print(secrets.token_hex(32))"
DATABASE_URL SQLite local Format MariaDB : mysql+pymysql://user:pass@host/db
HTTPS false Passer à true pour activer les cookies Secure et HSTS
FLASK_DEBUG false Ne jamais activer en production
PORT 5000 Port d'écoute
KEYCLOAK_ENABLED false Active le SSO Keycloak OIDC
KEYCLOAK_SERVER_URL - Ex : https://keycloak.example.com
KEYCLOAK_REALM argus Nom du realm Keycloak
KEYCLOAK_CLIENT_ID argus ID client OIDC
KEYCLOAK_CLIENT_SECRET - Secret client OIDC

À changer avant le premier démarrage

Argus est livré sans aucune donnée réelle, compte, ni secret. Avant de déployer, vérifiez cette liste :

  • SECRET_KEY - mettez une vraie valeur aléatoire (python -c "import secrets; print(secrets.token_hex(32))"). Le défaut de l'image Docker (changez_moi) et le placeholder de .env.example doivent être remplacés.
  • Premier compte admin - créé via l'assistant au premier lancement (/setup). Choisissez un mot de passe fort ; il n'y a aucun identifiant par défaut.
  • DATABASE_URL - vide pour SQLite (dev), ou pointez vers MariaDB/MySQL en production.
  • HTTPS=true - à activer derrière du TLS pour que les cookies de session soient Secure et que HSTS soit envoyé.
  • FLASK_DEBUG - laissez false en production.
  • SSO Keycloak (optionnel) - ne renseignez les KEYCLOAK_* que si vous l'utilisez ; sinon laissez KEYCLOAK_ENABLED=false.
  • Données de démo - le script argus_test_data.py et tests/fixtures/ contiennent des clients fictifs (Acme Corp, Globex…) et des hôtes d'exemple. Ne les chargez pas sur une instance de production.
  • argus.db - la base locale est git-ignorée ; ne la committez jamais. Une base neuve est créée au premier démarrage.
  • Personnalisation (optionnel) - ajustez le nom de l'app / le sous-titre de connexion dans templates/login.html et templates/base.html.

Déploiement production

server {
    listen 443 ssl http2;
    server_name argus.mondomaine.fr;

    ssl_certificate     /etc/ssl/argus.crt;
    ssl_certificate_key /etc/ssl/argus.key;
    ssl_protocols       TLSv1.3;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
    }
}

API

Toutes les routes d'écriture nécessitent une clé API générée dans Administration → Clés API.

Authorization: Bearer argus_<votre-cle>
Content-Type: application/json

Limite de débit : 200 requêtes/minute par IP sur tous les endpoints /api/v1/import/* et /api/v1/vuls/report.

Healthcheck

GET /api/v1/status

Retourne {"status": "ok", "app": "Argus", "version": "1.0.0"} - sans authentification.

Endpoints d'import

Chaque endpoint accepte le JSON natif du scanner correspondant, plus deux paramètres optionnels en query string :

Paramètre Requis Description
?host= Parfois Nom d'hôte. Obligatoire pour OSV-Scanner, CVE Binary Tool et OpenVAS quand il ne peut pas être détecté automatiquement.
?client= Non Nom du client pour regrouper les hôtes. Défaut : Inconnu.

Corrélation entre scanners - le diff résolu/fermeture-auto (voir Suivi différentiel des scans) se base sur le couple exact (nom d'hôte, tenant), jamais sur le scanner qui a produit le rapport. Si une même machine est scannée par plusieurs outils (ex : Vuls, puis plus tard Trivy), passez toujours le même ?host=<nom> explicitement sur chaque import. Sinon, les scanners qui déduisent un nom depuis les métadonnées du scan (ArtifactName pour Trivy, source.target.userInput pour Grype, manifest_hash pour Clair…) créeront un hôte distinct et la corrélation se cassera silencieusement.

Réponse commune à tous les imports :

{ "ok": true, "host": "srv-01", "cves_processed": 14, "new_cves": 3 }

Vuls (natif)

POST /api/v1/vuls/report

curl -X POST "https://argus.mondomaine.fr/api/v1/vuls/report" \
  -H "Authorization: Bearer argus_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d @/var/vuls/results/current/srv-01.json

Trivy

POST /api/v1/import/trivy

# Scan + envoi en une commande
trivy image --format json --quiet ubuntu:22.04 | \
  curl -X POST "https://argus.mondomaine.fr/api/v1/import/trivy?client=Prod" \
    -H "Authorization: Bearer argus_VOTRE_CLE" \
    -H "Content-Type: application/json" \
    -d @-

Le nom d'hôte est extrait de ArtifactName. Remplacez-le avec ?host=.

OSV-Scanner

?host= obligatoire - OSV-Scanner n'inclut pas le nom de la machine dans son rapport.

POST /api/v1/import/osv

osv-scanner --format json --output rapport.json /chemin/vers/projet

curl -X POST "https://argus.mondomaine.fr/api/v1/import/osv?host=mon-serveur&client=Prod" \
  -H "Authorization: Bearer argus_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d @rapport.json

CVE Binary Tool

?host= obligatoire - même raison qu'OSV-Scanner.

POST /api/v1/import/cve-binary-tool

cve-bin-tool --format json --output rapport.json /usr/bin/

curl -X POST "https://argus.mondomaine.fr/api/v1/import/cve-binary-tool?host=mon-serveur&client=Prod" \
  -H "Authorization: Bearer argus_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d @rapport.json

Grype / Syft

POST /api/v1/import/grype

# Scan direct
grype ubuntu:22.04 -o json | \
  curl -X POST "https://argus.mondomaine.fr/api/v1/import/grype?client=Prod" \
    -H "Authorization: Bearer argus_VOTRE_CLE" \
    -H "Content-Type: application/json" \
    -d @-

# Via SBOM Syft
syft ubuntu:22.04 -o json > sbom.json
grype sbom:sbom.json -o json | \
  curl -X POST "https://argus.mondomaine.fr/api/v1/import/grype?host=ubuntu-22.04&client=Prod" \
    -H "Authorization: Bearer argus_VOTRE_CLE" \
    -H "Content-Type: application/json" \
    -d @-

Clair v4

POST /api/v1/import/clair

MANIFEST="sha256:abc123..."
curl -s "http://clair:6060/matcher/api/v1/vulnerability_report/${MANIFEST}" | \
  curl -X POST "https://argus.mondomaine.fr/api/v1/import/clair?host=mon-image&client=Prod" \
    -H "Authorization: Bearer argus_VOTRE_CLE" \
    -H "Content-Type: application/json" \
    -d @-

Clair v4 ne fournit pas de scores CVSS numériques - la sévérité textuelle (Critical/High/Medium/Low/Negligible) est utilisée directement.

Wazuh

Deux formats acceptés :

Format 1 - API REST Wazuh (GET /vulnerability/{agent_id}) :

POST /api/v1/import/wazuh

TOKEN=$(curl -su admin:admin -X POST "https://wazuh-manager:55000/security/user/authenticate" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['token'])")

curl -sk -H "Authorization: Bearer $TOKEN" \
  "https://wazuh-manager:55000/vulnerability/001?limit=500" | \
  curl -X POST "https://argus.mondomaine.fr/api/v1/import/wazuh?client=Prod" \
    -H "Authorization: Bearer argus_VOTRE_CLE" \
    -H "Content-Type: application/json" \
    -d @-

Format 2 - Tableau d'alertes (export depuis Wazuh Indexer/OpenSearch) :

curl -X POST "https://argus.mondomaine.fr/api/v1/import/wazuh?host=nom-agent&client=Prod" \
  -H "Authorization: Bearer argus_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d @alertes-wazuh.json

OpenVAS / Greenbone GVM

OpenVAS exporte en XML. Convertissez d'abord avec xmltodict :

POST /api/v1/import/openvas

pip install python-gvm xmltodict

REPORT_ID="votre-uuid-rapport"
gvm-cli --gmp-username admin --gmp-password admin socket \
  --xml "<get_reports report_id='${REPORT_ID}' filter='rows=-1'/>" | \
  python3 -c "import sys,json,xmltodict; print(json.dumps(xmltodict.parse(sys.stdin.read())))" | \
  curl -X POST "https://argus.mondomaine.fr/api/v1/import/openvas?host=cible&client=Prod" \
    -H "Authorization: Bearer argus_VOTRE_CLE" \
    -H "Content-Type: application/json" \
    -d @-

Les CVE sont extraites du champ nvt.refs.ref (type="cve"). Les résultats sans CVE valide sont ignorés.

Webhooks & notifications

Administration des webhooks

Configurez les destinations sortantes dans Administration → Webhooks. Choisissez le type de destination :

Type Format envoyé
generic Payload JSON complet signé HMAC-SHA256 (en-tête X-Argus-Signature)
slack Message formaté pour un Incoming Webhook Slack ({"text": "..."})
discord Message formaté pour un Webhook Discord
teams Carte MessageCard pour un Connector Microsoft Teams

Trois événements disponibles :

Événement Déclencheur
new_cve Nouvelle CVE détectée sur un hôte
critical_cve Nouvelle CVE CRITICAL détectée
new_ticket Nouveau ticket de remédiation créé

Slack/Discord/Teams reçoivent un message lisible (sévérité, hôte, CVSS, flag KEV). Le type generic reçoit le payload brut signé ci-dessous.

Zulip / ntfy / autre outil - pas de type natif, mais Argus expose le même endpoint generic (compatible Slack pour Zulip, ou JSON brut pour ntfy). La documentation intégrée (/docs?section=zulip, /docs?section=ntfy) fournit l'URL à utiliser et un exemple de script relais Python pour reformater le payload si besoin.

Payload :

{
  "event": "critical_cve",
  "timestamp": "2026-06-18T10:00:00+00:00",
  "argus_version": "1.0.0",
  "host": { "name": "srv-01", "client": "Acme", "ip": "10.0.0.1" },
  "cve": {
    "id": "CVE-2024-6387",
    "severity": "HIGH",
    "cvss": 8.1,
    "package": "openssh-server",
    "fixed_in": "9.7p1"
  },
  "source": "trivy"
}

Vérification de signature côté récepteur :

import hmac, hashlib

def verifier_signature(payload: bytes, secret: str, header: str) -> bool:
    attendu = "sha256=" + hmac.new(
        secret.encode(), payload, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(attendu, header)

La signature est envoyée dans l'en-tête X-Argus-Signature.

Sécurité

Protection Implémentation
Hachage des mots de passe Argon2id (64 Mo mémoire, 3 itérations, 4 threads)
Cookies de session HttpOnly, SameSite=Strict, Secure (si HTTPS=true)
Protection CSRF Flask-WTF, jeton dans tous les formulaires
Rate limiting 10 tentatives de connexion par minute par IP
Verrouillage de compte 5 échecs → verrouillage 15 minutes
Injection SQL SQLAlchemy ORM uniquement (requêtes paramétrées)
XSS bleach sur les champs serveur (titres, descriptions, noms) ; commentaires Markdown sanitisés côté client après rendu (DOMParser, suppression des balises/attributs actifs)
Clés API Stockage SHA-256 uniquement - la clé brute s'affiche une seule fois
Signatures webhook HMAC-SHA256
Double authentification TOTP (RFC 6238) par compte, vérification à la connexion
En-têtes de sécurité CSP, X-Frame-Options, X-Content-Type-Options, HSTS
URLs webhook Validation du schéma - uniquement http:// et https://

Rôles utilisateur

Rôle Accès
admin Accès complet : utilisateurs, tenants, webhooks, clés API, AWX, Threat Intel, journal d'audit
analyst Dashboard, hôtes, CVE, tickets (création, modification, commentaires)
viewer Lecture seule : dashboard, hôtes, CVE

Un rôle peut aussi être attribué par tenant (voir ci-dessous).

Multi-tenant

Sélecteur de tenant et administration

Chaque hôte appartient à un tenant (un client / une organisation). Les CVE, hôtes et tickets ne se mélangent jamais entre tenants.

  • Sélecteur de workspace dans la barre latérale : changez de tenant à tout moment, le choix est conservé d'une page à l'autre (stocké en session).
  • Admins : disposent d'une vue Global (tous les tenants) en plus de chaque tenant pris isolément.
  • Non-admins : ne voient que les tenants auxquels ils sont rattachés.
  • Attribution des accès : Admin → Tenants → carte du tenant → Ajouter un membre (utilisateur + rôle).
  • Création : un tenant est créé automatiquement pour chaque nouveau ?client= reçu à l'import, ou manuellement dans Admin → Tenants.
  • Routage des imports : associez une clé API à un tenant (Admin → Clés API) pour y ranger ses rapports ; sinon le tenant est déduit du paramètre ?client=.

Remédiation AWX / Ansible

Lancement d'un playbook AWX depuis une fiche hôte

Argus peut déclencher un Job Template AWX / Ansible Tower sur un hôte précis.

  1. Admin → AWX → ajoutez une connexion (URL de base AWX + token Bearer OAuth2).
  2. Déclarez un ou plusieurs Job Templates (nom + ID du template dans AWX).
  3. Un bouton Lancer le playbook apparaît alors sur chaque fiche d'hôte et dans chaque ticket.

Au lancement, Argus appelle POST {awx_url}/api/v2/job_templates/{id}/launch/ avec {"limit": "<hostname>"} - le playbook ne s'exécute donc que sur la machine concernée. Le numéro du job AWX est affiché et journalisé dans l'audit.

Tant qu'aucun Job Template n'est configuré, la fiche d'hôte affiche un rappel pointant vers Admin → AWX.

Les rescans partiels sont sans risque - le diff résolu/fermeture-auto est strictement scopé à l'hôte en cours d'import (voir Suivi différentiel des scans), donc rescanner une ou quelques machines via AWX ne ferme jamais les tickets des hôtes absents de ce lot, même si le tenant en compte des centaines d'autres. Si votre playbook réinjecte le résultat du rescan dans Argus, appelez le même endpoint d'import avec ?host=<nom> par machine, et relancez le même scanner avec la même couverture que le scan initial - pas une simple vérification ponctuelle d'une CVE, qui ferait résoudre à tort toutes les autres CVE de cet hôte que le contrôle n'a pas examinées.

Commentaires Markdown & images

Détail d'un ticket avec commentaires Markdown

Les commentaires de tickets et leur description supportent le Markdown (gras, italique, listes, code, tableaux, liens, citations).

  • Barre d'outils + bouton Aperçu dans le formulaire de commentaire.
  • Images : bouton pour téléverser, ou collez directement une capture (Ctrl+V) - l'image est uploadée et insérée en ![](...).
  • Formats acceptés : PNG, JPG/JPEG, GIF, WebP (10 Mo max). Stockage dans static/uploads/.
  • Le Markdown est converti en HTML côté client (marked.js), puis assaini avant affichage par un sanitiseur DOM dédié (suppression des balises et attributs actifs - script, on*, javascript:…). Les autres champs (titres, descriptions, noms) sont assainis côté serveur avec bleach.

Threat Intelligence & score de risque

Page Admin Threat Intel

Argus enrichit chaque CVE avec des sources publiques officielles, gratuites et sans clé API :

Source Apport
CISA KEV Marque les CVE activement exploitées dans la nature
FIRST EPSS Probabilité d'exploitation à 30 jours (0→100 %)
NVD 2.0 Description + score CVSS manquants (optionnel, plus lent)

L'enrichissement est automatique après chaque import (en tâche de fond) et peut être relancé manuellement dans Admin → Threat Intel.

Score de risque (0→100) qui priorise au-delà du seul CVSS :

risque = 100 × (0.6 × CVSS/10 + 0.4 × EPSS)
         × bonus KEV (×1.3 +10 si exploitée)

Il alimente le classement du dashboard, la fiche d'hôte et le rapport exécutif.

Réglez la criticité d'un actif (1 = faible → 5 = joyau de la couronne) sur sa fiche d'hôte.

Note d'implémentation - intel.compute_risk() accepte un facteur de pondération par criticité (0.8 à 1.2 selon la note 1→5), mais aucun appelant ne le transmet actuellement : le risk_score est stocké par CVE (pas par couple hôte+CVE), donc identique quel que soit l'hôte qui la remonte. La criticité d'actif reste pour l'instant un champ informatif affiché sur la fiche hôte.

SLA de remédiation

Vue SLA

Chaque ticket reçoit une échéance calculée depuis la sévérité de la CVE :

Sévérité Délai
Critique 7 jours
Haute 30 jours
Moyenne 90 jours
Basse 180 jours

La vue SLA liste les tickets ouverts triés par échéance et signale les dépassements et échéances imminentes (< 3 j).

Rapport exécutif

Rapport exécutif

Rapport (menu latéral) génère une vue de synthèse du périmètre courant (tenant actif ou global) : KPIs, vulnérabilités KEV prioritaires, top-risque, tickets ouverts. Le bouton Imprimer / Exporter en PDF ouvre la boîte d'impression du navigateur (« Enregistrer au format PDF »).

Double authentification (2FA TOTP)

Activation de la 2FA

Chaque utilisateur active la 2FA depuis le footer → 2FA : scannez le QR code avec Google Authenticator / Aegis / 1Password, confirmez avec un code à 6 chiffres. Un code est ensuite demandé à chaque connexion. La désactivation exige le mot de passe du compte.

Données de test

Un script d'injection est fourni pour peupler rapidement une instance de dev :

# Créez d'abord une clé API dans Administration → Clés API
python argus_test_data.py --url http://localhost:5000 --key argus_VOTRE_CLE

Injecte 6 serveurs (CentOS, Rocky, Windows Server, Ubuntu) avec 11 CVE réelles dont regreSSHion (CVE-2024-6387), XZ backdoor (CVE-2024-3094), Looney Tunables (CVE-2023-4911).

Par défaut, chaque hôte est routé vers son propre tenant (champ Client du fixture) : Acme Corp (3 hôtes), Globex, Initech, Umbrella - 4 tenants créés automatiquement. Deux options supplémentaires :

# Forcer tous les hôtes envoyés vers un seul tenant, en ignorant le champ Client du fixture
python argus_test_data.py --url http://localhost:5000 --key argus_VOTRE_CLE --client "Mon Client"

# N'injecter qu'un hôte précis (combinable avec --client)
python argus_test_data.py --url http://localhost:5000 --key argus_VOTRE_CLE --host srv-mail-01

Si la clé API utilisée est rattachée à un tenant spécifique (Admin → Clés API), ce tenant prime sur --client et sur le Client de chaque hôte : tout finit dans le tenant de la clé. Utilisez une clé sans tenant assigné (« Auto (depuis le paramètre ?client=) ») pour profiter de la répartition automatique sur 4 tenants.

Tests

La suite automatisée (unitaire + intégration) utilise pytest sur une base SQLite temporaire isolée (la vraie argus.db n'est jamais touchée) :

pip install -r requirements-dev.txt
pytest                       # toute la suite
pytest tests/unit            # parsers, utils, scoring, helpers
pytest tests/integration     # auth, 2FA, imports, isolation tenant, tickets…
pytest --cov=app --cov=intel --cov=parsers --cov-report=term-missing

Organisation :

Dossier Contenu
tests/unit/ Tests unitaires (parsers, parsers/utils, intel, helpers d'app)
tests/integration/ Bout-en-bout via le client de test Flask
tests/fixtures/ Rapports JSON des scanners
tests/manual/ Scripts d'envoi vers un serveur réel (non collectés par pytest - voir leur README)

About

Open-source CVE vulnerability management platform for SOC teams. Centralize scanner results, enrich vulnerabilities with threat intelligence (KEV, EPSS, NVD), track remediation and automate response workflows.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages