Construire un workflow (schéma JSON)
Un workflow TechWatchAlert est un graphe d’automatisation : un déclencheur réagit à un événement (nouvelle CVE, fin de vie qui approche, certificat qui expire…), des conditions filtrent, et des actions notifient ou agissent. Ce graphe est décrit par un objet JSON que vous manipulez via l’éditeur visuel, l’assistant Vigie, ou l’import/export de workflow.
Structure générale
Section intitulée « Structure générale »Le graphe (workflow.graph) a exactement deux clés :
{ "nodes": [ /* les tuiles */ ], "edges": [ /* les connexions entre tuiles */ ]}Une tuile (node)
Section intitulée « Une tuile (node) »{ "id": "c1", "type": "condition.priorite", "params": { "critere": "au_dessus_seuil" }, "position": { "x": 320, "y": 140 }}| Champ | Type | Description |
|---|---|---|
id |
string |
Identifiant unique dans le graphe (libre : trigger, c1, a1…). |
type |
string |
Type de tuile (voir le catalogue). |
params |
object |
Paramètres de la tuile (selon son type). {} si aucun. |
position |
object (optionnel) |
Coordonnées { x, y } dans le canevas. Omettez-le : l’éditeur dispose automatiquement les tuiles. |
Une connexion (edge)
Section intitulée « Une connexion (edge) »{ "id": "e1", "source": "trigger", "target": "c1", "sourceHandle": "out" }| Champ | Type | Description |
|---|---|---|
source |
string |
id de la tuile de départ. |
target |
string |
id de la tuile d’arrivée. |
sourceHandle |
string |
Sortie utilisée sur la tuile de départ : out (déclencheurs et actions) ou true / false (conditions). Défaut : out. |
id |
string (optionnel) |
Identifiant de la connexion. |
Règles de validation
Section intitulée « Règles de validation »Un graphe est refusé (avec un message explicite) s’il enfreint l’une de ces règles :
- Un seul déclencheur par workflow, et exactement un (
trigger.*). - Un déclencheur ne peut pas avoir d’entrée (aucune connexion ne pointe vers lui).
- Les conditions ont deux sorties :
trueetfalse. LesourceHandled’une connexion partant d’une condition doit êtretrueoufalse. - Les déclencheurs et les actions ont une seule sortie :
out. - Le graphe est acyclique (DAG) — aucune boucle.
- 30 tuiles maximum (
MAX_NODES), profondeur 20 maximum depuis le déclencheur. idde tuile unique ; toute connexion référence des tuiles existantes.- Les tuiles non reliées au déclencheur sont ignorées à l’exécution (tolérées à l’enregistrement pour ne pas bloquer un brouillon).
- 10 workflows maximum par projet.
Catalogue des tuiles
Section intitulée « Catalogue des tuiles »Le registre complet est aussi servi par l’API interne (GET /projects/{id}/workflows/node-types) pour générer la palette de l’éditeur.
Déclencheurs (trigger.*)
Section intitulée « Déclencheurs (trigger.*) »Un déclencheur réagit à un événement. Le contexte (CVE, EOL ou certificat) détermine quelles conditions ont un sens en aval. Aucun n’a de paramètre, sauf ceux de conformité (ci-dessous).
| Type | Événement | Contexte | Déclenché quand… |
|---|---|---|---|
trigger.cve_new_match |
cve.new_match |
CVE | une nouvelle CVE correspond à votre stack surveillée. |
trigger.cve_updated |
cve.updated |
CVE | une CVE suivie est mise à jour (score, etc.). |
trigger.cve_poc_published |
cve.poc_published |
CVE | un PoC public apparaît pour une CVE suivie. |
trigger.cve_kev_added |
cve.kev_added |
CVE | une CVE suivie entre au catalogue KEV de la CISA. |
trigger.cve_exploitee |
cve.exploitee |
CVE | une CVE suivie passe en exploitation avérée (KEV de la CISA, VulnCheck, CERT-FR, EUVD). |
trigger.cve_correctif_disponible |
cve.correctif_disponible |
CVE | une version corrigée paraît pour une CVE suivie qui n’en avait pas (une fois par CVE). |
trigger.cve_rancongiciel |
cve.rancongiciel |
CVE | une CVE suivie est associée à un rançongiciel par la CISA ou VulnCheck (une fois par CVE). |
trigger.eol_approaching |
eol.approaching |
EOL | la fin de vie d’un produit suivi approche. |
trigger.eol_support_ending |
eol.support_ending |
EOL | la fin de support actif approche. |
trigger.eol_new_release |
eol.new_release |
EOL | une nouvelle version d’un produit suivi sort. |
trigger.url_cert_expiring |
url.cert_expiring |
Certificat | le certificat HTTPS d’une URL surveillée expire bientôt. |
Conditions (condition.*)
Section intitulée « Conditions (condition.*) »Chaque condition a deux sorties (true / false). Une tuile d’une autre famille que le déclencheur (ex. condition CVE après un déclencheur EOL) n’a aucune donnée à lire : elle renvoie systématiquement false. L’éditeur ne les propose plus dans sa palette et signale celles qui le seraient devenues après un changement de déclencheur.
| Type | Contexte | Paramètres |
|---|---|---|
condition.priorite |
CVE | critere ∈ exploitee (CVE exploitée selon la politique : KEV en tête, EPSS, PoC) | kev_en_tete (au catalogue KEV, en tête) | au_dessus_seuil (CVSS au seuil d’alerte) | sous_seuil_exception (sous le seuil, alertée parce qu’exploitée). Lit la politique de priorisation du projet, affinée par le seuil de l’élément de stack qui a fait correspondre la CVE. |
condition.has_poc |
CVE | (aucun) — vrai si un PoC public existe. |
condition.is_kev |
CVE | (aucun) — vrai si la CVE est au catalogue KEV. |
condition.product_name |
CVE | op ∈ contains | starts_with | ends_with | equals ; value texte (≤ 100). |
condition.vendor_name |
CVE | idem product_name, sur l’éditeur. |
condition.match_type |
CVE | value ∈ vendor | product (comment la CVE a matché votre stack). |
condition.case_status |
CVE | op ∈ eq | ne ; value ∈ statut de suivi ou NONE. |
condition.has_tag |
CVE | tag_id (UUID d’un tag de l’organisation). |
condition.eol_days_remaining |
EOL | op (numérique) ; value jours −3650 → 3650. |
condition.eol_is_past |
EOL | (aucun) — vrai si la date de fin de vie est dépassée. |
condition.eol_product_name |
EOL | op (texte) ; value texte (≤ 100). |
condition.cert_days_remaining |
Certificat | op (numérique) ; value jours −3650 → 3650. |
condition.cert_is_expired |
Certificat | (aucun) — vrai si le certificat est expiré. |
condition.url_contains |
Certificat | op (texte) ; value texte (≤ 100). |
Tuiles de seuil obsolètes. condition.severity (op ∈ gte | eq | in ; value ou values, sévérités CRITICAL, HIGH, MEDIUM, LOW, NONE, UNKNOWN), condition.cvss et condition.epss (op numérique ; value 0 → 10 ou 0 → 1) faisaient doublon avec la politique de priorisation du projet (réglages du projet, onglet Priorisation) : le « à partir de quel score » s’y règle désormais. Elles ne figurent plus dans la palette, mais un graphe qui les contient se valide et s’exécute toujours à l’identique ; l’éditeur les marque « Obsolète » et propose de les remplacer par condition.priorite.
Statuts de suivi : PENDING, ANALYZING, FIXING, MITIGATED (atténuée), ACCEPTED, RESOLVED, NOT_APPLICABLE. condition.case_status les accepte tous. action.create_case n’accepte pas MITIGATED, et écarte ACCEPTED à l’exécution (le journal le dit) : ces statuts exigent une mesure, une justification et une échéance, posées par une personne.
Actions (action.*)
Section intitulée « Actions (action.*) »Les actions s’exécutent quand le flux les atteint. Elles ont une sortie out et peuvent s’enchaîner.
| Type | Paramètres | Effet |
|---|---|---|
action.notify_inapp |
(aucun) | Notification dans l’application. |
action.send_email |
recipients (optionnel) : restreint aux IDs membres listés ; vide = tous les membres concernés. ai_enrich (booléen, optionnel) et ai_instruction (≤ 500) : cf. ci-dessous. |
E-mail. |
action.fire_webhooks |
webhook_id (optionnel) : un webhook précis, vide = tous les webhooks actifs ; format ∈ json | slack | discord | teams ; ai_enrich / ai_instruction comme ci-dessus. |
Webhook sortant signé (voir Signature des webhooks), envoyé après enregistrement, une fois par événement du projet. |
action.create_case |
status ∈ statut de suivi, sauf MITIGATED (requis). |
Crée une fiche d’alerte (suivi), seulement si le flux atteint la tuile. Nécessaire pour qu’une alerte apparaisse dans « Alertes » et que l’e-mail contienne le bouton « Voir l’alerte ». |
action.add_tag |
tag_id (UUID, requis). |
Ajoute un tag à la CVE dans le projet (auto-classement). |
Enrichissement par IA
Section intitulée « Enrichissement par IA »ai_enrich: true sur une tuile action.send_email ou action.fire_webhooks joint au message une analyse courte : niveau de risque, pourquoi, premières étapes de remédiation. ai_instruction (≤ 500 caractères) permet d’orienter la rédaction — « focus remédiation Debian », « réponds en anglais ».
L’analyse est produite à partir des seules données techniques publiques de l’événement. Ni l’identité des utilisateurs, ni le nom du projet ne font partie de la requête, et toute adresse e-mail est expurgée, y compris depuis votre consigne.
Format Teams
Section intitulée « Format Teams »format: "teams" produit une Adaptive Card dans l’enveloppe attachments attendue par un webhook Teams (Power Automate). Depuis le retrait des connecteurs Office 365, la MessageCard historique n’est plus rendue — c’est ce format qu’il faut utiliser.
Signature des webhooks
Section intitulée « Signature des webhooks »Chaque appel est signé avec le secret du webhook, montré une seule fois à sa création (et à chaque régénération) :
X-TWA-Timestamp: l’heure d’envoi, en secondes Unix ;X-TWA-Signature:sha256=+ HMAC-SHA256 hexadécimal deX-TWA-Timestamp + "." + corps brut.
cve.exploitee part une seule fois par CVE et par projet. cvss_score et epss_score valent null quand le score n’est pas publié : ce n’est pas un zéro.
{ "event": "cve.exploitee", "project_id": "…", "triggered_at": "2026-10-13T18:00:00+00:00", "cve_id": "CVE-2026-0001", "severity": "CRITICAL", "cvss_score": null, "epss_score": null, "has_poc": false, "is_kev": true, "rancongiciel": false, "description": "…", "matched_type": "product", "matched_value": "openssl"}attendu = "sha256=" + hmac.new(secret.encode(), f"{ts}.".encode() + corps_brut, hashlib.sha256).hexdigest()assert hmac.compare_digest(attendu, signature) and abs(time.time() - int(ts)) < 300La comparaison refuse un horodatage écarté de cinq minutes. Une copie renvoyée dans ce délai se vérifie encore : l’envoi ne garde pas les signatures déjà parties.
L’appel part après l’enregistrement de l’événement, une seule fois par projet même si le workflow tourne pour chaque membre ; une erreur passagère (délai, 429, 5xx) est rejouée avec un délai croissant. Le journal d’exécution affiche queued (en file d’attente), puis sent ou error.
Construire un workflow, de A à Z
Section intitulée « Construire un workflow, de A à Z »1. Le minimum : un déclencheur seul
Section intitulée « 1. Le minimum : un déclencheur seul »Valide (un workflow doit avoir exactement un déclencheur), mais n’agit pas.
{ "nodes": [ { "id": "trigger", "type": "trigger.cve_new_match", "params": {} } ], "edges": []}2. CVE au-dessus du seuil → Slack
Section intitulée « 2. CVE au-dessus du seuil → Slack »« Quand une nouvelle CVE de ma stack dépasse le seuil d’alerte du projet, notifie Slack. » Le seuil lui-même (CVSS 7, 9…) se règle dans la politique de priorisation du projet.
{ "nodes": [ { "id": "trigger", "type": "trigger.cve_new_match", "params": {} }, { "id": "c1", "type": "condition.priorite", "params": { "critere": "au_dessus_seuil" } }, { "id": "a1", "type": "action.fire_webhooks", "params": { "format": "slack" } } ], "edges": [ { "id": "e1", "source": "trigger", "target": "c1", "sourceHandle": "out" }, { "id": "e2", "source": "c1", "target": "a1", "sourceHandle": "true" } ]}La connexion c1 → a1 utilise sourceHandle: "true" : l’action ne se déclenche que si la condition est vraie.
3. Branche true / false
Section intitulée « 3. Branche true / false »« KEV → fiche d’alerte + e-mail ; sinon → notification in-app simple. »
{ "nodes": [ { "id": "trigger", "type": "trigger.cve_new_match", "params": {} }, { "id": "kev", "type": "condition.is_kev", "params": {} }, { "id": "case", "type": "action.create_case", "params": { "status": "PENDING" } }, { "id": "mail", "type": "action.send_email", "params": {} }, { "id": "inapp", "type": "action.notify_inapp", "params": {} } ], "edges": [ { "id": "e1", "source": "trigger", "target": "kev", "sourceHandle": "out" }, { "id": "e2", "source": "kev", "target": "case", "sourceHandle": "true" }, { "id": "e3", "source": "case", "target": "mail", "sourceHandle": "out" }, { "id": "e4", "source": "kev", "target": "inapp", "sourceHandle": "false" } ]}Les actions s’enchaînent (case → mail via out). La branche false mène à une notification légère.
4. E-mail enrichi par l’IA
Section intitulée « 4. E-mail enrichi par l’IA »{ "nodes": [ { "id": "trigger", "type": "trigger.cve_kev_added", "params": {} }, { "id": "mail", "type": "action.send_email", "params": { "ai_enrich": true, "ai_instruction": "Priorise la remédiation." } } ], "edges": [ { "id": "e1", "source": "trigger", "target": "mail", "sourceHandle": "out" } ]}L’analyse est une propriété du message, pas une étape du graphe : plus rien à ordonner.
5. Fin de vie qui approche → e-mail
Section intitulée « 5. Fin de vie qui approche → e-mail »{ "nodes": [ { "id": "trigger", "type": "trigger.eol_approaching", "params": {} }, { "id": "c1", "type": "condition.eol_days_remaining", "params": { "op": "lte", "value": 90 } }, { "id": "a1", "type": "action.send_email", "params": {} } ], "edges": [ { "id": "e1", "source": "trigger", "target": "c1", "sourceHandle": "out" }, { "id": "e2", "source": "c1", "target": "a1", "sourceHandle": "true" } ]}Conformité NIS2 (conformite)
Section intitulée « Conformité NIS2 (conformite) »Quand NIS2 est activée sur un projet, ses avis de conformité (échéances de correction, fin de vie, revues d’inventaire, risques acceptés, et côté organisation : incidents, fournisseurs, gouvernance) partent par des workflows créés à l’activation. Ils se modifient comme les autres ; leurs tuiles propres :
| Type | Portée | Paramètres |
|---|---|---|
trigger.conformite_projet |
workflow de projet | evenements (requis, liste) parmi conformite.echeance_proche, conformite.echeance_depassee, conformite.echeance_escaladee, conformite.fin_de_vie_depassee, conformite.fin_de_vie_escaladee, conformite.revue_due, conformite.risque_a_approuver, conformite.risque_decide, conformite.risque_a_revoir. |
trigger.conformite_org |
workflow d’organisation | evenements (requis, liste) parmi conformite.incident_echeance, conformite.fournisseur_a_evaluer, conformite.rapport_pret, conformite.fonction_attribuee. |
condition.evenement_conformite |
les deux | evenements (requis, liste) — vrai si l’avis est l’un d’eux. |
Après un déclencheur de conformité, action.notify_inapp et action.send_email prennent destinataires (liste) : prevus (la personne que l’avis concerne — porteur, demandeur, approbateur… — à défaut les administrateurs), rssi, direction, incident, admins_projet, admins_org. Vide = prevus. recipients ajoute des membres de l’organisation à l’e-mail. action.fire_webhooks n’existe que dans un workflow de projet. Une personne ne reçoit jamais deux fois le même avis par le même canal, même si deux workflows l’y destinent.
{ "nodes": [ { "id": "trigger", "type": "trigger.conformite_projet", "params": { "evenements": ["conformite.echeance_escaladee"] } }, { "id": "mail", "type": "action.send_email", "params": { "destinataires": ["rssi", "direction"] } } ], "edges": [{ "id": "e1", "source": "trigger", "target": "mail", "sourceHandle": "out" }]}Erreurs de validation courantes
Section intitulée « Erreurs de validation courantes »| Message | Cause |
|---|---|
Le workflow doit contenir exactement un déclencheur |
Zéro ou plusieurs tuiles trigger.*. |
Sortie '…' invalide pour … |
sourceHandle incohérent (ex. out sur une condition, ou true sur une action). |
Le workflow contient une boucle |
Le graphe n’est pas acyclique. |
Un déclencheur ne peut pas avoir d'entrée |
Une connexion pointe vers le trigger.*. |
Paramètre requis '…' manquant pour … |
Paramètre obligatoire absent (ex. critere sur condition.priorite). |
Valeur '…' invalide pour … |
Valeur hors de la liste autorisée (sévérité, statut, opérateur…). |
Valeur hors bornes pour … |
Nombre hors plage (CVSS 0–10, EPSS 0–1, jours −3650–3650). |
La tuile … n'est pas disponible dans un workflow d'organisation |
Tuile réservée aux projets (ou l’inverse), ex. action.fire_webhooks dans un workflow d’organisation. |
Trop de tuiles (max 30) / Workflow trop profond (max 20) |
Graphe trop grand. |
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Le plus simple : décrivez votre besoin à Vigie (assistant IA) sur la page Workflows — elle génère le graphe, que vous approuvez.
- L’éditeur visuel produit et relit ce même JSON ; l’export d’un workflow donne un fichier
{ name, description, graph }réimportable. - Voir aussi : Workflows (API).